From 9b7805a3ebc7ab3c4500290df889abedbfd377fc Mon Sep 17 00:00:00 2001 From: Sebastian Chlad Date: Tue, 27 May 2014 11:21:58 +0200 Subject: [PATCH] Imported Upstream version 5.19 --- AUTHORS | 7 + ChangeLog | 64 +- Makefile.am | 67 +- Makefile.in | 2445 +++++++++++++--- Makefile.plugins | 3 +- Makefile.tools | 206 +- TODO | 57 +- android/Android.mk | 602 +++- android/Makefile.am | 217 +- android/README | 301 +- android/a2dp.c | 1366 ++++++++- android/a2dp.h | 22 +- android/android-tester.c | 4827 +++++++++++++++++++++++++++++++ android/audio-ipc-api.txt | 87 + android/audio-msg.h | 81 + android/audio_utils/resampler.c | 270 ++ android/audio_utils/resampler.h | 109 + android/avctp.c | 1629 +++++++++++ android/avctp.h | 179 ++ android/avdtp.c | 214 +- android/avdtp.h | 15 +- android/avrcp-lib.c | 2910 +++++++++++++++++++ android/avrcp-lib.h | 336 +++ android/avrcp.c | 1097 +++++++ android/avrcp.h | 28 + android/bite-rfcomm.txt | 35 + android/bite-spp.txt | 19 + android/bluetooth.c | 2957 +++++++++++++++---- android/bluetooth.h | 47 +- android/bluetoothd-snoop.c | 251 ++ android/client/haltest.c | 24 +- android/client/history.c | 4 +- android/client/hwmodule.c | 27 - android/client/if-audio.c | 518 ++++ android/client/if-av.c | 4 +- android/client/if-bt.c | 56 +- android/client/if-gatt.c | 102 +- android/client/if-hh.c | 32 +- android/client/if-hl.c | 261 ++ android/client/if-main.h | 34 +- android/client/if-pan.c | 8 +- android/client/if-rc.c | 399 +++ android/client/if-sco.c | 521 ++++ android/client/if-sock.c | 15 +- android/client/tabcompletion.c | 20 +- android/cutils/properties.h | 54 +- android/gatt.c | 5226 ++++++++++++++++++++++++++++++++++ android/gatt.h | 25 + android/hal-a2dp.c | 7 +- android/hal-audio.c | 1898 ++++++++++++ android/hal-avrcp.c | 686 +++++ android/hal-bluetooth.c | 166 +- android/hal-gatt.c | 1354 +++++++++ android/hal-handsfree.c | 594 ++++ android/hal-health.c | 250 ++ android/hal-hidhost.c | 80 +- android/hal-ipc-api.txt | 1013 ++++++- android/hal-ipc.c | 43 +- android/hal-log.h | 2 +- android/hal-msg.h | 1106 ++++++- android/hal-pan.c | 25 +- android/hal-sco.c | 913 ++++++ android/{hal-sock.c => hal-socket.c} | 74 +- android/hal-utils.c | 2 - android/hal-utils.h | 2 +- android/hal.h | 12 +- android/handsfree.c | 2681 +++++++++++++++++ android/handsfree.h | 25 + android/hardware/audio.h | 564 ++++ android/hardware/audio_effect.h | 1009 +++++++ android/hardware/hardware.c | 124 + android/health.c | 126 + android/health.h | 25 + android/hidhost.c | 470 +-- android/hidhost.h | 22 +- android/init.bluetooth.rc | 38 + android/ipc-common.h | 38 + android/ipc-tester.c | 1261 ++++++++ android/ipc.c | 311 +- android/ipc.h | 53 +- android/main.c | 184 +- android/mcap-lib.c | 3177 +++++++++++++++++++++ android/mcap-lib.h | 437 +++ android/pan.c | 601 +++- android/pan.h | 22 +- android/pics-a2dp.txt | 86 + android/pics-avctp.txt | 75 + android/pics-avrcp.txt | 626 ++++ android/pics-did.txt | 23 +- android/pics-gap.txt | 238 +- android/pics-gatt.txt | 322 +++ android/pics-hdp.txt | 307 ++ android/pics-hfp.txt | 217 ++ android/pics-hid.txt | 7 +- android/pics-hsp.txt | 103 + android/pics-iopt.txt | 223 ++ android/pics-l2cap.txt | 159 ++ android/pics-map.txt | 175 ++ android/pics-mcap.txt | 141 + android/pics-mps.txt | 339 +++ android/pics-opp.txt | 118 +- android/pics-pan.txt | 49 +- android/pics-pbap.txt | 28 +- android/pics-rfcomm.txt | 54 + android/pics-sdp.txt | 137 + android/pics-sm.txt | 91 + android/pics-spp.txt | 64 + android/pixit-a2dp.txt | 30 + android/pixit-avctp.txt | 39 + android/pixit-avrcp.txt | 36 + android/pixit-did.txt | 24 + android/pixit-gap.txt | 57 + android/pixit-gatt.txt | 31 + android/pixit-hdp.txt | 32 + android/pixit-hfp.txt | 38 + android/pixit-hid.txt | 30 + android/pixit-hsp.txt | 30 + android/pixit-iopt.txt | 23 + android/pixit-l2cap.txt | 41 + android/pixit-map.txt | 47 + android/pixit-mcap.txt | 37 + android/pixit-mps.txt | 46 + android/pixit-opp.txt | 27 + android/pixit-pan.txt | 30 + android/pixit-pbap.txt | 35 + android/pixit-sm.txt | 70 + android/pts-a2dp.txt | 60 + android/pts-avctp.txt | 42 + android/pts-avrcp.txt | 199 ++ android/pts-did.txt | 20 + android/pts-gap.txt | 193 ++ android/pts-gatt.txt | 119 + android/pts-hdp.txt | 72 + android/pts-hfp.txt | 248 ++ android/pts-hid.txt | 76 + android/pts-hsp.txt | 41 + android/pts-iopt.txt | 23 + android/pts-l2cap.txt | 165 ++ android/pts-map.txt | 98 + android/pts-mcap.txt | 77 + android/pts-mps.txt | 53 + android/pts-opp.txt | 133 + android/pts-pan.txt | 82 + android/pts-pbap.txt | 143 + android/pts-sm.txt | 63 + android/sco-msg.h | 36 + android/socket.c | 927 +++--- android/socket.h | 24 +- android/system-emulator.c | 98 +- android/system/audio.h | 609 ++++ android/test-ipc.c | 577 ++++ android/utils.h | 21 +- attrib/att.c | 357 +-- attrib/att.h | 99 - attrib/gatt-service.c | 53 +- attrib/gatt.c | 360 ++- attrib/gatt.h | 66 +- attrib/gattrib.c | 20 +- attrib/gatttool.c | 60 +- attrib/interactive.c | 60 +- attrib/utils.c | 3 +- btio/btio.c | 224 +- client/agent.c | 26 +- client/main.c | 119 +- config.h.in | 3 + configure | 214 +- configure.ac | 23 +- doc/media-api.txt | 38 + doc/mgmt-api.txt | 858 +++++- doc/obex-api.txt | 26 +- doc/settings-storage.txt | 228 ++ doc/test-coverage.txt | 67 + emulator/amp.c | 6 +- emulator/btdev.c | 950 +++++- emulator/bthost.c | 1108 ++++++- emulator/bthost.h | 48 +- emulator/hfp.c | 119 + emulator/le.c | 809 ++++++ emulator/le.h | 32 + emulator/main.c | 45 +- emulator/server.c | 20 +- emulator/server.h | 20 +- emulator/smp.c | 276 ++ emulator/vhci.c | 20 +- emulator/vhci.h | 20 +- gdbus/client.c | 118 +- gdbus/gdbus.h | 9 +- gdbus/mainloop.c | 15 +- gdbus/object.c | 2 + gobex/gobex-header.c | 9 +- gobex/gobex-header.h | 1 + gobex/gobex-transfer.c | 9 +- gobex/gobex.c | 251 +- gobex/gobex.h | 3 + lib/bluetooth.c | 120 +- lib/mgmt.h | 71 +- lib/sdp.c | 38 +- lib/sdp.h | 10 +- lib/sdp_lib.h | 1 + lib/uuid.c | 45 +- lib/uuid.h | 37 + monitor/analyze.c | 325 +++ monitor/analyze.h | 25 + monitor/bt.h | 316 +- monitor/btsnoop.c | 423 --- monitor/btsnoop.h | 57 - monitor/control.c | 134 +- monitor/control.h | 22 +- monitor/crc.c | 4 +- monitor/crc.h | 4 +- monitor/display.c | 22 +- monitor/display.h | 22 +- monitor/ellisys.c | 162 ++ monitor/ellisys.h | 30 + monitor/hcidump.c | 22 +- monitor/hcidump.h | 22 +- monitor/hwdb.c | 137 + monitor/hwdb.h | 29 + monitor/keys.c | 144 + monitor/keys.h | 35 + monitor/l2cap.c | 296 +- monitor/l2cap.h | 22 +- monitor/ll.c | 60 +- monitor/ll.h | 22 +- monitor/lmp.c | 758 ++++- monitor/lmp.h | 24 +- monitor/main.c | 72 +- monitor/mainloop.c | 42 +- monitor/mainloop.h | 24 +- monitor/packet.c | 1030 +++++-- monitor/packet.h | 27 +- monitor/rfcomm.h | 80 + monitor/sdp.c | 85 +- monitor/sdp.h | 22 +- monitor/uuid.c | 22 +- monitor/uuid.h | 22 +- monitor/vendor.c | 22 +- monitor/vendor.h | 22 +- obexd/client/bluetooth.c | 3 +- obexd/client/session.c | 34 +- obexd/client/transfer.c | 104 +- obexd/plugins/bluetooth.c | 2 +- obexd/plugins/pbap.c | 4 +- obexd/plugins/phonebook.h | 2 +- obexd/src/manager.c | 2 +- obexd/src/obex.c | 97 +- plugins/autopair.c | 37 +- plugins/external-dummy.c | 4 +- plugins/gatt-example.c | 102 +- plugins/hostname.c | 8 +- plugins/neard.c | 67 +- plugins/policy.c | 286 ++ plugins/sixaxis.c | 70 +- plugins/wiimote.c | 11 +- profiles/alert/server.c | 58 +- profiles/audio/a2dp.c | 4 +- profiles/audio/avctp.c | 99 +- profiles/audio/avctp.h | 29 + profiles/audio/avdtp.c | 7 +- profiles/audio/avrcp.c | 319 ++- profiles/audio/avrcp.h | 1 + profiles/audio/control.c | 11 +- profiles/audio/media.c | 7 +- profiles/audio/player.c | 53 +- profiles/audio/sink.c | 6 +- profiles/audio/source.c | 6 +- profiles/audio/transport.c | 5 +- profiles/cyclingspeed/cyclingspeed.c | 110 +- profiles/deviceinfo/deviceinfo.c | 23 +- profiles/gatt/gas.c | 75 +- profiles/health/hdp.c | 18 +- profiles/health/hdp_main.c | 4 +- profiles/health/hdp_manager.c | 15 +- profiles/health/hdp_util.c | 25 +- profiles/health/mcap.c | 6 +- profiles/health/mcap_sync.c | 11 +- profiles/heartrate/heartrate.c | 90 +- profiles/input/device.c | 859 +++++- profiles/input/device.h | 1 + profiles/input/hidp_defs.h | 79 + profiles/input/hog.c | 242 +- profiles/input/input.conf | 4 + profiles/input/manager.c | 35 +- profiles/input/server.c | 67 +- profiles/input/suspend-dummy.c | 2 +- profiles/network/bnep.c | 341 ++- profiles/network/bnep.h | 29 +- profiles/network/connection.c | 68 +- profiles/network/manager.c | 15 +- profiles/network/server.c | 137 +- profiles/network/server.h | 1 - profiles/proximity/immalert.c | 17 +- profiles/proximity/linkloss.c | 18 +- profiles/proximity/main.c | 5 +- profiles/proximity/manager.c | 8 +- profiles/proximity/monitor.c | 27 +- profiles/proximity/reporter.c | 28 +- profiles/sap/main.c | 2 +- profiles/sap/manager.c | 10 +- profiles/sap/sap-dummy.c | 6 +- profiles/sap/sap-u8500.c | 2 +- profiles/sap/server.c | 15 +- profiles/scanparam/scan.c | 62 +- profiles/thermometer/thermometer.c | 79 +- profiles/time/server.c | 25 +- src/adapter.c | 877 ++++-- src/adapter.h | 32 +- src/agent.c | 49 +- src/attrib-server.c | 157 +- src/attrib-server.h | 1 + src/bluetooth.conf | 1 + src/device.c | 878 ++++-- src/device.h | 37 +- src/eir.c | 42 +- src/eir.h | 17 +- src/gatt-dbus.c | 658 +++++ src/gatt-dbus.h | 25 + src/gatt.c | 320 +++ src/gatt.h | 121 + src/hcid.h | 2 + src/main.c | 43 +- src/main.conf | 9 + src/plugin.c | 10 +- src/profile.c | 129 +- src/sdp-client.c | 24 +- src/sdp-client.h | 2 +- src/sdpd-request.c | 40 +- src/sdpd-service.c | 19 +- src/shared/btsnoop.c | 295 +- src/shared/btsnoop.h | 22 +- src/shared/crypto.c | 470 +++ src/shared/crypto.h | 48 + src/shared/gatt-db.c | 735 +++++ src/shared/gatt-db.h | 93 + src/shared/hci.c | 618 ++++ src/shared/hci.h | 53 + src/shared/hciemu.c | 71 +- src/shared/hciemu.h | 2 +- src/shared/hfp.c | 819 ++++++ src/shared/hfp.h | 126 + src/shared/io-glib.c | 334 +++ src/shared/io-mainloop.c | 304 ++ src/shared/io.h | 45 + src/shared/mgmt.c | 528 ++-- src/shared/mgmt.h | 2 +- src/shared/pcap.c | 10 +- src/shared/pcap.h | 2 +- src/shared/queue.c | 346 +++ src/shared/queue.h | 56 + src/shared/ringbuf.c | 312 ++ src/shared/ringbuf.h | 50 + src/shared/tester.c | 32 +- src/shared/tester.h | 2 +- src/shared/timeout-glib.c | 78 + src/shared/timeout-mainloop.c | 86 + src/shared/timeout.h | 27 + src/shared/util.c | 2 +- src/shared/util.h | 133 +- src/storage.c | 2 +- src/storage.h | 2 - src/textfile.h | 5 - src/{glib-helper.c => uuid-helper.c} | 52 +- src/{glib-helper.h => uuid-helper.h} | 0 test/ftp-client | 178 ++ test/map-client | 232 ++ test/monitor-bluetooth | 8 +- test/opp-client | 119 + test/pbap-client | 175 ++ test/simple-agent | 8 +- test/simple-endpoint | 7 +- test/simple-player | 10 +- test/simple-service | 128 - test/test-adapter | 4 +- test/test-alert | 15 +- test/test-cyclingspeed | 13 +- test/test-device | 10 +- test/test-discovery | 8 +- test/test-health | 11 +- test/test-health-sink | 9 +- test/test-heartrate | 10 +- test/test-hfp | 12 +- test/test-manager | 6 +- test/test-nap | 2 +- test/test-network | 2 +- test/test-profile | 8 +- test/test-proximity | 10 +- test/test-thermometer | 10 +- tools/3dsp.c | 478 ++++ tools/amptest.c | 12 +- tools/avinfo.c | 7 + tools/bccmd.c | 2 +- tools/bdaddr.c | 43 +- tools/bluemoon.c | 541 ++++ tools/bluetooth-player.c | 6 + tools/btinfo.c | 394 +-- tools/btiotest.c | 12 +- tools/btmgmt.c | 593 +++- tools/btproxy.c | 713 +++++ tools/btsnoop.c | 47 +- tools/cltest.c | 5 +- tools/csr_usb.c | 1 + tools/gatt-service.c | 538 ++++ tools/hci-tester.c | 673 +++++ tools/hciattach.c | 11 +- tools/hciattach.h | 2 + tools/hciattach_bcm43xx.c | 378 +++ tools/hciattach_qualcomm.c | 1 + tools/hciattach_tialt.c | 1 + tools/hciconfig.c | 29 +- tools/hcidump.c | 81 +- tools/hcitool.c | 110 +- tools/hex2hcd.c | 143 + tools/hid2hci.c | 1 + tools/ibeacon.c | 312 ++ tools/l2cap-tester.c | 838 +++++- tools/l2test.c | 16 +- tools/mgmt-tester.c | 972 ++++++- tools/obex-client-tool.c | 2 +- tools/obex-server-tool.c | 2 +- tools/obexctl.c | 143 +- tools/parser/avrcp.c | 4 + tools/parser/hci.c | 2 +- tools/parser/l2cap.c | 24 +- tools/parser/parser.h | 7 +- tools/parser/ppp.c | 2 +- tools/rctest.c | 11 +- tools/rfcomm-tester.c | 762 +++++ tools/sco-tester.c | 10 +- tools/scotest.c | 10 +- tools/sdptool.c | 8 +- tools/seq2bseq.c | 212 ++ tools/smp-tester.c | 313 +- unit/test-avctp.c | 339 +++ unit/test-avdtp.c | 268 +- unit/test-avrcp.c | 1627 +++++++++++ unit/test-eir.c | 38 +- unit/test-gdbus-client.c | 59 + unit/test-gobex-apparam.c | 2 + unit/test-gobex-transfer.c | 163 +- unit/test-gobex.c | 149 +- unit/test-hfp.c | 470 +++ unit/test-lib.c | 21 + unit/test-queue.c | 68 + unit/test-ringbuf.c | 154 + unit/test-sdp.c | 12 +- unit/test-uuid.c | 13 +- unit/util.c | 1 + unit/util.h | 1 + 448 files changed, 84916 insertions(+), 8550 deletions(-) create mode 100644 android/android-tester.c create mode 100644 android/audio-ipc-api.txt create mode 100644 android/audio-msg.h create mode 100644 android/audio_utils/resampler.c create mode 100644 android/audio_utils/resampler.h create mode 100644 android/avctp.c create mode 100644 android/avctp.h create mode 100644 android/avrcp-lib.c create mode 100644 android/avrcp-lib.h create mode 100644 android/avrcp.c create mode 100644 android/avrcp.h create mode 100644 android/bite-rfcomm.txt create mode 100644 android/bite-spp.txt create mode 100644 android/bluetoothd-snoop.c delete mode 100644 android/client/hwmodule.c create mode 100644 android/client/if-audio.c create mode 100644 android/client/if-hl.c create mode 100644 android/client/if-rc.c create mode 100644 android/client/if-sco.c create mode 100644 android/gatt.c create mode 100644 android/gatt.h create mode 100644 android/hal-audio.c create mode 100644 android/hal-avrcp.c create mode 100644 android/hal-gatt.c create mode 100644 android/hal-handsfree.c create mode 100644 android/hal-health.c create mode 100644 android/hal-sco.c rename android/{hal-sock.c => hal-socket.c} (55%) create mode 100644 android/handsfree.c create mode 100644 android/handsfree.h create mode 100644 android/hardware/audio.h create mode 100644 android/hardware/audio_effect.h create mode 100644 android/hardware/hardware.c create mode 100644 android/health.c create mode 100644 android/health.h create mode 100644 android/init.bluetooth.rc create mode 100644 android/ipc-common.h create mode 100644 android/ipc-tester.c create mode 100644 android/mcap-lib.c create mode 100644 android/mcap-lib.h create mode 100644 android/pics-a2dp.txt create mode 100644 android/pics-avctp.txt create mode 100644 android/pics-avrcp.txt create mode 100644 android/pics-gatt.txt create mode 100644 android/pics-hdp.txt create mode 100644 android/pics-hfp.txt create mode 100644 android/pics-hsp.txt create mode 100644 android/pics-iopt.txt create mode 100644 android/pics-l2cap.txt create mode 100644 android/pics-map.txt create mode 100644 android/pics-mcap.txt create mode 100644 android/pics-mps.txt create mode 100644 android/pics-rfcomm.txt create mode 100644 android/pics-sdp.txt create mode 100644 android/pics-sm.txt create mode 100644 android/pics-spp.txt create mode 100644 android/pixit-a2dp.txt create mode 100644 android/pixit-avctp.txt create mode 100644 android/pixit-avrcp.txt create mode 100644 android/pixit-did.txt create mode 100644 android/pixit-gap.txt create mode 100644 android/pixit-gatt.txt create mode 100644 android/pixit-hdp.txt create mode 100644 android/pixit-hfp.txt create mode 100644 android/pixit-hid.txt create mode 100644 android/pixit-hsp.txt create mode 100644 android/pixit-iopt.txt create mode 100644 android/pixit-l2cap.txt create mode 100644 android/pixit-map.txt create mode 100644 android/pixit-mcap.txt create mode 100644 android/pixit-mps.txt create mode 100644 android/pixit-opp.txt create mode 100644 android/pixit-pan.txt create mode 100644 android/pixit-pbap.txt create mode 100644 android/pixit-sm.txt create mode 100644 android/pts-a2dp.txt create mode 100644 android/pts-avctp.txt create mode 100644 android/pts-avrcp.txt create mode 100644 android/pts-did.txt create mode 100644 android/pts-gap.txt create mode 100644 android/pts-gatt.txt create mode 100644 android/pts-hdp.txt create mode 100644 android/pts-hfp.txt create mode 100644 android/pts-hid.txt create mode 100644 android/pts-hsp.txt create mode 100644 android/pts-iopt.txt create mode 100644 android/pts-l2cap.txt create mode 100644 android/pts-map.txt create mode 100644 android/pts-mcap.txt create mode 100644 android/pts-mps.txt create mode 100644 android/pts-opp.txt create mode 100644 android/pts-pan.txt create mode 100644 android/pts-pbap.txt create mode 100644 android/pts-sm.txt create mode 100644 android/sco-msg.h create mode 100644 android/system/audio.h create mode 100644 android/test-ipc.c create mode 100644 doc/settings-storage.txt create mode 100644 doc/test-coverage.txt create mode 100644 emulator/hfp.c create mode 100644 emulator/le.c create mode 100644 emulator/le.h create mode 100644 emulator/smp.c create mode 100644 monitor/analyze.c create mode 100644 monitor/analyze.h delete mode 100644 monitor/btsnoop.c delete mode 100644 monitor/btsnoop.h create mode 100644 monitor/ellisys.c create mode 100644 monitor/ellisys.h create mode 100644 monitor/hwdb.c create mode 100644 monitor/hwdb.h create mode 100644 monitor/keys.c create mode 100644 monitor/keys.h create mode 100644 monitor/rfcomm.h create mode 100644 profiles/input/hidp_defs.h create mode 100644 src/gatt-dbus.c create mode 100644 src/gatt-dbus.h create mode 100644 src/gatt.c create mode 100644 src/gatt.h create mode 100644 src/shared/crypto.c create mode 100644 src/shared/crypto.h create mode 100644 src/shared/gatt-db.c create mode 100644 src/shared/gatt-db.h create mode 100644 src/shared/hci.c create mode 100644 src/shared/hci.h create mode 100644 src/shared/hfp.c create mode 100644 src/shared/hfp.h create mode 100644 src/shared/io-glib.c create mode 100644 src/shared/io-mainloop.c create mode 100644 src/shared/io.h create mode 100644 src/shared/queue.c create mode 100644 src/shared/queue.h create mode 100644 src/shared/ringbuf.c create mode 100644 src/shared/ringbuf.h create mode 100644 src/shared/timeout-glib.c create mode 100644 src/shared/timeout-mainloop.c create mode 100644 src/shared/timeout.h rename src/{glib-helper.c => uuid-helper.c} (87%) rename src/{glib-helper.h => uuid-helper.h} (100%) create mode 100755 test/ftp-client create mode 100755 test/map-client create mode 100755 test/opp-client create mode 100755 test/pbap-client delete mode 100755 test/simple-service create mode 100644 tools/3dsp.c create mode 100644 tools/bluemoon.c create mode 100644 tools/btproxy.c create mode 100644 tools/gatt-service.c create mode 100644 tools/hci-tester.c create mode 100644 tools/hciattach_bcm43xx.c create mode 100644 tools/hex2hcd.c create mode 100644 tools/ibeacon.c create mode 100644 tools/rfcomm-tester.c create mode 100644 tools/seq2bseq.c create mode 100644 unit/test-avctp.c create mode 100644 unit/test-avrcp.c create mode 100644 unit/test-hfp.c create mode 100644 unit/test-queue.c create mode 100644 unit/test-ringbuf.c diff --git a/AUTHORS b/AUTHORS index 4cb3d63..085e319 100644 --- a/AUTHORS +++ b/AUTHORS @@ -80,3 +80,10 @@ Takashi Sasai Andre Dieb Martins Cristian Rodríguez Alex Deymo +Petri Gynther +Scott James Remnant +Jakub Tyszkowski +Grzegorz Kołodziejczyk +Marcin Krąglak +Łukasz Rymanowski +Jerzy Kasenberg diff --git a/ChangeLog b/ChangeLog index 1db42c9..1c9fd1a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,65 @@ +ver 5.19: + Fix issue with OBEX Put-Delete and Create-Empty methods. + Fix issue with AVRCP browsable/searchable player properties. + Fix issue with handling multiple default agents. + Fix issue with handling unpair event per bearer. + Fix issue with HID over GATT report ID presence. + Add support for HID protocol handling in userspace. + Add support for Bluetooth reconnection policy framework. + Add support for Android Bluetooth SCO over HCI transport. + Add support for Android Bluetooth audio quality control. + Add support for Android Bluetooth Low Energy only mode. + +ver 5.18: + Fix issue with identifying LE single mode devices. + Fix issue with L2CAP and RFCOMM peer address lookup. + Add support for handling OBEX authentication procedure. + Add support for Android Bluetooth GATT client interface. + +ver 5.17: + Fix issue with not resetting OBEX SRM setup. + Fix issue with BR/EDR devices and auto-connect list. + Fix issue with bonding complete detection as peripheral. + Fix issue with not updating bearer timestamp of connections. + Fix issue with paired property for multiple bearers. + Add support for Android Bluetooth Handsfree interface. + Add support for Android Bluetooth Wideband speech. + +ver 5.16: + Fix issue with HID over GATT physical location. + Fix issue with HID over GATT unique identifier. + Fix issue with missing paired property notification. + Fix issue with endianess of long term key storage. + Add support for storing signature resolving keys. + Add support for Android Bluetooth AVRCP interface. + +ver 5.15: + Fix issue with LE enabling and background scanning. + Fix issue with HID over GATT input device name. + Fix issue with storage of slave long term keys. + Add support for handling identity resolving keys. + Add support for Android Bluetooth A2DP interface. + Add support for Android Bluetooth audio interface. + +ver 5.14: + Fix issue with marking PS3 controllers as trusted. + Fix issue with authorization of PS3 controllers. + Add support for DualShock 4 controller detection. + Add support for legacy pairing emulation. + Add support for secure simple pairing emulation. + Add support for automated pairing testing. + Add support for RFCOMM protocol testing. + Add support for HCI controller testing. + +ver 5.13: + Fix issue with PS3 controller detection. + Add support for data transfers to L2CAP testing tool. + Add support for delay reporting to AVDTP testing tool. + Add support for Android Bluetooth Core interface. + Add support for Android Bluetooth Socket interface. + Add support for Android Bluetooth HID Host interface. + Add support for Android Bluetooth PAN interface. + ver 5.12: Fix issue with missing reply to DisconnectProfile. Fix issue with icon property and class of device changes. @@ -8,7 +70,7 @@ ver 5.12: Add support for LE L2CAP CoC test capabilities. Add support for AVDTP qualification test cases. Add support for SMP cryptographic test cases. -s + ver 5.11: Fix issue with connection attempt when not powered. Fix issue with assigning player to AVRCP target role. diff --git a/Makefile.am b/Makefile.am index 81a458b..14e735d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,7 +80,7 @@ include_HEADERS += $(lib_headers) lib_LTLIBRARIES += lib/libbluetooth.la lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) -lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:3:17 +lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:9:17 lib_libbluetooth_la_DEPENDENCIES = $(local_headers) endif @@ -137,7 +137,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \ src/sdp-xml.h src/sdp-xml.c \ src/sdp-client.h src/sdp-client.c \ src/textfile.h src/textfile.c \ - src/glib-helper.h src/glib-helper.c \ + src/uuid-helper.h src/uuid-helper.c \ src/uinput.h \ src/plugin.h src/plugin.c \ src/storage.h src/storage.c \ @@ -146,9 +146,14 @@ src_bluetoothd_SOURCES = $(builtin_sources) \ src/adapter.h src/adapter.c \ src/profile.h src/profile.c \ src/service.h src/service.c \ + src/gatt-dbus.h src/gatt-dbus.c \ + src/gatt.h src/gatt.c \ src/device.h src/device.c src/attio.h \ src/dbus-common.c src/dbus-common.h \ src/eir.h src/eir.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/timeout.h src/shared/timeout-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c src_bluetoothd_LDADD = lib/libbluetooth-internal.la gdbus/libgdbus-internal.la \ @@ -176,6 +181,7 @@ EXTRA_DIST += src/genbuiltin src/bluetooth.conf \ profiles/input/input.conf profiles/proximity/proximity.conf test_scripts = +unit_tests = include Makefile.tools include Makefile.obexd @@ -198,7 +204,8 @@ endif EXTRA_DIST += $(test_scripts) -EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt +EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt \ + doc/test-coverage.txt doc/settings-storage.txt EXTRA_DIST += doc/mgmt-api.txt \ doc/adapter-api.txt doc/device-api.txt \ @@ -216,13 +223,12 @@ EXTRA_DIST += tools/magic.btsnoop AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ -AM_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \ - -I$(srcdir)/gdbus -I$(srcdir)/btio +AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus -unit_tests = unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc +unit_tests += unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc -unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/glib-helper.c +unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c unit_test_eir_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ unit_test_uuid_SOURCES = unit/test-uuid.c @@ -234,9 +240,23 @@ unit_test_textfile_LDADD = @GLIB_LIBS@ unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c unit_test_crc_LDADD = @GLIB_LIBS@ +unit_tests += unit/test-ringbuf unit/test-queue + +unit_test_ringbuf_SOURCES = unit/test-ringbuf.c \ + src/shared/util.h src/shared/util.c \ + src/shared/ringbuf.h src/shared/ringbuf.c +unit_test_ringbuf_LDADD = @GLIB_LIBS@ + +unit_test_queue_SOURCES = unit/test-queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c +unit_test_queue_LDADD = @GLIB_LIBS@ + unit_tests += unit/test-mgmt unit_test_mgmt_SOURCES = unit/test-mgmt.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c unit_test_mgmt_LDADD = @GLIB_LIBS@ @@ -246,6 +266,7 @@ unit_tests += unit/test-sdp unit_test_sdp_SOURCES = unit/test-sdp.c \ src/shared/util.h src/shared/util.c \ src/sdpd.h src/sdpd-database.c \ + src/log.h src/log.c \ src/sdpd-service.c src/sdpd-request.c unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @@ -257,6 +278,35 @@ unit_test_avdtp_SOURCES = unit/test-avdtp.c \ android/avdtp.c android/avdtp.h unit_test_avdtp_LDADD = @GLIB_LIBS@ +unit_tests += unit/test-avctp + +unit_test_avctp_SOURCES = unit/test-avctp.c \ + src/shared/util.h src/shared/util.c \ + src/log.h src/log.c \ + android/avctp.c android/avctp.h +unit_test_avctp_LDADD = @GLIB_LIBS@ + +unit_tests += unit/test-avrcp + +unit_test_avrcp_SOURCES = unit/test-avrcp.c \ + src/shared/util.h src/shared/util.c \ + src/log.h src/log.c \ + android/avctp.c android/avctp.h \ + android/avrcp-lib.c android/avrcp-lib.h +unit_test_avrcp_LDADD = @GLIB_LIBS@ lib/libbluetooth-internal.la + +unit_tests += unit/test-hfp + +unit_test_hfp_SOURCES = unit/test-hfp.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/ringbuf.h src/shared/ringbuf.c \ + src/shared/hfp.h src/shared/hfp.c + +unit_test_hfp_LDADD = @GLIB_LIBS@ + unit_tests += unit/test-gdbus-client unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c @@ -302,8 +352,7 @@ pkgconfig_DATA = lib/bluez.pc endif DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \ - --disable-systemd --disable-udev \ - --enable-android + --disable-systemd --disable-udev DISTCLEANFILES = $(pkgconfig_DATA) diff --git a/Makefile.in b/Makefile.in index 9dfabfc..c0c179f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -58,7 +58,7 @@ build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) noinst_PROGRAMS = $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \ - $(am__EXEEXT_7) $(am__EXEEXT_8) + $(am__EXEEXT_7) $(am__EXEEXT_9) libexec_PROGRAMS = src/bluetoothd$(EXEEXT) obexd/src/obexd$(EXEEXT) @LIBRARY_TRUE@am__append_1 = $(lib_headers) @LIBRARY_TRUE@am__append_2 = lib/libbluetooth.la @@ -113,18 +113,20 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \ @CLIENT_TRUE@am__append_14 = client/bluetoothctl @MONITOR_TRUE@am__append_15 = monitor/btmon @EXPERIMENTAL_TRUE@am__append_16 = emulator/btvirt emulator/b1ee \ -@EXPERIMENTAL_TRUE@ tools/mgmt-tester tools/gap-tester \ -@EXPERIMENTAL_TRUE@ tools/l2cap-tester tools/sco-tester \ -@EXPERIMENTAL_TRUE@ tools/smp-tester tools/bdaddr tools/avinfo \ -@EXPERIMENTAL_TRUE@ tools/avtest tools/scotest tools/amptest \ -@EXPERIMENTAL_TRUE@ tools/hwdb tools/hcieventmask \ -@EXPERIMENTAL_TRUE@ tools/hcisecfilter tools/btmgmt \ -@EXPERIMENTAL_TRUE@ tools/btinfo tools/btattach tools/btsnoop \ -@EXPERIMENTAL_TRUE@ tools/btiotest tools/cltest \ -@EXPERIMENTAL_TRUE@ tools/mpris-player +@EXPERIMENTAL_TRUE@ emulator/hfp tools/3dsp tools/mgmt-tester \ +@EXPERIMENTAL_TRUE@ tools/gap-tester tools/l2cap-tester \ +@EXPERIMENTAL_TRUE@ tools/sco-tester tools/smp-tester \ +@EXPERIMENTAL_TRUE@ tools/hci-tester tools/rfcomm-tester \ +@EXPERIMENTAL_TRUE@ tools/bdaddr tools/avinfo tools/avtest \ +@EXPERIMENTAL_TRUE@ tools/scotest tools/amptest tools/hwdb \ +@EXPERIMENTAL_TRUE@ tools/hcieventmask tools/hcisecfilter \ +@EXPERIMENTAL_TRUE@ tools/btmgmt tools/btinfo tools/btattach \ +@EXPERIMENTAL_TRUE@ tools/btsnoop tools/btproxy tools/btiotest \ +@EXPERIMENTAL_TRUE@ tools/mpris-player tools/cltest \ +@EXPERIMENTAL_TRUE@ tools/seq2bseq tools/hex2hcd tools/ibeacon @TOOLS_TRUE@am__append_17 = tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \ @TOOLS_TRUE@ tools/rfcomm tools/rctest tools/l2test tools/l2ping \ -@TOOLS_TRUE@ tools/sdptool tools/ciptool tools/bccmd +@TOOLS_TRUE@ tools/sdptool tools/ciptool tools/bccmd tools/bluemoon @TOOLS_TRUE@am__append_18 = tools/hciattach.1 tools/hciconfig.1 \ @TOOLS_TRUE@ tools/hcitool.1 tools/hcidump.1 \ @@ -144,7 +146,8 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \ @READLINE_TRUE@ tools/obex-client-tool tools/obex-server-tool \ @READLINE_TRUE@ tools/bluetooth-player tools/obexctl -@EXPERIMENTAL_TRUE@am__append_24 = profiles/iap/iapd +@EXPERIMENTAL_TRUE@am__append_24 = tools/gatt-service \ +@EXPERIMENTAL_TRUE@ profiles/iap/iapd @CUPS_TRUE@cups_PROGRAMS = profiles/cups/bluetooth$(EXEEXT) @EXPERIMENTAL_TRUE@am__append_25 = pcsuite @EXPERIMENTAL_TRUE@am__append_26 = obexd/plugins/pcsuite.c @@ -154,10 +157,15 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \ @OBEX_TRUE@ obexd/plugins/phonebook.h \ @OBEX_TRUE@ obexd/plugins/phonebook-dummy.c @ANDROID_TRUE@am__append_29 = android/system-emulator \ -@ANDROID_TRUE@ android/bluetoothd android/haltest -@ANDROID_TRUE@am__append_30 = android/libhal-internal.la -@HID2HCI_TRUE@am__append_31 = $(rules_DATA) -TESTS = $(am__EXEEXT_8) +@ANDROID_TRUE@ android/bluetoothd-snoop android/bluetoothd \ +@ANDROID_TRUE@ android/haltest android/android-tester \ +@ANDROID_TRUE@ android/ipc-tester +@ANDROID_TRUE@am__append_30 = android/bluetooth.default.la \ +@ANDROID_TRUE@ android/audio.sco.default.la \ +@ANDROID_TRUE@ android/audio.a2dp.default.la +@ANDROID_TRUE@am__append_31 = android/test-ipc +@HID2HCI_TRUE@am__append_32 = $(rules_DATA) +TESTS = $(am__EXEEXT_9) subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ @@ -225,31 +233,75 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \ "$(DESTDIR)$(systemduserunitdir)" "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \ $(plugin_LTLIBRARIES) -android_libhal_internal_la_LIBADD = -am__android_libhal_internal_la_SOURCES_DIST = android/hal.h \ - android/hal-bluetooth.c android/hal-sock.c \ - android/hal-hidhost.c android/hal-pan.c android/hal-a2dp.c \ - android/hardware/bluetooth.h android/hardware/bt_av.h \ - android/hardware/bt_gatt.h android/hardware/bt_gatt_client.h \ +android_audio_a2dp_default_la_DEPENDENCIES = +am__android_audio_a2dp_default_la_SOURCES_DIST = android/audio-msg.h \ + android/hal-msg.h android/hal-audio.c android/hardware/audio.h \ + android/hardware/audio_effect.h android/hardware/hardware.h \ + android/system/audio.h +@ANDROID_TRUE@am_android_audio_a2dp_default_la_OBJECTS = android/android_audio_a2dp_default_la-hal-audio.lo +android_audio_a2dp_default_la_OBJECTS = \ + $(am_android_audio_a2dp_default_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +android_audio_a2dp_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) \ + $(android_audio_a2dp_default_la_LDFLAGS) $(LDFLAGS) -o $@ +@ANDROID_TRUE@am_android_audio_a2dp_default_la_rpath = -rpath \ +@ANDROID_TRUE@ $(plugindir) +android_audio_sco_default_la_DEPENDENCIES = +am__android_audio_sco_default_la_SOURCES_DIST = android/hal-log.h \ + android/sco-msg.h android/hal-sco.c android/hardware/audio.h \ + android/hardware/audio_effect.h android/hardware/hardware.h \ + android/audio_utils/resampler.c \ + android/audio_utils/resampler.h android/system/audio.h +@ANDROID_TRUE@am_android_audio_sco_default_la_OBJECTS = \ +@ANDROID_TRUE@ android/android_audio_sco_default_la-hal-sco.lo \ +@ANDROID_TRUE@ android/audio_utils/android_audio_sco_default_la-resampler.lo +android_audio_sco_default_la_OBJECTS = \ + $(am_android_audio_sco_default_la_OBJECTS) +android_audio_sco_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) \ + $(android_audio_sco_default_la_LDFLAGS) $(LDFLAGS) -o $@ +@ANDROID_TRUE@am_android_audio_sco_default_la_rpath = -rpath \ +@ANDROID_TRUE@ $(plugindir) +android_bluetooth_default_la_LIBADD = +am__android_bluetooth_default_la_SOURCES_DIST = android/hal.h \ + android/hal-bluetooth.c android/hal-socket.c \ + android/hal-hidhost.c android/hal-health.c android/hal-pan.c \ + android/hal-a2dp.c android/hal-avrcp.c android/hal-handsfree.c \ + android/hal-gatt.c android/hardware/bluetooth.h \ + android/hardware/bt_av.h android/hardware/bt_gatt.h \ + android/hardware/bt_gatt_client.h \ android/hardware/bt_gatt_server.h \ android/hardware/bt_gatt_types.h android/hardware/bt_hf.h \ android/hardware/bt_hh.h android/hardware/bt_hl.h \ android/hardware/bt_pan.h android/hardware/bt_rc.h \ android/hardware/bt_sock.h android/hardware/hardware.h \ - android/cutils/properties.h android/hal-log.h \ - android/hal-ipc.h android/hal-ipc.c -@ANDROID_TRUE@am_android_libhal_internal_la_OBJECTS = android/android_libhal_internal_la-hal-bluetooth.lo \ -@ANDROID_TRUE@ android/android_libhal_internal_la-hal-sock.lo \ -@ANDROID_TRUE@ android/android_libhal_internal_la-hal-hidhost.lo \ -@ANDROID_TRUE@ android/android_libhal_internal_la-hal-pan.lo \ -@ANDROID_TRUE@ android/android_libhal_internal_la-hal-a2dp.lo \ -@ANDROID_TRUE@ android/android_libhal_internal_la-hal-ipc.lo -android_libhal_internal_la_OBJECTS = \ - $(am_android_libhal_internal_la_OBJECTS) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -@ANDROID_TRUE@am_android_libhal_internal_la_rpath = + android/cutils/properties.h android/ipc-common.h \ + android/hal-log.h android/hal-ipc.h android/hal-ipc.c \ + android/hal-utils.h android/hal-utils.c +@ANDROID_TRUE@am_android_bluetooth_default_la_OBJECTS = android/android_bluetooth_default_la-hal-bluetooth.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-socket.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-hidhost.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-health.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-pan.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-a2dp.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-avrcp.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-handsfree.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-gatt.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-ipc.lo \ +@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-utils.lo +android_bluetooth_default_la_OBJECTS = \ + $(am_android_bluetooth_default_la_OBJECTS) +android_bluetooth_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) \ + $(android_bluetooth_default_la_LDFLAGS) $(LDFLAGS) -o $@ +@ANDROID_TRUE@am_android_bluetooth_default_la_rpath = -rpath \ +@ANDROID_TRUE@ $(plugindir) gdbus_libgdbus_internal_la_LIBADD = am_gdbus_libgdbus_internal_la_OBJECTS = gdbus/mainloop.lo \ gdbus/watch.lo gdbus/object.lo gdbus/client.lo gdbus/polkit.lo @@ -304,14 +356,18 @@ plugins_sixaxis_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ @TOOLS_TRUE@ tools/hcidump$(EXEEXT) tools/rfcomm$(EXEEXT) \ @TOOLS_TRUE@ tools/rctest$(EXEEXT) tools/l2test$(EXEEXT) \ @TOOLS_TRUE@ tools/l2ping$(EXEEXT) tools/sdptool$(EXEEXT) \ -@TOOLS_TRUE@ tools/ciptool$(EXEEXT) tools/bccmd$(EXEEXT) +@TOOLS_TRUE@ tools/ciptool$(EXEEXT) tools/bccmd$(EXEEXT) \ +@TOOLS_TRUE@ tools/bluemoon$(EXEEXT) @EXPERIMENTAL_TRUE@am__EXEEXT_4 = emulator/btvirt$(EXEEXT) \ @EXPERIMENTAL_TRUE@ emulator/b1ee$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ emulator/hfp$(EXEEXT) tools/3dsp$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/mgmt-tester$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/gap-tester$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/l2cap-tester$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/sco-tester$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/smp-tester$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ tools/hci-tester$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ tools/rfcomm-tester$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/bdaddr$(EXEEXT) tools/avinfo$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/avtest$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/scotest$(EXEEXT) \ @@ -321,66 +377,142 @@ plugins_sixaxis_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ @EXPERIMENTAL_TRUE@ tools/btmgmt$(EXEEXT) tools/btinfo$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/btattach$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/btsnoop$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ tools/btproxy$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/btiotest$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ tools/mpris-player$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/cltest$(EXEEXT) \ -@EXPERIMENTAL_TRUE@ tools/mpris-player$(EXEEXT) +@EXPERIMENTAL_TRUE@ tools/seq2bseq$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ tools/hex2hcd$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ tools/ibeacon$(EXEEXT) @READLINE_TRUE@am__EXEEXT_5 = attrib/gatttool$(EXEEXT) \ @READLINE_TRUE@ tools/obex-client-tool$(EXEEXT) \ @READLINE_TRUE@ tools/obex-server-tool$(EXEEXT) \ @READLINE_TRUE@ tools/bluetooth-player$(EXEEXT) \ @READLINE_TRUE@ tools/obexctl$(EXEEXT) -@EXPERIMENTAL_TRUE@am__EXEEXT_6 = profiles/iap/iapd$(EXEEXT) +@EXPERIMENTAL_TRUE@am__EXEEXT_6 = tools/gatt-service$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ profiles/iap/iapd$(EXEEXT) @ANDROID_TRUE@am__EXEEXT_7 = android/system-emulator$(EXEEXT) \ +@ANDROID_TRUE@ android/bluetoothd-snoop$(EXEEXT) \ @ANDROID_TRUE@ android/bluetoothd$(EXEEXT) \ -@ANDROID_TRUE@ android/haltest$(EXEEXT) -am__EXEEXT_8 = unit/test-eir$(EXEEXT) unit/test-uuid$(EXEEXT) \ - unit/test-textfile$(EXEEXT) unit/test-crc$(EXEEXT) \ - unit/test-mgmt$(EXEEXT) unit/test-sdp$(EXEEXT) \ - unit/test-avdtp$(EXEEXT) unit/test-gdbus-client$(EXEEXT) \ +@ANDROID_TRUE@ android/haltest$(EXEEXT) \ +@ANDROID_TRUE@ android/android-tester$(EXEEXT) \ +@ANDROID_TRUE@ android/ipc-tester$(EXEEXT) +@ANDROID_TRUE@am__EXEEXT_8 = android/test-ipc$(EXEEXT) +am__EXEEXT_9 = $(am__EXEEXT_8) unit/test-eir$(EXEEXT) \ + unit/test-uuid$(EXEEXT) unit/test-textfile$(EXEEXT) \ + unit/test-crc$(EXEEXT) unit/test-ringbuf$(EXEEXT) \ + unit/test-queue$(EXEEXT) unit/test-mgmt$(EXEEXT) \ + unit/test-sdp$(EXEEXT) unit/test-avdtp$(EXEEXT) \ + unit/test-avctp$(EXEEXT) unit/test-avrcp$(EXEEXT) \ + unit/test-hfp$(EXEEXT) unit/test-gdbus-client$(EXEEXT) \ unit/test-gobex-header$(EXEEXT) \ unit/test-gobex-packet$(EXEEXT) unit/test-gobex$(EXEEXT) \ unit/test-gobex-transfer$(EXEEXT) \ unit/test-gobex-apparam$(EXEEXT) unit/test-lib$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(cups_PROGRAMS) $(libexec_PROGRAMS) \ $(noinst_PROGRAMS) $(udev_PROGRAMS) +am__android_android_tester_SOURCES_DIST = emulator/btdev.h \ + emulator/btdev.c emulator/bthost.h emulator/bthost.c \ + emulator/smp.c src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c src/shared/queue.h \ + src/shared/queue.c src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \ + src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c \ + monitor/rfcomm.h android/hardware/hardware.c \ + android/android-tester.c +@ANDROID_TRUE@am_android_android_tester_OBJECTS = \ +@ANDROID_TRUE@ emulator/android_android_tester-btdev.$(OBJEXT) \ +@ANDROID_TRUE@ emulator/android_android_tester-bthost.$(OBJEXT) \ +@ANDROID_TRUE@ emulator/android_android_tester-smp.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_android_tester-crypto.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_android_tester-io-glib.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_android_tester-queue.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_android_tester-util.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_android_tester-mgmt.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_android_tester-hciemu.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_android_tester-tester.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_android_tester-timeout-glib.$(OBJEXT) \ +@ANDROID_TRUE@ android/hardware/android_android_tester-hardware.$(OBJEXT) \ +@ANDROID_TRUE@ android/android_android_tester-android-tester.$(OBJEXT) +android_android_tester_OBJECTS = $(am_android_android_tester_OBJECTS) +@ANDROID_TRUE@android_android_tester_DEPENDENCIES = \ +@ANDROID_TRUE@ lib/libbluetooth-internal.la +android_android_tester_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(android_android_tester_CFLAGS) $(CFLAGS) \ + $(android_android_tester_LDFLAGS) $(LDFLAGS) -o $@ am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c \ - android/hal-msg.h android/utils.h src/sdpd-database.c \ - src/sdpd-server.c src/sdpd-service.c src/sdpd-request.c \ - src/glib-helper.h src/glib-helper.c src/eir.h src/eir.c \ + android/hal-msg.h android/audio-msg.h android/sco-msg.h \ + android/utils.h src/sdpd-database.c src/sdpd-server.c \ + src/sdpd-service.c src/sdpd-request.c src/uuid-helper.h \ + src/uuid-helper.c src/eir.h src/eir.c src/shared/io.h \ + src/shared/io-glib.c src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c src/shared/mgmt.h \ - src/shared/mgmt.c android/bluetooth.h android/bluetooth.c \ - android/hidhost.h android/hidhost.c android/ipc.h \ - android/ipc.c android/avdtp.h android/avdtp.c android/a2dp.h \ - android/a2dp.c android/socket.h android/socket.c android/pan.h \ - android/pan.c btio/btio.h btio/btio.c src/sdp-client.h \ - src/sdp-client.c profiles/network/bnep.h \ - profiles/network/bnep.c + src/shared/mgmt.c src/shared/ringbuf.h src/shared/ringbuf.c \ + src/shared/hfp.h src/shared/hfp.c src/shared/gatt-db.h \ + src/shared/gatt-db.c android/bluetooth.h android/bluetooth.c \ + android/hidhost.h android/hidhost.c android/ipc-common.h \ + android/ipc.h android/ipc.c android/avdtp.h android/avdtp.c \ + android/a2dp.h android/a2dp.c android/avctp.h android/avctp.c \ + android/avrcp.h android/avrcp.c android/avrcp-lib.h \ + android/avrcp-lib.c android/socket.h android/socket.c \ + android/pan.h android/pan.c android/handsfree.h \ + android/handsfree.c android/gatt.h android/gatt.c \ + android/health.h android/health.c android/mcap-lib.h \ + android/mcap-lib.c attrib/att.c attrib/att.h attrib/gatt.c \ + attrib/gatt.h attrib/gattrib.c attrib/gattrib.h btio/btio.h \ + btio/btio.c src/sdp-client.h src/sdp-client.c \ + profiles/network/bnep.h profiles/network/bnep.c @ANDROID_TRUE@am_android_bluetoothd_OBJECTS = android/main.$(OBJEXT) \ @ANDROID_TRUE@ src/log.$(OBJEXT) src/sdpd-database.$(OBJEXT) \ @ANDROID_TRUE@ src/sdpd-server.$(OBJEXT) \ @ANDROID_TRUE@ src/sdpd-service.$(OBJEXT) \ @ANDROID_TRUE@ src/sdpd-request.$(OBJEXT) \ -@ANDROID_TRUE@ src/glib-helper.$(OBJEXT) src/eir.$(OBJEXT) \ +@ANDROID_TRUE@ src/uuid-helper.$(OBJEXT) src/eir.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/io-glib.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/queue.$(OBJEXT) \ @ANDROID_TRUE@ src/shared/util.$(OBJEXT) \ @ANDROID_TRUE@ src/shared/mgmt.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/ringbuf.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/hfp.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/gatt-db.$(OBJEXT) \ @ANDROID_TRUE@ android/bluetooth.$(OBJEXT) \ @ANDROID_TRUE@ android/hidhost.$(OBJEXT) android/ipc.$(OBJEXT) \ @ANDROID_TRUE@ android/avdtp.$(OBJEXT) android/a2dp.$(OBJEXT) \ +@ANDROID_TRUE@ android/avctp.$(OBJEXT) android/avrcp.$(OBJEXT) \ +@ANDROID_TRUE@ android/avrcp-lib.$(OBJEXT) \ @ANDROID_TRUE@ android/socket.$(OBJEXT) android/pan.$(OBJEXT) \ +@ANDROID_TRUE@ android/handsfree.$(OBJEXT) \ +@ANDROID_TRUE@ android/gatt.$(OBJEXT) android/health.$(OBJEXT) \ +@ANDROID_TRUE@ android/mcap-lib.$(OBJEXT) attrib/att.$(OBJEXT) \ +@ANDROID_TRUE@ attrib/gatt.$(OBJEXT) attrib/gattrib.$(OBJEXT) \ @ANDROID_TRUE@ btio/btio.$(OBJEXT) src/sdp-client.$(OBJEXT) \ @ANDROID_TRUE@ profiles/network/bnep.$(OBJEXT) android_bluetoothd_OBJECTS = $(am_android_bluetoothd_OBJECTS) @ANDROID_TRUE@android_bluetoothd_DEPENDENCIES = \ @ANDROID_TRUE@ lib/libbluetooth-internal.la +am__android_bluetoothd_snoop_SOURCES_DIST = \ + android/bluetoothd-snoop.c monitor/mainloop.h \ + monitor/mainloop.c src/shared/btsnoop.h src/shared/btsnoop.c +@ANDROID_TRUE@am_android_bluetoothd_snoop_OBJECTS = \ +@ANDROID_TRUE@ android/bluetoothd-snoop.$(OBJEXT) \ +@ANDROID_TRUE@ monitor/mainloop.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/btsnoop.$(OBJEXT) +android_bluetoothd_snoop_OBJECTS = \ + $(am_android_bluetoothd_snoop_OBJECTS) +android_bluetoothd_snoop_LDADD = $(LDADD) am__android_haltest_SOURCES_DIST = android/client/haltest.c \ android/client/pollhandler.h android/client/pollhandler.c \ android/client/terminal.h android/client/terminal.c \ android/client/history.h android/client/history.c \ android/client/tabcompletion.c android/client/if-main.h \ - android/client/if-av.c android/client/if-bt.c \ - android/client/if-gatt.c android/client/if-hf.c \ - android/client/if-hh.c android/client/if-pan.c \ - android/client/if-sock.c android/client/hwmodule.c \ + android/client/if-av.c android/client/if-rc.c \ + android/client/if-bt.c android/client/if-gatt.c \ + android/client/if-hf.c android/client/if-hh.c \ + android/client/if-pan.c android/client/if-hl.c \ + android/client/if-sock.c android/client/if-audio.c \ + android/client/if-sco.c android/hardware/hardware.c \ android/hal-utils.h android/hal-utils.c @ANDROID_TRUE@am_android_haltest_OBJECTS = android/client/android_haltest-haltest.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-pollhandler.$(OBJEXT) \ @@ -388,21 +520,55 @@ am__android_haltest_SOURCES_DIST = android/client/haltest.c \ @ANDROID_TRUE@ android/client/android_haltest-history.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-tabcompletion.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-if-av.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-rc.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-if-bt.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-if-gatt.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-if-hf.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-if-hh.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-if-pan.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-hl.$(OBJEXT) \ @ANDROID_TRUE@ android/client/android_haltest-if-sock.$(OBJEXT) \ -@ANDROID_TRUE@ android/client/android_haltest-hwmodule.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-audio.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-sco.$(OBJEXT) \ +@ANDROID_TRUE@ android/hardware/android_haltest-hardware.$(OBJEXT) \ @ANDROID_TRUE@ android/android_haltest-hal-utils.$(OBJEXT) android_haltest_OBJECTS = $(am_android_haltest_OBJECTS) -@ANDROID_TRUE@android_haltest_DEPENDENCIES = \ -@ANDROID_TRUE@ android/libhal-internal.la +android_haltest_LDADD = $(LDADD) android_haltest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(android_haltest_CFLAGS) $(CFLAGS) $(android_haltest_LDFLAGS) \ $(LDFLAGS) -o $@ +am__android_ipc_tester_SOURCES_DIST = emulator/btdev.h \ + emulator/btdev.c emulator/bthost.h emulator/bthost.c \ + emulator/smp.c src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c src/shared/queue.h \ + src/shared/queue.c src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \ + src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c \ + android/hal-utils.h android/hal-utils.c android/ipc-common.h \ + android/ipc-tester.c +@ANDROID_TRUE@am_android_ipc_tester_OBJECTS = \ +@ANDROID_TRUE@ emulator/android_ipc_tester-btdev.$(OBJEXT) \ +@ANDROID_TRUE@ emulator/android_ipc_tester-bthost.$(OBJEXT) \ +@ANDROID_TRUE@ emulator/android_ipc_tester-smp.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_ipc_tester-crypto.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_ipc_tester-io-glib.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_ipc_tester-queue.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_ipc_tester-util.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_ipc_tester-mgmt.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_ipc_tester-hciemu.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_ipc_tester-tester.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/android_ipc_tester-timeout-glib.$(OBJEXT) \ +@ANDROID_TRUE@ android/android_ipc_tester-hal-utils.$(OBJEXT) \ +@ANDROID_TRUE@ android/android_ipc_tester-ipc-tester.$(OBJEXT) +android_ipc_tester_OBJECTS = $(am_android_ipc_tester_OBJECTS) +@ANDROID_TRUE@android_ipc_tester_DEPENDENCIES = \ +@ANDROID_TRUE@ lib/libbluetooth-internal.la +android_ipc_tester_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(android_ipc_tester_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ am__android_system_emulator_SOURCES_DIST = android/system-emulator.c \ monitor/mainloop.h monitor/mainloop.c @ANDROID_TRUE@am_android_system_emulator_OBJECTS = \ @@ -411,6 +577,15 @@ am__android_system_emulator_SOURCES_DIST = android/system-emulator.c \ android_system_emulator_OBJECTS = \ $(am_android_system_emulator_OBJECTS) android_system_emulator_LDADD = $(LDADD) +am__android_test_ipc_SOURCES_DIST = android/test-ipc.c \ + src/shared/util.h src/shared/util.c src/log.h src/log.c \ + android/ipc-common.h android/ipc.c android/ipc.h +@ANDROID_TRUE@am_android_test_ipc_OBJECTS = \ +@ANDROID_TRUE@ android/test-ipc.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \ +@ANDROID_TRUE@ android/ipc.$(OBJEXT) +android_test_ipc_OBJECTS = $(am_android_test_ipc_OBJECTS) +android_test_ipc_DEPENDENCIES = am__attrib_gatttool_SOURCES_DIST = attrib/gatttool.c attrib/att.c \ attrib/gatt.c attrib/gattrib.c btio/btio.c attrib/gatttool.h \ attrib/interactive.c attrib/utils.c src/log.c client/display.c \ @@ -440,40 +615,72 @@ am__emulator_b1ee_SOURCES_DIST = emulator/b1ee.c monitor/mainloop.h \ emulator_b1ee_OBJECTS = $(am_emulator_b1ee_OBJECTS) emulator_b1ee_LDADD = $(LDADD) am__emulator_btvirt_SOURCES_DIST = emulator/main.c monitor/bt.h \ - monitor/mainloop.h monitor/mainloop.c emulator/server.h \ - emulator/server.c emulator/vhci.h emulator/vhci.c \ - emulator/btdev.h emulator/btdev.c emulator/bthost.h \ - emulator/bthost.c emulator/amp.h emulator/amp.c + monitor/mainloop.h monitor/mainloop.c src/shared/timeout.h \ + src/shared/timeout-mainloop.c src/shared/util.h \ + src/shared/util.c src/shared/crypto.h src/shared/crypto.c \ + emulator/server.h emulator/server.c emulator/vhci.h \ + emulator/vhci.c emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c emulator/smp.c \ + emulator/amp.h emulator/amp.c emulator/le.h emulator/le.c @EXPERIMENTAL_TRUE@am_emulator_btvirt_OBJECTS = \ @EXPERIMENTAL_TRUE@ emulator/main.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ monitor/mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/server.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/vhci.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/btdev.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/bthost.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ emulator/amp.$(OBJEXT) +@EXPERIMENTAL_TRUE@ emulator/smp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/amp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/le.$(OBJEXT) emulator_btvirt_OBJECTS = $(am_emulator_btvirt_OBJECTS) -emulator_btvirt_LDADD = $(LDADD) +@EXPERIMENTAL_TRUE@emulator_btvirt_DEPENDENCIES = \ +@EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la +am__emulator_hfp_SOURCES_DIST = emulator/hfp.c monitor/mainloop.h \ + monitor/mainloop.c src/shared/io.h src/shared/io-mainloop.c \ + src/shared/util.h src/shared/util.c src/shared/queue.h \ + src/shared/queue.c src/shared/ringbuf.h src/shared/ringbuf.c \ + src/shared/hfp.h src/shared/hfp.c +@EXPERIMENTAL_TRUE@am_emulator_hfp_OBJECTS = emulator/hfp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/hfp.$(OBJEXT) +emulator_hfp_OBJECTS = $(am_emulator_hfp_OBJECTS) +emulator_hfp_LDADD = $(LDADD) am__monitor_btmon_SOURCES_DIST = monitor/main.c monitor/bt.h \ monitor/mainloop.h monitor/mainloop.c monitor/display.h \ monitor/display.c monitor/hcidump.h monitor/hcidump.c \ - monitor/btsnoop.h monitor/btsnoop.c monitor/control.h \ + monitor/ellisys.h monitor/ellisys.c monitor/control.h \ monitor/control.c monitor/packet.h monitor/packet.c \ monitor/vendor.h monitor/vendor.c monitor/lmp.h monitor/lmp.c \ - monitor/l2cap.h monitor/l2cap.c monitor/uuid.h monitor/uuid.c \ - monitor/sdp.h monitor/sdp.c monitor/crc.h monitor/crc.c \ - monitor/ll.h monitor/ll.c + monitor/crc.h monitor/crc.c monitor/ll.h monitor/ll.c \ + monitor/l2cap.h monitor/l2cap.c monitor/sdp.h monitor/sdp.c \ + monitor/uuid.h monitor/uuid.c monitor/hwdb.h monitor/hwdb.c \ + monitor/keys.h monitor/keys.c monitor/analyze.h \ + monitor/analyze.c src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c src/shared/crypto.h \ + src/shared/crypto.c src/shared/btsnoop.h src/shared/btsnoop.c @MONITOR_TRUE@am_monitor_btmon_OBJECTS = monitor/main.$(OBJEXT) \ @MONITOR_TRUE@ monitor/mainloop.$(OBJEXT) \ @MONITOR_TRUE@ monitor/display.$(OBJEXT) \ @MONITOR_TRUE@ monitor/hcidump.$(OBJEXT) \ -@MONITOR_TRUE@ monitor/btsnoop.$(OBJEXT) \ +@MONITOR_TRUE@ monitor/ellisys.$(OBJEXT) \ @MONITOR_TRUE@ monitor/control.$(OBJEXT) \ @MONITOR_TRUE@ monitor/packet.$(OBJEXT) \ @MONITOR_TRUE@ monitor/vendor.$(OBJEXT) monitor/lmp.$(OBJEXT) \ -@MONITOR_TRUE@ monitor/l2cap.$(OBJEXT) monitor/uuid.$(OBJEXT) \ -@MONITOR_TRUE@ monitor/sdp.$(OBJEXT) monitor/crc.$(OBJEXT) \ -@MONITOR_TRUE@ monitor/ll.$(OBJEXT) +@MONITOR_TRUE@ monitor/crc.$(OBJEXT) monitor/ll.$(OBJEXT) \ +@MONITOR_TRUE@ monitor/l2cap.$(OBJEXT) monitor/sdp.$(OBJEXT) \ +@MONITOR_TRUE@ monitor/uuid.$(OBJEXT) monitor/hwdb.$(OBJEXT) \ +@MONITOR_TRUE@ monitor/keys.$(OBJEXT) monitor/analyze.$(OBJEXT) \ +@MONITOR_TRUE@ src/shared/util.$(OBJEXT) \ +@MONITOR_TRUE@ src/shared/queue.$(OBJEXT) \ +@MONITOR_TRUE@ src/shared/crypto.$(OBJEXT) \ +@MONITOR_TRUE@ src/shared/btsnoop.$(OBJEXT) monitor_btmon_OBJECTS = $(am_monitor_btmon_OBJECTS) @MONITOR_TRUE@monitor_btmon_DEPENDENCIES = \ @MONITOR_TRUE@ lib/libbluetooth-internal.la @@ -603,23 +810,23 @@ am__src_bluetoothd_SOURCES_DIST = plugins/hostname.c plugins/wiimote.c \ profiles/network/connection.c profiles/input/manager.c \ profiles/input/server.h profiles/input/server.c \ profiles/input/device.h profiles/input/device.c \ - profiles/input/hog.c profiles/input/uhid_copy.h \ - profiles/input/suspend.h profiles/input/suspend-dummy.c \ - profiles/health/mcap_lib.h profiles/health/mcap_internal.h \ - profiles/health/mcap.h profiles/health/mcap.c \ - profiles/health/mcap_sync.c profiles/health/hdp_main.c \ - profiles/health/hdp_types.h profiles/health/hdp_manager.h \ - profiles/health/hdp_manager.c profiles/health/hdp.h \ - profiles/health/hdp.c profiles/health/hdp_util.h \ - profiles/health/hdp_util.c profiles/gatt/gas.c \ - profiles/scanparam/scan.c profiles/deviceinfo/deviceinfo.c \ - profiles/alert/server.c profiles/time/server.c \ - profiles/proximity/main.c profiles/proximity/manager.h \ - profiles/proximity/manager.c profiles/proximity/monitor.h \ - profiles/proximity/monitor.c profiles/proximity/reporter.h \ - profiles/proximity/reporter.c profiles/proximity/linkloss.h \ - profiles/proximity/linkloss.c profiles/proximity/immalert.h \ - profiles/proximity/immalert.c \ + profiles/input/uhid_copy.h profiles/input/hidp_defs.h \ + profiles/input/hog.c profiles/input/suspend.h \ + profiles/input/suspend-dummy.c profiles/health/mcap_lib.h \ + profiles/health/mcap_internal.h profiles/health/mcap.h \ + profiles/health/mcap.c profiles/health/mcap_sync.c \ + profiles/health/hdp_main.c profiles/health/hdp_types.h \ + profiles/health/hdp_manager.h profiles/health/hdp_manager.c \ + profiles/health/hdp.h profiles/health/hdp.c \ + profiles/health/hdp_util.h profiles/health/hdp_util.c \ + profiles/gatt/gas.c profiles/scanparam/scan.c \ + profiles/deviceinfo/deviceinfo.c profiles/alert/server.c \ + profiles/time/server.c profiles/proximity/main.c \ + profiles/proximity/manager.h profiles/proximity/manager.c \ + profiles/proximity/monitor.h profiles/proximity/monitor.c \ + profiles/proximity/reporter.h profiles/proximity/reporter.c \ + profiles/proximity/linkloss.h profiles/proximity/linkloss.c \ + profiles/proximity/immalert.h profiles/proximity/immalert.c \ profiles/thermometer/thermometer.c \ profiles/heartrate/heartrate.c \ profiles/cyclingspeed/cyclingspeed.c attrib/att.h \ @@ -631,14 +838,18 @@ am__src_bluetoothd_SOURCES_DIST = plugins/hostname.c plugins/wiimote.c \ 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/sdp-client.h src/sdp-client.c \ - src/textfile.h src/textfile.c src/glib-helper.h \ - src/glib-helper.c src/uinput.h src/plugin.h src/plugin.c \ + src/textfile.h src/textfile.c src/uuid-helper.h \ + src/uuid-helper.c src/uinput.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/adapter.h src/adapter.c \ src/profile.h src/profile.c src/service.h src/service.c \ + src/gatt-dbus.h src/gatt-dbus.c src/gatt.h src/gatt.c \ src/device.h src/device.c src/attio.h src/dbus-common.c \ - src/dbus-common.h src/eir.h src/eir.c src/shared/util.h \ - src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c + src/dbus-common.h src/eir.h src/eir.c src/shared/io.h \ + src/shared/io-glib.c src/shared/timeout.h \ + src/shared/timeout-glib.c src/shared/queue.h \ + src/shared/queue.c src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c @MAINTAINER_MODE_TRUE@am__objects_10 = plugins/bluetoothd-gatt-example.$(OBJEXT) @EXPERIMENTAL_TRUE@am__objects_11 = \ @EXPERIMENTAL_TRUE@ plugins/bluetoothd-neard.$(OBJEXT) \ @@ -709,16 +920,20 @@ am_src_bluetoothd_OBJECTS = $(am__objects_14) $(am__objects_15) \ src/bluetoothd-sdp-xml.$(OBJEXT) \ src/bluetoothd-sdp-client.$(OBJEXT) \ src/bluetoothd-textfile.$(OBJEXT) \ - src/bluetoothd-glib-helper.$(OBJEXT) \ + src/bluetoothd-uuid-helper.$(OBJEXT) \ src/bluetoothd-plugin.$(OBJEXT) \ src/bluetoothd-storage.$(OBJEXT) \ src/bluetoothd-agent.$(OBJEXT) src/bluetoothd-error.$(OBJEXT) \ src/bluetoothd-adapter.$(OBJEXT) \ src/bluetoothd-profile.$(OBJEXT) \ src/bluetoothd-service.$(OBJEXT) \ - src/bluetoothd-device.$(OBJEXT) \ + src/bluetoothd-gatt-dbus.$(OBJEXT) \ + src/bluetoothd-gatt.$(OBJEXT) src/bluetoothd-device.$(OBJEXT) \ src/bluetoothd-dbus-common.$(OBJEXT) \ src/bluetoothd-eir.$(OBJEXT) \ + src/shared/bluetoothd-io-glib.$(OBJEXT) \ + src/shared/bluetoothd-timeout-glib.$(OBJEXT) \ + src/shared/bluetoothd-queue.$(OBJEXT) \ src/shared/bluetoothd-util.$(OBJEXT) \ src/shared/bluetoothd-mgmt.$(OBJEXT) nodist_src_bluetoothd_OBJECTS = $(am__objects_9) @@ -728,6 +943,23 @@ src_bluetoothd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(src_bluetoothd_CFLAGS) $(CFLAGS) $(src_bluetoothd_LDFLAGS) \ $(LDFLAGS) -o $@ +am__tools_3dsp_SOURCES_DIST = tools/3dsp.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c src/shared/io.h \ + src/shared/io-mainloop.c src/shared/timeout.h \ + src/shared/timeout-mainloop.c src/shared/hci.h \ + src/shared/hci.c src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c src/shared/ringbuf.h \ + src/shared/ringbuf.c +@EXPERIMENTAL_TRUE@am_tools_3dsp_OBJECTS = tools/3dsp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/hci.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.$(OBJEXT) +tools_3dsp_OBJECTS = $(am_tools_3dsp_OBJECTS) +tools_3dsp_LDADD = $(LDADD) tools_amptest_SOURCES = tools/amptest.c tools_amptest_OBJECTS = tools/amptest.$(OBJEXT) @EXPERIMENTAL_TRUE@tools_amptest_DEPENDENCIES = \ @@ -756,6 +988,19 @@ am__tools_bdaddr_SOURCES_DIST = tools/bdaddr.c src/oui.h src/oui.c tools_bdaddr_OBJECTS = $(am_tools_bdaddr_OBJECTS) @EXPERIMENTAL_TRUE@tools_bdaddr_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la +am__tools_bluemoon_SOURCES_DIST = tools/bluemoon.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c src/shared/io.h \ + src/shared/io-mainloop.c src/shared/hci.h src/shared/hci.c \ + src/shared/util.h src/shared/util.c src/shared/queue.h \ + src/shared/queue.c src/shared/ringbuf.h src/shared/ringbuf.c +@TOOLS_TRUE@am_tools_bluemoon_OBJECTS = tools/bluemoon.$(OBJEXT) \ +@TOOLS_TRUE@ monitor/mainloop.$(OBJEXT) \ +@TOOLS_TRUE@ src/shared/io-mainloop.$(OBJEXT) \ +@TOOLS_TRUE@ src/shared/hci.$(OBJEXT) src/shared/util.$(OBJEXT) \ +@TOOLS_TRUE@ src/shared/queue.$(OBJEXT) \ +@TOOLS_TRUE@ src/shared/ringbuf.$(OBJEXT) +tools_bluemoon_OBJECTS = $(am_tools_bluemoon_OBJECTS) +tools_bluemoon_LDADD = $(LDADD) am__tools_bluetooth_player_SOURCES_DIST = tools/bluetooth-player.c \ client/display.h client/display.c @READLINE_TRUE@am_tools_bluetooth_player_OBJECTS = \ @@ -767,8 +1012,21 @@ tools_bluetooth_player_OBJECTS = $(am_tools_bluetooth_player_OBJECTS) tools_btattach_SOURCES = tools/btattach.c tools_btattach_OBJECTS = tools/btattach.$(OBJEXT) tools_btattach_LDADD = $(LDADD) -am__tools_btinfo_SOURCES_DIST = tools/btinfo.c -@EXPERIMENTAL_TRUE@am_tools_btinfo_OBJECTS = tools/btinfo.$(OBJEXT) +am__tools_btinfo_SOURCES_DIST = tools/btinfo.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c src/shared/io.h \ + src/shared/io-mainloop.c src/shared/timeout.h \ + src/shared/timeout-mainloop.c src/shared/hci.h \ + src/shared/hci.c src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c src/shared/ringbuf.h \ + src/shared/ringbuf.c +@EXPERIMENTAL_TRUE@am_tools_btinfo_OBJECTS = tools/btinfo.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/hci.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.$(OBJEXT) tools_btinfo_OBJECTS = $(am_tools_btinfo_OBJECTS) tools_btinfo_LDADD = $(LDADD) am__tools_btiotest_SOURCES_DIST = tools/btiotest.c btio/btio.h \ @@ -779,16 +1037,29 @@ am__tools_btiotest_SOURCES_DIST = tools/btiotest.c btio/btio.h \ tools_btiotest_OBJECTS = $(am_tools_btiotest_OBJECTS) @EXPERIMENTAL_TRUE@tools_btiotest_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la -am__tools_btmgmt_SOURCES_DIST = tools/btmgmt.c src/glib-helper.c \ - src/eir.c src/shared/util.h src/shared/util.c \ - src/shared/mgmt.h src/shared/mgmt.c +am__tools_btmgmt_SOURCES_DIST = tools/btmgmt.c src/uuid-helper.c \ + monitor/mainloop.h monitor/mainloop.c src/shared/io.h \ + src/shared/io-mainloop.c src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c src/shared/mgmt.h \ + src/shared/mgmt.c @EXPERIMENTAL_TRUE@am_tools_btmgmt_OBJECTS = tools/btmgmt.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ src/glib-helper.$(OBJEXT) src/eir.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/uuid-helper.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.$(OBJEXT) tools_btmgmt_OBJECTS = $(am_tools_btmgmt_OBJECTS) @EXPERIMENTAL_TRUE@tools_btmgmt_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la +am__tools_btproxy_SOURCES_DIST = tools/btproxy.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c src/shared/util.h \ + src/shared/util.c +@EXPERIMENTAL_TRUE@am_tools_btproxy_OBJECTS = tools/btproxy.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) +tools_btproxy_OBJECTS = $(am_tools_btproxy_OBJECTS) +tools_btproxy_LDADD = $(LDADD) am__tools_btsnoop_SOURCES_DIST = tools/btsnoop.c src/shared/pcap.h \ src/shared/pcap.c src/shared/btsnoop.h src/shared/btsnoop.c @EXPERIMENTAL_TRUE@am_tools_btsnoop_OBJECTS = tools/btsnoop.$(OBJEXT) \ @@ -808,28 +1079,60 @@ tools_cltest_OBJECTS = $(am_tools_cltest_OBJECTS) @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la am__tools_gap_tester_SOURCES_DIST = tools/gap-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c emulator/bthost.h \ - emulator/bthost.c src/shared/hciemu.h src/shared/hciemu.c \ - src/shared/tester.h src/shared/tester.c + emulator/bthost.c emulator/smp.c src/shared/crypto.h \ + src/shared/crypto.c src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c src/shared/hciemu.h \ + src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@am_tools_gap_tester_OBJECTS = \ @EXPERIMENTAL_TRUE@ tools/gap-tester.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/btdev.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/bthost.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/smp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) +@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-glib.$(OBJEXT) tools_gap_tester_OBJECTS = $(am_tools_gap_tester_OBJECTS) @EXPERIMENTAL_TRUE@tools_gap_tester_DEPENDENCIES = \ +@EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la \ @EXPERIMENTAL_TRUE@ gdbus/libgdbus-internal.la +am__tools_gatt_service_SOURCES_DIST = tools/gatt-service.c +@EXPERIMENTAL_TRUE@am_tools_gatt_service_OBJECTS = \ +@EXPERIMENTAL_TRUE@ tools/gatt-service.$(OBJEXT) +tools_gatt_service_OBJECTS = $(am_tools_gatt_service_OBJECTS) +@EXPERIMENTAL_TRUE@tools_gatt_service_DEPENDENCIES = \ +@EXPERIMENTAL_TRUE@ gdbus/libgdbus-internal.la +am__tools_hci_tester_SOURCES_DIST = tools/hci-tester.c monitor/bt.h \ + src/shared/io.h src/shared/io-glib.c src/shared/hci.h \ + src/shared/hci.c src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c src/shared/ringbuf.h \ + src/shared/ringbuf.c src/shared/tester.h src/shared/tester.c +@EXPERIMENTAL_TRUE@am_tools_hci_tester_OBJECTS = \ +@EXPERIMENTAL_TRUE@ tools/hci-tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-glib.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/hci.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) +tools_hci_tester_OBJECTS = $(am_tools_hci_tester_OBJECTS) +tools_hci_tester_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/hciattach_intel.c + tools/hciattach_qualcomm.c tools/hciattach_intel.c \ + tools/hciattach_bcm43xx.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_TRUE@ tools/hciattach_intel.$(OBJEXT) +@TOOLS_TRUE@ tools/hciattach_intel.$(OBJEXT) \ +@TOOLS_TRUE@ tools/hciattach_bcm43xx.$(OBJEXT) tools_hciattach_OBJECTS = $(am_tools_hciattach_OBJECTS) @TOOLS_TRUE@tools_hciattach_DEPENDENCIES = \ @TOOLS_TRUE@ lib/libbluetooth-internal.la @@ -889,6 +1192,10 @@ am__tools_hcitool_SOURCES_DIST = tools/hcitool.c src/oui.h src/oui.c @TOOLS_TRUE@ src/oui.$(OBJEXT) tools_hcitool_OBJECTS = $(am_tools_hcitool_OBJECTS) @TOOLS_TRUE@tools_hcitool_DEPENDENCIES = lib/libbluetooth-internal.la +am__tools_hex2hcd_SOURCES_DIST = tools/hex2hcd.c +@EXPERIMENTAL_TRUE@am_tools_hex2hcd_OBJECTS = tools/hex2hcd.$(OBJEXT) +tools_hex2hcd_OBJECTS = $(am_tools_hex2hcd_OBJECTS) +tools_hex2hcd_LDADD = $(LDADD) tools_hid2hci_SOURCES = tools/hid2hci.c tools_hid2hci_OBJECTS = tools/hid2hci.$(OBJEXT) tools_hid2hci_DEPENDENCIES = @@ -896,20 +1203,45 @@ tools_hwdb_SOURCES = tools/hwdb.c tools_hwdb_OBJECTS = tools/hwdb.$(OBJEXT) @EXPERIMENTAL_TRUE@tools_hwdb_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la +am__tools_ibeacon_SOURCES_DIST = tools/ibeacon.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c src/shared/io.h \ + src/shared/io-mainloop.c src/shared/timeout.h \ + src/shared/timeout-mainloop.c src/shared/hci.h \ + src/shared/hci.c src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c src/shared/ringbuf.h \ + src/shared/ringbuf.c +@EXPERIMENTAL_TRUE@am_tools_ibeacon_OBJECTS = tools/ibeacon.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-mainloop.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/hci.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.$(OBJEXT) +tools_ibeacon_OBJECTS = $(am_tools_ibeacon_OBJECTS) +tools_ibeacon_LDADD = $(LDADD) am__tools_l2cap_tester_SOURCES_DIST = tools/l2cap-tester.c \ monitor/bt.h emulator/btdev.h emulator/btdev.c \ - emulator/bthost.h emulator/bthost.c src/shared/util.h \ - src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \ - src/shared/hciemu.h src/shared/hciemu.c src/shared/tester.h \ - src/shared/tester.c + emulator/bthost.h emulator/bthost.c emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c src/shared/io.h \ + src/shared/io-glib.c src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c src/shared/mgmt.h \ + src/shared/mgmt.c src/shared/hciemu.h src/shared/hciemu.c \ + src/shared/tester.h src/shared/tester.c src/shared/timeout.h \ + src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@am_tools_l2cap_tester_OBJECTS = \ @EXPERIMENTAL_TRUE@ tools/l2cap-tester.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/btdev.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/bthost.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/smp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-glib.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) +@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-glib.$(OBJEXT) tools_l2cap_tester_OBJECTS = $(am_tools_l2cap_tester_OBJECTS) @EXPERIMENTAL_TRUE@tools_l2cap_tester_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la @@ -921,17 +1253,26 @@ tools_l2test_OBJECTS = tools/l2test.$(OBJEXT) @TOOLS_TRUE@tools_l2test_DEPENDENCIES = lib/libbluetooth-internal.la am__tools_mgmt_tester_SOURCES_DIST = tools/mgmt-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c emulator/bthost.h \ - emulator/bthost.c src/shared/util.h src/shared/util.c \ - src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \ - src/shared/hciemu.c src/shared/tester.h src/shared/tester.c + emulator/bthost.c emulator/smp.c src/shared/crypto.h \ + src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c src/shared/util.h \ + src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c src/shared/tester.h \ + src/shared/tester.c src/shared/timeout.h \ + src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@am_tools_mgmt_tester_OBJECTS = \ @EXPERIMENTAL_TRUE@ tools/mgmt-tester.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/btdev.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/bthost.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/smp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-glib.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) +@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-glib.$(OBJEXT) tools_mgmt_tester_OBJECTS = $(am_tools_mgmt_tester_OBJECTS) @EXPERIMENTAL_TRUE@tools_mgmt_tester_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la @@ -982,19 +1323,53 @@ tools_rctest_OBJECTS = tools/rctest.$(OBJEXT) tools_rfcomm_SOURCES = tools/rfcomm.c tools_rfcomm_OBJECTS = tools/rfcomm.$(OBJEXT) @TOOLS_TRUE@tools_rfcomm_DEPENDENCIES = lib/libbluetooth-internal.la +am__tools_rfcomm_tester_SOURCES_DIST = tools/rfcomm-tester.c \ + monitor/bt.h emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c src/shared/io.h \ + src/shared/io-glib.c src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c src/shared/mgmt.h \ + src/shared/mgmt.c src/shared/hciemu.h src/shared/hciemu.c \ + src/shared/tester.h src/shared/tester.c src/shared/timeout.h \ + src/shared/timeout-glib.c +@EXPERIMENTAL_TRUE@am_tools_rfcomm_tester_OBJECTS = \ +@EXPERIMENTAL_TRUE@ tools/rfcomm-tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/btdev.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/bthost.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/smp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-glib.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/mgmt.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/hciemu.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-glib.$(OBJEXT) +tools_rfcomm_tester_OBJECTS = $(am_tools_rfcomm_tester_OBJECTS) +@EXPERIMENTAL_TRUE@tools_rfcomm_tester_DEPENDENCIES = \ +@EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la am__tools_sco_tester_SOURCES_DIST = tools/sco-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c emulator/bthost.h \ - emulator/bthost.c src/shared/util.h src/shared/util.c \ - src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \ - src/shared/hciemu.c src/shared/tester.h src/shared/tester.c + emulator/bthost.c emulator/smp.c src/shared/crypto.h \ + src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c src/shared/util.h \ + src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c src/shared/tester.h \ + src/shared/tester.c src/shared/timeout.h \ + src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@am_tools_sco_tester_OBJECTS = \ @EXPERIMENTAL_TRUE@ tools/sco-tester.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/btdev.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/bthost.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/smp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-glib.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) +@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-glib.$(OBJEXT) tools_sco_tester_OBJECTS = $(am_tools_sco_tester_OBJECTS) @EXPERIMENTAL_TRUE@tools_sco_tester_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la @@ -1008,33 +1383,57 @@ am__tools_sdptool_SOURCES_DIST = tools/sdptool.c src/sdp-xml.h \ @TOOLS_TRUE@ src/sdp-xml.$(OBJEXT) tools_sdptool_OBJECTS = $(am_tools_sdptool_OBJECTS) @TOOLS_TRUE@tools_sdptool_DEPENDENCIES = lib/libbluetooth-internal.la +am__tools_seq2bseq_SOURCES_DIST = tools/seq2bseq.c +@EXPERIMENTAL_TRUE@am_tools_seq2bseq_OBJECTS = \ +@EXPERIMENTAL_TRUE@ tools/seq2bseq.$(OBJEXT) +tools_seq2bseq_OBJECTS = $(am_tools_seq2bseq_OBJECTS) +tools_seq2bseq_LDADD = $(LDADD) am__tools_smp_tester_SOURCES_DIST = tools/smp-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c emulator/bthost.h \ - emulator/bthost.c src/shared/util.h src/shared/util.c \ - src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \ - src/shared/hciemu.c src/shared/tester.h src/shared/tester.c + emulator/bthost.c emulator/smp.c src/shared/crypto.h \ + src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c src/shared/util.h \ + src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c src/shared/tester.h \ + src/shared/tester.c src/shared/timeout.h \ + src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@am_tools_smp_tester_OBJECTS = \ @EXPERIMENTAL_TRUE@ tools/smp-tester.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/btdev.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ emulator/bthost.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/smp.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/io-glib.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/queue.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) +@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-glib.$(OBJEXT) tools_smp_tester_OBJECTS = $(am_tools_smp_tester_OBJECTS) @EXPERIMENTAL_TRUE@tools_smp_tester_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la +am_unit_test_avctp_OBJECTS = unit/test-avctp.$(OBJEXT) \ + src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \ + android/avctp.$(OBJEXT) +unit_test_avctp_OBJECTS = $(am_unit_test_avctp_OBJECTS) +unit_test_avctp_DEPENDENCIES = am_unit_test_avdtp_OBJECTS = unit/test-avdtp.$(OBJEXT) \ src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \ android/avdtp.$(OBJEXT) unit_test_avdtp_OBJECTS = $(am_unit_test_avdtp_OBJECTS) unit_test_avdtp_DEPENDENCIES = +am_unit_test_avrcp_OBJECTS = unit/test-avrcp.$(OBJEXT) \ + src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \ + android/avctp.$(OBJEXT) android/avrcp-lib.$(OBJEXT) +unit_test_avrcp_OBJECTS = $(am_unit_test_avrcp_OBJECTS) +unit_test_avrcp_DEPENDENCIES = lib/libbluetooth-internal.la am_unit_test_crc_OBJECTS = unit/test-crc.$(OBJEXT) \ monitor/crc.$(OBJEXT) unit_test_crc_OBJECTS = $(am_unit_test_crc_OBJECTS) unit_test_crc_DEPENDENCIES = am_unit_test_eir_OBJECTS = unit/test-eir.$(OBJEXT) src/eir.$(OBJEXT) \ - src/glib-helper.$(OBJEXT) + src/uuid-helper.$(OBJEXT) unit_test_eir_OBJECTS = $(am_unit_test_eir_OBJECTS) unit_test_eir_DEPENDENCIES = lib/libbluetooth-internal.la am_unit_test_gdbus_client_OBJECTS = unit/test-gdbus-client.$(OBJEXT) @@ -1062,16 +1461,32 @@ am_unit_test_gobex_transfer_OBJECTS = $(am__objects_17) \ unit_test_gobex_transfer_OBJECTS = \ $(am_unit_test_gobex_transfer_OBJECTS) unit_test_gobex_transfer_DEPENDENCIES = +am_unit_test_hfp_OBJECTS = unit/test-hfp.$(OBJEXT) \ + src/shared/io-glib.$(OBJEXT) src/shared/queue.$(OBJEXT) \ + src/shared/util.$(OBJEXT) src/shared/mgmt.$(OBJEXT) \ + src/shared/ringbuf.$(OBJEXT) src/shared/hfp.$(OBJEXT) +unit_test_hfp_OBJECTS = $(am_unit_test_hfp_OBJECTS) +unit_test_hfp_DEPENDENCIES = am_unit_test_lib_OBJECTS = unit/test-lib.$(OBJEXT) unit_test_lib_OBJECTS = $(am_unit_test_lib_OBJECTS) unit_test_lib_DEPENDENCIES = lib/libbluetooth-internal.la am_unit_test_mgmt_OBJECTS = unit/test-mgmt.$(OBJEXT) \ + src/shared/io-glib.$(OBJEXT) src/shared/queue.$(OBJEXT) \ src/shared/util.$(OBJEXT) src/shared/mgmt.$(OBJEXT) unit_test_mgmt_OBJECTS = $(am_unit_test_mgmt_OBJECTS) unit_test_mgmt_DEPENDENCIES = +am_unit_test_queue_OBJECTS = unit/test-queue.$(OBJEXT) \ + src/shared/util.$(OBJEXT) src/shared/queue.$(OBJEXT) +unit_test_queue_OBJECTS = $(am_unit_test_queue_OBJECTS) +unit_test_queue_DEPENDENCIES = +am_unit_test_ringbuf_OBJECTS = unit/test-ringbuf.$(OBJEXT) \ + src/shared/util.$(OBJEXT) src/shared/ringbuf.$(OBJEXT) +unit_test_ringbuf_OBJECTS = $(am_unit_test_ringbuf_OBJECTS) +unit_test_ringbuf_DEPENDENCIES = am_unit_test_sdp_OBJECTS = unit/test-sdp.$(OBJEXT) \ src/shared/util.$(OBJEXT) src/sdpd-database.$(OBJEXT) \ - src/sdpd-service.$(OBJEXT) src/sdpd-request.$(OBJEXT) + src/log.$(OBJEXT) src/sdpd-service.$(OBJEXT) \ + src/sdpd-request.$(OBJEXT) unit_test_sdp_OBJECTS = $(am_unit_test_sdp_OBJECTS) unit_test_sdp_DEPENDENCIES = lib/libbluetooth-internal.la am_unit_test_textfile_OBJECTS = unit/test-textfile.$(OBJEXT) \ @@ -1106,95 +1521,126 @@ AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(profiles_sap_libsap_a_SOURCES) \ - $(android_libhal_internal_la_SOURCES) \ + $(android_audio_a2dp_default_la_SOURCES) \ + $(android_audio_sco_default_la_SOURCES) \ + $(android_bluetooth_default_la_SOURCES) \ $(gdbus_libgdbus_internal_la_SOURCES) \ $(lib_libbluetooth_internal_la_SOURCES) \ $(lib_libbluetooth_la_SOURCES) \ $(plugins_external_dummy_la_SOURCES) \ - $(plugins_sixaxis_la_SOURCES) $(android_bluetoothd_SOURCES) \ - $(android_haltest_SOURCES) $(android_system_emulator_SOURCES) \ + $(plugins_sixaxis_la_SOURCES) \ + $(android_android_tester_SOURCES) \ + $(android_bluetoothd_SOURCES) \ + $(android_bluetoothd_snoop_SOURCES) $(android_haltest_SOURCES) \ + $(android_ipc_tester_SOURCES) \ + $(android_system_emulator_SOURCES) $(android_test_ipc_SOURCES) \ $(attrib_gatttool_SOURCES) $(client_bluetoothctl_SOURCES) \ $(emulator_b1ee_SOURCES) $(emulator_btvirt_SOURCES) \ - $(monitor_btmon_SOURCES) $(obexd_src_obexd_SOURCES) \ - $(nodist_obexd_src_obexd_SOURCES) \ + $(emulator_hfp_SOURCES) $(monitor_btmon_SOURCES) \ + $(obexd_src_obexd_SOURCES) $(nodist_obexd_src_obexd_SOURCES) \ $(profiles_cups_bluetooth_SOURCES) \ $(profiles_iap_iapd_SOURCES) $(src_bluetoothd_SOURCES) \ - $(nodist_src_bluetoothd_SOURCES) tools/amptest.c \ - tools/avinfo.c tools/avtest.c $(tools_bccmd_SOURCES) \ - $(tools_bdaddr_SOURCES) $(tools_bluetooth_player_SOURCES) \ + $(nodist_src_bluetoothd_SOURCES) $(tools_3dsp_SOURCES) \ + tools/amptest.c tools/avinfo.c tools/avtest.c \ + $(tools_bccmd_SOURCES) $(tools_bdaddr_SOURCES) \ + $(tools_bluemoon_SOURCES) $(tools_bluetooth_player_SOURCES) \ tools/btattach.c $(tools_btinfo_SOURCES) \ $(tools_btiotest_SOURCES) $(tools_btmgmt_SOURCES) \ - $(tools_btsnoop_SOURCES) tools/ciptool.c \ - $(tools_cltest_SOURCES) $(tools_gap_tester_SOURCES) \ - $(tools_hciattach_SOURCES) $(tools_hciconfig_SOURCES) \ - $(tools_hcidump_SOURCES) tools/hcieventmask.c \ - tools/hcisecfilter.c $(tools_hcitool_SOURCES) tools/hid2hci.c \ - tools/hwdb.c $(tools_l2cap_tester_SOURCES) tools/l2ping.c \ - tools/l2test.c $(tools_mgmt_tester_SOURCES) \ - $(tools_mpris_player_SOURCES) \ + $(tools_btproxy_SOURCES) $(tools_btsnoop_SOURCES) \ + tools/ciptool.c $(tools_cltest_SOURCES) \ + $(tools_gap_tester_SOURCES) $(tools_gatt_service_SOURCES) \ + $(tools_hci_tester_SOURCES) $(tools_hciattach_SOURCES) \ + $(tools_hciconfig_SOURCES) $(tools_hcidump_SOURCES) \ + tools/hcieventmask.c tools/hcisecfilter.c \ + $(tools_hcitool_SOURCES) $(tools_hex2hcd_SOURCES) \ + tools/hid2hci.c tools/hwdb.c $(tools_ibeacon_SOURCES) \ + $(tools_l2cap_tester_SOURCES) tools/l2ping.c tools/l2test.c \ + $(tools_mgmt_tester_SOURCES) $(tools_mpris_player_SOURCES) \ $(tools_obex_client_tool_SOURCES) \ $(tools_obex_server_tool_SOURCES) $(tools_obexctl_SOURCES) \ - tools/rctest.c tools/rfcomm.c $(tools_sco_tester_SOURCES) \ - tools/scotest.c $(tools_sdptool_SOURCES) \ - $(tools_smp_tester_SOURCES) $(unit_test_avdtp_SOURCES) \ + tools/rctest.c tools/rfcomm.c $(tools_rfcomm_tester_SOURCES) \ + $(tools_sco_tester_SOURCES) tools/scotest.c \ + $(tools_sdptool_SOURCES) $(tools_seq2bseq_SOURCES) \ + $(tools_smp_tester_SOURCES) $(unit_test_avctp_SOURCES) \ + $(unit_test_avdtp_SOURCES) $(unit_test_avrcp_SOURCES) \ $(unit_test_crc_SOURCES) $(unit_test_eir_SOURCES) \ $(unit_test_gdbus_client_SOURCES) $(unit_test_gobex_SOURCES) \ $(unit_test_gobex_apparam_SOURCES) \ $(unit_test_gobex_header_SOURCES) \ $(unit_test_gobex_packet_SOURCES) \ - $(unit_test_gobex_transfer_SOURCES) $(unit_test_lib_SOURCES) \ - $(unit_test_mgmt_SOURCES) $(unit_test_sdp_SOURCES) \ - $(unit_test_textfile_SOURCES) $(unit_test_uuid_SOURCES) + $(unit_test_gobex_transfer_SOURCES) $(unit_test_hfp_SOURCES) \ + $(unit_test_lib_SOURCES) $(unit_test_mgmt_SOURCES) \ + $(unit_test_queue_SOURCES) $(unit_test_ringbuf_SOURCES) \ + $(unit_test_sdp_SOURCES) $(unit_test_textfile_SOURCES) \ + $(unit_test_uuid_SOURCES) DIST_SOURCES = $(am__profiles_sap_libsap_a_SOURCES_DIST) \ - $(am__android_libhal_internal_la_SOURCES_DIST) \ + $(am__android_audio_a2dp_default_la_SOURCES_DIST) \ + $(am__android_audio_sco_default_la_SOURCES_DIST) \ + $(am__android_bluetooth_default_la_SOURCES_DIST) \ $(gdbus_libgdbus_internal_la_SOURCES) \ $(lib_libbluetooth_internal_la_SOURCES) \ $(am__lib_libbluetooth_la_SOURCES_DIST) \ $(am__plugins_external_dummy_la_SOURCES_DIST) \ $(am__plugins_sixaxis_la_SOURCES_DIST) \ + $(am__android_android_tester_SOURCES_DIST) \ $(am__android_bluetoothd_SOURCES_DIST) \ + $(am__android_bluetoothd_snoop_SOURCES_DIST) \ $(am__android_haltest_SOURCES_DIST) \ + $(am__android_ipc_tester_SOURCES_DIST) \ $(am__android_system_emulator_SOURCES_DIST) \ + $(am__android_test_ipc_SOURCES_DIST) \ $(am__attrib_gatttool_SOURCES_DIST) \ $(am__client_bluetoothctl_SOURCES_DIST) \ $(am__emulator_b1ee_SOURCES_DIST) \ $(am__emulator_btvirt_SOURCES_DIST) \ + $(am__emulator_hfp_SOURCES_DIST) \ $(am__monitor_btmon_SOURCES_DIST) \ $(am__obexd_src_obexd_SOURCES_DIST) \ $(am__profiles_cups_bluetooth_SOURCES_DIST) \ $(am__profiles_iap_iapd_SOURCES_DIST) \ - $(am__src_bluetoothd_SOURCES_DIST) tools/amptest.c \ - tools/avinfo.c tools/avtest.c $(am__tools_bccmd_SOURCES_DIST) \ + $(am__src_bluetoothd_SOURCES_DIST) \ + $(am__tools_3dsp_SOURCES_DIST) tools/amptest.c tools/avinfo.c \ + tools/avtest.c $(am__tools_bccmd_SOURCES_DIST) \ $(am__tools_bdaddr_SOURCES_DIST) \ + $(am__tools_bluemoon_SOURCES_DIST) \ $(am__tools_bluetooth_player_SOURCES_DIST) tools/btattach.c \ $(am__tools_btinfo_SOURCES_DIST) \ $(am__tools_btiotest_SOURCES_DIST) \ $(am__tools_btmgmt_SOURCES_DIST) \ + $(am__tools_btproxy_SOURCES_DIST) \ $(am__tools_btsnoop_SOURCES_DIST) tools/ciptool.c \ $(am__tools_cltest_SOURCES_DIST) \ $(am__tools_gap_tester_SOURCES_DIST) \ + $(am__tools_gatt_service_SOURCES_DIST) \ + $(am__tools_hci_tester_SOURCES_DIST) \ $(am__tools_hciattach_SOURCES_DIST) \ $(am__tools_hciconfig_SOURCES_DIST) \ $(am__tools_hcidump_SOURCES_DIST) tools/hcieventmask.c \ tools/hcisecfilter.c $(am__tools_hcitool_SOURCES_DIST) \ - tools/hid2hci.c tools/hwdb.c \ + $(am__tools_hex2hcd_SOURCES_DIST) tools/hid2hci.c tools/hwdb.c \ + $(am__tools_ibeacon_SOURCES_DIST) \ $(am__tools_l2cap_tester_SOURCES_DIST) tools/l2ping.c \ tools/l2test.c $(am__tools_mgmt_tester_SOURCES_DIST) \ $(am__tools_mpris_player_SOURCES_DIST) \ $(am__tools_obex_client_tool_SOURCES_DIST) \ $(am__tools_obex_server_tool_SOURCES_DIST) \ $(am__tools_obexctl_SOURCES_DIST) tools/rctest.c \ - tools/rfcomm.c $(am__tools_sco_tester_SOURCES_DIST) \ - tools/scotest.c $(am__tools_sdptool_SOURCES_DIST) \ + tools/rfcomm.c $(am__tools_rfcomm_tester_SOURCES_DIST) \ + $(am__tools_sco_tester_SOURCES_DIST) tools/scotest.c \ + $(am__tools_sdptool_SOURCES_DIST) \ + $(am__tools_seq2bseq_SOURCES_DIST) \ $(am__tools_smp_tester_SOURCES_DIST) \ - $(unit_test_avdtp_SOURCES) $(unit_test_crc_SOURCES) \ + $(unit_test_avctp_SOURCES) $(unit_test_avdtp_SOURCES) \ + $(unit_test_avrcp_SOURCES) $(unit_test_crc_SOURCES) \ $(unit_test_eir_SOURCES) $(unit_test_gdbus_client_SOURCES) \ $(unit_test_gobex_SOURCES) $(unit_test_gobex_apparam_SOURCES) \ $(unit_test_gobex_header_SOURCES) \ $(unit_test_gobex_packet_SOURCES) \ - $(unit_test_gobex_transfer_SOURCES) $(unit_test_lib_SOURCES) \ - $(unit_test_mgmt_SOURCES) $(unit_test_sdp_SOURCES) \ - $(unit_test_textfile_SOURCES) $(unit_test_uuid_SOURCES) + $(unit_test_gobex_transfer_SOURCES) $(unit_test_hfp_SOURCES) \ + $(unit_test_lib_SOURCES) $(unit_test_mgmt_SOURCES) \ + $(unit_test_queue_SOURCES) $(unit_test_ringbuf_SOURCES) \ + $(unit_test_sdp_SOURCES) $(unit_test_textfile_SOURCES) \ + $(unit_test_uuid_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -1319,9 +1765,13 @@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ +SBC_CFLAGS = @SBC_CFLAGS@ +SBC_LIBS = @SBC_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SPEEXDSP_CFLAGS = @SPEEXDSP_CFLAGS@ +SPEEXDSP_LIBS = @SPEEXDSP_LIBS@ STRIP = @STRIP@ SYSTEMD_SYSTEMUNITDIR = @SYSTEMD_SYSTEMUNITDIR@ SYSTEMD_USERUNITDIR = @SYSTEMD_USERUNITDIR@ @@ -1386,23 +1836,49 @@ AM_MAKEFLAGS = --no-print-directory lib_LTLIBRARIES = $(am__append_2) noinst_LIBRARIES = $(am__append_7) noinst_LTLIBRARIES = lib/libbluetooth-internal.la \ - gdbus/libgdbus-internal.la $(am__append_30) + gdbus/libgdbus-internal.la dist_man_MANS = $(am__append_18) $(am__append_20) dist_noinst_MANS = CLEANFILES = $(builtin_files) src/bluetooth.service \ obexd/src/builtin.h $(builtin_files) obexd/src/obex.service \ - $(am__append_31) + $(am__append_32) EXTRA_DIST = src/bluetooth.service.in src/org.bluez.service \ src/genbuiltin src/bluetooth.conf src/main.conf \ profiles/network/network.conf profiles/input/input.conf \ profiles/proximity/proximity.conf $(am__append_19) \ $(am__append_21) $(am__append_22) obexd/src/obex.service.in \ obexd/src/org.bluez.obex.service obexd/src/genbuiltin \ - android/Android.mk android/hal-ipc-api.txt android/README \ - android/pics-gap.txt android/pics-hid.txt android/pics-pan.txt \ - android/pics-did.txt android/pics-opp.txt \ - android/pics-pbap.txt tools/hid2hci.rules $(test_scripts) \ + android/Android.mk android/README android/init.bluetooth.rc \ + android/hal-ipc-api.txt android/audio-ipc-api.txt \ + android/pics-rfcomm.txt android/pics-spp.txt \ + android/pics-sdp.txt android/pics-l2cap.txt \ + android/pics-gap.txt android/pics-did.txt android/pics-hid.txt \ + android/pics-pan.txt android/pics-opp.txt android/pics-map.txt \ + android/pics-pbap.txt android/pics-a2dp.txt \ + android/pics-avctp.txt android/pics-avrcp.txt \ + android/pics-hsp.txt android/pics-hfp.txt \ + android/pics-gatt.txt android/pics-mcap.txt \ + android/pics-hdp.txt android/pics-iopt.txt android/pics-sm.txt \ + android/pics-mps.txt android/pixit-l2cap.txt \ + android/pixit-gap.txt android/pixit-did.txt \ + android/pixit-hid.txt android/pixit-pan.txt \ + android/pixit-opp.txt android/pixit-map.txt \ + android/pixit-pbap.txt android/pixit-a2dp.txt \ + android/pixit-avctp.txt android/pixit-avrcp.txt \ + android/pixit-hsp.txt android/pixit-hfp.txt \ + android/pixit-gatt.txt android/pixit-mcap.txt \ + android/pixit-hdp.txt android/pixit-iopt.txt \ + android/pixit-sm.txt android/pixit-mps.txt \ + android/bite-rfcomm.txt android/bite-spp.txt \ + android/pts-l2cap.txt android/pts-gap.txt android/pts-did.txt \ + android/pts-hid.txt android/pts-pan.txt android/pts-opp.txt \ + android/pts-map.txt android/pts-a2dp.txt android/pts-avrcp.txt \ + android/pts-avctp.txt android/pts-pbap.txt android/pts-hfp.txt \ + android/pts-gatt.txt android/pts-hsp.txt android/pts-iopt.txt \ + android/pts-hdp.txt android/pts-mcap.txt android/pts-mps.txt \ + android/pts-sm.txt tools/hid2hci.rules $(test_scripts) \ doc/assigned-numbers.txt doc/supported-features.txt \ + doc/test-coverage.txt doc/settings-storage.txt \ doc/mgmt-api.txt doc/adapter-api.txt doc/device-api.txt \ doc/agent-api.txt doc/profile-api.txt doc/network-api.txt \ doc/media-api.txt doc/health-api.txt doc/sap-api.txt \ @@ -1426,7 +1902,8 @@ AM_LDFLAGS = $(MISC_LDFLAGS) plugindir = $(libdir)/bluetooth/plugins @MAINTAINER_MODE_FALSE@build_plugindir = $(plugindir) @MAINTAINER_MODE_TRUE@build_plugindir = $(abs_top_srcdir)/plugins/.libs -plugin_LTLIBRARIES = $(am__append_12) $(am__append_13) +plugin_LTLIBRARIES = $(am__append_12) $(am__append_13) \ + $(am__append_30) lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \ @@ -1437,7 +1914,7 @@ extra_sources = lib/uuid.c local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file))) BUILT_SOURCES = $(local_headers) src/builtin.h obexd/src/builtin.h @LIBRARY_TRUE@lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) -@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:3:17 +@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:9:17 @LIBRARY_TRUE@lib_libbluetooth_la_DEPENDENCIES = $(local_headers) lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \ $(extra_headers) $(extra_sources) @@ -1481,6 +1958,7 @@ builtin_sources = plugins/hostname.c plugins/wiimote.c \ profiles/network/connection.c profiles/input/manager.c \ profiles/input/server.h profiles/input/server.c \ profiles/input/device.h profiles/input/device.c \ + profiles/input/uhid_copy.h profiles/input/hidp_defs.h \ profiles/input/hog.c profiles/input/uhid_copy.h \ profiles/input/suspend.h profiles/input/suspend-dummy.c \ $(am__append_9) profiles/gatt/gas.c profiles/scanparam/scan.c \ @@ -1509,7 +1987,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \ src/sdp-xml.h src/sdp-xml.c \ src/sdp-client.h src/sdp-client.c \ src/textfile.h src/textfile.c \ - src/glib-helper.h src/glib-helper.c \ + src/uuid-helper.h src/uuid-helper.c \ src/uinput.h \ src/plugin.h src/plugin.c \ src/storage.h src/storage.c \ @@ -1518,9 +1996,14 @@ src_bluetoothd_SOURCES = $(builtin_sources) \ src/adapter.h src/adapter.c \ src/profile.h src/profile.c \ src/service.h src/service.c \ + src/gatt-dbus.h src/gatt-dbus.c \ + src/gatt.h src/gatt.c \ src/device.h src/device.c src/attio.h \ src/dbus-common.c src/dbus-common.h \ src/eir.h src/eir.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/timeout.h src/shared/timeout-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c @@ -1543,14 +2026,22 @@ man_MANS = src/bluetoothd.8 test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \ test/monitor-bluetooth test/list-devices test/test-discovery \ test/test-manager test/test-adapter test/test-device \ - test/simple-agent test/simple-service test/simple-endpoint \ - test/test-sap-server test/test-proximity test/test-network \ - test/test-thermometer test/test-profile test/test-health \ - test/test-health-sink test/service-record.dtd \ - test/service-did.xml test/service-spp.xml test/service-opp.xml \ - test/service-ftp.xml test/simple-player test/test-nap \ - test/test-heartrate test/test-alert test/test-hfp \ - test/test-cyclingspeed + test/simple-agent test/simple-endpoint test/test-sap-server \ + test/test-proximity test/test-network test/test-thermometer \ + test/test-profile test/test-health test/test-health-sink \ + test/service-record.dtd test/service-did.xml \ + test/service-spp.xml test/service-opp.xml test/service-ftp.xml \ + test/simple-player test/test-nap test/test-heartrate \ + test/test-alert test/test-hfp test/test-cyclingspeed \ + test/opp-client test/ftp-client test/pbap-client \ + test/map-client +unit_tests = $(am__append_31) unit/test-eir unit/test-uuid \ + unit/test-textfile unit/test-crc unit/test-ringbuf \ + unit/test-queue unit/test-mgmt unit/test-sdp unit/test-avdtp \ + unit/test-avctp unit/test-avrcp unit/test-hfp \ + unit/test-gdbus-client unit/test-gobex-header \ + unit/test-gobex-packet unit/test-gobex \ + unit/test-gobex-transfer unit/test-gobex-apparam unit/test-lib @CLIENT_TRUE@client_bluetoothctl_SOURCES = client/main.c \ @CLIENT_TRUE@ client/display.h client/display.c \ @CLIENT_TRUE@ client/agent.h client/agent.c \ @@ -1560,86 +2051,170 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \ @CLIENT_TRUE@ -lreadline @MONITOR_TRUE@monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \ -@MONITOR_TRUE@ monitor/mainloop.h monitor/mainloop.c \ -@MONITOR_TRUE@ monitor/display.h monitor/display.c \ -@MONITOR_TRUE@ monitor/hcidump.h monitor/hcidump.c \ -@MONITOR_TRUE@ monitor/btsnoop.h monitor/btsnoop.c \ -@MONITOR_TRUE@ monitor/control.h monitor/control.c \ -@MONITOR_TRUE@ monitor/packet.h monitor/packet.c \ -@MONITOR_TRUE@ monitor/vendor.h monitor/vendor.c \ -@MONITOR_TRUE@ monitor/lmp.h monitor/lmp.c \ -@MONITOR_TRUE@ monitor/l2cap.h monitor/l2cap.c \ -@MONITOR_TRUE@ monitor/uuid.h monitor/uuid.c \ -@MONITOR_TRUE@ monitor/sdp.h monitor/sdp.c \ -@MONITOR_TRUE@ monitor/crc.h monitor/crc.c \ -@MONITOR_TRUE@ monitor/ll.h monitor/ll.c - -@MONITOR_TRUE@monitor_btmon_LDADD = lib/libbluetooth-internal.la +@MONITOR_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@MONITOR_TRUE@ monitor/display.h monitor/display.c \ +@MONITOR_TRUE@ monitor/hcidump.h monitor/hcidump.c \ +@MONITOR_TRUE@ monitor/ellisys.h monitor/ellisys.c \ +@MONITOR_TRUE@ monitor/control.h monitor/control.c \ +@MONITOR_TRUE@ monitor/packet.h monitor/packet.c \ +@MONITOR_TRUE@ monitor/vendor.h monitor/vendor.c \ +@MONITOR_TRUE@ monitor/lmp.h monitor/lmp.c \ +@MONITOR_TRUE@ monitor/crc.h monitor/crc.c \ +@MONITOR_TRUE@ monitor/ll.h monitor/ll.c \ +@MONITOR_TRUE@ monitor/l2cap.h monitor/l2cap.c \ +@MONITOR_TRUE@ monitor/sdp.h monitor/sdp.c \ +@MONITOR_TRUE@ monitor/uuid.h monitor/uuid.c \ +@MONITOR_TRUE@ monitor/hwdb.h monitor/hwdb.c \ +@MONITOR_TRUE@ monitor/keys.h monitor/keys.c \ +@MONITOR_TRUE@ monitor/analyze.h monitor/analyze.c \ +@MONITOR_TRUE@ src/shared/util.h src/shared/util.c \ +@MONITOR_TRUE@ src/shared/queue.h src/shared/queue.c \ +@MONITOR_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@MONITOR_TRUE@ src/shared/btsnoop.h src/shared/btsnoop.c + +@MONITOR_TRUE@monitor_btmon_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@ @EXPERIMENTAL_TRUE@emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \ -@EXPERIMENTAL_TRUE@ monitor/mainloop.h monitor/mainloop.c \ -@EXPERIMENTAL_TRUE@ emulator/server.h emulator/server.c \ -@EXPERIMENTAL_TRUE@ emulator/vhci.h emulator/vhci.c \ -@EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ -@EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ -@EXPERIMENTAL_TRUE@ emulator/amp.h emulator/amp.c +@EXPERIMENTAL_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@EXPERIMENTAL_TRUE@ emulator/server.h emulator/server.c \ +@EXPERIMENTAL_TRUE@ emulator/vhci.h emulator/vhci.c \ +@EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ +@EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ +@EXPERIMENTAL_TRUE@ emulator/smp.c \ +@EXPERIMENTAL_TRUE@ emulator/amp.h emulator/amp.c \ +@EXPERIMENTAL_TRUE@ emulator/le.h emulator/le.c +@EXPERIMENTAL_TRUE@emulator_btvirt_LDADD = lib/libbluetooth-internal.la @EXPERIMENTAL_TRUE@emulator_b1ee_SOURCES = emulator/b1ee.c monitor/mainloop.h monitor/mainloop.c +@EXPERIMENTAL_TRUE@emulator_hfp_SOURCES = emulator/hfp.c \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.h src/shared/ringbuf.c \ +@EXPERIMENTAL_TRUE@ src/shared/hfp.h src/shared/hfp.c + +@EXPERIMENTAL_TRUE@tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/hci.h src/shared/hci.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.h src/shared/ringbuf.c + @EXPERIMENTAL_TRUE@tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \ @EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ @EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ +@EXPERIMENTAL_TRUE@ emulator/smp.c \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ @EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ -@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c +@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @EXPERIMENTAL_TRUE@tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \ @EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ @EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ +@EXPERIMENTAL_TRUE@ emulator/smp.c \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ @EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ -@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c +@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +@EXPERIMENTAL_TRUE@tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \ +@EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ +@EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ +@EXPERIMENTAL_TRUE@ emulator/smp.c \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ +@EXPERIMENTAL_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ +@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h src/shared/timeout-glib.c + +@EXPERIMENTAL_TRUE@tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @EXPERIMENTAL_TRUE@tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \ @EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ @EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ +@EXPERIMENTAL_TRUE@ emulator/smp.c \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ @EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ -@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c +@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@tools_smp_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @EXPERIMENTAL_TRUE@tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \ @EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ @EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ +@EXPERIMENTAL_TRUE@ emulator/smp.c \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ -@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c +@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h src/shared/timeout-glib.c + +@EXPERIMENTAL_TRUE@tools_gap_tester_LDADD = lib/libbluetooth-internal.la \ +@EXPERIMENTAL_TRUE@ gdbus/libgdbus-internal.la \ +@EXPERIMENTAL_TRUE@ @GLIB_LIBS@ @DBUS_LIBS@ -@EXPERIMENTAL_TRUE@tools_gap_tester_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ @EXPERIMENTAL_TRUE@tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \ @EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ @EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ +@EXPERIMENTAL_TRUE@ emulator/smp.c \ +@EXPERIMENTAL_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ @EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ @EXPERIMENTAL_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ -@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c +@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h src/shared/timeout-glib.c @EXPERIMENTAL_TRUE@tools_sco_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +@EXPERIMENTAL_TRUE@tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@EXPERIMENTAL_TRUE@ src/shared/hci.h src/shared/hci.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.h src/shared/ringbuf.c \ +@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c + +@EXPERIMENTAL_TRUE@tools_hci_tester_LDADD = @GLIB_LIBS@ @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_intel.c +@TOOLS_TRUE@ tools/hciattach_intel.c \ +@TOOLS_TRUE@ tools/hciattach_bcm43xx.c @TOOLS_TRUE@tools_hciattach_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c @TOOLS_TRUE@tools_hciconfig_LDADD = lib/libbluetooth-internal.la @TOOLS_TRUE@tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c -@TOOLS_TRUE@tools_hcitool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @UDEV_LIBS@ +@TOOLS_TRUE@tools_hcitool_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@ @TOOLS_TRUE@tools_hcidump_SOURCES = tools/hcidump.c \ @TOOLS_TRUE@ tools/parser/parser.h tools/parser/parser.c \ @TOOLS_TRUE@ tools/parser/lmp.c \ @@ -1680,6 +2255,14 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \ @TOOLS_TRUE@ tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c @TOOLS_TRUE@tools_bccmd_LDADD = lib/libbluetooth-internal.la +@TOOLS_TRUE@tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h \ +@TOOLS_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@TOOLS_TRUE@ src/shared/io.h src/shared/io-mainloop.c \ +@TOOLS_TRUE@ src/shared/hci.h src/shared/hci.c \ +@TOOLS_TRUE@ src/shared/util.h src/shared/util.c \ +@TOOLS_TRUE@ src/shared/queue.h src/shared/queue.c \ +@TOOLS_TRUE@ src/shared/ringbuf.h src/shared/ringbuf.c + @HID2HCI_TRUE@udevdir = @UDEV_DIR@ @HID2HCI_TRUE@tools_hid2hci_LDADD = @UDEV_LIBS@ @EXPERIMENTAL_TRUE@tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c @@ -1690,22 +2273,50 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \ @EXPERIMENTAL_TRUE@tools_amptest_LDADD = lib/libbluetooth-internal.la @EXPERIMENTAL_TRUE@tools_hwdb_LDADD = lib/libbluetooth-internal.la @EXPERIMENTAL_TRUE@tools_hcieventmask_LDADD = lib/libbluetooth-internal.la -@EXPERIMENTAL_TRUE@tools_btmgmt_SOURCES = tools/btmgmt.c src/glib-helper.c src/eir.c \ +@EXPERIMENTAL_TRUE@tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ @EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ @EXPERIMENTAL_TRUE@ src/shared/mgmt.h src/shared/mgmt.c -@EXPERIMENTAL_TRUE@tools_btmgmt_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -@EXPERIMENTAL_TRUE@tools_btinfo_SOURCES = tools/btinfo.c +@EXPERIMENTAL_TRUE@tools_btmgmt_LDADD = lib/libbluetooth-internal.la +@EXPERIMENTAL_TRUE@tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/hci.h src/shared/hci.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.h src/shared/ringbuf.c + @EXPERIMENTAL_TRUE@tools_btsnoop_SOURCES = tools/btsnoop.c \ @EXPERIMENTAL_TRUE@ src/shared/pcap.h src/shared/pcap.c \ @EXPERIMENTAL_TRUE@ src/shared/btsnoop.h src/shared/btsnoop.c +@EXPERIMENTAL_TRUE@tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c + @EXPERIMENTAL_TRUE@tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c @EXPERIMENTAL_TRUE@tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @EXPERIMENTAL_TRUE@tools_mpris_player_SOURCES = tools/mpris-player.c @EXPERIMENTAL_TRUE@tools_mpris_player_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ @EXPERIMENTAL_TRUE@tools_cltest_SOURCES = tools/cltest.c monitor/mainloop.h monitor/mainloop.c @EXPERIMENTAL_TRUE@tools_cltest_LDADD = lib/libbluetooth-internal.la +@EXPERIMENTAL_TRUE@tools_seq2bseq_SOURCES = tools/seq2bseq.c +@EXPERIMENTAL_TRUE@tools_hex2hcd_SOURCES = tools/hex2hcd.c +@EXPERIMENTAL_TRUE@tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h \ +@EXPERIMENTAL_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/io.h src/shared/io-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/timeout.h \ +@EXPERIMENTAL_TRUE@ src/shared/timeout-mainloop.c \ +@EXPERIMENTAL_TRUE@ src/shared/hci.h src/shared/hci.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/queue.h src/shared/queue.c \ +@EXPERIMENTAL_TRUE@ src/shared/ringbuf.h src/shared/ringbuf.c + @READLINE_TRUE@attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \ @READLINE_TRUE@ attrib/gattrib.c btio/btio.c \ @READLINE_TRUE@ attrib/gatttool.h attrib/interactive.c \ @@ -1735,6 +2346,8 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \ @READLINE_TRUE@tools_obexctl_LDADD = gdbus/libgdbus-internal.la \ @READLINE_TRUE@ @GLIB_LIBS@ @DBUS_LIBS@ -lreadline +@EXPERIMENTAL_TRUE@tools_gatt_service_SOURCES = tools/gatt-service.c +@EXPERIMENTAL_TRUE@tools_gatt_service_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ gdbus/libgdbus-internal.la @EXPERIMENTAL_TRUE@profiles_iap_iapd_SOURCES = profiles/iap/main.c @EXPERIMENTAL_TRUE@profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ @CUPS_TRUE@cupsdir = $(libdir)/cups/backend @@ -1806,36 +2419,63 @@ obexd_src_obexd_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/obexd/src \ obexd_src_obexd_SHORTNAME = obexd obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist) nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files) +@ANDROID_TRUE@android_plugindir = $(abs_top_srcdir)/android/.libs @ANDROID_TRUE@android_system_emulator_SOURCES = android/system-emulator.c \ @ANDROID_TRUE@ monitor/mainloop.h monitor/mainloop.c +@ANDROID_TRUE@android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c \ +@ANDROID_TRUE@ monitor/mainloop.h monitor/mainloop.c \ +@ANDROID_TRUE@ src/shared/btsnoop.h src/shared/btsnoop.c + @ANDROID_TRUE@android_bluetoothd_SOURCES = android/main.c \ @ANDROID_TRUE@ src/log.c \ @ANDROID_TRUE@ android/hal-msg.h \ +@ANDROID_TRUE@ android/audio-msg.h \ +@ANDROID_TRUE@ android/sco-msg.h \ @ANDROID_TRUE@ android/utils.h \ @ANDROID_TRUE@ src/sdpd-database.c src/sdpd-server.c \ @ANDROID_TRUE@ src/sdpd-service.c src/sdpd-request.c \ -@ANDROID_TRUE@ src/glib-helper.h src/glib-helper.c \ +@ANDROID_TRUE@ src/uuid-helper.h src/uuid-helper.c \ @ANDROID_TRUE@ src/eir.h src/eir.c \ +@ANDROID_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@ANDROID_TRUE@ src/shared/queue.h src/shared/queue.c \ @ANDROID_TRUE@ src/shared/util.h src/shared/util.c \ @ANDROID_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ +@ANDROID_TRUE@ src/shared/ringbuf.h src/shared/ringbuf.c \ +@ANDROID_TRUE@ src/shared/hfp.h src/shared/hfp.c \ +@ANDROID_TRUE@ src/shared/gatt-db.h src/shared/gatt-db.c \ @ANDROID_TRUE@ android/bluetooth.h android/bluetooth.c \ @ANDROID_TRUE@ android/hidhost.h android/hidhost.c \ +@ANDROID_TRUE@ android/ipc-common.h \ @ANDROID_TRUE@ android/ipc.h android/ipc.c \ @ANDROID_TRUE@ android/avdtp.h android/avdtp.c \ @ANDROID_TRUE@ android/a2dp.h android/a2dp.c \ +@ANDROID_TRUE@ android/avctp.h android/avctp.c \ +@ANDROID_TRUE@ android/avrcp.h android/avrcp.c \ +@ANDROID_TRUE@ android/avrcp-lib.h android/avrcp-lib.c \ @ANDROID_TRUE@ android/socket.h android/socket.c \ @ANDROID_TRUE@ android/pan.h android/pan.c \ +@ANDROID_TRUE@ android/handsfree.h android/handsfree.c \ +@ANDROID_TRUE@ android/gatt.h android/gatt.c \ +@ANDROID_TRUE@ android/health.h android/health.c \ +@ANDROID_TRUE@ android/mcap-lib.h android/mcap-lib.c \ +@ANDROID_TRUE@ attrib/att.c attrib/att.h \ +@ANDROID_TRUE@ attrib/gatt.c attrib/gatt.h \ +@ANDROID_TRUE@ attrib/gattrib.c attrib/gattrib.h \ @ANDROID_TRUE@ btio/btio.h btio/btio.c \ @ANDROID_TRUE@ src/sdp-client.h src/sdp-client.c \ @ANDROID_TRUE@ profiles/network/bnep.h profiles/network/bnep.c @ANDROID_TRUE@android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -@ANDROID_TRUE@android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \ -@ANDROID_TRUE@ android/hal-sock.c \ +@ANDROID_TRUE@android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \ +@ANDROID_TRUE@ android/hal-socket.c \ @ANDROID_TRUE@ android/hal-hidhost.c \ +@ANDROID_TRUE@ android/hal-health.c \ @ANDROID_TRUE@ android/hal-pan.c \ @ANDROID_TRUE@ android/hal-a2dp.c \ +@ANDROID_TRUE@ android/hal-avrcp.c \ +@ANDROID_TRUE@ android/hal-handsfree.c \ +@ANDROID_TRUE@ android/hal-gatt.c \ @ANDROID_TRUE@ android/hardware/bluetooth.h \ @ANDROID_TRUE@ android/hardware/bt_av.h \ @ANDROID_TRUE@ android/hardware/bt_gatt.h \ @@ -1850,10 +2490,15 @@ nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files) @ANDROID_TRUE@ android/hardware/bt_sock.h \ @ANDROID_TRUE@ android/hardware/hardware.h \ @ANDROID_TRUE@ android/cutils/properties.h \ +@ANDROID_TRUE@ android/ipc-common.h \ @ANDROID_TRUE@ android/hal-log.h \ -@ANDROID_TRUE@ android/hal-ipc.h android/hal-ipc.c +@ANDROID_TRUE@ android/hal-ipc.h android/hal-ipc.c \ +@ANDROID_TRUE@ android/hal-utils.h android/hal-utils.c + +@ANDROID_TRUE@android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android +@ANDROID_TRUE@android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ +@ANDROID_TRUE@ -no-undefined -@ANDROID_TRUE@android_libhal_internal_la_CPPFLAGS = -I$(srcdir)/android @ANDROID_TRUE@android_haltest_SOURCES = android/client/haltest.c \ @ANDROID_TRUE@ android/client/pollhandler.h \ @ANDROID_TRUE@ android/client/pollhandler.c \ @@ -1864,33 +2509,100 @@ nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files) @ANDROID_TRUE@ android/client/tabcompletion.c \ @ANDROID_TRUE@ android/client/if-main.h \ @ANDROID_TRUE@ android/client/if-av.c \ +@ANDROID_TRUE@ android/client/if-rc.c \ @ANDROID_TRUE@ android/client/if-bt.c \ @ANDROID_TRUE@ android/client/if-gatt.c \ @ANDROID_TRUE@ android/client/if-hf.c \ @ANDROID_TRUE@ android/client/if-hh.c \ @ANDROID_TRUE@ android/client/if-pan.c \ +@ANDROID_TRUE@ android/client/if-hl.c \ @ANDROID_TRUE@ android/client/if-sock.c \ -@ANDROID_TRUE@ android/client/hwmodule.c \ +@ANDROID_TRUE@ android/client/if-audio.c \ +@ANDROID_TRUE@ android/client/if-sco.c \ +@ANDROID_TRUE@ android/hardware/hardware.c \ @ANDROID_TRUE@ android/hal-utils.h android/hal-utils.c -@ANDROID_TRUE@android_haltest_LDADD = android/libhal-internal.la @ANDROID_TRUE@android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ -@ANDROID_TRUE@ -DPLATFORM_SDK_VERSION=19 +@ANDROID_TRUE@ -DPLUGINDIR=\""$(android_plugindir)"\" + +@ANDROID_TRUE@android_haltest_LDFLAGS = -pthread -ldl -lm +@ANDROID_TRUE@android_android_tester_SOURCES = emulator/btdev.h emulator/btdev.c \ +@ANDROID_TRUE@ emulator/bthost.h emulator/bthost.c \ +@ANDROID_TRUE@ emulator/smp.c \ +@ANDROID_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@ANDROID_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@ANDROID_TRUE@ src/shared/queue.h src/shared/queue.c \ +@ANDROID_TRUE@ src/shared/util.h src/shared/util.c \ +@ANDROID_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ +@ANDROID_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ +@ANDROID_TRUE@ src/shared/tester.h src/shared/tester.c \ +@ANDROID_TRUE@ src/shared/timeout.h src/shared/timeout-glib.c \ +@ANDROID_TRUE@ monitor/rfcomm.h \ +@ANDROID_TRUE@ android/hardware/hardware.c \ +@ANDROID_TRUE@ android/android-tester.c + +@ANDROID_TRUE@android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ +@ANDROID_TRUE@ -DPLUGINDIR=\""$(android_plugindir)"\" + +@ANDROID_TRUE@android_android_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +@ANDROID_TRUE@android_android_tester_LDFLAGS = -pthread -ldl +@ANDROID_TRUE@android_ipc_tester_SOURCES = emulator/btdev.h emulator/btdev.c \ +@ANDROID_TRUE@ emulator/bthost.h emulator/bthost.c \ +@ANDROID_TRUE@ emulator/smp.c \ +@ANDROID_TRUE@ src/shared/crypto.h src/shared/crypto.c \ +@ANDROID_TRUE@ src/shared/io.h src/shared/io-glib.c \ +@ANDROID_TRUE@ src/shared/queue.h src/shared/queue.c \ +@ANDROID_TRUE@ src/shared/util.h src/shared/util.c \ +@ANDROID_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ +@ANDROID_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ +@ANDROID_TRUE@ src/shared/tester.h src/shared/tester.c \ +@ANDROID_TRUE@ src/shared/timeout.h src/shared/timeout-glib.c \ +@ANDROID_TRUE@ android/hal-utils.h android/hal-utils.c \ +@ANDROID_TRUE@ android/ipc-common.h android/ipc-tester.c + +@ANDROID_TRUE@android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android +@ANDROID_TRUE@android_ipc_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +@ANDROID_TRUE@android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \ +@ANDROID_TRUE@ android/hal-msg.h \ +@ANDROID_TRUE@ android/hal-audio.c \ +@ANDROID_TRUE@ android/hardware/audio.h \ +@ANDROID_TRUE@ android/hardware/audio_effect.h \ +@ANDROID_TRUE@ android/hardware/hardware.h \ +@ANDROID_TRUE@ android/system/audio.h + +@ANDROID_TRUE@android_audio_sco_default_la_SOURCES = android/hal-log.h \ +@ANDROID_TRUE@ android/sco-msg.h \ +@ANDROID_TRUE@ android/hal-sco.c \ +@ANDROID_TRUE@ android/hardware/audio.h \ +@ANDROID_TRUE@ android/hardware/audio_effect.h \ +@ANDROID_TRUE@ android/hardware/hardware.h \ +@ANDROID_TRUE@ android/audio_utils/resampler.c \ +@ANDROID_TRUE@ android/audio_utils/resampler.h \ +@ANDROID_TRUE@ android/system/audio.h + +@ANDROID_TRUE@android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android +@ANDROID_TRUE@android_audio_sco_default_la_LIBADD = @SPEEXDSP_LIBS@ +@ANDROID_TRUE@android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ +@ANDROID_TRUE@ -no-undefined -lrt + +@ANDROID_TRUE@android_test_ipc_SOURCES = android/test-ipc.c \ +@ANDROID_TRUE@ src/shared/util.h src/shared/util.c \ +@ANDROID_TRUE@ src/log.h src/log.c \ +@ANDROID_TRUE@ android/ipc-common.h \ +@ANDROID_TRUE@ android/ipc.c android/ipc.h + +@ANDROID_TRUE@android_test_ipc_LDADD = @GLIB_LIBS@ +@ANDROID_TRUE@android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android +@ANDROID_TRUE@android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@ +@ANDROID_TRUE@android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ +@ANDROID_TRUE@ -no-undefined -pthread -lrt -@ANDROID_TRUE@android_haltest_LDFLAGS = -pthread @HID2HCI_TRUE@rulesdir = @UDEV_DIR@/rules.d @HID2HCI_TRUE@rules_DATA = tools/97-hid2hci.rules @TEST_TRUE@testdir = $(pkglibdir)/test @TEST_TRUE@test_SCRIPTS = $(test_scripts) -AM_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \ - -I$(srcdir)/gdbus -I$(srcdir)/btio - -unit_tests = unit/test-eir unit/test-uuid unit/test-textfile \ - unit/test-crc unit/test-mgmt unit/test-sdp unit/test-avdtp \ - unit/test-gdbus-client unit/test-gobex-header \ - unit/test-gobex-packet unit/test-gobex \ - unit/test-gobex-transfer unit/test-gobex-apparam unit/test-lib -unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/glib-helper.c +AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus +unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c unit_test_eir_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ unit_test_uuid_SOURCES = unit/test-uuid.c unit_test_uuid_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @@ -1898,7 +2610,19 @@ unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c unit_test_textfile_LDADD = @GLIB_LIBS@ unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c unit_test_crc_LDADD = @GLIB_LIBS@ +unit_test_ringbuf_SOURCES = unit/test-ringbuf.c \ + src/shared/util.h src/shared/util.c \ + src/shared/ringbuf.h src/shared/ringbuf.c + +unit_test_ringbuf_LDADD = @GLIB_LIBS@ +unit_test_queue_SOURCES = unit/test-queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c + +unit_test_queue_LDADD = @GLIB_LIBS@ unit_test_mgmt_SOURCES = unit/test-mgmt.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c @@ -1906,6 +2630,7 @@ unit_test_mgmt_LDADD = @GLIB_LIBS@ unit_test_sdp_SOURCES = unit/test-sdp.c \ src/shared/util.h src/shared/util.c \ src/sdpd.h src/sdpd-database.c \ + src/log.h src/log.c \ src/sdpd-service.c src/sdpd-request.c unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @@ -1915,6 +2640,28 @@ unit_test_avdtp_SOURCES = unit/test-avdtp.c \ android/avdtp.c android/avdtp.h unit_test_avdtp_LDADD = @GLIB_LIBS@ +unit_test_avctp_SOURCES = unit/test-avctp.c \ + src/shared/util.h src/shared/util.c \ + src/log.h src/log.c \ + android/avctp.c android/avctp.h + +unit_test_avctp_LDADD = @GLIB_LIBS@ +unit_test_avrcp_SOURCES = unit/test-avrcp.c \ + src/shared/util.h src/shared/util.c \ + src/log.h src/log.c \ + android/avctp.c android/avctp.h \ + android/avrcp-lib.c android/avrcp-lib.h + +unit_test_avrcp_LDADD = @GLIB_LIBS@ lib/libbluetooth-internal.la +unit_test_hfp_SOURCES = unit/test-hfp.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/ringbuf.h src/shared/ringbuf.c \ + src/shared/hfp.h src/shared/hfp.c + +unit_test_hfp_LDADD = @GLIB_LIBS@ unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \ @GLIB_LIBS@ @DBUS_LIBS@ @@ -1944,8 +2691,7 @@ unit_test_lib_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ pkgconfigdir = $(libdir)/pkgconfig @LIBRARY_TRUE@pkgconfig_DATA = lib/bluez.pc DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \ - --disable-systemd --disable-udev \ - --enable-android + --disable-systemd --disable-udev DISTCLEANFILES = $(pkgconfig_DATA) MAINTAINERCLEANFILES = Makefile.in \ @@ -2109,20 +2855,47 @@ android/$(am__dirstamp): android/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) android/$(DEPDIR) @: > android/$(DEPDIR)/$(am__dirstamp) -android/android_libhal_internal_la-hal-bluetooth.lo: \ +android/android_audio_a2dp_default_la-hal-audio.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/audio.a2dp.default.la: $(android_audio_a2dp_default_la_OBJECTS) $(android_audio_a2dp_default_la_DEPENDENCIES) $(EXTRA_android_audio_a2dp_default_la_DEPENDENCIES) android/$(am__dirstamp) + $(AM_V_CCLD)$(android_audio_a2dp_default_la_LINK) $(am_android_audio_a2dp_default_la_rpath) $(android_audio_a2dp_default_la_OBJECTS) $(android_audio_a2dp_default_la_LIBADD) $(LIBS) +android/android_audio_sco_default_la-hal-sco.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/audio_utils/$(am__dirstamp): + @$(MKDIR_P) android/audio_utils + @: > android/audio_utils/$(am__dirstamp) +android/audio_utils/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) android/audio_utils/$(DEPDIR) + @: > android/audio_utils/$(DEPDIR)/$(am__dirstamp) +android/audio_utils/android_audio_sco_default_la-resampler.lo: \ + android/audio_utils/$(am__dirstamp) \ + android/audio_utils/$(DEPDIR)/$(am__dirstamp) +android/audio.sco.default.la: $(android_audio_sco_default_la_OBJECTS) $(android_audio_sco_default_la_DEPENDENCIES) $(EXTRA_android_audio_sco_default_la_DEPENDENCIES) android/$(am__dirstamp) + $(AM_V_CCLD)$(android_audio_sco_default_la_LINK) $(am_android_audio_sco_default_la_rpath) $(android_audio_sco_default_la_OBJECTS) $(android_audio_sco_default_la_LIBADD) $(LIBS) +android/android_bluetooth_default_la-hal-bluetooth.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) -android/android_libhal_internal_la-hal-sock.lo: \ +android/android_bluetooth_default_la-hal-socket.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) -android/android_libhal_internal_la-hal-hidhost.lo: \ +android/android_bluetooth_default_la-hal-hidhost.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) -android/android_libhal_internal_la-hal-pan.lo: \ +android/android_bluetooth_default_la-hal-health.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) -android/android_libhal_internal_la-hal-a2dp.lo: \ +android/android_bluetooth_default_la-hal-pan.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) -android/android_libhal_internal_la-hal-ipc.lo: \ +android/android_bluetooth_default_la-hal-a2dp.lo: \ android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) -android/libhal-internal.la: $(android_libhal_internal_la_OBJECTS) $(android_libhal_internal_la_DEPENDENCIES) $(EXTRA_android_libhal_internal_la_DEPENDENCIES) android/$(am__dirstamp) - $(AM_V_CCLD)$(LINK) $(am_android_libhal_internal_la_rpath) $(android_libhal_internal_la_OBJECTS) $(android_libhal_internal_la_LIBADD) $(LIBS) +android/android_bluetooth_default_la-hal-avrcp.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_bluetooth_default_la-hal-handsfree.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_bluetooth_default_la-hal-gatt.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_bluetooth_default_la-hal-ipc.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_bluetooth_default_la-hal-utils.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/bluetooth.default.la: $(android_bluetooth_default_la_OBJECTS) $(android_bluetooth_default_la_DEPENDENCIES) $(EXTRA_android_bluetooth_default_la_DEPENDENCIES) android/$(am__dirstamp) + $(AM_V_CCLD)$(android_bluetooth_default_la_LINK) $(am_android_bluetooth_default_la_rpath) $(android_bluetooth_default_la_OBJECTS) $(android_bluetooth_default_la_LIBADD) $(LIBS) gdbus/$(am__dirstamp): @$(MKDIR_P) gdbus @: > gdbus/$(am__dirstamp) @@ -2358,6 +3131,62 @@ clean-udevPROGRAMS: list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list +emulator/$(am__dirstamp): + @$(MKDIR_P) emulator + @: > emulator/$(am__dirstamp) +emulator/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) emulator/$(DEPDIR) + @: > emulator/$(DEPDIR)/$(am__dirstamp) +emulator/android_android_tester-btdev.$(OBJEXT): \ + emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) +emulator/android_android_tester-bthost.$(OBJEXT): \ + emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) +emulator/android_android_tester-smp.$(OBJEXT): \ + emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) +src/shared/$(am__dirstamp): + @$(MKDIR_P) src/shared + @: > src/shared/$(am__dirstamp) +src/shared/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/shared/$(DEPDIR) + @: > src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_android_tester-crypto.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_android_tester-io-glib.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_android_tester-queue.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_android_tester-util.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_android_tester-mgmt.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_android_tester-hciemu.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_android_tester-tester.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_android_tester-timeout-glib.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +android/hardware/$(am__dirstamp): + @$(MKDIR_P) android/hardware + @: > android/hardware/$(am__dirstamp) +android/hardware/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) android/hardware/$(DEPDIR) + @: > android/hardware/$(DEPDIR)/$(am__dirstamp) +android/hardware/android_android_tester-hardware.$(OBJEXT): \ + android/hardware/$(am__dirstamp) \ + android/hardware/$(DEPDIR)/$(am__dirstamp) +android/android_android_tester-android-tester.$(OBJEXT): \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android-tester$(EXEEXT): $(android_android_tester_OBJECTS) $(android_android_tester_DEPENDENCIES) $(EXTRA_android_android_tester_DEPENDENCIES) android/$(am__dirstamp) + @rm -f android/android-tester$(EXEEXT) + $(AM_V_CCLD)$(android_android_tester_LINK) $(android_android_tester_OBJECTS) $(android_android_tester_LDADD) $(LIBS) android/main.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) src/$(am__dirstamp): @@ -2375,19 +3204,23 @@ src/sdpd-service.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/sdpd-request.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) -src/glib-helper.$(OBJEXT): src/$(am__dirstamp) \ +src/uuid-helper.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/eir.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) -src/shared/$(am__dirstamp): - @$(MKDIR_P) src/shared - @: > src/shared/$(am__dirstamp) -src/shared/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) src/shared/$(DEPDIR) - @: > src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/io-glib.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/queue.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/util.$(OBJEXT): src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/ringbuf.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/hfp.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/gatt-db.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) android/bluetooth.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/hidhost.$(OBJEXT): android/$(am__dirstamp) \ @@ -2398,10 +3231,36 @@ android/avdtp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/a2dp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) +android/avctp.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/avrcp.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/avrcp-lib.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) android/socket.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/pan.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) +android/handsfree.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/gatt.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/health.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/mcap-lib.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +attrib/$(am__dirstamp): + @$(MKDIR_P) attrib + @: > attrib/$(am__dirstamp) +attrib/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) attrib/$(DEPDIR) + @: > 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) @@ -2423,6 +3282,21 @@ profiles/network/bnep.$(OBJEXT): profiles/network/$(am__dirstamp) \ android/bluetoothd$(EXEEXT): $(android_bluetoothd_OBJECTS) $(android_bluetoothd_DEPENDENCIES) $(EXTRA_android_bluetoothd_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/bluetoothd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(android_bluetoothd_OBJECTS) $(android_bluetoothd_LDADD) $(LIBS) +android/bluetoothd-snoop.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +monitor/$(am__dirstamp): + @$(MKDIR_P) monitor + @: > monitor/$(am__dirstamp) +monitor/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) monitor/$(DEPDIR) + @: > monitor/$(DEPDIR)/$(am__dirstamp) +monitor/mainloop.$(OBJEXT): monitor/$(am__dirstamp) \ + monitor/$(DEPDIR)/$(am__dirstamp) +src/shared/btsnoop.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +android/bluetoothd-snoop$(EXEEXT): $(android_bluetoothd_snoop_OBJECTS) $(android_bluetoothd_snoop_DEPENDENCIES) $(EXTRA_android_bluetoothd_snoop_DEPENDENCIES) android/$(am__dirstamp) + @rm -f android/bluetoothd-snoop$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(android_bluetoothd_snoop_OBJECTS) $(android_bluetoothd_snoop_LDADD) $(LIBS) android/client/$(am__dirstamp): @$(MKDIR_P) android/client @: > android/client/$(am__dirstamp) @@ -2447,6 +3321,9 @@ android/client/android_haltest-tabcompletion.$(OBJEXT): \ android/client/android_haltest-if-av.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-rc.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) android/client/android_haltest-if-bt.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) @@ -2462,44 +3339,75 @@ android/client/android_haltest-if-hh.$(OBJEXT): \ android/client/android_haltest-if-pan.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-hl.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) android/client/android_haltest-if-sock.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) -android/client/android_haltest-hwmodule.$(OBJEXT): \ +android/client/android_haltest-if-audio.$(OBJEXT): \ android/client/$(am__dirstamp) \ android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-sco.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/hardware/android_haltest-hardware.$(OBJEXT): \ + android/hardware/$(am__dirstamp) \ + android/hardware/$(DEPDIR)/$(am__dirstamp) android/android_haltest-hal-utils.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/haltest$(EXEEXT): $(android_haltest_OBJECTS) $(android_haltest_DEPENDENCIES) $(EXTRA_android_haltest_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/haltest$(EXEEXT) $(AM_V_CCLD)$(android_haltest_LINK) $(android_haltest_OBJECTS) $(android_haltest_LDADD) $(LIBS) +emulator/android_ipc_tester-btdev.$(OBJEXT): emulator/$(am__dirstamp) \ + emulator/$(DEPDIR)/$(am__dirstamp) +emulator/android_ipc_tester-bthost.$(OBJEXT): \ + emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp) +emulator/android_ipc_tester-smp.$(OBJEXT): emulator/$(am__dirstamp) \ + emulator/$(DEPDIR)/$(am__dirstamp) +src/shared/android_ipc_tester-crypto.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_ipc_tester-io-glib.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_ipc_tester-queue.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_ipc_tester-util.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_ipc_tester-mgmt.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_ipc_tester-hciemu.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_ipc_tester-tester.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/android_ipc_tester-timeout-glib.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +android/android_ipc_tester-hal-utils.$(OBJEXT): \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_ipc_tester-ipc-tester.$(OBJEXT): \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/ipc-tester$(EXEEXT): $(android_ipc_tester_OBJECTS) $(android_ipc_tester_DEPENDENCIES) $(EXTRA_android_ipc_tester_DEPENDENCIES) android/$(am__dirstamp) + @rm -f android/ipc-tester$(EXEEXT) + $(AM_V_CCLD)$(android_ipc_tester_LINK) $(android_ipc_tester_OBJECTS) $(android_ipc_tester_LDADD) $(LIBS) android/system-emulator.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) -monitor/$(am__dirstamp): - @$(MKDIR_P) monitor - @: > monitor/$(am__dirstamp) -monitor/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) monitor/$(DEPDIR) - @: > monitor/$(DEPDIR)/$(am__dirstamp) -monitor/mainloop.$(OBJEXT): monitor/$(am__dirstamp) \ - monitor/$(DEPDIR)/$(am__dirstamp) android/system-emulator$(EXEEXT): $(android_system_emulator_OBJECTS) $(android_system_emulator_DEPENDENCIES) $(EXTRA_android_system_emulator_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/system-emulator$(EXEEXT) $(AM_V_CCLD)$(LINK) $(android_system_emulator_OBJECTS) $(android_system_emulator_LDADD) $(LIBS) -attrib/$(am__dirstamp): - @$(MKDIR_P) attrib - @: > attrib/$(am__dirstamp) -attrib/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) attrib/$(DEPDIR) - @: > attrib/$(DEPDIR)/$(am__dirstamp) +android/test-ipc.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/test-ipc$(EXEEXT): $(android_test_ipc_OBJECTS) $(android_test_ipc_DEPENDENCIES) $(EXTRA_android_test_ipc_DEPENDENCIES) android/$(am__dirstamp) + @rm -f android/test-ipc$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(android_test_ipc_OBJECTS) $(android_test_ipc_LDADD) $(LIBS) 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) attrib/interactive.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/utils.$(OBJEXT): attrib/$(am__dirstamp) \ @@ -2524,12 +3432,6 @@ monitor/uuid.$(OBJEXT): monitor/$(am__dirstamp) \ client/bluetoothctl$(EXEEXT): $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_DEPENDENCIES) $(EXTRA_client_bluetoothctl_DEPENDENCIES) client/$(am__dirstamp) @rm -f client/bluetoothctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_LDADD) $(LIBS) -emulator/$(am__dirstamp): - @$(MKDIR_P) emulator - @: > emulator/$(am__dirstamp) -emulator/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) emulator/$(DEPDIR) - @: > emulator/$(DEPDIR)/$(am__dirstamp) emulator/b1ee.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/b1ee$(EXEEXT): $(emulator_b1ee_OBJECTS) $(emulator_b1ee_DEPENDENCIES) $(EXTRA_emulator_b1ee_DEPENDENCIES) emulator/$(am__dirstamp) @@ -2537,6 +3439,10 @@ emulator/b1ee$(EXEEXT): $(emulator_b1ee_OBJECTS) $(emulator_b1ee_DEPENDENCIES) $ $(AM_V_CCLD)$(LINK) $(emulator_b1ee_OBJECTS) $(emulator_b1ee_LDADD) $(LIBS) emulator/main.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) +src/shared/timeout-mainloop.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/crypto.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) emulator/server.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/vhci.$(OBJEXT): emulator/$(am__dirstamp) \ @@ -2545,18 +3451,29 @@ emulator/btdev.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) emulator/bthost.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) +emulator/smp.$(OBJEXT): emulator/$(am__dirstamp) \ + emulator/$(DEPDIR)/$(am__dirstamp) emulator/amp.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) +emulator/le.$(OBJEXT): emulator/$(am__dirstamp) \ + emulator/$(DEPDIR)/$(am__dirstamp) emulator/btvirt$(EXEEXT): $(emulator_btvirt_OBJECTS) $(emulator_btvirt_DEPENDENCIES) $(EXTRA_emulator_btvirt_DEPENDENCIES) emulator/$(am__dirstamp) @rm -f emulator/btvirt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(emulator_btvirt_OBJECTS) $(emulator_btvirt_LDADD) $(LIBS) +emulator/hfp.$(OBJEXT): emulator/$(am__dirstamp) \ + emulator/$(DEPDIR)/$(am__dirstamp) +src/shared/io-mainloop.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +emulator/hfp$(EXEEXT): $(emulator_hfp_OBJECTS) $(emulator_hfp_DEPENDENCIES) $(EXTRA_emulator_hfp_DEPENDENCIES) emulator/$(am__dirstamp) + @rm -f emulator/hfp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(emulator_hfp_OBJECTS) $(emulator_hfp_LDADD) $(LIBS) monitor/main.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/display.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/hcidump.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) -monitor/btsnoop.$(OBJEXT): monitor/$(am__dirstamp) \ +monitor/ellisys.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/control.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) @@ -2566,13 +3483,19 @@ monitor/vendor.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/lmp.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) +monitor/crc.$(OBJEXT): monitor/$(am__dirstamp) \ + monitor/$(DEPDIR)/$(am__dirstamp) +monitor/ll.$(OBJEXT): monitor/$(am__dirstamp) \ + monitor/$(DEPDIR)/$(am__dirstamp) monitor/l2cap.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/sdp.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) -monitor/crc.$(OBJEXT): monitor/$(am__dirstamp) \ +monitor/hwdb.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) -monitor/ll.$(OBJEXT): monitor/$(am__dirstamp) \ +monitor/keys.$(OBJEXT): monitor/$(am__dirstamp) \ + monitor/$(DEPDIR)/$(am__dirstamp) +monitor/analyze.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) monitor/btmon$(EXEEXT): $(monitor_btmon_OBJECTS) $(monitor_btmon_DEPENDENCIES) $(EXTRA_monitor_btmon_DEPENDENCIES) monitor/$(am__dirstamp) @rm -f monitor/btmon$(EXEEXT) @@ -2963,7 +3886,7 @@ src/bluetoothd-sdp-client.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-textfile.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) -src/bluetoothd-glib-helper.$(OBJEXT): src/$(am__dirstamp) \ +src/bluetoothd-uuid-helper.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-plugin.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) @@ -2979,12 +3902,23 @@ src/bluetoothd-profile.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-service.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +src/bluetoothd-gatt-dbus.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) +src/bluetoothd-gatt.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-device.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-dbus-common.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-eir.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +src/shared/bluetoothd-io-glib.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/bluetoothd-timeout-glib.$(OBJEXT): \ + src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/bluetoothd-queue.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/bluetoothd-util.$(OBJEXT): src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/bluetoothd-mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \ @@ -2998,6 +3932,13 @@ tools/$(am__dirstamp): tools/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tools/$(DEPDIR) @: > tools/$(DEPDIR)/$(am__dirstamp) +tools/3dsp.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +src/shared/hci.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +tools/3dsp$(EXEEXT): $(tools_3dsp_OBJECTS) $(tools_3dsp_DEPENDENCIES) $(EXTRA_tools_3dsp_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/3dsp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_3dsp_OBJECTS) $(tools_3dsp_LDADD) $(LIBS) tools/amptest.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/amptest$(EXEEXT): $(tools_amptest_OBJECTS) $(tools_amptest_DEPENDENCIES) $(EXTRA_tools_amptest_DEPENDENCIES) tools/$(am__dirstamp) @@ -3038,6 +3979,11 @@ src/oui.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) tools/bdaddr$(EXEEXT): $(tools_bdaddr_OBJECTS) $(tools_bdaddr_DEPENDENCIES) $(EXTRA_tools_bdaddr_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/bdaddr$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_bdaddr_OBJECTS) $(tools_bdaddr_LDADD) $(LIBS) +tools/bluemoon.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/bluemoon$(EXEEXT): $(tools_bluemoon_OBJECTS) $(tools_bluemoon_DEPENDENCIES) $(EXTRA_tools_bluemoon_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/bluemoon$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_bluemoon_OBJECTS) $(tools_bluemoon_LDADD) $(LIBS) tools/bluetooth-player.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/bluetooth-player$(EXEEXT): $(tools_bluetooth_player_OBJECTS) $(tools_bluetooth_player_DEPENDENCIES) $(EXTRA_tools_bluetooth_player_DEPENDENCIES) tools/$(am__dirstamp) @@ -3063,12 +4009,15 @@ tools/btmgmt.$(OBJEXT): tools/$(am__dirstamp) \ tools/btmgmt$(EXEEXT): $(tools_btmgmt_OBJECTS) $(tools_btmgmt_DEPENDENCIES) $(EXTRA_tools_btmgmt_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btmgmt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btmgmt_OBJECTS) $(tools_btmgmt_LDADD) $(LIBS) +tools/btproxy.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/btproxy$(EXEEXT): $(tools_btproxy_OBJECTS) $(tools_btproxy_DEPENDENCIES) $(EXTRA_tools_btproxy_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/btproxy$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_btproxy_OBJECTS) $(tools_btproxy_LDADD) $(LIBS) tools/btsnoop.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) src/shared/pcap.$(OBJEXT): src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) -src/shared/btsnoop.$(OBJEXT): src/shared/$(am__dirstamp) \ - src/shared/$(DEPDIR)/$(am__dirstamp) tools/btsnoop$(EXEEXT): $(tools_btsnoop_OBJECTS) $(tools_btsnoop_DEPENDENCIES) $(EXTRA_tools_btsnoop_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btsnoop$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btsnoop_OBJECTS) $(tools_btsnoop_LDADD) $(LIBS) @@ -3088,9 +4037,21 @@ src/shared/hciemu.$(OBJEXT): src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/tester.$(OBJEXT): src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/timeout-glib.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) tools/gap-tester$(EXEEXT): $(tools_gap_tester_OBJECTS) $(tools_gap_tester_DEPENDENCIES) $(EXTRA_tools_gap_tester_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/gap-tester$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_gap_tester_OBJECTS) $(tools_gap_tester_LDADD) $(LIBS) +tools/gatt-service.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/gatt-service$(EXEEXT): $(tools_gatt_service_OBJECTS) $(tools_gatt_service_DEPENDENCIES) $(EXTRA_tools_gatt_service_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/gatt-service$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_gatt_service_OBJECTS) $(tools_gatt_service_LDADD) $(LIBS) +tools/hci-tester.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/hci-tester$(EXEEXT): $(tools_hci_tester_OBJECTS) $(tools_hci_tester_DEPENDENCIES) $(EXTRA_tools_hci_tester_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/hci-tester$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_hci_tester_OBJECTS) $(tools_hci_tester_LDADD) $(LIBS) tools/hciattach.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_st.$(OBJEXT): tools/$(am__dirstamp) \ @@ -3105,6 +4066,8 @@ tools/hciattach_qualcomm.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach_intel.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) +tools/hciattach_bcm43xx.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) tools/hciattach$(EXEEXT): $(tools_hciattach_OBJECTS) $(tools_hciattach_DEPENDENCIES) $(EXTRA_tools_hciattach_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hciattach$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hciattach_OBJECTS) $(tools_hciattach_LDADD) $(LIBS) @@ -3187,6 +4150,11 @@ tools/hcitool.$(OBJEXT): tools/$(am__dirstamp) \ tools/hcitool$(EXEEXT): $(tools_hcitool_OBJECTS) $(tools_hcitool_DEPENDENCIES) $(EXTRA_tools_hcitool_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hcitool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hcitool_OBJECTS) $(tools_hcitool_LDADD) $(LIBS) +tools/hex2hcd.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/hex2hcd$(EXEEXT): $(tools_hex2hcd_OBJECTS) $(tools_hex2hcd_DEPENDENCIES) $(EXTRA_tools_hex2hcd_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/hex2hcd$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_hex2hcd_OBJECTS) $(tools_hex2hcd_LDADD) $(LIBS) tools/hid2hci.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/hid2hci$(EXEEXT): $(tools_hid2hci_OBJECTS) $(tools_hid2hci_DEPENDENCIES) $(EXTRA_tools_hid2hci_DEPENDENCIES) tools/$(am__dirstamp) @@ -3197,6 +4165,11 @@ tools/hwdb.$(OBJEXT): tools/$(am__dirstamp) \ tools/hwdb$(EXEEXT): $(tools_hwdb_OBJECTS) $(tools_hwdb_DEPENDENCIES) $(EXTRA_tools_hwdb_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/hwdb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_hwdb_OBJECTS) $(tools_hwdb_LDADD) $(LIBS) +tools/ibeacon.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/ibeacon$(EXEEXT): $(tools_ibeacon_OBJECTS) $(tools_ibeacon_DEPENDENCIES) $(EXTRA_tools_ibeacon_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/ibeacon$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_ibeacon_OBJECTS) $(tools_ibeacon_LDADD) $(LIBS) tools/l2cap-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/l2cap-tester$(EXEEXT): $(tools_l2cap_tester_OBJECTS) $(tools_l2cap_tester_DEPENDENCIES) $(EXTRA_tools_l2cap_tester_DEPENDENCIES) tools/$(am__dirstamp) @@ -3259,6 +4232,11 @@ tools/rfcomm.$(OBJEXT): tools/$(am__dirstamp) \ tools/rfcomm$(EXEEXT): $(tools_rfcomm_OBJECTS) $(tools_rfcomm_DEPENDENCIES) $(EXTRA_tools_rfcomm_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/rfcomm$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_rfcomm_OBJECTS) $(tools_rfcomm_LDADD) $(LIBS) +tools/rfcomm-tester.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/rfcomm-tester$(EXEEXT): $(tools_rfcomm_tester_OBJECTS) $(tools_rfcomm_tester_DEPENDENCIES) $(EXTRA_tools_rfcomm_tester_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/rfcomm-tester$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_rfcomm_tester_OBJECTS) $(tools_rfcomm_tester_LDADD) $(LIBS) tools/sco-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/sco-tester$(EXEEXT): $(tools_sco_tester_OBJECTS) $(tools_sco_tester_DEPENDENCIES) $(EXTRA_tools_sco_tester_DEPENDENCIES) tools/$(am__dirstamp) @@ -3276,6 +4254,11 @@ src/sdp-xml.$(OBJEXT): src/$(am__dirstamp) \ tools/sdptool$(EXEEXT): $(tools_sdptool_OBJECTS) $(tools_sdptool_DEPENDENCIES) $(EXTRA_tools_sdptool_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/sdptool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_sdptool_OBJECTS) $(tools_sdptool_LDADD) $(LIBS) +tools/seq2bseq.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/seq2bseq$(EXEEXT): $(tools_seq2bseq_OBJECTS) $(tools_seq2bseq_DEPENDENCIES) $(EXTRA_tools_seq2bseq_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/seq2bseq$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_seq2bseq_OBJECTS) $(tools_seq2bseq_LDADD) $(LIBS) tools/smp-tester.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) tools/smp-tester$(EXEEXT): $(tools_smp_tester_OBJECTS) $(tools_smp_tester_DEPENDENCIES) $(EXTRA_tools_smp_tester_DEPENDENCIES) tools/$(am__dirstamp) @@ -3287,11 +4270,21 @@ unit/$(am__dirstamp): unit/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) unit/$(DEPDIR) @: > unit/$(DEPDIR)/$(am__dirstamp) +unit/test-avctp.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test-avctp$(EXEEXT): $(unit_test_avctp_OBJECTS) $(unit_test_avctp_DEPENDENCIES) $(EXTRA_unit_test_avctp_DEPENDENCIES) unit/$(am__dirstamp) + @rm -f unit/test-avctp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit_test_avctp_OBJECTS) $(unit_test_avctp_LDADD) $(LIBS) unit/test-avdtp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-avdtp$(EXEEXT): $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_DEPENDENCIES) $(EXTRA_unit_test_avdtp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-avdtp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_LDADD) $(LIBS) +unit/test-avrcp.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test-avrcp$(EXEEXT): $(unit_test_avrcp_OBJECTS) $(unit_test_avrcp_DEPENDENCIES) $(EXTRA_unit_test_avrcp_DEPENDENCIES) unit/$(am__dirstamp) + @rm -f unit/test-avrcp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit_test_avrcp_OBJECTS) $(unit_test_avrcp_LDADD) $(LIBS) unit/test-crc.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-crc$(EXEEXT): $(unit_test_crc_OBJECTS) $(unit_test_crc_DEPENDENCIES) $(EXTRA_unit_test_crc_DEPENDENCIES) unit/$(am__dirstamp) @@ -3334,6 +4327,11 @@ unit/test-gobex-transfer.$(OBJEXT): unit/$(am__dirstamp) \ unit/test-gobex-transfer$(EXEEXT): $(unit_test_gobex_transfer_OBJECTS) $(unit_test_gobex_transfer_DEPENDENCIES) $(EXTRA_unit_test_gobex_transfer_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-gobex-transfer$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_gobex_transfer_OBJECTS) $(unit_test_gobex_transfer_LDADD) $(LIBS) +unit/test-hfp.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test-hfp$(EXEEXT): $(unit_test_hfp_OBJECTS) $(unit_test_hfp_DEPENDENCIES) $(EXTRA_unit_test_hfp_DEPENDENCIES) unit/$(am__dirstamp) + @rm -f unit/test-hfp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit_test_hfp_OBJECTS) $(unit_test_hfp_LDADD) $(LIBS) unit/test-lib.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-lib$(EXEEXT): $(unit_test_lib_OBJECTS) $(unit_test_lib_DEPENDENCIES) $(EXTRA_unit_test_lib_DEPENDENCIES) unit/$(am__dirstamp) @@ -3344,6 +4342,16 @@ unit/test-mgmt.$(OBJEXT): unit/$(am__dirstamp) \ unit/test-mgmt$(EXEEXT): $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_DEPENDENCIES) $(EXTRA_unit_test_mgmt_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-mgmt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_LDADD) $(LIBS) +unit/test-queue.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test-queue$(EXEEXT): $(unit_test_queue_OBJECTS) $(unit_test_queue_DEPENDENCIES) $(EXTRA_unit_test_queue_DEPENDENCIES) unit/$(am__dirstamp) + @rm -f unit/test-queue$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit_test_queue_OBJECTS) $(unit_test_queue_LDADD) $(LIBS) +unit/test-ringbuf.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test-ringbuf$(EXEEXT): $(unit_test_ringbuf_OBJECTS) $(unit_test_ringbuf_DEPENDENCIES) $(EXTRA_unit_test_ringbuf_DEPENDENCIES) unit/$(am__dirstamp) + @rm -f unit/test-ringbuf$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit_test_ringbuf_OBJECTS) $(unit_test_ringbuf_LDADD) $(LIBS) unit/test-sdp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-sdp$(EXEEXT): $(unit_test_sdp_OBJECTS) $(unit_test_sdp_DEPENDENCIES) $(EXTRA_unit_test_sdp_DEPENDENCIES) unit/$(am__dirstamp) @@ -3400,40 +4408,73 @@ uninstall-testSCRIPTS: mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f android/a2dp.$(OBJEXT) + -rm -f android/android_android_tester-android-tester.$(OBJEXT) + -rm -f android/android_audio_a2dp_default_la-hal-audio.$(OBJEXT) + -rm -f android/android_audio_a2dp_default_la-hal-audio.lo + -rm -f android/android_audio_sco_default_la-hal-sco.$(OBJEXT) + -rm -f android/android_audio_sco_default_la-hal-sco.lo + -rm -f android/android_bluetooth_default_la-hal-a2dp.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-a2dp.lo + -rm -f android/android_bluetooth_default_la-hal-avrcp.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-avrcp.lo + -rm -f android/android_bluetooth_default_la-hal-bluetooth.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-bluetooth.lo + -rm -f android/android_bluetooth_default_la-hal-gatt.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-gatt.lo + -rm -f android/android_bluetooth_default_la-hal-handsfree.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-handsfree.lo + -rm -f android/android_bluetooth_default_la-hal-health.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-health.lo + -rm -f android/android_bluetooth_default_la-hal-hidhost.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-hidhost.lo + -rm -f android/android_bluetooth_default_la-hal-ipc.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-ipc.lo + -rm -f android/android_bluetooth_default_la-hal-pan.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-pan.lo + -rm -f android/android_bluetooth_default_la-hal-socket.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-socket.lo + -rm -f android/android_bluetooth_default_la-hal-utils.$(OBJEXT) + -rm -f android/android_bluetooth_default_la-hal-utils.lo -rm -f android/android_haltest-hal-utils.$(OBJEXT) - -rm -f android/android_libhal_internal_la-hal-a2dp.$(OBJEXT) - -rm -f android/android_libhal_internal_la-hal-a2dp.lo - -rm -f android/android_libhal_internal_la-hal-bluetooth.$(OBJEXT) - -rm -f android/android_libhal_internal_la-hal-bluetooth.lo - -rm -f android/android_libhal_internal_la-hal-hidhost.$(OBJEXT) - -rm -f android/android_libhal_internal_la-hal-hidhost.lo - -rm -f android/android_libhal_internal_la-hal-ipc.$(OBJEXT) - -rm -f android/android_libhal_internal_la-hal-ipc.lo - -rm -f android/android_libhal_internal_la-hal-pan.$(OBJEXT) - -rm -f android/android_libhal_internal_la-hal-pan.lo - -rm -f android/android_libhal_internal_la-hal-sock.$(OBJEXT) - -rm -f android/android_libhal_internal_la-hal-sock.lo + -rm -f android/android_ipc_tester-hal-utils.$(OBJEXT) + -rm -f android/android_ipc_tester-ipc-tester.$(OBJEXT) + -rm -f android/audio_utils/android_audio_sco_default_la-resampler.$(OBJEXT) + -rm -f android/audio_utils/android_audio_sco_default_la-resampler.lo + -rm -f android/avctp.$(OBJEXT) -rm -f android/avdtp.$(OBJEXT) + -rm -f android/avrcp-lib.$(OBJEXT) + -rm -f android/avrcp.$(OBJEXT) -rm -f android/bluetooth.$(OBJEXT) + -rm -f android/bluetoothd-snoop.$(OBJEXT) -rm -f android/client/android_haltest-haltest.$(OBJEXT) -rm -f android/client/android_haltest-history.$(OBJEXT) - -rm -f android/client/android_haltest-hwmodule.$(OBJEXT) + -rm -f android/client/android_haltest-if-audio.$(OBJEXT) -rm -f android/client/android_haltest-if-av.$(OBJEXT) -rm -f android/client/android_haltest-if-bt.$(OBJEXT) -rm -f android/client/android_haltest-if-gatt.$(OBJEXT) -rm -f android/client/android_haltest-if-hf.$(OBJEXT) -rm -f android/client/android_haltest-if-hh.$(OBJEXT) + -rm -f android/client/android_haltest-if-hl.$(OBJEXT) -rm -f android/client/android_haltest-if-pan.$(OBJEXT) + -rm -f android/client/android_haltest-if-rc.$(OBJEXT) + -rm -f android/client/android_haltest-if-sco.$(OBJEXT) -rm -f android/client/android_haltest-if-sock.$(OBJEXT) -rm -f android/client/android_haltest-pollhandler.$(OBJEXT) -rm -f android/client/android_haltest-tabcompletion.$(OBJEXT) -rm -f android/client/android_haltest-terminal.$(OBJEXT) + -rm -f android/gatt.$(OBJEXT) + -rm -f android/handsfree.$(OBJEXT) + -rm -f android/hardware/android_android_tester-hardware.$(OBJEXT) + -rm -f android/hardware/android_haltest-hardware.$(OBJEXT) + -rm -f android/health.$(OBJEXT) -rm -f android/hidhost.$(OBJEXT) -rm -f android/ipc.$(OBJEXT) -rm -f android/main.$(OBJEXT) + -rm -f android/mcap-lib.$(OBJEXT) -rm -f android/pan.$(OBJEXT) -rm -f android/socket.$(OBJEXT) -rm -f android/system-emulator.$(OBJEXT) + -rm -f android/test-ipc.$(OBJEXT) -rm -f attrib/att.$(OBJEXT) -rm -f attrib/bluetoothd-att.$(OBJEXT) -rm -f attrib/bluetoothd-gatt-service.$(OBJEXT) @@ -3451,11 +4492,20 @@ mostlyclean-compile: -rm -f client/display.$(OBJEXT) -rm -f client/main.$(OBJEXT) -rm -f emulator/amp.$(OBJEXT) + -rm -f emulator/android_android_tester-btdev.$(OBJEXT) + -rm -f emulator/android_android_tester-bthost.$(OBJEXT) + -rm -f emulator/android_android_tester-smp.$(OBJEXT) + -rm -f emulator/android_ipc_tester-btdev.$(OBJEXT) + -rm -f emulator/android_ipc_tester-bthost.$(OBJEXT) + -rm -f emulator/android_ipc_tester-smp.$(OBJEXT) -rm -f emulator/b1ee.$(OBJEXT) -rm -f emulator/btdev.$(OBJEXT) -rm -f emulator/bthost.$(OBJEXT) + -rm -f emulator/hfp.$(OBJEXT) + -rm -f emulator/le.$(OBJEXT) -rm -f emulator/main.$(OBJEXT) -rm -f emulator/server.$(OBJEXT) + -rm -f emulator/smp.$(OBJEXT) -rm -f emulator/vhci.$(OBJEXT) -rm -f gdbus/client.$(OBJEXT) -rm -f gdbus/client.lo @@ -3487,11 +4537,14 @@ mostlyclean-compile: -rm -f lib/sdp.lo -rm -f lib/uuid.$(OBJEXT) -rm -f lib/uuid.lo - -rm -f monitor/btsnoop.$(OBJEXT) + -rm -f monitor/analyze.$(OBJEXT) -rm -f monitor/control.$(OBJEXT) -rm -f monitor/crc.$(OBJEXT) -rm -f monitor/display.$(OBJEXT) + -rm -f monitor/ellisys.$(OBJEXT) -rm -f monitor/hcidump.$(OBJEXT) + -rm -f monitor/hwdb.$(OBJEXT) + -rm -f monitor/keys.$(OBJEXT) -rm -f monitor/l2cap.$(OBJEXT) -rm -f monitor/ll.$(OBJEXT) -rm -f monitor/lmp.$(OBJEXT) @@ -3602,7 +4655,8 @@ mostlyclean-compile: -rm -f src/bluetoothd-device.$(OBJEXT) -rm -f src/bluetoothd-eir.$(OBJEXT) -rm -f src/bluetoothd-error.$(OBJEXT) - -rm -f src/bluetoothd-glib-helper.$(OBJEXT) + -rm -f src/bluetoothd-gatt-dbus.$(OBJEXT) + -rm -f src/bluetoothd-gatt.$(OBJEXT) -rm -f src/bluetoothd-log.$(OBJEXT) -rm -f src/bluetoothd-main.$(OBJEXT) -rm -f src/bluetoothd-plugin.$(OBJEXT) @@ -3618,8 +4672,8 @@ mostlyclean-compile: -rm -f src/bluetoothd-storage.$(OBJEXT) -rm -f src/bluetoothd-systemd.$(OBJEXT) -rm -f src/bluetoothd-textfile.$(OBJEXT) + -rm -f src/bluetoothd-uuid-helper.$(OBJEXT) -rm -f src/eir.$(OBJEXT) - -rm -f src/glib-helper.$(OBJEXT) -rm -f src/log.$(OBJEXT) -rm -f src/oui.$(OBJEXT) -rm -f src/sdp-client.$(OBJEXT) @@ -3628,25 +4682,58 @@ mostlyclean-compile: -rm -f src/sdpd-request.$(OBJEXT) -rm -f src/sdpd-server.$(OBJEXT) -rm -f src/sdpd-service.$(OBJEXT) + -rm -f src/shared/android_android_tester-crypto.$(OBJEXT) + -rm -f src/shared/android_android_tester-hciemu.$(OBJEXT) + -rm -f src/shared/android_android_tester-io-glib.$(OBJEXT) + -rm -f src/shared/android_android_tester-mgmt.$(OBJEXT) + -rm -f src/shared/android_android_tester-queue.$(OBJEXT) + -rm -f src/shared/android_android_tester-tester.$(OBJEXT) + -rm -f src/shared/android_android_tester-timeout-glib.$(OBJEXT) + -rm -f src/shared/android_android_tester-util.$(OBJEXT) + -rm -f src/shared/android_ipc_tester-crypto.$(OBJEXT) + -rm -f src/shared/android_ipc_tester-hciemu.$(OBJEXT) + -rm -f src/shared/android_ipc_tester-io-glib.$(OBJEXT) + -rm -f src/shared/android_ipc_tester-mgmt.$(OBJEXT) + -rm -f src/shared/android_ipc_tester-queue.$(OBJEXT) + -rm -f src/shared/android_ipc_tester-tester.$(OBJEXT) + -rm -f src/shared/android_ipc_tester-timeout-glib.$(OBJEXT) + -rm -f src/shared/android_ipc_tester-util.$(OBJEXT) + -rm -f src/shared/bluetoothd-io-glib.$(OBJEXT) -rm -f src/shared/bluetoothd-mgmt.$(OBJEXT) + -rm -f src/shared/bluetoothd-queue.$(OBJEXT) + -rm -f src/shared/bluetoothd-timeout-glib.$(OBJEXT) -rm -f src/shared/bluetoothd-util.$(OBJEXT) -rm -f src/shared/btsnoop.$(OBJEXT) + -rm -f src/shared/crypto.$(OBJEXT) + -rm -f src/shared/gatt-db.$(OBJEXT) + -rm -f src/shared/hci.$(OBJEXT) -rm -f src/shared/hciemu.$(OBJEXT) + -rm -f src/shared/hfp.$(OBJEXT) + -rm -f src/shared/io-glib.$(OBJEXT) + -rm -f src/shared/io-mainloop.$(OBJEXT) -rm -f src/shared/mgmt.$(OBJEXT) -rm -f src/shared/pcap.$(OBJEXT) + -rm -f src/shared/queue.$(OBJEXT) + -rm -f src/shared/ringbuf.$(OBJEXT) -rm -f src/shared/tester.$(OBJEXT) + -rm -f src/shared/timeout-glib.$(OBJEXT) + -rm -f src/shared/timeout-mainloop.$(OBJEXT) -rm -f src/shared/util.$(OBJEXT) -rm -f src/textfile.$(OBJEXT) + -rm -f src/uuid-helper.$(OBJEXT) + -rm -f tools/3dsp.$(OBJEXT) -rm -f tools/amptest.$(OBJEXT) -rm -f tools/avinfo.$(OBJEXT) -rm -f tools/avtest.$(OBJEXT) -rm -f tools/bccmd.$(OBJEXT) -rm -f tools/bdaddr.$(OBJEXT) + -rm -f tools/bluemoon.$(OBJEXT) -rm -f tools/bluetooth-player.$(OBJEXT) -rm -f tools/btattach.$(OBJEXT) -rm -f tools/btinfo.$(OBJEXT) -rm -f tools/btiotest.$(OBJEXT) -rm -f tools/btmgmt.$(OBJEXT) + -rm -f tools/btproxy.$(OBJEXT) -rm -f tools/btsnoop.$(OBJEXT) -rm -f tools/ciptool.$(OBJEXT) -rm -f tools/cltest.$(OBJEXT) @@ -3657,8 +4744,11 @@ mostlyclean-compile: -rm -f tools/csr_hci.$(OBJEXT) -rm -f tools/csr_usb.$(OBJEXT) -rm -f tools/gap-tester.$(OBJEXT) + -rm -f tools/gatt-service.$(OBJEXT) + -rm -f tools/hci-tester.$(OBJEXT) -rm -f tools/hciattach.$(OBJEXT) -rm -f tools/hciattach_ath3k.$(OBJEXT) + -rm -f tools/hciattach_bcm43xx.$(OBJEXT) -rm -f tools/hciattach_intel.$(OBJEXT) -rm -f tools/hciattach_qualcomm.$(OBJEXT) -rm -f tools/hciattach_st.$(OBJEXT) @@ -3669,8 +4759,10 @@ mostlyclean-compile: -rm -f tools/hcieventmask.$(OBJEXT) -rm -f tools/hcisecfilter.$(OBJEXT) -rm -f tools/hcitool.$(OBJEXT) + -rm -f tools/hex2hcd.$(OBJEXT) -rm -f tools/hid2hci.$(OBJEXT) -rm -f tools/hwdb.$(OBJEXT) + -rm -f tools/ibeacon.$(OBJEXT) -rm -f tools/l2cap-tester.$(OBJEXT) -rm -f tools/l2ping.$(OBJEXT) -rm -f tools/l2test.$(OBJEXT) @@ -3704,13 +4796,17 @@ mostlyclean-compile: -rm -f tools/parser/smp.$(OBJEXT) -rm -f tools/parser/tcpip.$(OBJEXT) -rm -f tools/rctest.$(OBJEXT) + -rm -f tools/rfcomm-tester.$(OBJEXT) -rm -f tools/rfcomm.$(OBJEXT) -rm -f tools/sco-tester.$(OBJEXT) -rm -f tools/scotest.$(OBJEXT) -rm -f tools/sdptool.$(OBJEXT) + -rm -f tools/seq2bseq.$(OBJEXT) -rm -f tools/smp-tester.$(OBJEXT) -rm -f tools/ubcsp.$(OBJEXT) + -rm -f unit/test-avctp.$(OBJEXT) -rm -f unit/test-avdtp.$(OBJEXT) + -rm -f unit/test-avrcp.$(OBJEXT) -rm -f unit/test-crc.$(OBJEXT) -rm -f unit/test-eir.$(OBJEXT) -rm -f unit/test-gdbus-client.$(OBJEXT) @@ -3719,8 +4815,11 @@ mostlyclean-compile: -rm -f unit/test-gobex-packet.$(OBJEXT) -rm -f unit/test-gobex-transfer.$(OBJEXT) -rm -f unit/test-gobex.$(OBJEXT) + -rm -f unit/test-hfp.$(OBJEXT) -rm -f unit/test-lib.$(OBJEXT) -rm -f unit/test-mgmt.$(OBJEXT) + -rm -f unit/test-queue.$(OBJEXT) + -rm -f unit/test-ringbuf.$(OBJEXT) -rm -f unit/test-sdp.$(OBJEXT) -rm -f unit/test-textfile.$(OBJEXT) -rm -f unit/test-uuid.$(OBJEXT) @@ -3730,34 +4829,59 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/a2dp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_android_tester-android-tester.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_audio_sco_default_la-hal-sco.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_haltest-hal-utils.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_ipc_tester-hal-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avctp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avdtp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avrcp-lib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avrcp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetoothd-snoop.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/gatt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/handsfree.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/health.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/hidhost.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/ipc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/mcap-lib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/pan.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/socket.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/system-emulator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/test-ipc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/audio_utils/$(DEPDIR)/android_audio_sco_default_la-resampler.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-haltest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-history.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-hwmodule.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-audio.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-av.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-bt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-gatt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-pan.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-rc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-sco.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-sock.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-pollhandler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-tabcompletion.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-terminal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/hardware/$(DEPDIR)/android_android_tester-hardware.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/hardware/$(DEPDIR)/android_haltest-hardware.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/att.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-att.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gatt-service.Po@am__quote@ @@ -3775,11 +4899,20 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/display.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/amp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-btdev.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-bthost.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-smp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-btdev.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-bthost.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-smp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/b1ee.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/btdev.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/bthost.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/hfp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/le.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/smp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/vhci.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/client.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/mainloop.Plo@am__quote@ @@ -3802,11 +4935,14 @@ distclean-compile: @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@monitor/$(DEPDIR)/btsnoop.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/analyze.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/control.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/crc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/display.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/ellisys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hcidump.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hwdb.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/keys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/l2cap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/ll.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/lmp.Po@am__quote@ @@ -3915,7 +5051,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-device.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-eir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-error.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-glib-helper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-gatt-dbus.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-gatt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-plugin.Po@am__quote@ @@ -3931,8 +5068,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-storage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-systemd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-textfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-uuid-helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/eir.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)/oui.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-client.Po@am__quote@ @@ -3942,24 +5079,57 @@ distclean-compile: @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)/textfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/uuid-helper.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-crypto.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-hciemu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-io-glib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-mgmt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-tester.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-tester.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-io-glib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-mgmt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/btsnoop.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/crypto.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/gatt-db.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hci.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hciemu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hfp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/io-glib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/io-mainloop.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/mgmt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/pcap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/ringbuf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/tester.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/timeout-glib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/timeout-mainloop.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/3dsp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/amptest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avinfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avtest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bccmd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bdaddr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bluemoon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bluetooth-player.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btattach.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btinfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btiotest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btmgmt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btproxy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btsnoop.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ciptool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/cltest.Po@am__quote@ @@ -3970,8 +5140,11 @@ distclean-compile: @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)/gap-tester.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/gatt-service.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hci-tester.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_bcm43xx.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_intel.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@ @@ -3982,8 +5155,10 @@ distclean-compile: @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)/hex2hcd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hid2hci.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hwdb.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ibeacon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2cap-tester.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2ping.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2test.Po@am__quote@ @@ -3993,10 +5168,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obex-server-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obexctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rctest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm-tester.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sco-tester.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/scotest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sdptool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/seq2bseq.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/smp-tester.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ubcsp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/amp.Po@am__quote@ @@ -4023,7 +5200,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/sdp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/smp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/tcpip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avctp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avdtp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avrcp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-crc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-eir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gdbus-client.Po@am__quote@ @@ -4032,8 +5211,11 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-packet.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-transfer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-hfp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-lib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-mgmt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-queue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-ringbuf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-sdp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-textfile.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-uuid.Po@am__quote@ @@ -4063,47 +5245,103 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< -android/android_libhal_internal_la-hal-bluetooth.lo: android/hal-bluetooth.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-bluetooth.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Tpo -c -o android/android_libhal_internal_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-bluetooth.c' object='android/android_libhal_internal_la-hal-bluetooth.lo' libtool=yes @AMDEPBACKSLASH@ +android/android_audio_a2dp_default_la-hal-audio.lo: android/hal-audio.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) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -MT android/android_audio_a2dp_default_la-hal-audio.lo -MD -MP -MF android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Tpo -c -o android/android_audio_a2dp_default_la-hal-audio.lo `test -f 'android/hal-audio.c' || echo '$(srcdir)/'`android/hal-audio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Tpo android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-audio.c' object='android/android_audio_a2dp_default_la-hal-audio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -c -o android/android_audio_a2dp_default_la-hal-audio.lo `test -f 'android/hal-audio.c' || echo '$(srcdir)/'`android/hal-audio.c + +android/android_audio_sco_default_la-hal-sco.lo: android/hal-sco.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) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -MT android/android_audio_sco_default_la-hal-sco.lo -MD -MP -MF android/$(DEPDIR)/android_audio_sco_default_la-hal-sco.Tpo -c -o android/android_audio_sco_default_la-hal-sco.lo `test -f 'android/hal-sco.c' || echo '$(srcdir)/'`android/hal-sco.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_audio_sco_default_la-hal-sco.Tpo android/$(DEPDIR)/android_audio_sco_default_la-hal-sco.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-sco.c' object='android/android_audio_sco_default_la-hal-sco.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -c -o android/android_audio_sco_default_la-hal-sco.lo `test -f 'android/hal-sco.c' || echo '$(srcdir)/'`android/hal-sco.c + +android/audio_utils/android_audio_sco_default_la-resampler.lo: android/audio_utils/resampler.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) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -MT android/audio_utils/android_audio_sco_default_la-resampler.lo -MD -MP -MF android/audio_utils/$(DEPDIR)/android_audio_sco_default_la-resampler.Tpo -c -o android/audio_utils/android_audio_sco_default_la-resampler.lo `test -f 'android/audio_utils/resampler.c' || echo '$(srcdir)/'`android/audio_utils/resampler.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/audio_utils/$(DEPDIR)/android_audio_sco_default_la-resampler.Tpo android/audio_utils/$(DEPDIR)/android_audio_sco_default_la-resampler.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/audio_utils/resampler.c' object='android/audio_utils/android_audio_sco_default_la-resampler.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -c -o android/audio_utils/android_audio_sco_default_la-resampler.lo `test -f 'android/audio_utils/resampler.c' || echo '$(srcdir)/'`android/audio_utils/resampler.c + +android/android_bluetooth_default_la-hal-bluetooth.lo: android/hal-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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-bluetooth.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Tpo -c -o android/android_bluetooth_default_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-bluetooth.c' object='android/android_bluetooth_default_la-hal-bluetooth.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c + +android/android_bluetooth_default_la-hal-socket.lo: android/hal-socket.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-socket.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Tpo -c -o android/android_bluetooth_default_la-hal-socket.lo `test -f 'android/hal-socket.c' || echo '$(srcdir)/'`android/hal-socket.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-socket.c' object='android/android_bluetooth_default_la-hal-socket.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-socket.lo `test -f 'android/hal-socket.c' || echo '$(srcdir)/'`android/hal-socket.c -android/android_libhal_internal_la-hal-sock.lo: android/hal-sock.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-sock.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Tpo -c -o android/android_libhal_internal_la-hal-sock.lo `test -f 'android/hal-sock.c' || echo '$(srcdir)/'`android/hal-sock.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-sock.c' object='android/android_libhal_internal_la-hal-sock.lo' libtool=yes @AMDEPBACKSLASH@ +android/android_bluetooth_default_la-hal-hidhost.lo: android/hal-hidhost.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-hidhost.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Tpo -c -o android/android_bluetooth_default_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-hidhost.c' object='android/android_bluetooth_default_la-hal-hidhost.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-sock.lo `test -f 'android/hal-sock.c' || echo '$(srcdir)/'`android/hal-sock.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c -android/android_libhal_internal_la-hal-hidhost.lo: android/hal-hidhost.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-hidhost.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Tpo -c -o android/android_libhal_internal_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-hidhost.c' object='android/android_libhal_internal_la-hal-hidhost.lo' libtool=yes @AMDEPBACKSLASH@ +android/android_bluetooth_default_la-hal-health.lo: android/hal-health.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-health.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Tpo -c -o android/android_bluetooth_default_la-hal-health.lo `test -f 'android/hal-health.c' || echo '$(srcdir)/'`android/hal-health.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-health.c' object='android/android_bluetooth_default_la-hal-health.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-health.lo `test -f 'android/hal-health.c' || echo '$(srcdir)/'`android/hal-health.c -android/android_libhal_internal_la-hal-pan.lo: android/hal-pan.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-pan.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Tpo -c -o android/android_libhal_internal_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-pan.c' object='android/android_libhal_internal_la-hal-pan.lo' libtool=yes @AMDEPBACKSLASH@ +android/android_bluetooth_default_la-hal-pan.lo: android/hal-pan.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-pan.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Tpo -c -o android/android_bluetooth_default_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-pan.c' object='android/android_bluetooth_default_la-hal-pan.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c -android/android_libhal_internal_la-hal-a2dp.lo: android/hal-a2dp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-a2dp.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Tpo -c -o android/android_libhal_internal_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-a2dp.c' object='android/android_libhal_internal_la-hal-a2dp.lo' libtool=yes @AMDEPBACKSLASH@ +android/android_bluetooth_default_la-hal-a2dp.lo: android/hal-a2dp.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-a2dp.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Tpo -c -o android/android_bluetooth_default_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-a2dp.c' object='android/android_bluetooth_default_la-hal-a2dp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c -android/android_libhal_internal_la-hal-ipc.lo: android/hal-ipc.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-ipc.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Tpo -c -o android/android_libhal_internal_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-ipc.c' object='android/android_libhal_internal_la-hal-ipc.lo' libtool=yes @AMDEPBACKSLASH@ +android/android_bluetooth_default_la-hal-avrcp.lo: android/hal-avrcp.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-avrcp.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Tpo -c -o android/android_bluetooth_default_la-hal-avrcp.lo `test -f 'android/hal-avrcp.c' || echo '$(srcdir)/'`android/hal-avrcp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-avrcp.c' object='android/android_bluetooth_default_la-hal-avrcp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-avrcp.lo `test -f 'android/hal-avrcp.c' || echo '$(srcdir)/'`android/hal-avrcp.c + +android/android_bluetooth_default_la-hal-handsfree.lo: android/hal-handsfree.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-handsfree.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Tpo -c -o android/android_bluetooth_default_la-hal-handsfree.lo `test -f 'android/hal-handsfree.c' || echo '$(srcdir)/'`android/hal-handsfree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-handsfree.c' object='android/android_bluetooth_default_la-hal-handsfree.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-handsfree.lo `test -f 'android/hal-handsfree.c' || echo '$(srcdir)/'`android/hal-handsfree.c + +android/android_bluetooth_default_la-hal-gatt.lo: android/hal-gatt.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-gatt.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Tpo -c -o android/android_bluetooth_default_la-hal-gatt.lo `test -f 'android/hal-gatt.c' || echo '$(srcdir)/'`android/hal-gatt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-gatt.c' object='android/android_bluetooth_default_la-hal-gatt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-gatt.lo `test -f 'android/hal-gatt.c' || echo '$(srcdir)/'`android/hal-gatt.c + +android/android_bluetooth_default_la-hal-ipc.lo: android/hal-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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-ipc.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Tpo -c -o android/android_bluetooth_default_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-ipc.c' object='android/android_bluetooth_default_la-hal-ipc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c + +android/android_bluetooth_default_la-hal-utils.lo: android/hal-utils.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-utils.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Tpo -c -o android/android_bluetooth_default_la-hal-utils.lo `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/android_bluetooth_default_la-hal-utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-utils.lo `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c plugins/plugins_external_dummy_la-external-dummy.lo: plugins/external-dummy.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) $(plugins_external_dummy_la_CFLAGS) $(CFLAGS) -MT plugins/plugins_external_dummy_la-external-dummy.lo -MD -MP -MF plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Tpo -c -o plugins/plugins_external_dummy_la-external-dummy.lo `test -f 'plugins/external-dummy.c' || echo '$(srcdir)/'`plugins/external-dummy.c @@ -4119,6 +5357,188 @@ plugins/plugins_sixaxis_la-sixaxis.lo: plugins/sixaxis.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_sixaxis_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_sixaxis_la-sixaxis.lo `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c +emulator/android_android_tester-btdev.o: emulator/btdev.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-btdev.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-btdev.Tpo -c -o emulator/android_android_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-btdev.Tpo emulator/$(DEPDIR)/android_android_tester-btdev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_android_tester-btdev.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c + +emulator/android_android_tester-btdev.obj: emulator/btdev.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-btdev.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-btdev.Tpo -c -o emulator/android_android_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-btdev.Tpo emulator/$(DEPDIR)/android_android_tester-btdev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_android_tester-btdev.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi` + +emulator/android_android_tester-bthost.o: emulator/bthost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-bthost.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-bthost.Tpo -c -o emulator/android_android_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-bthost.Tpo emulator/$(DEPDIR)/android_android_tester-bthost.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_android_tester-bthost.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c + +emulator/android_android_tester-bthost.obj: emulator/bthost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-bthost.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-bthost.Tpo -c -o emulator/android_android_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-bthost.Tpo emulator/$(DEPDIR)/android_android_tester-bthost.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_android_tester-bthost.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi` + +emulator/android_android_tester-smp.o: emulator/smp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-smp.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-smp.Tpo -c -o emulator/android_android_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-smp.Tpo emulator/$(DEPDIR)/android_android_tester-smp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/smp.c' object='emulator/android_android_tester-smp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c + +emulator/android_android_tester-smp.obj: emulator/smp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-smp.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-smp.Tpo -c -o emulator/android_android_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-smp.Tpo emulator/$(DEPDIR)/android_android_tester-smp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/smp.c' object='emulator/android_android_tester-smp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi` + +src/shared/android_android_tester-crypto.o: src/shared/crypto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-crypto.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo -c -o src/shared/android_android_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo src/shared/$(DEPDIR)/android_android_tester-crypto.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_android_tester-crypto.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c + +src/shared/android_android_tester-crypto.obj: src/shared/crypto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-crypto.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo -c -o src/shared/android_android_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo src/shared/$(DEPDIR)/android_android_tester-crypto.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_android_tester-crypto.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi` + +src/shared/android_android_tester-io-glib.o: src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo -c -o src/shared/android_android_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-io-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_android_tester-io-glib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c + +src/shared/android_android_tester-io-glib.obj: src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo -c -o src/shared/android_android_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-io-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_android_tester-io-glib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi` + +src/shared/android_android_tester-queue.o: src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-queue.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-queue.Tpo -c -o src/shared/android_android_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-queue.Tpo src/shared/$(DEPDIR)/android_android_tester-queue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_android_tester-queue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c + +src/shared/android_android_tester-queue.obj: src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-queue.Tpo -c -o src/shared/android_android_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-queue.Tpo src/shared/$(DEPDIR)/android_android_tester-queue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_android_tester-queue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi` + +src/shared/android_android_tester-util.o: src/shared/util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-util.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-util.Tpo -c -o src/shared/android_android_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-util.Tpo src/shared/$(DEPDIR)/android_android_tester-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_android_tester-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c + +src/shared/android_android_tester-util.obj: src/shared/util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-util.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-util.Tpo -c -o src/shared/android_android_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-util.Tpo src/shared/$(DEPDIR)/android_android_tester-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_android_tester-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi` + +src/shared/android_android_tester-mgmt.o: src/shared/mgmt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-mgmt.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo -c -o src/shared/android_android_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_android_tester-mgmt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_android_tester-mgmt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c + +src/shared/android_android_tester-mgmt.obj: src/shared/mgmt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-mgmt.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo -c -o src/shared/android_android_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_android_tester-mgmt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_android_tester-mgmt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi` + +src/shared/android_android_tester-hciemu.o: src/shared/hciemu.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-hciemu.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo -c -o src/shared/android_android_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_android_tester-hciemu.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_android_tester-hciemu.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c + +src/shared/android_android_tester-hciemu.obj: src/shared/hciemu.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-hciemu.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo -c -o src/shared/android_android_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_android_tester-hciemu.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_android_tester-hciemu.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi` + +src/shared/android_android_tester-tester.o: src/shared/tester.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-tester.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-tester.Tpo -c -o src/shared/android_android_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-tester.Tpo src/shared/$(DEPDIR)/android_android_tester-tester.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_android_tester-tester.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c + +src/shared/android_android_tester-tester.obj: src/shared/tester.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-tester.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-tester.Tpo -c -o src/shared/android_android_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-tester.Tpo src/shared/$(DEPDIR)/android_android_tester-tester.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_android_tester-tester.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi` + +src/shared/android_android_tester-timeout-glib.o: src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo -c -o src/shared/android_android_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_android_tester-timeout-glib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c + +src/shared/android_android_tester-timeout-glib.obj: src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo -c -o src/shared/android_android_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_android_tester-timeout-glib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi` + +android/hardware/android_android_tester-hardware.o: android/hardware/hardware.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/hardware/android_android_tester-hardware.o -MD -MP -MF android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo -c -o android/hardware/android_android_tester-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo android/hardware/$(DEPDIR)/android_android_tester-hardware.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_android_tester-hardware.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/hardware/android_android_tester-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c + +android/hardware/android_android_tester-hardware.obj: android/hardware/hardware.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/hardware/android_android_tester-hardware.obj -MD -MP -MF android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo -c -o android/hardware/android_android_tester-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo android/hardware/$(DEPDIR)/android_android_tester-hardware.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_android_tester-hardware.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/hardware/android_android_tester-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi` + +android/android_android_tester-android-tester.o: android/android-tester.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/android_android_tester-android-tester.o -MD -MP -MF android/$(DEPDIR)/android_android_tester-android-tester.Tpo -c -o android/android_android_tester-android-tester.o `test -f 'android/android-tester.c' || echo '$(srcdir)/'`android/android-tester.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_android_tester-android-tester.Tpo android/$(DEPDIR)/android_android_tester-android-tester.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/android-tester.c' object='android/android_android_tester-android-tester.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/android_android_tester-android-tester.o `test -f 'android/android-tester.c' || echo '$(srcdir)/'`android/android-tester.c + +android/android_android_tester-android-tester.obj: android/android-tester.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/android_android_tester-android-tester.obj -MD -MP -MF android/$(DEPDIR)/android_android_tester-android-tester.Tpo -c -o android/android_android_tester-android-tester.obj `if test -f 'android/android-tester.c'; then $(CYGPATH_W) 'android/android-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/android-tester.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_android_tester-android-tester.Tpo android/$(DEPDIR)/android_android_tester-android-tester.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/android-tester.c' object='android/android_android_tester-android-tester.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/android_android_tester-android-tester.obj `if test -f 'android/android-tester.c'; then $(CYGPATH_W) 'android/android-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/android-tester.c'; fi` + android/client/android_haltest-haltest.o: android/client/haltest.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-haltest.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-haltest.Tpo -c -o android/client/android_haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-haltest.Tpo android/client/$(DEPDIR)/android_haltest-haltest.Po @@ -4203,6 +5623,20 @@ android/client/android_haltest-if-av.obj: android/client/if-av.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-av.obj `if test -f 'android/client/if-av.c'; then $(CYGPATH_W) 'android/client/if-av.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av.c'; fi` +android/client/android_haltest-if-rc.o: android/client/if-rc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-rc.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-rc.Tpo -c -o android/client/android_haltest-if-rc.o `test -f 'android/client/if-rc.c' || echo '$(srcdir)/'`android/client/if-rc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-rc.Tpo android/client/$(DEPDIR)/android_haltest-if-rc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-rc.c' object='android/client/android_haltest-if-rc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-rc.o `test -f 'android/client/if-rc.c' || echo '$(srcdir)/'`android/client/if-rc.c + +android/client/android_haltest-if-rc.obj: android/client/if-rc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-rc.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-rc.Tpo -c -o android/client/android_haltest-if-rc.obj `if test -f 'android/client/if-rc.c'; then $(CYGPATH_W) 'android/client/if-rc.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-rc.Tpo android/client/$(DEPDIR)/android_haltest-if-rc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-rc.c' object='android/client/android_haltest-if-rc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-rc.obj `if test -f 'android/client/if-rc.c'; then $(CYGPATH_W) 'android/client/if-rc.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc.c'; fi` + android/client/android_haltest-if-bt.o: android/client/if-bt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-bt.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-bt.Tpo -c -o android/client/android_haltest-if-bt.o `test -f 'android/client/if-bt.c' || echo '$(srcdir)/'`android/client/if-bt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-bt.Tpo android/client/$(DEPDIR)/android_haltest-if-bt.Po @@ -4273,6 +5707,20 @@ android/client/android_haltest-if-pan.obj: android/client/if-pan.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-pan.obj `if test -f 'android/client/if-pan.c'; then $(CYGPATH_W) 'android/client/if-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-pan.c'; fi` +android/client/android_haltest-if-hl.o: android/client/if-hl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hl.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hl.Tpo -c -o android/client/android_haltest-if-hl.o `test -f 'android/client/if-hl.c' || echo '$(srcdir)/'`android/client/if-hl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hl.Tpo android/client/$(DEPDIR)/android_haltest-if-hl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hl.c' object='android/client/android_haltest-if-hl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hl.o `test -f 'android/client/if-hl.c' || echo '$(srcdir)/'`android/client/if-hl.c + +android/client/android_haltest-if-hl.obj: android/client/if-hl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hl.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hl.Tpo -c -o android/client/android_haltest-if-hl.obj `if test -f 'android/client/if-hl.c'; then $(CYGPATH_W) 'android/client/if-hl.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hl.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hl.Tpo android/client/$(DEPDIR)/android_haltest-if-hl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hl.c' object='android/client/android_haltest-if-hl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hl.obj `if test -f 'android/client/if-hl.c'; then $(CYGPATH_W) 'android/client/if-hl.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hl.c'; fi` + android/client/android_haltest-if-sock.o: android/client/if-sock.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sock.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sock.Tpo -c -o android/client/android_haltest-if-sock.o `test -f 'android/client/if-sock.c' || echo '$(srcdir)/'`android/client/if-sock.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sock.Tpo android/client/$(DEPDIR)/android_haltest-if-sock.Po @@ -4287,19 +5735,47 @@ android/client/android_haltest-if-sock.obj: android/client/if-sock.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sock.obj `if test -f 'android/client/if-sock.c'; then $(CYGPATH_W) 'android/client/if-sock.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sock.c'; fi` -android/client/android_haltest-hwmodule.o: android/client/hwmodule.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-hwmodule.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo -c -o android/client/android_haltest-hwmodule.o `test -f 'android/client/hwmodule.c' || echo '$(srcdir)/'`android/client/hwmodule.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo android/client/$(DEPDIR)/android_haltest-hwmodule.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/hwmodule.c' object='android/client/android_haltest-hwmodule.o' libtool=no @AMDEPBACKSLASH@ +android/client/android_haltest-if-audio.o: android/client/if-audio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-audio.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-audio.Tpo -c -o android/client/android_haltest-if-audio.o `test -f 'android/client/if-audio.c' || echo '$(srcdir)/'`android/client/if-audio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-audio.Tpo android/client/$(DEPDIR)/android_haltest-if-audio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-audio.c' object='android/client/android_haltest-if-audio.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-audio.o `test -f 'android/client/if-audio.c' || echo '$(srcdir)/'`android/client/if-audio.c + +android/client/android_haltest-if-audio.obj: android/client/if-audio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-audio.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-audio.Tpo -c -o android/client/android_haltest-if-audio.obj `if test -f 'android/client/if-audio.c'; then $(CYGPATH_W) 'android/client/if-audio.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-audio.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-audio.Tpo android/client/$(DEPDIR)/android_haltest-if-audio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-audio.c' object='android/client/android_haltest-if-audio.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-audio.obj `if test -f 'android/client/if-audio.c'; then $(CYGPATH_W) 'android/client/if-audio.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-audio.c'; fi` + +android/client/android_haltest-if-sco.o: android/client/if-sco.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sco.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sco.Tpo -c -o android/client/android_haltest-if-sco.o `test -f 'android/client/if-sco.c' || echo '$(srcdir)/'`android/client/if-sco.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sco.Tpo android/client/$(DEPDIR)/android_haltest-if-sco.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-sco.c' object='android/client/android_haltest-if-sco.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sco.o `test -f 'android/client/if-sco.c' || echo '$(srcdir)/'`android/client/if-sco.c + +android/client/android_haltest-if-sco.obj: android/client/if-sco.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sco.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sco.Tpo -c -o android/client/android_haltest-if-sco.obj `if test -f 'android/client/if-sco.c'; then $(CYGPATH_W) 'android/client/if-sco.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sco.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sco.Tpo android/client/$(DEPDIR)/android_haltest-if-sco.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-sco.c' object='android/client/android_haltest-if-sco.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-hwmodule.o `test -f 'android/client/hwmodule.c' || echo '$(srcdir)/'`android/client/hwmodule.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sco.obj `if test -f 'android/client/if-sco.c'; then $(CYGPATH_W) 'android/client/if-sco.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sco.c'; fi` -android/client/android_haltest-hwmodule.obj: android/client/hwmodule.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-hwmodule.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo -c -o android/client/android_haltest-hwmodule.obj `if test -f 'android/client/hwmodule.c'; then $(CYGPATH_W) 'android/client/hwmodule.c'; else $(CYGPATH_W) '$(srcdir)/android/client/hwmodule.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo android/client/$(DEPDIR)/android_haltest-hwmodule.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/hwmodule.c' object='android/client/android_haltest-hwmodule.obj' libtool=no @AMDEPBACKSLASH@ +android/hardware/android_haltest-hardware.o: android/hardware/hardware.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/hardware/android_haltest-hardware.o -MD -MP -MF android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo -c -o android/hardware/android_haltest-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo android/hardware/$(DEPDIR)/android_haltest-hardware.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_haltest-hardware.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-hwmodule.obj `if test -f 'android/client/hwmodule.c'; then $(CYGPATH_W) 'android/client/hwmodule.c'; else $(CYGPATH_W) '$(srcdir)/android/client/hwmodule.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/hardware/android_haltest-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c + +android/hardware/android_haltest-hardware.obj: android/hardware/hardware.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/hardware/android_haltest-hardware.obj -MD -MP -MF android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo -c -o android/hardware/android_haltest-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo android/hardware/$(DEPDIR)/android_haltest-hardware.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_haltest-hardware.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/hardware/android_haltest-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi` android/android_haltest-hal-utils.o: android/hal-utils.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/android_haltest-hal-utils.o -MD -MP -MF android/$(DEPDIR)/android_haltest-hal-utils.Tpo -c -o android/android_haltest-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c @@ -4315,6 +5791,188 @@ android/android_haltest-hal-utils.obj: android/hal-utils.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/android_haltest-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` +emulator/android_ipc_tester-btdev.o: emulator/btdev.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-btdev.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo -c -o emulator/android_ipc_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo emulator/$(DEPDIR)/android_ipc_tester-btdev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_ipc_tester-btdev.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c + +emulator/android_ipc_tester-btdev.obj: emulator/btdev.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-btdev.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo -c -o emulator/android_ipc_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo emulator/$(DEPDIR)/android_ipc_tester-btdev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_ipc_tester-btdev.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi` + +emulator/android_ipc_tester-bthost.o: emulator/bthost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-bthost.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo -c -o emulator/android_ipc_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo emulator/$(DEPDIR)/android_ipc_tester-bthost.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_ipc_tester-bthost.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c + +emulator/android_ipc_tester-bthost.obj: emulator/bthost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-bthost.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo -c -o emulator/android_ipc_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo emulator/$(DEPDIR)/android_ipc_tester-bthost.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_ipc_tester-bthost.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi` + +emulator/android_ipc_tester-smp.o: emulator/smp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-smp.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo -c -o emulator/android_ipc_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo emulator/$(DEPDIR)/android_ipc_tester-smp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/smp.c' object='emulator/android_ipc_tester-smp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c + +emulator/android_ipc_tester-smp.obj: emulator/smp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-smp.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo -c -o emulator/android_ipc_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo emulator/$(DEPDIR)/android_ipc_tester-smp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emulator/smp.c' object='emulator/android_ipc_tester-smp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi` + +src/shared/android_ipc_tester-crypto.o: src/shared/crypto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-crypto.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo -c -o src/shared/android_ipc_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_ipc_tester-crypto.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c + +src/shared/android_ipc_tester-crypto.obj: src/shared/crypto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-crypto.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo -c -o src/shared/android_ipc_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_ipc_tester-crypto.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi` + +src/shared/android_ipc_tester-io-glib.o: src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo -c -o src/shared/android_ipc_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_ipc_tester-io-glib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c + +src/shared/android_ipc_tester-io-glib.obj: src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo -c -o src/shared/android_ipc_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_ipc_tester-io-glib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi` + +src/shared/android_ipc_tester-queue.o: src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-queue.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo -c -o src/shared/android_ipc_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo src/shared/$(DEPDIR)/android_ipc_tester-queue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_ipc_tester-queue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c + +src/shared/android_ipc_tester-queue.obj: src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo -c -o src/shared/android_ipc_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo src/shared/$(DEPDIR)/android_ipc_tester-queue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_ipc_tester-queue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi` + +src/shared/android_ipc_tester-util.o: src/shared/util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-util.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo -c -o src/shared/android_ipc_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo src/shared/$(DEPDIR)/android_ipc_tester-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_ipc_tester-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c + +src/shared/android_ipc_tester-util.obj: src/shared/util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-util.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo -c -o src/shared/android_ipc_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo src/shared/$(DEPDIR)/android_ipc_tester-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_ipc_tester-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi` + +src/shared/android_ipc_tester-mgmt.o: src/shared/mgmt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-mgmt.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo -c -o src/shared/android_ipc_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_ipc_tester-mgmt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c + +src/shared/android_ipc_tester-mgmt.obj: src/shared/mgmt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-mgmt.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo -c -o src/shared/android_ipc_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_ipc_tester-mgmt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi` + +src/shared/android_ipc_tester-hciemu.o: src/shared/hciemu.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-hciemu.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo -c -o src/shared/android_ipc_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_ipc_tester-hciemu.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c + +src/shared/android_ipc_tester-hciemu.obj: src/shared/hciemu.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-hciemu.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo -c -o src/shared/android_ipc_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_ipc_tester-hciemu.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi` + +src/shared/android_ipc_tester-tester.o: src/shared/tester.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-tester.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo -c -o src/shared/android_ipc_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo src/shared/$(DEPDIR)/android_ipc_tester-tester.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_ipc_tester-tester.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c + +src/shared/android_ipc_tester-tester.obj: src/shared/tester.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-tester.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo -c -o src/shared/android_ipc_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo src/shared/$(DEPDIR)/android_ipc_tester-tester.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_ipc_tester-tester.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi` + +src/shared/android_ipc_tester-timeout-glib.o: src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo -c -o src/shared/android_ipc_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_ipc_tester-timeout-glib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c + +src/shared/android_ipc_tester-timeout-glib.obj: src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo -c -o src/shared/android_ipc_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_ipc_tester-timeout-glib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi` + +android/android_ipc_tester-hal-utils.o: android/hal-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-hal-utils.o -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo -c -o android/android_ipc_tester-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo android/$(DEPDIR)/android_ipc_tester-hal-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/android_ipc_tester-hal-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c + +android/android_ipc_tester-hal-utils.obj: android/hal-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-hal-utils.obj -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo -c -o android/android_ipc_tester-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo android/$(DEPDIR)/android_ipc_tester-hal-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/android_ipc_tester-hal-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` + +android/android_ipc_tester-ipc-tester.o: android/ipc-tester.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-ipc-tester.o -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo -c -o android/android_ipc_tester-ipc-tester.o `test -f 'android/ipc-tester.c' || echo '$(srcdir)/'`android/ipc-tester.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/ipc-tester.c' object='android/android_ipc_tester-ipc-tester.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-ipc-tester.o `test -f 'android/ipc-tester.c' || echo '$(srcdir)/'`android/ipc-tester.c + +android/android_ipc_tester-ipc-tester.obj: android/ipc-tester.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-ipc-tester.obj -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo -c -o android/android_ipc_tester-ipc-tester.obj `if test -f 'android/ipc-tester.c'; then $(CYGPATH_W) 'android/ipc-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/ipc-tester.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/ipc-tester.c' object='android/android_ipc_tester-ipc-tester.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-ipc-tester.obj `if test -f 'android/ipc-tester.c'; then $(CYGPATH_W) 'android/ipc-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/ipc-tester.c'; fi` + btio/obexd-btio.o: btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT btio/obexd-btio.o -MD -MP -MF btio/$(DEPDIR)/obexd-btio.Tpo -c -o btio/obexd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btio/$(DEPDIR)/obexd-btio.Tpo btio/$(DEPDIR)/obexd-btio.Po @@ -5813,19 +7471,19 @@ src/bluetoothd-textfile.obj: src/textfile.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-textfile.obj `if test -f 'src/textfile.c'; then $(CYGPATH_W) 'src/textfile.c'; else $(CYGPATH_W) '$(srcdir)/src/textfile.c'; fi` -src/bluetoothd-glib-helper.o: src/glib-helper.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-glib-helper.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-glib-helper.Tpo -c -o src/bluetoothd-glib-helper.o `test -f 'src/glib-helper.c' || echo '$(srcdir)/'`src/glib-helper.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-glib-helper.Tpo src/$(DEPDIR)/bluetoothd-glib-helper.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/glib-helper.c' object='src/bluetoothd-glib-helper.o' libtool=no @AMDEPBACKSLASH@ +src/bluetoothd-uuid-helper.o: src/uuid-helper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-uuid-helper.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo -c -o src/bluetoothd-uuid-helper.o `test -f 'src/uuid-helper.c' || echo '$(srcdir)/'`src/uuid-helper.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo src/$(DEPDIR)/bluetoothd-uuid-helper.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/uuid-helper.c' object='src/bluetoothd-uuid-helper.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-glib-helper.o `test -f 'src/glib-helper.c' || echo '$(srcdir)/'`src/glib-helper.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-uuid-helper.o `test -f 'src/uuid-helper.c' || echo '$(srcdir)/'`src/uuid-helper.c -src/bluetoothd-glib-helper.obj: src/glib-helper.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-glib-helper.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-glib-helper.Tpo -c -o src/bluetoothd-glib-helper.obj `if test -f 'src/glib-helper.c'; then $(CYGPATH_W) 'src/glib-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/glib-helper.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-glib-helper.Tpo src/$(DEPDIR)/bluetoothd-glib-helper.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/glib-helper.c' object='src/bluetoothd-glib-helper.obj' libtool=no @AMDEPBACKSLASH@ +src/bluetoothd-uuid-helper.obj: src/uuid-helper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-uuid-helper.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo -c -o src/bluetoothd-uuid-helper.obj `if test -f 'src/uuid-helper.c'; then $(CYGPATH_W) 'src/uuid-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/uuid-helper.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo src/$(DEPDIR)/bluetoothd-uuid-helper.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/uuid-helper.c' object='src/bluetoothd-uuid-helper.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-glib-helper.obj `if test -f 'src/glib-helper.c'; then $(CYGPATH_W) 'src/glib-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/glib-helper.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-uuid-helper.obj `if test -f 'src/uuid-helper.c'; then $(CYGPATH_W) 'src/uuid-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/uuid-helper.c'; fi` src/bluetoothd-plugin.o: src/plugin.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-plugin.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-plugin.Tpo -c -o src/bluetoothd-plugin.o `test -f 'src/plugin.c' || echo '$(srcdir)/'`src/plugin.c @@ -5925,6 +7583,34 @@ src/bluetoothd-service.obj: src/service.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-service.obj `if test -f 'src/service.c'; then $(CYGPATH_W) 'src/service.c'; else $(CYGPATH_W) '$(srcdir)/src/service.c'; fi` +src/bluetoothd-gatt-dbus.o: src/gatt-dbus.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-dbus.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo -c -o src/bluetoothd-gatt-dbus.o `test -f 'src/gatt-dbus.c' || echo '$(srcdir)/'`src/gatt-dbus.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo src/$(DEPDIR)/bluetoothd-gatt-dbus.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/gatt-dbus.c' object='src/bluetoothd-gatt-dbus.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt-dbus.o `test -f 'src/gatt-dbus.c' || echo '$(srcdir)/'`src/gatt-dbus.c + +src/bluetoothd-gatt-dbus.obj: src/gatt-dbus.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-dbus.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo -c -o src/bluetoothd-gatt-dbus.obj `if test -f 'src/gatt-dbus.c'; then $(CYGPATH_W) 'src/gatt-dbus.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-dbus.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo src/$(DEPDIR)/bluetoothd-gatt-dbus.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/gatt-dbus.c' object='src/bluetoothd-gatt-dbus.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt-dbus.obj `if test -f 'src/gatt-dbus.c'; then $(CYGPATH_W) 'src/gatt-dbus.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-dbus.c'; fi` + +src/bluetoothd-gatt.o: src/gatt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o src/bluetoothd-gatt.o `test -f 'src/gatt.c' || echo '$(srcdir)/'`src/gatt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt.Tpo src/$(DEPDIR)/bluetoothd-gatt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/gatt.c' object='src/bluetoothd-gatt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt.o `test -f 'src/gatt.c' || echo '$(srcdir)/'`src/gatt.c + +src/bluetoothd-gatt.obj: src/gatt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o src/bluetoothd-gatt.obj `if test -f 'src/gatt.c'; then $(CYGPATH_W) 'src/gatt.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt.Tpo src/$(DEPDIR)/bluetoothd-gatt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/gatt.c' object='src/bluetoothd-gatt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-gatt.obj `if test -f 'src/gatt.c'; then $(CYGPATH_W) 'src/gatt.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt.c'; fi` + src/bluetoothd-device.o: src/device.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-device.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-device.Tpo -c -o src/bluetoothd-device.o `test -f 'src/device.c' || echo '$(srcdir)/'`src/device.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-device.Tpo src/$(DEPDIR)/bluetoothd-device.Po @@ -5967,6 +7653,48 @@ src/bluetoothd-eir.obj: src/eir.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi` +src/shared/bluetoothd-io-glib.o: src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo -c -o src/shared/bluetoothd-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-io-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/bluetoothd-io-glib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c + +src/shared/bluetoothd-io-glib.obj: src/shared/io-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo -c -o src/shared/bluetoothd-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-io-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/bluetoothd-io-glib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi` + +src/shared/bluetoothd-timeout-glib.o: src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo -c -o src/shared/bluetoothd-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/bluetoothd-timeout-glib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c + +src/shared/bluetoothd-timeout-glib.obj: src/shared/timeout-glib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo -c -o src/shared/bluetoothd-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/bluetoothd-timeout-glib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi` + +src/shared/bluetoothd-queue.o: src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-queue.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-queue.Tpo -c -o src/shared/bluetoothd-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-queue.Tpo src/shared/$(DEPDIR)/bluetoothd-queue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/bluetoothd-queue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c + +src/shared/bluetoothd-queue.obj: src/shared/queue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-queue.Tpo -c -o src/shared/bluetoothd-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-queue.Tpo src/shared/$(DEPDIR)/bluetoothd-queue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/shared/queue.c' object='src/shared/bluetoothd-queue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/shared/bluetoothd-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi` + src/shared/bluetoothd-util.o: src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-util.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-util.Tpo -c -o src/shared/bluetoothd-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-util.Tpo src/shared/$(DEPDIR)/bluetoothd-util.Po @@ -6001,6 +7729,7 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -rm -rf android/.libs android/_libs + -rm -rf android/audio_utils/.libs android/audio_utils/_libs -rm -rf attrib/.libs attrib/_libs -rm -rf client/.libs client/_libs -rm -rf emulator/.libs emulator/_libs @@ -6675,8 +8404,12 @@ distclean-generic: -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f android/$(DEPDIR)/$(am__dirstamp) -rm -f android/$(am__dirstamp) + -rm -f android/audio_utils/$(DEPDIR)/$(am__dirstamp) + -rm -f android/audio_utils/$(am__dirstamp) -rm -f android/client/$(DEPDIR)/$(am__dirstamp) -rm -f android/client/$(am__dirstamp) + -rm -f android/hardware/$(DEPDIR)/$(am__dirstamp) + -rm -f android/hardware/$(am__dirstamp) -rm -f attrib/$(DEPDIR)/$(am__dirstamp) -rm -f attrib/$(am__dirstamp) -rm -f btio/$(DEPDIR)/$(am__dirstamp) @@ -6760,7 +8493,7 @@ clean-am: clean-binPROGRAMS clean-cupsPROGRAMS clean-generic \ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf android/$(DEPDIR) android/client/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR) + -rm -rf android/$(DEPDIR) android/audio_utils/$(DEPDIR) android/client/$(DEPDIR) android/hardware/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags @@ -6815,7 +8548,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf android/$(DEPDIR) android/client/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR) + -rm -rf android/$(DEPDIR) android/audio_utils/$(DEPDIR) android/client/$(DEPDIR) android/hardware/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/Makefile.plugins b/Makefile.plugins index 6a1ddbf..f0eada9 100644 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -55,7 +55,8 @@ builtin_sources += profiles/network/manager.c \ builtin_modules += input builtin_sources += profiles/input/manager.c \ profiles/input/server.h profiles/input/server.c \ - profiles/input/device.h profiles/input/device.c + profiles/input/device.h profiles/input/device.c \ + profiles/input/uhid_copy.h profiles/input/hidp_defs.h builtin_modules += hog builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \ diff --git a/Makefile.tools b/Makefile.tools index 78034f5..412a998 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -14,86 +14,169 @@ if MONITOR bin_PROGRAMS += monitor/btmon monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \ - monitor/mainloop.h monitor/mainloop.c \ - monitor/display.h monitor/display.c \ - monitor/hcidump.h monitor/hcidump.c \ - monitor/btsnoop.h monitor/btsnoop.c \ - monitor/control.h monitor/control.c \ - monitor/packet.h monitor/packet.c \ - monitor/vendor.h monitor/vendor.c \ - monitor/lmp.h monitor/lmp.c \ - monitor/l2cap.h monitor/l2cap.c \ - monitor/uuid.h monitor/uuid.c \ - monitor/sdp.h monitor/sdp.c \ - monitor/crc.h monitor/crc.c \ - monitor/ll.h monitor/ll.c -monitor_btmon_LDADD = lib/libbluetooth-internal.la + monitor/mainloop.h monitor/mainloop.c \ + monitor/display.h monitor/display.c \ + monitor/hcidump.h monitor/hcidump.c \ + monitor/ellisys.h monitor/ellisys.c \ + monitor/control.h monitor/control.c \ + monitor/packet.h monitor/packet.c \ + monitor/vendor.h monitor/vendor.c \ + monitor/lmp.h monitor/lmp.c \ + monitor/crc.h monitor/crc.c \ + monitor/ll.h monitor/ll.c \ + monitor/l2cap.h monitor/l2cap.c \ + monitor/sdp.h monitor/sdp.c \ + monitor/uuid.h monitor/uuid.c \ + monitor/hwdb.h monitor/hwdb.c \ + monitor/keys.h monitor/keys.c \ + monitor/analyze.h monitor/analyze.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/btsnoop.h src/shared/btsnoop.c +monitor_btmon_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@ endif if EXPERIMENTAL -noinst_PROGRAMS += emulator/btvirt emulator/b1ee \ +noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp tools/3dsp \ tools/mgmt-tester tools/gap-tester \ tools/l2cap-tester tools/sco-tester \ - tools/smp-tester + tools/smp-tester tools/hci-tester \ + tools/rfcomm-tester emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \ - monitor/mainloop.h monitor/mainloop.c \ - emulator/server.h emulator/server.c \ - emulator/vhci.h emulator/vhci.c \ - emulator/btdev.h emulator/btdev.c \ - emulator/bthost.h emulator/bthost.c \ - emulator/amp.h emulator/amp.c + monitor/mainloop.h monitor/mainloop.c \ + src/shared/timeout.h \ + src/shared/timeout-mainloop.c \ + src/shared/util.h src/shared/util.c \ + src/shared/crypto.h src/shared/crypto.c \ + emulator/server.h emulator/server.c \ + emulator/vhci.h emulator/vhci.c \ + emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + emulator/amp.h emulator/amp.c \ + emulator/le.h emulator/le.c +emulator_btvirt_LDADD = lib/libbluetooth-internal.la emulator_b1ee_SOURCES = emulator/b1ee.c monitor/mainloop.h monitor/mainloop.c +emulator_hfp_SOURCES = emulator/hfp.c \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/io.h src/shared/io-mainloop.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/ringbuf.h src/shared/ringbuf.c \ + src/shared/hfp.h src/shared/hfp.c + +tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/io.h src/shared/io-mainloop.c \ + src/shared/timeout.h \ + src/shared/timeout-mainloop.c \ + src/shared/hci.h src/shared/hci.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/ringbuf.h src/shared/ringbuf.c + tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c \ src/shared/hciemu.h src/shared/hciemu.c \ - src/shared/tester.h src/shared/tester.c + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c \ src/shared/hciemu.h src/shared/hciemu.c \ - src/shared/tester.h src/shared/tester.c + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \ + emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c \ + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c +tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c \ src/shared/hciemu.h src/shared/hciemu.c \ - src/shared/tester.h src/shared/tester.c + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c tools_smp_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/hciemu.h src/shared/hciemu.c \ - src/shared/tester.h src/shared/tester.c -tools_gap_tester_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c +tools_gap_tester_LDADD = lib/libbluetooth-internal.la \ + gdbus/libgdbus-internal.la \ + @GLIB_LIBS@ @DBUS_LIBS@ tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c \ src/shared/hciemu.h src/shared/hciemu.c \ - src/shared/tester.h src/shared/tester.c + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c tools_sco_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/hci.h src/shared/hci.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/ringbuf.h src/shared/ringbuf.c \ + src/shared/tester.h src/shared/tester.c +tools_hci_tester_LDADD = @GLIB_LIBS@ endif if TOOLS bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \ tools/rfcomm tools/rctest tools/l2test tools/l2ping \ - tools/sdptool tools/ciptool tools/bccmd + tools/sdptool tools/ciptool tools/bccmd tools/bluemoon tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ tools/hciattach_st.c \ @@ -101,14 +184,15 @@ tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ tools/hciattach_tialt.c \ tools/hciattach_ath3k.c \ tools/hciattach_qualcomm.c \ - tools/hciattach_intel.c + tools/hciattach_intel.c \ + tools/hciattach_bcm43xx.c tools_hciattach_LDADD = lib/libbluetooth-internal.la tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c tools_hciconfig_LDADD = lib/libbluetooth-internal.la tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c -tools_hcitool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @UDEV_LIBS@ +tools_hcitool_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@ tools_hcidump_SOURCES = tools/hcidump.c \ tools/parser/parser.h tools/parser/parser.c \ @@ -156,6 +240,14 @@ tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \ tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c tools_bccmd_LDADD = lib/libbluetooth-internal.la +tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/io.h src/shared/io-mainloop.c \ + src/shared/hci.h src/shared/hci.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/ringbuf.h src/shared/ringbuf.c + dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \ tools/hcitool.1 tools/hcidump.1 \ tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \ @@ -184,8 +276,9 @@ noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \ tools/scotest tools/amptest tools/hwdb \ tools/hcieventmask tools/hcisecfilter \ tools/btmgmt tools/btinfo tools/btattach \ - tools/btsnoop tools/btiotest tools/cltest \ - tools/mpris-player + tools/btsnoop tools/btproxy tools/btiotest \ + tools/mpris-player tools/cltest tools/seq2bseq \ + tools/hex2hcd tools/ibeacon tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@ @@ -202,17 +295,32 @@ tools_hwdb_LDADD = lib/libbluetooth-internal.la tools_hcieventmask_LDADD = lib/libbluetooth-internal.la -tools_btmgmt_SOURCES = tools/btmgmt.c src/glib-helper.c src/eir.c \ +tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/io.h src/shared/io-mainloop.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c -tools_btmgmt_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ - -tools_btinfo_SOURCES = tools/btinfo.c +tools_btmgmt_LDADD = lib/libbluetooth-internal.la + +tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/io.h src/shared/io-mainloop.c \ + src/shared/timeout.h \ + src/shared/timeout-mainloop.c \ + src/shared/hci.h src/shared/hci.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/ringbuf.h src/shared/ringbuf.c tools_btsnoop_SOURCES = tools/btsnoop.c \ src/shared/pcap.h src/shared/pcap.c \ src/shared/btsnoop.h src/shared/btsnoop.c +tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/util.h src/shared/util.c + tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @@ -222,6 +330,20 @@ tools_mpris_player_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ tools_cltest_SOURCES = tools/cltest.c monitor/mainloop.h monitor/mainloop.c tools_cltest_LDADD = lib/libbluetooth-internal.la +tools_seq2bseq_SOURCES = tools/seq2bseq.c + +tools_hex2hcd_SOURCES = tools/hex2hcd.c + +tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/io.h src/shared/io-mainloop.c \ + src/shared/timeout.h \ + src/shared/timeout-mainloop.c \ + src/shared/hci.h src/shared/hci.c \ + src/shared/util.h src/shared/util.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/ringbuf.h src/shared/ringbuf.c + EXTRA_DIST += tools/bdaddr.1 endif @@ -258,6 +380,11 @@ tools_obexctl_LDADD = gdbus/libgdbus-internal.la \ endif if EXPERIMENTAL +noinst_PROGRAMS += tools/gatt-service + +tools_gatt_service_SOURCES = tools/gatt-service.c +tools_gatt_service_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ gdbus/libgdbus-internal.la + noinst_PROGRAMS += profiles/iap/iapd profiles_iap_iapd_SOURCES = profiles/iap/main.c @@ -284,11 +411,12 @@ test_scripts += test/sap_client.py test/bluezutils.py \ test/dbusdef.py test/monitor-bluetooth test/list-devices \ test/test-discovery test/test-manager test/test-adapter \ test/test-device test/simple-agent \ - test/simple-service test/simple-endpoint test/test-sap-server \ + test/simple-endpoint test/test-sap-server \ test/test-proximity test/test-network \ test/test-thermometer test/test-profile test/test-health \ test/test-health-sink test/service-record.dtd \ test/service-did.xml test/service-spp.xml test/service-opp.xml \ test/service-ftp.xml test/simple-player test/test-nap \ test/test-heartrate test/test-alert test/test-hfp \ - test/test-cyclingspeed + test/test-cyclingspeed test/opp-client test/ftp-client \ + test/pbap-client test/map-client diff --git a/TODO b/TODO index 7bb100b..50678d3 100644 --- a/TODO +++ b/TODO @@ -24,11 +24,18 @@ General Priority: high Complexity: C4 -- Rename glib-helper file to a more convenient name. The idea is try to keep - only sdp helpers functions. bt_* prefix shall be also changed. +- Update PBAP client/server implementation to 1.2 and create necessary APIs for + new features it introduces. - Priority: Low - Complexity: C1 + Priority: Medium + Complexity: C4 + +- Create GOEP unit tests based on its test specification: + + https://www.bluetooth.org/docman/handlers/DownloadDoc.ashx?doc_id=230559 + + Priority: Medium + Complexity: C2 - Function in src/adapter.c to convert old storage files to new ini-file format should be removed 6-8 months after first BlueZ 5 release. @@ -96,12 +103,6 @@ Low Energy 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. @@ -147,12 +148,6 @@ ATT/GATT 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. @@ -160,11 +155,6 @@ ATT/GATT Priority: Medium Complexity: C1 -- Fix hard-coded PSM for GATT services over basic rate. - - Priority: Low - 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(). @@ -185,17 +175,6 @@ ATT/GATT 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 @@ -215,11 +194,6 @@ ATT/GATT Priority: Low Complecity: C1 -- Add sdp discovery support to gatttool 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. @@ -234,15 +208,6 @@ ATT/GATT 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 - Management Interface ==================== diff --git a/android/Android.mk b/android/Android.mk index 549613c..4235a7c 100644 --- a/android/Android.mk +++ b/android/Android.mk @@ -1,17 +1,27 @@ -LOCAL_PATH := $(call my-dir) +LOCAL_PATH := external/bluetooth # Retrieve BlueZ version from configure.ac file -BLUEZ_VERSION := $(shell grep ^AC_INIT $(LOCAL_PATH)/../configure.ac | cpp -P -D'AC_INIT(_,v)=v') +BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"` -# Specify pathmap for glib -pathmap_INCL += glib:external/bluetooth/glib +# Specify pathmap for glib and sbc +pathmap_INCL += glib:external/bluetooth/glib \ + sbc:external/bluetooth/sbc \ # Specify common compiler flags BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \ - -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) + -DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \ + +# Enable warnings enabled in autotools build +BLUEZ_COMMON_CFLAGS += -Wall -Wextra \ + -Wdeclaration-after-statement \ + -Wmissing-declarations \ + -Wredundant-decls \ + -Wcast-align \ # Disable warnings enabled by Android but not enabled in autotools build -BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith -Wno-missing-field-initializers +BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith \ + -Wno-missing-field-initializers \ + -Wno-unused-parameter \ # # Android BlueZ daemon (bluetoothd) @@ -20,60 +30,73 @@ BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith -Wno-missing-field-initializers include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - main.c \ - bluetooth.c \ - hidhost.c \ - socket.c \ - ipc.c ipc.h \ - avdtp.c \ - a2dp.c \ - pan.c \ - ../src/log.c \ - ../src/shared/mgmt.c \ - ../src/shared/util.c \ - ../src/sdpd-database.c \ - ../src/sdpd-service.c \ - ../src/sdpd-request.c \ - ../src/sdpd-server.c \ - ../src/glib-helper.c \ - ../src/eir.c \ - ../lib/sdp.c \ - ../lib/bluetooth.c \ - ../lib/hci.c \ - ../btio/btio.c \ - ../src/sdp-client.c \ - ../profiles/network/bnep.c \ + bluez/android/main.c \ + bluez/android/bluetooth.c \ + bluez/android/hidhost.c \ + bluez/android/socket.c \ + bluez/android/ipc.c \ + bluez/android/avdtp.c \ + bluez/android/a2dp.c \ + bluez/android/avctp.c \ + bluez/android/avrcp.c \ + bluez/android/avrcp-lib.c \ + bluez/android/pan.c \ + bluez/android/handsfree.c \ + bluez/android/gatt.c \ + bluez/android/health.c \ + bluez/android/mcap-lib.c \ + bluez/src/log.c \ + bluez/src/shared/mgmt.c \ + bluez/src/shared/util.c \ + bluez/src/shared/queue.c \ + bluez/src/shared/ringbuf.c \ + bluez/src/shared/hfp.c \ + bluez/src/shared/gatt-db.c \ + bluez/src/shared/io-glib.c \ + bluez/src/sdpd-database.c \ + bluez/src/sdpd-service.c \ + bluez/src/sdpd-request.c \ + bluez/src/sdpd-server.c \ + bluez/src/uuid-helper.c \ + bluez/src/eir.c \ + bluez/lib/sdp.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + bluez/lib/uuid.c \ + bluez/btio/btio.c \ + bluez/src/sdp-client.c \ + bluez/profiles/network/bnep.c \ + bluez/attrib/gattrib.c \ + bluez/attrib/gatt.c \ + bluez/attrib/att.c LOCAL_C_INCLUDES := \ $(call include-path-for, glib) \ $(call include-path-for, glib)/glib \ LOCAL_C_INCLUDES += \ - $(LOCAL_PATH)/../ \ - $(LOCAL_PATH)/../src \ - $(LOCAL_PATH)/../lib \ + $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_SHARED_LIBRARIES := \ libglib \ -lib_headers := \ - bluetooth.h \ - hci.h \ - hci_lib.h \ - l2cap.h \ - sdp_lib.h \ - sdp.h \ - rfcomm.h \ - sco.h \ - bnep.h \ - -$(shell mkdir -p $(LOCAL_PATH)/../lib/bluetooth) +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ -$(foreach file,$(lib_headers), $(shell ln -sf ../$(file) $(LOCAL_PATH)/../lib/bluetooth/$(file))) +LOCAL_MODULE_TAGS := optional +# for userdebug/eng this module is bluetoothd-main since bluetoothd is used as +# wrapper to launch bluetooth with Valgrind +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +LOCAL_MODULE := bluetoothd-main +LOCAL_STRIP_MODULE := false +else LOCAL_MODULE := bluetoothd +endif + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac include $(BUILD_EXECUTABLE) @@ -84,13 +107,17 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - hal-ipc.c \ - hal-bluetooth.c \ - hal-sock.c \ - hal-hidhost.c \ - hal-pan.c \ - hal-a2dp.c \ - hal-utils.c \ + bluez/android/hal-ipc.c \ + bluez/android/hal-bluetooth.c \ + bluez/android/hal-socket.c \ + bluez/android/hal-hidhost.c \ + bluez/android/hal-pan.c \ + bluez/android/hal-a2dp.c \ + bluez/android/hal-avrcp.c \ + bluez/android/hal-handsfree.c \ + bluez/android/hal-gatt.c \ + bluez/android/hal-utils.c \ + bluez/android/hal-health.c \ LOCAL_C_INCLUDES += \ $(call include-path-for, system-core) \ @@ -99,13 +126,13 @@ LOCAL_C_INCLUDES += \ LOCAL_SHARED_LIBRARIES := \ libcutils \ -LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) \ +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_MODULE := bluetooth.default LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES -LOCAL_REQUIRED_MODULES := haltest bluetoothd +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc include $(BUILD_SHARED_LIBRARY) @@ -116,25 +143,23 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - client/haltest.c \ - client/pollhandler.c \ - client/terminal.c \ - client/history.c \ - client/tabcompletion.c \ - client/if-av.c \ - client/if-bt.c \ - client/if-hf.c \ - client/if-hh.c \ - client/if-pan.c \ - client/if-sock.c \ - hal-utils.c \ - -ANDROID_4_3_OR_ABOVE := $(shell echo 0 | awk -v v=$(PLATFORM_SDK_VERSION) 'END {print (v > 17) ? 1 : 0}') - -ifeq ($(ANDROID_4_3_OR_ABOVE), 1) -LOCAL_SRC_FILES += \ - client/if-gatt.c -endif + bluez/android/client/haltest.c \ + bluez/android/client/pollhandler.c \ + bluez/android/client/terminal.c \ + bluez/android/client/history.c \ + bluez/android/client/tabcompletion.c \ + bluez/android/client/if-audio.c \ + bluez/android/client/if-sco.c \ + bluez/android/client/if-av.c \ + bluez/android/client/if-rc.c \ + bluez/android/client/if-bt.c \ + bluez/android/client/if-hf.c \ + bluez/android/client/if-hh.c \ + bluez/android/client/if-pan.c \ + bluez/android/client/if-hl.c \ + bluez/android/client/if-sock.c \ + bluez/android/client/if-gatt.c \ + bluez/android/hal-utils.c \ LOCAL_C_INCLUDES += \ $(call include-path-for, system-core) \ @@ -144,7 +169,8 @@ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_SHARED_LIBRARIES := libhardware -LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug LOCAL_MODULE := haltest include $(BUILD_EXECUTABLE) @@ -156,50 +182,402 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - ../monitor/main.c \ - ../monitor/bt.h \ - ../monitor/mainloop.h \ - ../monitor/mainloop.c \ - ../monitor/display.h \ - ../monitor/display.c \ - ../monitor/hcidump.h \ - ../monitor/hcidump.c \ - ../monitor/btsnoop.h \ - ../monitor/btsnoop.c \ - ../monitor/control.h \ - ../monitor/control.c \ - ../monitor/packet.h \ - ../monitor/packet.c \ - ../monitor/l2cap.h \ - ../monitor/l2cap.c \ - ../monitor/uuid.h \ - ../monitor/uuid.c \ - ../monitor/sdp.h \ - ../monitor/sdp.c \ - ../monitor/vendor.h \ - ../monitor/vendor.c \ - ../monitor/lmp.h \ - ../monitor/lmp.c \ - ../monitor/crc.h \ - ../monitor/crc.c \ - ../monitor/ll.h \ - ../monitor/ll.c \ - ../lib/hci.c \ - ../lib/bluetooth.c \ + bluez/monitor/main.c \ + bluez/monitor/mainloop.c \ + bluez/monitor/display.c \ + bluez/monitor/hcidump.c \ + bluez/monitor/control.c \ + bluez/monitor/packet.c \ + bluez/monitor/l2cap.c \ + bluez/monitor/uuid.c \ + bluez/monitor/sdp.c \ + bluez/monitor/vendor.c \ + bluez/monitor/lmp.c \ + bluez/monitor/crc.c \ + bluez/monitor/ll.c \ + bluez/monitor/hwdb.c \ + bluez/monitor/keys.c \ + bluez/monitor/ellisys.c \ + bluez/monitor/analyze.c \ + bluez/src/shared/util.c \ + bluez/src/shared/queue.c \ + bluez/src/shared/crypto.c \ + bluez/src/shared/btsnoop.c \ + bluez/lib/hci.c \ + bluez/lib/bluetooth.c \ LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/.. \ - $(LOCAL_PATH)/../lib \ - $(LOCAL_PATH)/../src/shared \ - -LOCAL_C_INCLUDES += \ - $(call include-path-for, glib) \ - $(call include-path-for, glib)/glib \ + $(LOCAL_PATH)/bluez \ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := debug LOCAL_MODULE := btmon +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# btproxy +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/btproxy.c \ + bluez/monitor/mainloop.c \ + bluez/src/shared/util.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := btproxy + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# A2DP audio +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := bluez/android/hal-audio.c + +LOCAL_C_INCLUDES = \ + $(call include-path-for, system-core) \ + $(call include-path-for, libhardware) \ + $(call include-path-for, sbc) \ + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libsbc \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := audio.a2dp.default + +include $(BUILD_SHARED_LIBRARY) + +# +# SCO audio +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := bluez/android/hal-sco.c + +LOCAL_C_INCLUDES = \ + $(call include-path-for, system-core) \ + $(call include-path-for, libhardware) \ + $(call include-path-for, audio-utils) \ + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libaudioutils \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := audio.sco.default + +include $(BUILD_SHARED_LIBRARY) + +# +# l2cap-test +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/l2test.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := l2test + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# bluetoothd-snoop +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/android/bluetoothd-snoop.c \ + bluez/monitor/mainloop.c \ + bluez/src/shared/btsnoop.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + $(LOCAL_PATH)/bluez/lib \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bluetoothd-snoop + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# init.bluetooth.rc +# + +include $(CLEAR_VARS) + +LOCAL_MODULE := init.bluetooth.rc +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := bluez/android/$(LOCAL_MODULE) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) + +include $(BUILD_PREBUILT) + +# +# btmgmt +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/btmgmt.c \ + bluez/lib/bluetooth.c \ + bluez/lib/sdp.c \ + bluez/monitor/mainloop.c \ + bluez/src/shared/io-mainloop.c \ + bluez/src/shared/mgmt.c \ + bluez/src/shared/queue.c \ + bluez/src/shared/util.c \ + bluez/src/uuid-helper.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := btmgmt + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# hcitool +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/hcitool.c \ + bluez/src/oui.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := hcitool + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + include $(BUILD_EXECUTABLE) + +# +# l2ping +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/l2ping.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := l2ping + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# avtest +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/avtest.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := avtest + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# hciattach +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/hciattach.c \ + bluez/tools/hciattach_st.c \ + bluez/tools/hciattach_ti.c \ + bluez/tools/hciattach_tialt.c \ + bluez/tools/hciattach_ath3k.c \ + bluez/tools/hciattach_qualcomm.c \ + bluez/tools/hciattach_intel.c \ + bluez/tools/hciattach_bcm43xx.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := hciattach + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# libsbc +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + sbc/sbc/sbc.c \ + sbc/sbc/sbc_primitives.c \ + sbc/sbc/sbc_primitives_mmx.c \ + sbc/sbc/sbc_primitives_neon.c \ + sbc/sbc/sbc_primitives_armv6.c \ + sbc/sbc/sbc_primitives_iwmmxt.c \ + +LOCAL_C_INCLUDES:= \ + $(LOCAL_PATH)/sbc \ + +LOCAL_CFLAGS:= \ + -Os \ + -Wno-sign-compare \ + -Wno-missing-field-initializers \ + -Wno-unused-parameter \ + -Wno-type-limits \ + -Wno-empty-body \ + +LOCAL_MODULE := libsbc + +include $(BUILD_SHARED_LIBRARY) + +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) + +# +# bluetoothd (debug) +# this is just a wrapper used in userdebug/eng to launch bluetoothd-main +# with/without Valgrind +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/android/bluetoothd-wrapper.c + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bluetoothd + +LOCAL_REQUIRED_MODULES := \ + bluetoothd-main \ + valgrind \ + memcheck-$(TARGET_ARCH)-linux \ + vgpreload_core-$(TARGET_ARCH)-linux \ + vgpreload_memcheck-$(TARGET_ARCH)-linux \ + default.supp + +include $(BUILD_EXECUTABLE) + +endif + +# +# bluetooth-headers +# + +include $(CLEAR_VARS) + +LOCAL_MODULE := bluetooth-headers +LOCAL_NODULE_TAGS := optional +LOCAL_MODULE_CLASS := STATIC_LIBRARIES + +include_path := $(local-intermediates-dir)/include +include_files := $(wildcard $(LOCAL_PATH)/bluez/lib/*.h) +$(shell mkdir -p $(include_path)/bluetooth) +$(foreach file,$(include_files),$(shell cp -u $(file) $(include_path)/bluetooth)) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path) + +include $(BUILD_STATIC_LIBRARY) diff --git a/android/Makefile.am b/android/Makefile.am index df04762..e663790 100644 --- a/android/Makefile.am +++ b/android/Makefile.am @@ -1,41 +1,71 @@ if ANDROID +android_plugindir = $(abs_top_srcdir)/android/.libs + noinst_PROGRAMS += android/system-emulator android_system_emulator_SOURCES = android/system-emulator.c \ monitor/mainloop.h monitor/mainloop.c +noinst_PROGRAMS += android/bluetoothd-snoop + +android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/btsnoop.h src/shared/btsnoop.c + noinst_PROGRAMS += android/bluetoothd android_bluetoothd_SOURCES = android/main.c \ src/log.c \ android/hal-msg.h \ + android/audio-msg.h \ + android/sco-msg.h \ android/utils.h \ src/sdpd-database.c src/sdpd-server.c \ src/sdpd-service.c src/sdpd-request.c \ - src/glib-helper.h src/glib-helper.c \ + src/uuid-helper.h src/uuid-helper.c \ src/eir.h src/eir.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ src/shared/util.h src/shared/util.c \ src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/ringbuf.h src/shared/ringbuf.c \ + src/shared/hfp.h src/shared/hfp.c \ + src/shared/gatt-db.h src/shared/gatt-db.c \ android/bluetooth.h android/bluetooth.c \ android/hidhost.h android/hidhost.c \ + android/ipc-common.h \ android/ipc.h android/ipc.c \ android/avdtp.h android/avdtp.c \ android/a2dp.h android/a2dp.c \ + android/avctp.h android/avctp.c \ + android/avrcp.h android/avrcp.c \ + android/avrcp-lib.h android/avrcp-lib.c \ android/socket.h android/socket.c \ android/pan.h android/pan.c \ + android/handsfree.h android/handsfree.c \ + android/gatt.h android/gatt.c \ + android/health.h android/health.c \ + android/mcap-lib.h android/mcap-lib.c \ + attrib/att.c attrib/att.h \ + attrib/gatt.c attrib/gatt.h \ + attrib/gattrib.c attrib/gattrib.h \ btio/btio.h btio/btio.c \ src/sdp-client.h src/sdp-client.c \ profiles/network/bnep.h profiles/network/bnep.c android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -noinst_LTLIBRARIES += android/libhal-internal.la +plugin_LTLIBRARIES += android/bluetooth.default.la -android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \ - android/hal-sock.c \ +android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \ + android/hal-socket.c \ android/hal-hidhost.c \ + android/hal-health.c \ android/hal-pan.c \ android/hal-a2dp.c \ + android/hal-avrcp.c \ + android/hal-handsfree.c \ + android/hal-gatt.c \ android/hardware/bluetooth.h \ android/hardware/bt_av.h \ android/hardware/bt_gatt.h \ @@ -50,10 +80,14 @@ android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \ android/hardware/bt_sock.h \ android/hardware/hardware.h \ android/cutils/properties.h \ + android/ipc-common.h \ android/hal-log.h \ - android/hal-ipc.h android/hal-ipc.c + android/hal-ipc.h android/hal-ipc.c \ + android/hal-utils.h android/hal-utils.c -android_libhal_internal_la_CPPFLAGS = -I$(srcdir)/android +android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android +android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ + -no-undefined noinst_PROGRAMS += android/haltest @@ -67,25 +101,178 @@ android_haltest_SOURCES = android/client/haltest.c \ android/client/tabcompletion.c \ android/client/if-main.h \ android/client/if-av.c \ + android/client/if-rc.c \ android/client/if-bt.c \ android/client/if-gatt.c \ android/client/if-hf.c \ android/client/if-hh.c \ android/client/if-pan.c \ + android/client/if-hl.c \ android/client/if-sock.c \ - android/client/hwmodule.c \ + android/client/if-audio.c \ + android/client/if-sco.c \ + android/hardware/hardware.c \ android/hal-utils.h android/hal-utils.c -android_haltest_LDADD = android/libhal-internal.la - android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ - -DPLATFORM_SDK_VERSION=19 + -DPLUGINDIR=\""$(android_plugindir)"\" + +android_haltest_LDFLAGS = -pthread -ldl -lm + +noinst_PROGRAMS += android/android-tester + +android_android_tester_SOURCES = emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c \ + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c \ + monitor/rfcomm.h \ + android/hardware/hardware.c \ + android/android-tester.c + +android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ + -DPLUGINDIR=\""$(android_plugindir)"\" + +android_android_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +android_android_tester_LDFLAGS = -pthread -ldl + +noinst_PROGRAMS += android/ipc-tester + +android_ipc_tester_SOURCES = emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c \ + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c \ + android/hal-utils.h android/hal-utils.c \ + android/ipc-common.h android/ipc-tester.c + +android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android + +android_ipc_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \ + android/hal-msg.h \ + android/hal-audio.c \ + android/hardware/audio.h \ + android/hardware/audio_effect.h \ + android/hardware/hardware.h \ + android/system/audio.h + +android_audio_sco_default_la_SOURCES = android/hal-log.h \ + android/sco-msg.h \ + android/hal-sco.c \ + android/hardware/audio.h \ + android/hardware/audio_effect.h \ + android/hardware/hardware.h \ + android/audio_utils/resampler.c \ + android/audio_utils/resampler.h \ + android/system/audio.h + +android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android + +android_audio_sco_default_la_LIBADD = @SPEEXDSP_LIBS@ + +android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ + -no-undefined -lrt + +plugin_LTLIBRARIES += android/audio.sco.default.la + +unit_tests += android/test-ipc + +android_test_ipc_SOURCES = android/test-ipc.c \ + src/shared/util.h src/shared/util.c \ + src/log.h src/log.c \ + android/ipc-common.h \ + android/ipc.c android/ipc.h +android_test_ipc_LDADD = @GLIB_LIBS@ + +plugin_LTLIBRARIES += android/audio.a2dp.default.la + +android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android + +android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@ -android_haltest_LDFLAGS = -pthread +android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ + -no-undefined -pthread -lrt endif -EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \ - android/pics-gap.txt android/pics-hid.txt \ - android/pics-pan.txt android/pics-did.txt \ - android/pics-opp.txt android/pics-pbap.txt +EXTRA_DIST += android/Android.mk android/README \ + android/init.bluetooth.rc \ + android/hal-ipc-api.txt \ + android/audio-ipc-api.txt \ + android/pics-rfcomm.txt \ + android/pics-spp.txt \ + android/pics-sdp.txt \ + android/pics-l2cap.txt \ + android/pics-gap.txt \ + android/pics-did.txt \ + android/pics-hid.txt \ + android/pics-pan.txt \ + android/pics-opp.txt \ + android/pics-map.txt \ + android/pics-pbap.txt \ + android/pics-a2dp.txt \ + android/pics-avctp.txt \ + android/pics-avrcp.txt \ + android/pics-hsp.txt \ + android/pics-hfp.txt \ + android/pics-gatt.txt \ + android/pics-mcap.txt \ + android/pics-hdp.txt \ + android/pics-iopt.txt \ + android/pics-sm.txt \ + android/pics-mps.txt \ + android/pixit-l2cap.txt \ + android/pixit-gap.txt \ + android/pixit-did.txt \ + android/pixit-hid.txt \ + android/pixit-pan.txt \ + android/pixit-opp.txt \ + android/pixit-map.txt \ + android/pixit-pbap.txt \ + android/pixit-a2dp.txt \ + android/pixit-avctp.txt \ + android/pixit-avrcp.txt \ + android/pixit-hsp.txt \ + android/pixit-hfp.txt \ + android/pixit-gatt.txt \ + android/pixit-mcap.txt \ + android/pixit-hdp.txt \ + android/pixit-iopt.txt \ + android/pixit-sm.txt \ + android/pixit-mps.txt \ + android/bite-rfcomm.txt \ + android/bite-spp.txt \ + android/pts-l2cap.txt \ + android/pts-gap.txt \ + android/pts-did.txt \ + android/pts-hid.txt \ + android/pts-pan.txt \ + android/pts-opp.txt \ + android/pts-map.txt \ + android/pts-a2dp.txt \ + android/pts-avrcp.txt \ + android/pts-avctp.txt \ + android/pts-pbap.txt \ + android/pts-hfp.txt \ + android/pts-gatt.txt \ + android/pts-hsp.txt \ + android/pts-iopt.txt \ + android/pts-hdp.txt \ + android/pts-mcap.txt \ + android/pts-mps.txt \ + android/pts-sm.txt diff --git a/android/README b/android/README index 68c3e9f..0895b71 100644 --- a/android/README +++ b/android/README @@ -7,79 +7,187 @@ of the stack of choice on Android. Android BlueZ is intended as a drop-in replacement to Android provided Bluetooth stack. More details about BlueZ for Android architecture and components can be found -in android/hal-apc-api.txt file. +in android/hal-ipc-api.txt file. + +Supported Android version: 4.4 + -=============================== Building and running on Android =============================== +Steps needed to build and run Android Open Source Project 4.4.2 with +integrated BlueZ. + + Build requirements -================== +------------------ - GLib - Android 4.2 or later don't provide GLib and one must provide it in 'external/bluetooth/glib' folder of Android tree. Sample Android GLib port -is available at https://code.google.com/p/android-bluez.glib/ +is available at https://code.google.com/p/aosp-bluez.glib/ + +- SBC - A2DP code requires SBC library (version 1.2 or higher) present in +'external/bluetooth/sbc' directory. Library is build from Android.mk provided +by BlueZ. SBC code is available at git://git.kernel.org/pub/scm/bluetooth/sbc -- Bionic support - BlueZ requires signalfd and timerfd APIs to be provided -by libc library. Currently only 'master' branch available at +- Bionic support - Currently only 'master' branch available at https://android.googlesource.com/platform/bionic provides all required -functionality and running BlueZ on older branch requires backporting missing -features. Sample Bionic for Android on Intel Architecture (Android-IA) with all -required features backported is available at -https://code.google.com/p/android-bluez.bionic/ +functionality and running BlueZ on release branch requires backporting missing +features (currently epoll_create1 and ppoll calls for Android 4.4.2). Sample +Bionic for Android 4.4.2 with all required features backported is available at +https://code.google.com/p/aosp-bluez.platform-bionic/ + Runtime requirements -==================== +-------------------- -BlueZ HAL library requires 'bluetoothd' service to be available on Android -system. This can be done by defining service in init.rc file of targeted board: +BlueZ HAL library requires 'bluetoothd' and 'bluetoothd-snoop' services to be +available on Android system. Some permissions settings are also required. -service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd - class main - group bluetooth net_admin - disabled - oneshot +This can be done by importing init.bluetooth.rc file in init.rc file of targeted +board: +import init.bluetooth.rc -It is required that bluetooth user could start and stop bluetoothd service by -setting 'ctl.start' or 'ctl.stop' property. This can be achieved by -whitelisting bluetooth user and bluetoothd service in init source code. +For convenience examples are provided at: +https://code.google.com/p/aosp-bluez.device-lge-mako/ (Nexus 4) +https://code.google.com/p/aosp-bluez.device-asus-flo/ (Nexus 7 2013) -Required Android init system modifications can be found at -https://code.google.com/p/android-bluez.system-core/ Downloading and building -======================== +------------------------ -Building for Android requires full Android AOSP source tree. Sample Android-IA -tree with all required components present is available at -http://code.google.com/p/android-bluez/ +Building for Android requires full Android AOSP source tree. Sample Android +4.4.2 tree with all required components present is available at +http://code.google.com/p/aosp-bluez/ + +This tree provides support for Nexus4 (target aosp_mako-userdebug) and +Nexus 7 2013 (target aosp_flo-userdebug). Tree does not provide binary blobs +needed to run Android on supported devices. Those can be obtained from +https://developers.google.com/android/nexus/drivers. Binary blobs needs to be +unpacked (EULA acceptance required) into 'vendor' directory of Android tree. Downloading: -repo init -u https://code.google.com/p/android-bluez.manifest/ -m topics/bluez +repo init -u https://code.google.com/p/aosp-bluez.platform-manifest -b kitkat repo sync -Build for Intel ultrabook: -'source build/envsetup.sh' -'lunch core_mesa-eng' -'make allimages -j8' +Building: +source build/envsetup.sh +lunch aosp_mako-userdebug or lunch aosp_flo-userdebug +make -j8 + +Flashing: +adb reboot bootloader +fastboot flashall -w After full build is done it is possible to rebuild only BlueZ: 'cd external/bluetooth/bluez/android/' 'mm' (or 'mm -B' to force rebuilding of all files) 'adb sync' to update target device. -============================= + +Linux Kernel requirements +------------------------- + +BlueZ for Android uses Linux Bluetooth subsystem and it must be enabled in +kernel. Minimal required version of management interface is 1.3. This +corresponds to Linux 3.9 but latest available version is recommended. Other +requirements include UHID and network bridge support. + +Following kernel options should be enabled: +CONFIG_BT +CONFIG_BT_RFCOMM +CONFIG_BT_RFCOMM_TTY +CONFIG_BT_BNEP +CONFIG_BT_BNEP_MC_FILTER +CONFIG_BT_BNEP_PROTO_FILTER +CONFIG_BRIDGE +CONFIG_UHID + +Also BT chip driver needs to be enabled e.g: +CONFIG_BT_HCIBTUSB + +If it is not possible to use new enough Linux kernel one can use updated +bluetooth subsytem from Backports project. More information about Backports can +be found at https://backports.wiki.kernel.org. Sample kernels using backports +for running BlueZ on Android are available at +https://code.google.com/p/aosp-bluez. + + +Running with Valgrind +--------------------- + +BlueZ for Android is preconfigured to be easily run under Valgrind memcheck. +Appropriate configuration and required modules are automatically included when +building either userdebug or eng variant of Android platform. + +Valgrind can be enabled in runtime by setting "persist.sys.bluetooth.valgrind" +property to either literal "true" or any numeric value >0. For example: +adb root +adb shell setprop persist.sys.bluetooth.valgrind true + +After changing property value Bluetooth need to be restarted to apply changes +(this can be done using UI, just disable and enable it again). Property is +persistent, i.e. there's no need to enable Valgrind again after reboot. + +It's recommended to have unstripped libglib.so installed which will enable +complete backtraces in Valgrind output. Otherwise, in many cases backtrace +will break at e.g. g_free() function without prior callers. It's possible to +have proper library installed automatically by appropriate entry in Android.mk, +see https://code.google.com/p/aosp-bluez.glib/ for an example. + + +Enabling BlueZ debugs +--------------------- + +BlueZ debug logs can be enabled in runtime by setting "persist.sys.bluetooth.debug" +property to either literal "true" or any numeric value >0. For example: +adb root +adb shell setprop persist.sys.bluetooth.debug 1 + +After changing property value Bluetooth needs to be restarted to apply changes. + +There is also a possibility to enable mgmt debug logs which also enables debugs +as above. To enable it procced in the same way as described above but use +system properties called: persist.sys.bluetooth.mgmtdbg + +Note: Debugs are only available on NON USER build variants + + +Customization +------------- + +It is possible to customize BlueZ for Android through Android system properties. +This may include enabling extra profiles or features inside HALs implementation +These properties are read on Bluetooth stack startup only and require stack +restart if changed. All customization properties names start with +"persist.sys.bluetooth." followed by specific HAL name e.g. +"persist.sys.bluetooth.handsfree". This section list available customization +options. + +Property Value Description +------------------------------------------- +mode bredr Enable BlueZ in BR/EDR mode + le Enable BlueZ in LE mode + Enable BlueZ in default mode - enable BR/EDR/LE + if available. +handsfree hfp Enable Handsfree Profile (HFP) with narrowband + speech only + hfp_wbs Enable Handsfree Profile (HFP) with narrowband + and wideband speech support + Don't enable Handsfree Profile (HFP) + + Building and running on Linux -============================= +----------------------------- It is possible to build and test BlueZ for Android daemon on Linux (eg. PC). Simply follow instructions available at README file in BlueZ top directory. Android daemon binary is located at android/bluetoothd. See next section on how to test Android daemon on Linux. -============ + Testing tool -============ +------------ BT HAL test tools located in android/haltest is provided for HAL level testing of both Android daemon and HAL library. Start it with '-n' parameter and type @@ -91,3 +199,124 @@ use provided android/system-emulator, which takes care of launching daemon automatically on HAL library initialization. To deinitialize HAL library and stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab completion is also supported. + + +Implementation status +===================== + +Summary of HALs implementation status. + +complete - implementation is feature complete and Android Framework is able + to use it normally +partial - implementation is in progress and not all required features are + present, Android Framework is able to use some of features +initial - only initial implementations is present, Android Framework is + able to initialize but most likely not able to use it +not started - no implementation, Android Framework is not able to initialize it + +Profile ID HAL header Status +--------------------------------------- +core bluetooth.h complete +a2dp bt_av.h complete +gatt bt_gatt.h partial + bt_gatt_client.h partial + bt_gatt_server.h initial +handsfree bt_hf.h complete +hidhost bt_hh.h complete +health bt_hl.h initial +pan bt_pan.h complete +avrcp bt_rc.h complete +socket bt_sock.h complete + + +Implementation shortcomings +=========================== + +It is possible that some of HAL functionality (although being marked as +complete) is missing implementation due to reasons like feature feasibility or +necessity for latest Android Framework. This sections provides list of such +deficiencies. Note that HAL library is always expected to fully implement HAL +API so missing implementation might happen only in daemon. + + +HAL Bluetooth +------------- + +methods: +dut_mode_send never called from Android Framework +le_test_mode never called from Android Framework +get_remote_service_record never called from Android Framework + +callbacks: +dut_mode_recv_cb empty JNI implementation +le_test_mode_cb empty JNI implementation + +properties: +BT_PROPERTY_SERVICE_RECORD not supported for adapter and device, for + device this property is to be returned as + response to get_remote_service_record, + not sure what to return on get_property + calls (records of all services?) + +BT_PROPERTY_REMOTE_VERSION_INFO information required by this property (LMP + information) are not accessible from mgmt + interface, also marking this property as + settable is probably a typo in HAL header + +HAL Socket +---------- + +Support only for BTSOCK_RFCOMM socket type. + + +HAL AVRCP +--------- + +methods: +list_player_app_attr_rsp never called from Android Framework +list_player_app_value_rsp never called from Android Framework +get_player_app_value_rsp never called from Android Framework +get_player_app_attr_text_rsp never called from Android Framework +get_player_app_value_text_rsp never called from Android Framework +set_player_app_value_rsp never called from Android Framework + +callbacks: +list_player_app_attr_cb NULL JNI implementation +list_player_app_values_cb NULL JNI implementation +get_player_app_value_cb NULL JNI implementation +get_player_app_attrs_text_cb NULL JNI implementation +get_player_app_values_text_cb NULL JNI implementation +set_player_app_value_cb NULL JNI implementation + + +Known Android issues +==================== + +It is possible that BlueZ is triggering bugs on Android Framework that could +affect qualification or user experience. This section provides list of +recommended Android fixes that are not part of latest AOSP release supported by +BlueZ. + +https://android-review.googlesource.com/82757 +https://android-review.googlesource.com/87670 +https://android-review.googlesource.com/88384 + + +Unimplemented Bluetooth features +================================ + +Some Bluetooth functionality require support from outside of BT stack +eg. telephony stack. This sections describes profiles optional features not +implemented due to lack of support in other Android subsystems or missing API +in respective BT HALs. + +Profile Feature Comments +-------------------------------------------------------- +HFP Attach a phone number to AT+BINP=1 + a voice tag +HFP Enhanced Call Control AT+CHLD={1x,2x} +HFP Explicit Call Transfer AT+CHLD=4 +HFP Response and Hold AT+BTRH, +BTRH +HFP In-band Ring Tone +BSIR +AVRCP Player Settings HAL API present but not used +AVRCP Browsing No HAL API diff --git a/android/a2dp.c b/android/a2dp.c index 107cbb8..452fdab 100644 --- a/android/a2dp.c +++ b/android/a2dp.c @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -37,28 +37,62 @@ #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" -#include "log.h" -#include "a2dp.h" +#include "profiles/audio/a2dp-codecs.h" +#include "src/log.h" #include "hal-msg.h" +#include "ipc-common.h" #include "ipc.h" +#include "a2dp.h" #include "utils.h" #include "bluetooth.h" #include "avdtp.h" +#include "avrcp.h" +#include "audio-msg.h" #define L2CAP_PSM_AVDTP 0x19 #define SVC_HINT_CAPTURING 0x08 +#define IDLE_TIMEOUT 1 +#define AUDIO_RETRY_TIMEOUT 2 static GIOChannel *server = NULL; static GSList *devices = NULL; +static GSList *endpoints = NULL; +static GSList *setups = NULL; static bdaddr_t adapter_addr; static uint32_t record_id = 0; +static guint audio_retry_id = 0; +static bool audio_retrying = false; + +static struct ipc *hal_ipc = NULL; +static struct ipc *audio_ipc = NULL; + +struct a2dp_preset { + void *data; + int8_t len; +}; + +struct a2dp_endpoint { + uint8_t id; + uint8_t codec; + struct avdtp_local_sep *sep; + struct a2dp_preset *caps; + GSList *presets; +}; struct a2dp_device { bdaddr_t dst; uint8_t state; GIOChannel *io; - guint watch; struct avdtp *session; + guint idle_id; +}; + +struct a2dp_setup { + struct a2dp_device *dev; + struct a2dp_endpoint *endpoint; + struct a2dp_preset *preset; + struct avdtp_stream *stream; + uint8_t state; }; static int device_cmp(gconstpointer s, gconstpointer user_data) @@ -69,21 +103,86 @@ static int device_cmp(gconstpointer s, gconstpointer user_data) return bacmp(&dev->dst, dst); } -static void a2dp_device_free(struct a2dp_device *dev) +static void preset_free(void *data) +{ + struct a2dp_preset *preset = data; + + g_free(preset->data); + g_free(preset); +} + +static void unregister_endpoint(void *data) +{ + struct a2dp_endpoint *endpoint = data; + + if (endpoint->sep) + avdtp_unregister_sep(endpoint->sep); + + if (endpoint->caps) + preset_free(endpoint->caps); + + g_slist_free_full(endpoint->presets, preset_free); + + g_free(endpoint); +} + +static void setup_free(void *data) +{ + struct a2dp_setup *setup = data; + + if (!g_slist_find(setup->endpoint->presets, setup->preset)) + preset_free(setup->preset); + + g_free(setup); +} + +static void setup_remove(struct a2dp_setup *setup) +{ + setups = g_slist_remove(setups, setup); + setup_free(setup); +} + +static void setup_remove_all_by_dev(struct a2dp_device *dev) +{ + GSList *l = setups; + + while (l) { + struct a2dp_setup *setup = l->data; + GSList *next = g_slist_next(l); + + if (setup->dev == dev) + setup_remove(setup); + + l = next; + } +} + +static void a2dp_device_free(void *data) { + struct a2dp_device *dev = data; + + if (dev->idle_id > 0) + g_source_remove(dev->idle_id); + if (dev->session) avdtp_unref(dev->session); - if (dev->watch > 0) - g_source_remove(dev->watch); - - if (dev->io) + if (dev->io) { + g_io_channel_shutdown(dev->io, FALSE, NULL); g_io_channel_unref(dev->io); + } + + setup_remove_all_by_dev(dev); - devices = g_slist_remove(devices, dev); g_free(dev); } +static void a2dp_device_remove(struct a2dp_device *dev) +{ + devices = g_slist_remove(devices, dev); + a2dp_device_free(dev); +} + static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst) { struct a2dp_device *dev; @@ -95,6 +194,25 @@ static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst) return dev; } +static bool a2dp_device_connect(struct a2dp_device *dev, BtIOConnect cb) +{ + GError *err = NULL; + + dev->io = bt_io_connect(cb, dev, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, L2CAP_PSM_AVDTP, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + return false; + } + + return true; +} + static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state) { struct hal_ev_a2dp_conn_state ev; @@ -111,19 +229,280 @@ static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state) bdaddr2android(&dev->dst, ev.bdaddr); ev.state = state; - ipc_send_notif(HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE, sizeof(ev), - &ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE, + sizeof(ev), &ev); if (state != HAL_A2DP_STATE_DISCONNECTED) return; - a2dp_device_free(dev); + bt_avrcp_disconnect(&dev->dst); + + a2dp_device_remove(dev); } -static gboolean watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) +static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state) { - struct a2dp_device *dev = data; + struct hal_ev_a2dp_audio_state ev; + char address[18]; + + if (setup->state == state) + return; + + setup->state = state; + + ba2str(&setup->dev->dst, address); + DBG("device %s state %u", address, state); + + bdaddr2android(&setup->dev->dst, ev.bdaddr); + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE, + sizeof(ev), &ev); +} + +static void disconnect_cb(void *user_data) +{ + struct a2dp_device *dev = user_data; + + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); +} + +static int sbc_check_config(void *caps, uint8_t caps_len, void *conf, + uint8_t conf_len) +{ + a2dp_sbc_t *cap, *config; + + if (conf_len != caps_len || conf_len != sizeof(a2dp_sbc_t)) { + error("SBC: Invalid configuration size (%u)", conf_len); + return -EINVAL; + } + + cap = caps; + config = conf; + + if (!(cap->frequency & config->frequency)) { + error("SBC: Unsupported frequency (%u) by endpoint", + config->frequency); + return -EINVAL; + } + + if (!(cap->channel_mode & config->channel_mode)) { + error("SBC: Unsupported channel mode (%u) by endpoint", + config->channel_mode); + return -EINVAL; + } + + if (!(cap->block_length & config->block_length)) { + error("SBC: Unsupported block length (%u) by endpoint", + config->block_length); + return -EINVAL; + } + + if (!(cap->allocation_method & config->allocation_method)) { + error("SBC: Unsupported allocation method (%u) by endpoint", + config->block_length); + return -EINVAL; + } + + if (config->max_bitpool < cap->min_bitpool) { + error("SBC: Invalid maximun bitpool (%u < %u)", + config->max_bitpool, cap->min_bitpool); + return -EINVAL; + } + + if (config->min_bitpool > cap->max_bitpool) { + error("SBC: Invalid minimun bitpool (%u > %u)", + config->min_bitpool, cap->min_bitpool); + return -EINVAL; + } + + if (config->max_bitpool > cap->max_bitpool) + return -ERANGE; + + if (config->min_bitpool < cap->min_bitpool) + return -ERANGE; + + return 0; +} + +static int check_capabilities(struct a2dp_preset *preset, + struct avdtp_media_codec_capability *codec, + uint8_t codec_len) +{ + /* Codec specific */ + switch (codec->media_codec_type) { + case A2DP_CODEC_SBC: + return sbc_check_config(codec->data, codec_len, preset->data, + preset->len); + default: + return -EINVAL; + } +} + +static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len, + void *conf, uint8_t conf_len) +{ + struct a2dp_preset *p; + a2dp_sbc_t *cap, *config; + + cap = caps; + config = conf; + + config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool); + config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool); + + p = g_new0(struct a2dp_preset, 1); + p->len = conf_len; + p->data = g_memdup(conf, p->len); + + return p; +} + +static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset, + struct avdtp_media_codec_capability *codec, + uint8_t codec_len) +{ + /* Codec specific */ + switch (codec->media_codec_type) { + case A2DP_CODEC_SBC: + return sbc_select_range(codec->data, codec_len, preset->data, + preset->len); + default: + return NULL; + } +} + +static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint, + struct avdtp_remote_sep *rsep) +{ + struct avdtp_service_capability *service; + struct avdtp_media_codec_capability *codec; + GSList *l; + uint8_t codec_len; + + service = avdtp_get_codec(rsep); + codec = (struct avdtp_media_codec_capability *) service->data; + codec_len = service->length - sizeof(*codec); + + for (l = endpoint->presets; l; l = g_slist_next(l)) { + struct a2dp_preset *preset = l->data; + int err; + + err = check_capabilities(preset, codec, codec_len); + if (err == 0) + return preset; + + if (err == -ERANGE) + return select_preset_range(preset, codec, codec_len); + } + + return NULL; +} + +static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint, + struct a2dp_preset *preset, struct avdtp_stream *stream) +{ + struct a2dp_setup *setup; + + setup = g_new0(struct a2dp_setup, 1); + setup->dev = dev; + setup->endpoint = endpoint; + setup->preset = preset; + setup->stream = stream; + setups = g_slist_append(setups, setup); + + if (dev->idle_id > 0) { + g_source_remove(dev->idle_id); + dev->idle_id = 0; + } +} + +static int select_configuration(struct a2dp_device *dev, + struct a2dp_endpoint *endpoint, + struct avdtp_remote_sep *rsep) +{ + struct a2dp_preset *preset; + struct avdtp_stream *stream; + struct avdtp_service_capability *service; + struct avdtp_media_codec_capability *codec; + GSList *caps; + int err; + + preset = select_preset(endpoint, rsep); + if (!preset) { + error("Unable to select codec preset"); + return -EINVAL; + } + + service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); + caps = g_slist_append(NULL, service); + + codec = g_malloc0(sizeof(*codec) + preset->len); + codec->media_type = AVDTP_MEDIA_TYPE_AUDIO; + codec->media_codec_type = endpoint->codec; + memcpy(codec->data, preset->data, preset->len); + + service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec, + sizeof(*codec) + preset->len); + caps = g_slist_append(caps, service); + + g_free(codec); + + err = avdtp_set_configuration(dev->session, rsep, endpoint->sep, caps, + &stream); + g_slist_free_full(caps, g_free); + if (err < 0) { + error("avdtp_set_configuration: %s", strerror(-err)); + return err; + } + + setup_add(dev, endpoint, preset, stream); + + return 0; +} + +static void discover_cb(struct avdtp *session, GSList *seps, + struct avdtp_error *err, void *user_data) +{ + struct a2dp_device *dev = user_data; + struct a2dp_endpoint *endpoint = NULL; + struct avdtp_remote_sep *rsep = NULL; + GSList *l; + + for (l = endpoints; l; l = g_slist_next(l)) { + endpoint = l->data; + + rsep = avdtp_find_remote_sep(session, endpoint->sep); + if (rsep) + break; + } + + if (!rsep) { + error("Unable to find matching endpoint"); + goto failed; + } + + if (select_configuration(dev, endpoint, rsep) < 0) + goto failed; + + return; +failed: + avdtp_shutdown(session); +} + +static gboolean idle_timeout(gpointer user_data) +{ + struct a2dp_device *dev = user_data; + int err; + + dev->idle_id = 0; + + err = avdtp_discover(dev->session, discover_cb, dev); + if (err == 0) + return FALSE; + + error("avdtp_discover: %s", strerror(-err)); bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); return FALSE; @@ -148,20 +527,43 @@ static void signaling_connect_cb(GIOChannel *chan, GError *err, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_INVALID); if (gerr) { - bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); error("%s", gerr->message); g_error_free(gerr); - return; + goto failed; } - /* FIXME: Add proper version */ fd = g_io_channel_unix_get_fd(chan); + + /* FIXME: Add proper version */ dev->session = avdtp_new(fd, imtu, omtu, 0x0100); + if (!dev->session) + goto failed; - dev->watch = g_io_add_watch(dev->io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, - watch_cb, dev); + avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev); - bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED); + if (dev->io) { + g_io_channel_unref(dev->io); + dev->io = NULL; + } + + /* Proceed to stream setup if initiator */ + if (dev->state == HAL_A2DP_STATE_CONNECTING) { + int perr; + + perr = avdtp_discover(dev->session, discover_cb, dev); + if (perr < 0) { + error("avdtp_discover: %s", strerror(-perr)); + goto failed; + } + bt_avrcp_connect(&dev->dst); + } else /* Init idle timeout to discover */ + dev->idle_id = g_timeout_add_seconds(IDLE_TIMEOUT, idle_timeout, + dev); + + return; + +failed: + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); } static void bt_a2dp_connect(const void *buf, uint16_t len) @@ -172,7 +574,6 @@ static void bt_a2dp_connect(const void *buf, uint16_t len) char addr[18]; bdaddr_t dst; GSList *l; - GError *err = NULL; DBG(""); @@ -185,16 +586,8 @@ static void bt_a2dp_connect(const void *buf, uint16_t len) } dev = a2dp_device_new(&dst); - dev->io = bt_io_connect(signaling_connect_cb, dev, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, - BT_IO_OPT_DEST_BDADDR, &dev->dst, - BT_IO_OPT_PSM, L2CAP_PSM_AVDTP, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_INVALID); - if (err) { - error("%s", err->message); - g_error_free(err); - a2dp_device_free(dev); + if (!a2dp_device_connect(dev, signaling_connect_cb)) { + a2dp_device_remove(dev); status = HAL_STATUS_FAILED; goto failed; } @@ -207,7 +600,7 @@ static void bt_a2dp_connect(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status); } static void bt_a2dp_disconnect(const void *buf, uint16_t len) @@ -229,17 +622,20 @@ static void bt_a2dp_disconnect(const void *buf, uint16_t len) } dev = l->data; + status = HAL_STATUS_SUCCESS; - /* Wait signaling channel to HUP */ - if (dev->io) - g_io_channel_shutdown(dev->io, TRUE, NULL); + if (dev->io) { + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); + goto failed; + } + /* Wait AVDTP session to shutdown */ + avdtp_shutdown(dev->session); bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING); - status = HAL_STATUS_SUCCESS; - failed: - ipc_send_rsp(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, + status); } static const struct ipc_handler cmd_handlers[] = { @@ -249,10 +645,71 @@ static const struct ipc_handler cmd_handlers[] = { { bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) }, }; +static struct a2dp_setup *find_setup_by_device(struct a2dp_device *dev) +{ + GSList *l; + + for (l = setups; l; l = g_slist_next(l)) { + struct a2dp_setup *setup = l->data; + + if (setup->dev == dev) + return setup; + } + + return NULL; +} + +static void transport_connect_cb(GIOChannel *chan, GError *err, + gpointer user_data) +{ + struct a2dp_device *dev = user_data; + struct a2dp_setup *setup; + uint16_t imtu, omtu; + GError *gerr = NULL; + int fd; + + if (err) { + error("%s", err->message); + return; + } + + setup = find_setup_by_device(dev); + if (!setup) { + error("Unable to find stream setup"); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + return; + } + + fd = g_io_channel_unix_get_fd(chan); + + if (!avdtp_stream_set_transport(setup->stream, fd, imtu, omtu)) { + error("avdtp_stream_set_transport: failed"); + return; + } + + g_io_channel_set_close_on_unref(chan, FALSE); + + if (dev->io) { + g_io_channel_unref(dev->io); + dev->io = NULL; + } + + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED); +} + static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct a2dp_device *dev; - bdaddr_t src, dst; + bdaddr_t dst; char address[18]; GError *gerr = NULL; GSList *l; @@ -263,7 +720,6 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) } bt_io_get(chan, &gerr, - BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); if (gerr) { @@ -273,13 +729,15 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) return; } - l = g_slist_find_custom(devices, &dst, device_cmp); - if (l) - return; - ba2str(&dst, address); DBG("Incoming connection from %s", address); + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) { + transport_connect_cb(chan, err, l->data); + return; + } + dev = a2dp_device_new(&dst); signaling_connect_cb(chan, err, dev); } @@ -345,12 +803,772 @@ static sdp_record_t *a2dp_record(void) return record; } -bool bt_a2dp_register(const bdaddr_t *addr) +static gboolean sep_getcap_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err, + void *user_data) { - GError *err = NULL; - sdp_record_t *rec; + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_preset *cap = endpoint->caps; + struct avdtp_service_capability *service; + struct avdtp_media_codec_capability *codec; - DBG(""); + *caps = NULL; + + service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); + *caps = g_slist_append(*caps, service); + + codec = g_malloc0(sizeof(*codec) + cap->len); + codec->media_type = AVDTP_MEDIA_TYPE_AUDIO; + codec->media_codec_type = endpoint->codec; + memcpy(codec->data, cap->data, cap->len); + + service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec, + sizeof(*codec) + cap->len); + *caps = g_slist_append(*caps, service); + g_free(codec); + + return TRUE; +} + +static int check_config(struct a2dp_endpoint *endpoint, + struct a2dp_preset *config) +{ + GSList *l; + struct a2dp_preset *caps; + + for (l = endpoint->presets; l; l = g_slist_next(l)) { + struct a2dp_preset *preset = l->data; + + if (preset->len != config->len) + continue; + + if (memcmp(preset->data, config->data, preset->len) == 0) + return 0; + } + + caps = endpoint->caps; + + /* Codec specific */ + switch (endpoint->codec) { + case A2DP_CODEC_SBC: + return sbc_check_config(caps->data, caps->len, config->data, + config->len); + default: + return -EINVAL; + } +} + +static struct a2dp_device *find_device_by_session(struct avdtp *session) +{ + GSList *l; + + for (l = devices; l; l = g_slist_next(l)) { + struct a2dp_device *dev = l->data; + + if (dev->session == session) + return dev; + } + + return NULL; +} + +static struct a2dp_setup *find_setup(uint8_t id) +{ + GSList *l; + + for (l = setups; l; l = g_slist_next(l)) { + struct a2dp_setup *setup = l->data; + + if (setup->endpoint->id == id) + return setup; + } + + return NULL; +} + +static void setup_remove_by_id(uint8_t id) +{ + struct a2dp_setup *setup; + + setup = find_setup(id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", id); + return; + } + + setup_remove(setup); +} + +static gboolean sep_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_endpoint *endpoint = user_data; + struct a2dp_device *dev; + struct a2dp_preset *preset = NULL; + + DBG(""); + + dev = find_device_by_session(session); + if (!dev) { + error("Unable to find device for session %p", session); + return FALSE; + } + + for (; caps != NULL; caps = g_slist_next(caps)) { + struct avdtp_service_capability *cap = caps->data; + struct avdtp_media_codec_capability *codec; + + if (cap->category == AVDTP_DELAY_REPORTING) + return FALSE; + + if (cap->category != AVDTP_MEDIA_CODEC) + continue; + + codec = (struct avdtp_media_codec_capability *) cap->data; + + if (codec->media_codec_type != endpoint->codec) + return FALSE; + + preset = g_new0(struct a2dp_preset, 1); + preset->len = cap->length - sizeof(*codec); + preset->data = g_memdup(codec->data, preset->len); + + if (check_config(endpoint, preset) < 0) { + preset_free(preset); + return FALSE; + } + } + + if (!preset) + return FALSE; + + setup_add(dev, endpoint, preset, stream); + + cb(session, stream, NULL); + + return TRUE; +} + +static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + *err = AVDTP_SEP_NOT_IN_USE; + return FALSE; + } + + return TRUE; +} + +static gboolean sep_close_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + *err = AVDTP_SEP_NOT_IN_USE; + return FALSE; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); + + setup_remove(setup); + + return TRUE; +} + +static gboolean sep_start_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + *err = AVDTP_SEP_NOT_IN_USE; + return FALSE; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STARTED); + + return TRUE; +} + +static gboolean sep_suspend_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + *err = AVDTP_SEP_NOT_IN_USE; + return FALSE; + } + + bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND); + + return TRUE; +} + +static struct avdtp_sep_ind sep_ind = { + .get_capability = sep_getcap_ind, + .set_configuration = sep_setconf_ind, + .open = sep_open_ind, + .close = sep_close_ind, + .start = sep_start_ind, + .suspend = sep_suspend_ind, +}; + +static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + int ret; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + return; + } + + if (err) + goto failed; + + ret = avdtp_open(session, stream); + if (ret < 0) { + error("avdtp_open: %s", strerror(-ret)); + goto failed; + } + + return; + +failed: + setup_remove(setup); +} + +static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_device *dev; + + DBG(""); + + if (err) + goto failed; + + dev = find_device_by_session(session); + if (!dev) { + error("Unable to find device for session"); + goto failed; + } + + a2dp_device_connect(dev, transport_connect_cb); + + return; + +failed: + setup_remove_by_id(endpoint->id); +} + +static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + if (err) { + setup_remove_by_id(endpoint->id); + return; + } + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for %u endpoint", + endpoint->id); + return; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STARTED); +} + +static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + if (err) { + setup_remove_by_id(endpoint->id); + return; + } + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for %u endpoint", + endpoint->id); + return; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); +} + +static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + if (err) + return; + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for %u endpoint", + endpoint->id); + return; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); + + setup_remove(setup); +} + +static void sep_abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + + DBG(""); + + if (err) + return; + + setup_remove_by_id(endpoint->id); +} + +static struct avdtp_sep_cfm sep_cfm = { + .set_configuration = sep_setconf_cfm, + .open = sep_open_cfm, + .start = sep_start_cfm, + .suspend = sep_suspend_cfm, + .close = sep_close_cfm, + .abort = sep_abort_cfm, +}; + +static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec, + GSList *presets) +{ + struct a2dp_endpoint *endpoint; + + /* FIXME: Add proper check for uuid */ + + endpoint = g_new0(struct a2dp_endpoint, 1); + endpoint->id = g_slist_length(endpoints) + 1; + endpoint->codec = codec; + endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, + AVDTP_MEDIA_TYPE_AUDIO, + codec, FALSE, &sep_ind, + &sep_cfm, endpoint); + endpoint->caps = presets->data; + endpoint->presets = g_slist_copy(g_slist_nth(presets, 1)); + + endpoints = g_slist_append(endpoints, endpoint); + + return endpoint->id; +} + +static GSList *parse_presets(const struct audio_preset *p, uint8_t count, + uint16_t len) +{ + GSList *l = NULL; + uint8_t i; + + for (i = 0; count > i; i++) { + const uint8_t *ptr = (const uint8_t *) p; + struct a2dp_preset *preset; + + if (len < sizeof(struct audio_preset)) { + DBG("Invalid preset index %u", i); + g_slist_free_full(l, preset_free); + return NULL; + } + + len -= sizeof(struct audio_preset); + if (len == 0 || len < p->len) { + DBG("Invalid preset size of %u for index %u", len, i); + g_slist_free_full(l, preset_free); + return NULL; + } + + preset = g_new0(struct a2dp_preset, 1); + preset->len = p->len; + preset->data = g_memdup(p->data, preset->len); + l = g_slist_append(l, preset); + + len -= preset->len; + ptr += sizeof(*p) + preset->len; + p = (const struct audio_preset *) ptr; + } + + return l; +} + +static void bt_audio_open(const void *buf, uint16_t len) +{ + const struct audio_cmd_open *cmd = buf; + struct audio_rsp_open rsp; + GSList *presets; + + DBG(""); + + audio_retrying = false; + + if (cmd->presets == 0) { + error("No audio presets found"); + goto failed; + } + + presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd)); + if (!presets) { + error("No audio presets found"); + goto failed; + } + + rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets); + if (rsp.id == 0) { + g_slist_free_full(presets, preset_free); + error("Unable to register endpoint"); + goto failed; + } + + g_slist_free(presets); + + ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN, + sizeof(rsp), &rsp, -1); + + return; + +failed: + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN, + AUDIO_STATUS_FAILED); +} + +static struct a2dp_endpoint *find_endpoint(uint8_t id) +{ + GSList *l; + + for (l = endpoints; l; l = g_slist_next(l)) { + struct a2dp_endpoint *endpoint = l->data; + + if (endpoint->id == id) + return endpoint; + } + + return NULL; +} + +static void bt_audio_close(const void *buf, uint16_t len) +{ + const struct audio_cmd_close *cmd = buf; + struct a2dp_endpoint *endpoint; + + DBG(""); + + endpoint = find_endpoint(cmd->id); + if (!endpoint) { + error("Unable to find endpoint %u", cmd->id); + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, + AUDIO_STATUS_FAILED); + return; + } + + endpoints = g_slist_remove(endpoints, endpoint); + unregister_endpoint(endpoint); + + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, + AUDIO_STATUS_SUCCESS); +} + +static void bt_stream_open(const void *buf, uint16_t len) +{ + const struct audio_cmd_open_stream *cmd = buf; + struct audio_rsp_open_stream *rsp; + struct a2dp_setup *setup; + int fd; + uint16_t omtu; + + DBG(""); + + setup = find_setup(cmd->id); + if (!setup) { + error("Unable to find stream for endpoint %u", cmd->id); + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, + AUDIO_STATUS_FAILED); + return; + } + + if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu, + NULL)) { + error("avdtp_stream_get_transport: failed"); + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, + AUDIO_STATUS_FAILED); + return; + } + + len = sizeof(struct audio_rsp_open_stream) + + sizeof(struct audio_preset) + setup->preset->len; + rsp = g_malloc0(len); + rsp->mtu = omtu; + rsp->preset->len = setup->preset->len; + memcpy(rsp->preset->data, setup->preset->data, setup->preset->len); + + ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, + len, rsp, fd); + + g_free(rsp); +} + +static void bt_stream_close(const void *buf, uint16_t len) +{ + const struct audio_cmd_close_stream *cmd = buf; + struct a2dp_setup *setup; + int err; + + DBG(""); + + setup = find_setup(cmd->id); + if (!setup) { + error("Unable to find stream for endpoint %u", cmd->id); + goto failed; + } + + err = avdtp_close(setup->dev->session, setup->stream, FALSE); + if (err < 0) { + error("avdtp_close: %s", strerror(-err)); + goto failed; + } + + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, + AUDIO_STATUS_SUCCESS); + + return; + +failed: + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, + AUDIO_STATUS_FAILED); +} + +static void bt_stream_resume(const void *buf, uint16_t len) +{ + const struct audio_cmd_resume_stream *cmd = buf; + struct a2dp_setup *setup; + int err; + + DBG(""); + + setup = find_setup(cmd->id); + if (!setup) { + error("Unable to find stream for endpoint %u", cmd->id); + goto failed; + } + + if (setup->state != HAL_AUDIO_STARTED) { + err = avdtp_start(setup->dev->session, setup->stream); + if (err < 0) { + error("avdtp_start: %s", strerror(-err)); + goto failed; + } + } + + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, + AUDIO_STATUS_SUCCESS); + + return; + +failed: + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, + AUDIO_STATUS_FAILED); +} + +static void bt_stream_suspend(const void *buf, uint16_t len) +{ + const struct audio_cmd_suspend_stream *cmd = buf; + struct a2dp_setup *setup; + int err; + + DBG(""); + + setup = find_setup(cmd->id); + if (!setup) { + error("Unable to find stream for endpoint %u", cmd->id); + goto failed; + } + + err = avdtp_suspend(setup->dev->session, setup->stream); + if (err < 0) { + error("avdtp_suspend: %s", strerror(-err)); + goto failed; + } + + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, + AUDIO_STATUS_SUCCESS); + + return; + +failed: + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, + AUDIO_STATUS_FAILED); +} + +static const struct ipc_handler audio_handlers[] = { + /* AUDIO_OP_OPEN */ + { bt_audio_open, true, sizeof(struct audio_cmd_open) }, + /* AUDIO_OP_CLOSE */ + { bt_audio_close, false, sizeof(struct audio_cmd_close) }, + /* AUDIO_OP_OPEN_STREAM */ + { bt_stream_open, false, sizeof(struct audio_cmd_open_stream) }, + /* AUDIO_OP_CLOSE_STREAM */ + { bt_stream_close, false, sizeof(struct audio_cmd_close_stream) }, + /* AUDIO_OP_RESUME_STREAM */ + { bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) }, + /* AUDIO_OP_SUSPEND_STREAM */ + { bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) }, +}; + +static void bt_audio_unregister(void) +{ + DBG(""); + + if (audio_retry_id > 0) + g_source_remove(audio_retry_id); + + g_slist_free_full(endpoints, unregister_endpoint); + endpoints = NULL; + + g_slist_free_full(setups, setup_free); + setups = NULL; + + ipc_cleanup(audio_ipc); + audio_ipc = NULL; +} + +static bool bt_audio_register(ipc_disconnect_cb disconnect) +{ + DBG(""); + + audio_ipc = ipc_init(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH), + AUDIO_SERVICE_ID_MAX, false, disconnect, NULL); + if (!audio_ipc) + return false; + + ipc_register(audio_ipc, AUDIO_SERVICE_ID, audio_handlers, + G_N_ELEMENTS(audio_handlers)); + + return true; +} + +static gboolean audio_retry_register(void *data) +{ + ipc_disconnect_cb cb = data; + + audio_retry_id = 0; + audio_retrying = true; + + bt_audio_register(cb); + + return FALSE; +} + +static void audio_disconnected(void *data) +{ + GSList *l; + bool restart; + + DBG(""); + + if (audio_retrying) + goto retry; + + restart = endpoints != NULL ? true : false; + + bt_audio_unregister(); + + for (l = devices; l; l = g_slist_next(l)) { + struct a2dp_device *dev = l->data; + + avdtp_shutdown(dev->session); + } + + if (!restart) + return; + +retry: + audio_retry_id = g_timeout_add_seconds(AUDIO_RETRY_TIMEOUT, + audio_retry_register, + audio_disconnected); +} + +bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + GError *err = NULL; + sdp_record_t *rec; + + DBG(""); bacpy(&adapter_addr, addr); @@ -358,6 +1576,7 @@ bool bt_a2dp_register(const bdaddr_t *addr) BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_PSM, L2CAP_PSM_AVDTP, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_MASTER, true, BT_IO_OPT_INVALID); if (!server) { error("Failed to listen on AVDTP channel: %s", err->message); @@ -378,10 +1597,13 @@ bool bt_a2dp_register(const bdaddr_t *addr) } record_id = rec->handle; - ipc_register(HAL_SERVICE_ID_A2DP, cmd_handlers, + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); - return true; + if (bt_audio_register(audio_disconnected)) + return true; fail: g_io_channel_shutdown(server, TRUE, NULL); @@ -390,22 +1612,22 @@ fail: return false; } -static void a2dp_device_disconnected(gpointer data, gpointer user_data) -{ - struct a2dp_device *dev = data; - - bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); -} - void bt_a2dp_unregister(void) { DBG(""); - g_slist_foreach(devices, a2dp_device_disconnected, NULL); + g_slist_free_full(setups, setup_free); + setups = NULL; + + g_slist_free_full(endpoints, unregister_endpoint); + endpoints = NULL; + + g_slist_free_full(devices, a2dp_device_free); devices = NULL; + ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP); + hal_ipc = NULL; - ipc_unregister(HAL_SERVICE_ID_A2DP); bt_adapter_remove_record(record_id); record_id = 0; @@ -414,4 +1636,10 @@ void bt_a2dp_unregister(void) g_io_channel_unref(server); server = NULL; } + + if (audio_ipc) { + ipc_unregister(audio_ipc, AUDIO_SERVICE_ID); + ipc_cleanup(audio_ipc); + audio_ipc = NULL; + } } diff --git a/android/a2dp.h b/android/a2dp.h index 7e9b2f6..8a70407 100644 --- a/android/a2dp.h +++ b/android/a2dp.h @@ -2,24 +2,24 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ -bool bt_a2dp_register(const bdaddr_t *addr); +bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_a2dp_unregister(void); diff --git a/android/android-tester.c b/android/android-tester.c new file mode 100644 index 0000000..f04f8ac --- /dev/null +++ b/android/android-tester.c @@ -0,0 +1,4827 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/mgmt.h" + +#include "src/shared/util.h" +#include "src/shared/tester.h" +#include "src/shared/mgmt.h" +#include "src/shared/hciemu.h" + +#include "emulator/bthost.h" +#include "monitor/bt.h" + +#include +#include +#include +#include + +#include "utils.h" + +struct priority_property { + bt_property_t prop; + int prio; +}; + +struct generic_data { + int expected_adapter_status; + uint32_t expect_settings_set; + int expected_cb_count; + bt_property_t set_property; + bt_callbacks_t expected_hal_cb; + struct priority_property *expected_properties; + uint8_t expected_properties_num; +}; + +struct socket_data { + btsock_type_t sock_type; + const char *service_name; + const uint8_t *service_uuid; + const bt_bdaddr_t *bdaddr; + int channel; + int flags; + bt_status_t expected_status; + bool test_channel; +}; + +struct hidhost_generic_data { + bthh_status_t expected_status; + int expected_conn_state; + int expected_cb_count; + bthh_protocol_mode_t expected_protocol_mode; + int expected_report; + bthh_callbacks_t expected_hal_cb; + int expected_report_size; +}; + +#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */ +#define EMULATOR_SIGNAL "emulator_started" + +#define BT_STATUS_NOT_EXPECTED -1 + +struct test_data { + struct mgmt *mgmt; + uint16_t mgmt_index; + unsigned int mgmt_settings_id; + struct hciemu *hciemu; + enum hciemu_type hciemu_type; + const void *test_data; + pid_t bluetoothd_pid; + guint signalfd; + + struct hw_device_t *device; + const bt_interface_t *if_bluetooth; + const btsock_interface_t *if_sock; + const bthh_interface_t *if_hid; + + int conditions_left; + + /* Set to true if test conditions should be verified */ + bool test_checks_valid; + + bool test_result_set; + + int cb_count; + GSList *expected_properties_list; + + /* hidhost */ + uint16_t sdp_handle; + uint16_t sdp_cid; + uint16_t ctrl_handle; + uint16_t ctrl_cid; + uint16_t intr_handle; + uint16_t intr_cid; +}; + +struct bt_cb_data { + bt_state_t state; + bt_status_t status; + + bt_bdaddr_t bdaddr; + bt_bdname_t bdname; + uint32_t cod; + + bt_ssp_variant_t ssp_variant; + uint32_t passkey; + + int num; + bt_property_t *props; +}; + +struct hh_cb_data { + bt_bdaddr_t bdaddr; + + bthh_status_t status; + bthh_hid_info_t hid_info; + bthh_protocol_mode_t mode; + bthh_connection_state_t state; + + uint8_t *report; + int size; +}; + +static char exec_dir[PATH_MAX + 1]; + +static gint scheduled_cbacks_num = 0; + +static gboolean check_callbacks_called(gpointer user_data) +{ + /* + * Wait for all callbacks scheduled in current test context to execute + * in main loop. This will avoid late callback calls after test case has + * already failed or timed out. + */ + + if (g_atomic_int_get(&scheduled_cbacks_num) == 0) { + tester_teardown_complete(); + return FALSE; + } + + return TRUE; +} +static void check_daemon_term(void) +{ + int status; + pid_t pid; + struct test_data *data = tester_get_data(); + + if (!data) + return; + + pid = waitpid(data->bluetoothd_pid, &status, WNOHANG); + if (pid != data->bluetoothd_pid) + return; + + data->bluetoothd_pid = 0; + + if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS)) { + g_idle_add(check_callbacks_called, NULL); + return; + } + + tester_warn("Unexpected Daemon shutdown with status %d", status); +} + +static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct signalfd_siginfo si; + ssize_t result; + int fd; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) + return FALSE; + + fd = g_io_channel_unix_get_fd(channel); + + result = read(fd, &si, sizeof(si)); + if (result != sizeof(si)) + return FALSE; + + switch (si.ssi_signo) { + case SIGCHLD: + check_daemon_term(); + break; + } + + return TRUE; +} + +static guint setup_signalfd(void) +{ + GIOChannel *channel; + guint source; + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) + return 0; + + fd = signalfd(-1, &mask, 0); + if (fd < 0) + return 0; + + channel = g_io_channel_unix_new(fd); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +static void mgmt_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + tester_print("%s%s", prefix, str); +} + +static void test_update_state(void) +{ + struct test_data *data = tester_get_data(); + + if (data->conditions_left == 0 && !data->test_result_set) { + data->test_result_set = true; + tester_test_passed(); + } +} + +static void test_mgmt_settings_set(struct test_data *data) +{ + data->conditions_left--; + + test_update_state(); +} + +static void command_generic_new_settings(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test_data = data->test_data; + uint32_t settings; + + if (length != 4) { + tester_warn("Invalid parameter size for new settings event"); + tester_test_failed(); + return; + } + + settings = get_le32(param); + + if ((settings & test_data->expect_settings_set) != + test_data->expect_settings_set) + return; + + test_mgmt_settings_set(data); + mgmt_unregister(data->mgmt, data->mgmt_settings_id); +} + +static void check_cb_count(void) +{ + struct test_data *data = tester_get_data(); + + if (!data->test_checks_valid) + return; + + if (data->cb_count == 0) { + data->conditions_left--; + test_update_state(); + } +} + +static void expected_cb_count_init(struct test_data *data) +{ + const struct generic_data *test_data = data->test_data; + + data->cb_count = test_data->expected_cb_count; + + check_cb_count(); +} + +static void mgmt_cb_init(struct test_data *data) +{ + const struct generic_data *test_data = data->test_data; + + if (!test_data->expect_settings_set) + test_mgmt_settings_set(data); + else + data->mgmt_settings_id = mgmt_register(data->mgmt, + MGMT_EV_NEW_SETTINGS, data->mgmt_index, + command_generic_new_settings, NULL, NULL); +} + +static void expected_status_init(struct test_data *data) +{ + const struct generic_data *test_data = data->test_data; + + if (test_data->expected_adapter_status == BT_STATUS_NOT_EXPECTED) + data->conditions_left--; +} + +static void test_property_init(struct test_data *data) +{ + const struct generic_data *test_data = data->test_data; + GSList *l = data->expected_properties_list; + int i; + + if (!test_data->expected_properties_num) { + data->conditions_left--; + return; + } + + for (i = 0; i < test_data->expected_properties_num; i++) + l = g_slist_prepend(l, &(test_data->expected_properties[i])); + + data->expected_properties_list = l; +} + +static void init_test_conditions(struct test_data *data) +{ + data->test_checks_valid = true; + + data->conditions_left = 4; + + expected_cb_count_init(data); + mgmt_cb_init(data); + expected_status_init(data); + test_property_init(data); +} + +static void check_expected_status(uint8_t status) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test_data = data->test_data; + + if (test_data->expected_adapter_status == status) { + data->conditions_left--; + test_update_state(); + } else + tester_test_failed(); +} + +static int locate_property(gconstpointer expected_data, + gconstpointer received_prop) +{ + bt_property_t rec_prop = *((bt_property_t *)received_prop); + bt_property_t exp_prop = + ((struct priority_property *)expected_data)->prop; + + if (exp_prop.type && (exp_prop.type != rec_prop.type)) + return 1; + if (exp_prop.len && (exp_prop.len != rec_prop.len)) + return 1; + if (exp_prop.val && memcmp(exp_prop.val, rec_prop.val, exp_prop.len)) + return 1; + + return 0; +} + +static int compare_priorities(gconstpointer prop_list, gconstpointer priority) +{ + int prio = GPOINTER_TO_INT(priority); + int comp_prio = ((struct priority_property *)prop_list)->prio; + + if (prio > comp_prio) + return 0; + + return 1; +} + +static bool check_prop_priority(int rec_prop_prio) +{ + struct test_data *data = tester_get_data(); + GSList *l = data->expected_properties_list; + + if (!rec_prop_prio || !g_slist_length(l)) + return true; + + if (g_slist_find_custom(l, GINT_TO_POINTER(rec_prop_prio), + &compare_priorities)) + return false; + + return true; +} + +static void check_expected_property(bt_property_t received_prop) +{ + struct test_data *data = tester_get_data(); + int rec_prio; + GSList *l = data->expected_properties_list; + GSList *found_exp_prop; + + if (!g_slist_length(l)) + return; + + found_exp_prop = g_slist_find_custom(l, &received_prop, + &locate_property); + + if (found_exp_prop) { + rec_prio = ((struct priority_property *) + (found_exp_prop->data))->prio; + if (check_prop_priority(rec_prio)) + l = g_slist_remove(l, found_exp_prop->data); + } + + data->expected_properties_list = l; + + if (g_slist_length(l)) + return; + + data->conditions_left--; + test_update_state(); +} + +static bool check_test_property(bt_property_t received_prop, + bt_property_t expected_prop) +{ + if (expected_prop.type && (expected_prop.type != received_prop.type)) + return false; + if (expected_prop.len && (expected_prop.len != received_prop.len)) + return false; + if (expected_prop.val && memcmp(expected_prop.val, received_prop.val, + expected_prop.len)) + return false; + + return true; +} + +static void read_info_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct mgmt_rp_read_info *rp = param; + char addr[18]; + uint16_t manufacturer; + uint32_t supported_settings, current_settings; + + tester_print("Read Info callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + ba2str(&rp->bdaddr, addr); + manufacturer = btohs(rp->manufacturer); + supported_settings = btohl(rp->supported_settings); + current_settings = btohl(rp->current_settings); + + tester_print(" Address: %s", addr); + tester_print(" Version: 0x%02x", rp->version); + tester_print(" Manufacturer: 0x%04x", manufacturer); + tester_print(" Supported settings: 0x%08x", supported_settings); + tester_print(" Current settings: 0x%08x", current_settings); + tester_print(" Class: 0x%02x%02x%02x", + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); + tester_print(" Name: %s", rp->name); + tester_print(" Short name: %s", rp->short_name); + + if (strcmp(hciemu_get_address(data->hciemu), addr)) { + tester_pre_setup_failed(); + return; + } + + tester_pre_setup_complete(); +} + +static void index_added_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Added callback"); + tester_print(" Index: 0x%04x", index); + + data->mgmt_index = index; + + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, + read_info_callback, NULL, NULL); +} + +static void index_removed_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Removed callback"); + tester_print(" Index: 0x%04x", index); + + if (index != data->mgmt_index) + return; + + mgmt_unregister_index(data->mgmt, data->mgmt_index); + + mgmt_unref(data->mgmt); + data->mgmt = NULL; + + tester_post_teardown_complete(); +} + +static void read_index_list_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Read Index List callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + index_added_callback, NULL, NULL); + + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + index_removed_callback, NULL, NULL); + + data->hciemu = hciemu_new(data->hciemu_type); + if (!data->hciemu) { + tester_warn("Failed to setup HCI emulation"); + tester_pre_setup_failed(); + return; + } + + tester_print("New hciemu instance created"); +} + +static void test_pre_setup(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + data->signalfd = setup_signalfd(); + if (!data->signalfd) { + tester_warn("Failed to setup signalfd"); + tester_pre_setup_failed(); + return; + } + + data->mgmt = mgmt_new_default(); + if (!data->mgmt) { + tester_warn("Failed to setup management interface"); + tester_pre_setup_failed(); + return; + } + + if (!tester_use_debug()) + fclose(stderr); + else + mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL); + + mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, + NULL, read_index_list_callback, NULL, NULL); +} + +static void test_post_teardown(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + hciemu_unref(data->hciemu); + data->hciemu = NULL; + + g_source_remove(data->signalfd); + data->signalfd = 0; +} + +static void bluetoothd_start(int hci_index) +{ + char prg_name[PATH_MAX + 1]; + char index[8]; + char *prg_argv[5]; + + snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); + snprintf(index, sizeof(index), "%d", hci_index); + + prg_argv[0] = prg_name; + prg_argv[1] = "-i"; + prg_argv[2] = index; + prg_argv[3] = "-d"; + prg_argv[4] = NULL; + + if (!tester_use_debug()) + fclose(stderr); + + execve(prg_argv[0], prg_argv, NULL); +} + +static void emulator(int pipe, int hci_index) +{ + static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; + char buf[1024]; + struct sockaddr_un addr; + struct timeval tv; + int fd; + ssize_t len; + + fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) + goto failed; + + tv.tv_sec = WAIT_FOR_SIGNAL_TIME; + tv.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to bind system socket"); + goto failed; + } + + len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL)); + if (len != sizeof(EMULATOR_SIGNAL)) + goto failed; + + memset(buf, 0, sizeof(buf)); + + len = read(fd, buf, sizeof(buf)); + if (len <= 0 || strcmp(buf, "bluetooth.start=daemon")) + goto failed; + + close(pipe); + close(fd); + return bluetoothd_start(hci_index); + +failed: + close(pipe); + + if (fd >= 0) + close(fd); +} + +static void emu_connectable_complete(uint16_t opcode, uint8_t status, + const void *param, uint8_t len, + void *user_data) +{ + switch (opcode) { + case BT_HCI_CMD_WRITE_SCAN_ENABLE: + case BT_HCI_CMD_LE_SET_ADV_ENABLE: + break; + default: + return; + } + + tester_print("Emulated remote set connectable status 0x%02x", status); + + if (status) + tester_setup_failed(); + else + tester_setup_complete(); +} + +static void setup_powered_emulated_remote(void) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + + tester_print("Controller powered on"); + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_cmd_complete_cb(bthost, emu_connectable_complete, data); + + if (data->hciemu_type == HCIEMU_TYPE_LE) + bthost_set_adv_enable(bthost, 0x01); + else + bthost_write_scan_enable(bthost, 0x03); +} + +static void enable_success_cb(bt_state_t state) +{ + struct test_data *data = tester_get_data(); + + if (state == BT_STATE_ON) { + setup_powered_emulated_remote(); + data->cb_count--; + check_cb_count(); + } +} + +static void disable_success_cb(bt_state_t state) +{ + struct test_data *data = tester_get_data(); + + if (state == BT_STATE_OFF) { + data->cb_count--; + check_cb_count(); + } +} + +static gboolean adapter_state_changed(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bt_cb_data *cb_data = user_data; + + if (data->test_checks_valid && + test->expected_hal_cb.adapter_state_changed_cb) { + test->expected_hal_cb.adapter_state_changed_cb(cb_data->state); + goto cleanup; + } + + if (!data->test_checks_valid && cb_data->state == BT_STATE_ON) + setup_powered_emulated_remote(); + +cleanup: + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void adapter_state_changed_cb(bt_state_t state) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->state = state; + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(adapter_state_changed, cb_data); +} + +static void discovery_start_success_cb(bt_discovery_state_t state) +{ + struct test_data *data = tester_get_data(); + + if (state == BT_DISCOVERY_STARTED) { + data->cb_count--; + check_cb_count(); + } +} + +static void discovery_start_done_cb(bt_discovery_state_t state) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + + status = data->if_bluetooth->start_discovery(); + data->cb_count--; + + check_cb_count(); + check_expected_status(status); +} + +static void discovery_stop_success_cb(bt_discovery_state_t state) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + + if (state == BT_DISCOVERY_STARTED && data->cb_count == 2) { + status = data->if_bluetooth->cancel_discovery(); + check_expected_status(status); + data->cb_count--; + return; + } + if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) { + data->cb_count--; + check_cb_count(); + } +} + +static void discovery_device_found_state_changed_cb(bt_discovery_state_t state) +{ + struct test_data *data = tester_get_data(); + + if (state == BT_DISCOVERY_STARTED && data->cb_count == 3) { + data->cb_count--; + return; + } + if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) { + data->cb_count--; + check_cb_count(); + } +} + +static void remote_discovery_state_changed_cb(bt_discovery_state_t state) +{ + struct test_data *data = tester_get_data(); + + if (state == BT_DISCOVERY_STARTED && data->cb_count == 3) { + data->cb_count--; + return; + } + if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) { + data->cb_count--; + check_cb_count(); + } +} + +static void remote_setprop_disc_state_changed_cb(bt_discovery_state_t state) +{ + struct test_data *data = tester_get_data(); + + if (state == BT_DISCOVERY_STARTED && data->cb_count == 3) { + data->cb_count--; + return; + } + if (state == BT_DISCOVERY_STOPPED) { + data->cb_count--; + check_cb_count(); + } +} + +static gboolean discovery_state_changed(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bt_cb_data *cb_data = user_data; + + if (test && test->expected_hal_cb.discovery_state_changed_cb) + test->expected_hal_cb.discovery_state_changed_cb( + cb_data->state); + + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void discovery_state_changed_cb(bt_discovery_state_t state) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->state = state; + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(discovery_state_changed, cb_data); +} + +static bt_property_t *copy_properties(int num_properties, + bt_property_t *properties) +{ + int i; + bt_property_t *props = g_new0(bt_property_t, num_properties); + + for (i = 0; i < num_properties; i++) { + props[i].type = properties[i].type; + props[i].len = properties[i].len; + props[i].val = g_memdup(properties[i].val, properties[i].len); + } + + return props; +} + +static void free_properties(int num_properties, bt_property_t *properties) +{ + int i; + + for (i = 0; i < num_properties; i++) + g_free(properties[i].val); + + g_free(properties); +} + +static void discovery_device_found_cb(int num_properties, + bt_property_t *properties) +{ + struct test_data *data = tester_get_data(); + uint8_t *remote_bdaddr = + (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + uint32_t emu_remote_type = BT_DEVICE_DEVTYPE_BREDR; + int32_t emu_remote_rssi = -60; + bt_bdaddr_t emu_remote_bdaddr; + int i; + bt_property_t expected_prop; + bt_property_t received_prop; + + data->cb_count--; + check_cb_count(); + + if (num_properties < 1) { + tester_test_failed(); + return; + } + + bdaddr2android((const bdaddr_t *) remote_bdaddr, &emu_remote_bdaddr); + + for (i = 0; i < num_properties; i++) { + received_prop = properties[i]; + + switch (properties[i].type) { + case BT_PROPERTY_BDADDR: + expected_prop.type = BT_PROPERTY_BDADDR; + expected_prop.len = sizeof(emu_remote_bdaddr); + expected_prop.val = &emu_remote_bdaddr; + break; + + case BT_PROPERTY_TYPE_OF_DEVICE: + expected_prop.type = BT_PROPERTY_TYPE_OF_DEVICE; + expected_prop.len = sizeof(emu_remote_type); + expected_prop.val = &emu_remote_type; + break; + + case BT_PROPERTY_REMOTE_RSSI: + expected_prop.type = BT_PROPERTY_REMOTE_RSSI; + expected_prop.len = sizeof(emu_remote_rssi); + expected_prop.val = &emu_remote_rssi; + break; + + default: + expected_prop.type = 0; + expected_prop.len = 0; + expected_prop.val = NULL; + break; + } + + if (!check_test_property(received_prop, expected_prop)) { + data->if_bluetooth->cancel_discovery(); + tester_test_failed(); + return; + } + } + + data->if_bluetooth->cancel_discovery(); +} + +static void remote_getprops_device_found_cb(int num_properties, + bt_property_t *properties) +{ + struct test_data *data = tester_get_data(); + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + if (data->cb_count == 2) + data->cb_count--; + + data->if_bluetooth->cancel_discovery(); + data->if_bluetooth->get_remote_device_properties(&remote_addr); +} + +static void remote_get_property_device_found_cb(int num_properties, + bt_property_t *properties) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + bt_status_t status; + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + + const bt_property_t prop = test->expected_properties[0].prop; + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + if (data->cb_count == 2) + data->cb_count--; + + data->if_bluetooth->cancel_discovery(); + status = data->if_bluetooth->get_remote_device_property(&remote_addr, + prop.type); + check_expected_status(status); +} + +static void remote_setprop_device_found_cb(int num_properties, + bt_property_t *properties) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + bt_status_t status; + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + + const bt_property_t prop = test->expected_properties[0].prop; + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + if (data->cb_count == 2) + data->cb_count--; + + data->if_bluetooth->cancel_discovery(); + status = data->if_bluetooth->set_remote_device_property(&remote_addr, + &prop); + check_expected_status(status); + + status = data->if_bluetooth->get_remote_device_property(&remote_addr, prop.type); +} + +static void remote_setprop_fail_device_found_cb(int num_properties, + bt_property_t *properties) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + bt_status_t status; + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + + const bt_property_t prop = test->expected_properties[0].prop; + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + if (data->cb_count == 2) + data->cb_count--; + + data->if_bluetooth->cancel_discovery(); + status = data->if_bluetooth->set_remote_device_property(&remote_addr, + &prop); + check_expected_status(status); +} + +static void bond_device_found_cb(int num_properties, bt_property_t *properties) +{ + struct test_data *data = tester_get_data(); + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + bt_status_t status; + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + if (data->cb_count == 4) { + data->cb_count--; + status = data->if_bluetooth->create_bond(&remote_addr); + check_expected_status(status); + } +} + +static void bond_nostatus_device_found_cb(int num_properties, + bt_property_t *properties) +{ + struct test_data *data = tester_get_data(); + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + if (data->cb_count == 4) { + data->cb_count--; + data->if_bluetooth->create_bond(&remote_addr); + } +} + +static gboolean device_found(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bt_cb_data *cb_data = user_data; + + if (data->test_checks_valid && test->expected_hal_cb.device_found_cb) + test->expected_hal_cb.device_found_cb(cb_data->num, + cb_data->props); + + free_properties(cb_data->num, cb_data->props); + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void device_found_cb(int num_properties, bt_property_t *properties) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->num = num_properties; + cb_data->props = copy_properties(num_properties, properties); + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(device_found, cb_data); +} + +static void check_count_properties_cb(bt_status_t status, int num_properties, + bt_property_t *properties) +{ + int i; + + for (i = 0; i < num_properties; i++) + check_expected_property(properties[i]); +} + +static gboolean adapter_properties(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bt_cb_data *cb_data = user_data; + + if (data->test_checks_valid && + test->expected_hal_cb.adapter_properties_cb) + test->expected_hal_cb.adapter_properties_cb(cb_data->status, + cb_data->num, cb_data->props); + + free_properties(cb_data->num, cb_data->props); + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void adapter_properties_cb(bt_status_t status, int num_properties, + bt_property_t *properties) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->status = status; + cb_data->num = num_properties; + cb_data->props = copy_properties(num_properties, properties); + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(adapter_properties, cb_data); +} + +static void remote_test_device_properties_cb(bt_status_t status, + bt_bdaddr_t *bd_addr, int num_properties, + bt_property_t *properties) +{ + int i; + + for (i = 0; i < num_properties; i++) + check_expected_property(properties[i]); +} + +static gboolean remote_device_properties(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bt_cb_data *cb_data = user_data; + + if (data->test_checks_valid && + test->expected_hal_cb.remote_device_properties_cb) + test->expected_hal_cb.remote_device_properties_cb( + cb_data->status, &cb_data->bdaddr, + cb_data->num, cb_data->props); + + free_properties(cb_data->num, cb_data->props); + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void remote_device_properties_cb(bt_status_t status, + bt_bdaddr_t *bd_addr, int num_properties, + bt_property_t *properties) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->status = status; + cb_data->bdaddr = *bd_addr; + cb_data->num = num_properties; + cb_data->props = copy_properties(num_properties, properties); + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(remote_device_properties, cb_data); +} + +static void bond_test_bonded_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state) +{ + struct test_data *data = tester_get_data(); + + switch (state) { + case BT_BOND_STATE_BONDING: + data->cb_count--; + break; + case BT_BOND_STATE_BONDED: + data->cb_count--; + check_cb_count(); + break; + default: + tester_test_failed(); + break; + } +} + +static void bond_test_none_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state) +{ + struct test_data *data = tester_get_data(); + + switch (state) { + case BT_BOND_STATE_BONDING: + data->cb_count--; + break; + case BT_BOND_STATE_NONE: + data->cb_count--; + check_cb_count(); + break; + default: + tester_test_failed(); + break; + } +} + +static void bond_remove_success_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state) +{ + struct test_data *data = tester_get_data(); + bt_status_t remove_status; + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + if (state == BT_BOND_STATE_BONDED) { + data->cb_count--; + remove_status = data->if_bluetooth->remove_bond(&remote_addr); + check_expected_status(remove_status); + return; + } + + if (state == BT_BOND_STATE_NONE) { + data->cb_count--; + check_cb_count(); + } +} + +static gboolean bond_state_changed(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bt_cb_data *cb_data = user_data; + + if (data->test_checks_valid && + test->expected_hal_cb.bond_state_changed_cb) + test->expected_hal_cb.bond_state_changed_cb(cb_data->status, + &cb_data->bdaddr, cb_data->state); + + g_free(cb_data); + return FALSE; +} + +static void bond_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->status = status; + cb_data->bdaddr = *remote_bd_addr; + cb_data->state = state; + + g_idle_add(bond_state_changed, cb_data); +} + +static void bond_create_pin_success_request_cb(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod) +{ + struct test_data *data = tester_get_data(); + const bt_bdaddr_t *bdaddr = remote_bd_addr; + bt_pin_code_t pin_code = { + .pin = { 0x30, 0x30, 0x30, 0x30 }, + }; + uint8_t pin_len = 4; + + data->cb_count--; + + data->if_bluetooth->pin_reply(bdaddr, TRUE, pin_len, &pin_code); +} + +static void bond_create_pin_fail_request_cb(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod) +{ + struct test_data *data = tester_get_data(); + const bt_bdaddr_t *bdaddr = remote_bd_addr; + bt_pin_code_t pin_code = { + .pin = { 0x31, 0x31, 0x31, 0x31 }, + }; + uint8_t pin_len = 4; + + data->cb_count--; + + data->if_bluetooth->pin_reply(bdaddr, TRUE, pin_len, &pin_code); +} + +static gboolean pin_request(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bt_cb_data *cb_data = user_data; + + if (data->test_checks_valid && test->expected_hal_cb.pin_request_cb) + test->expected_hal_cb.pin_request_cb(&cb_data->bdaddr, + &cb_data->bdname, cb_data->cod); + + g_free(cb_data); + return FALSE; +} + +static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->bdaddr = *remote_bd_addr; + cb_data->bdname = *bd_name; + cb_data->cod = cod; + + g_idle_add(pin_request, cb_data); +} + +static void bond_create_ssp_request_cb(const bt_bdaddr_t *remote_bd_addr, + bt_ssp_variant_t pairing_variant, + bool accept, uint32_t pass_key) +{ + struct test_data *data = tester_get_data(); + + data->if_bluetooth->ssp_reply(remote_bd_addr, + BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + accept, pass_key); + + data->cb_count--; +} + +static void bond_create_ssp_success_request_cb(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod, + bt_ssp_variant_t pairing_variant, + uint32_t pass_key) +{ + bool accept = true; + + bond_create_ssp_request_cb(remote_bd_addr, pairing_variant, accept, + pass_key); +} + +static void bond_create_ssp_fail_request_cb(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod, + bt_ssp_variant_t pairing_variant, + uint32_t pass_key) +{ + bool accept = false; + + bond_create_ssp_request_cb(remote_bd_addr, pairing_variant, accept, + pass_key); +} + +static void bond_cancel_success_ssp_request_cb(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod, + bt_ssp_variant_t pairing_variant, + uint32_t pass_key) +{ + struct test_data *data = tester_get_data(); + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + bt_status_t status; + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + data->cb_count--; + + status = data->if_bluetooth->cancel_bond(&remote_addr); + check_expected_status(status); +} + +static gboolean ssp_request(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bt_cb_data *cb_data = user_data; + + if (data->test_checks_valid && test->expected_hal_cb.ssp_request_cb) + test->expected_hal_cb.ssp_request_cb(&cb_data->bdaddr, + &cb_data->bdname, cb_data->cod, + cb_data->ssp_variant, cb_data->passkey); + + g_free(cb_data); + return FALSE; +} + +static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, + uint32_t cod, bt_ssp_variant_t pairing_variant, + uint32_t pass_key) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->bdaddr = *remote_bd_addr; + cb_data->bdname = *bd_name; + cb_data->cod = cod; + cb_data->ssp_variant = pairing_variant; + cb_data->passkey = pass_key; + + g_idle_add(ssp_request, cb_data); +} + +static bt_bdaddr_t enable_done_bdaddr_val = { {0x00} }; +static const char enable_done_bdname_val[] = "BlueZ for Android"; +static bt_uuid_t enable_done_uuids_val = { + .uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}, +}; +static bt_device_type_t enable_done_tod_val = BT_DEVICE_DEVTYPE_DUAL; +static bt_scan_mode_t enable_done_scanmode_val = BT_SCAN_MODE_NONE; +static uint32_t enable_done_disctimeout_val = 120; + +static struct priority_property enable_done_props[] = { + { + .prop.type = BT_PROPERTY_BDADDR, + .prop.len = sizeof(enable_done_bdaddr_val), + .prop.val = &enable_done_bdaddr_val, + .prio = 1, + }, + { + .prop.type = BT_PROPERTY_BDNAME, + .prop.len = sizeof(enable_done_bdname_val) - 1, + .prop.val = &enable_done_bdname_val, + .prio = 2, + }, + { + .prop.type = BT_PROPERTY_UUIDS, + .prop.len = sizeof(enable_done_uuids_val), + .prop.val = &enable_done_uuids_val, + .prio = 2, + }, + { + .prop.type = BT_PROPERTY_CLASS_OF_DEVICE, + .prop.len = sizeof(uint32_t), + .prop.val = NULL, + .prio = 2, + }, + { + .prop.type = BT_PROPERTY_TYPE_OF_DEVICE, + .prop.len = sizeof(enable_done_tod_val), + .prop.val = &enable_done_tod_val, + .prio = 2, + }, + { + .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .prop.len = sizeof(enable_done_scanmode_val), + .prop.val = &enable_done_scanmode_val, + .prio = 2, + }, + { + .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, + .prop.len = 0, + .prop.val = NULL, + .prio = 2, + }, + { + .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + .prop.len = sizeof(enable_done_disctimeout_val), + .prop.val = &enable_done_disctimeout_val, + .prio = 2, + }, +}; + +static const struct generic_data bluetooth_enable_success_test = { + .expected_hal_cb.adapter_state_changed_cb = enable_success_cb, + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_cb_count = 1, + .expected_properties_num = 8, + .expected_properties = enable_done_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bluetooth_enable_success2_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_adapter_status = BT_STATUS_SUCCESS, + .expected_properties_num = 8, + .expected_properties = enable_done_props, +}; + +static const struct generic_data bluetooth_disable_success_test = { + .expected_hal_cb.adapter_state_changed_cb = disable_success_cb, + .expected_cb_count = 1, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static char test_set_bdname[] = "test_bdname_set"; + +static struct priority_property setprop_bdname_props[] = { + { + .prop.type = BT_PROPERTY_BDNAME, + .prop.val = test_set_bdname, + .prop.len = sizeof(test_set_bdname) - 1, + .prio = 0, + }, +}; + +static const struct generic_data bluetooth_setprop_bdname_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = setprop_bdname_props, +}; + +static bt_scan_mode_t test_setprop_scanmode_val = + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE; + +static struct priority_property setprop_scanmode_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .prop.val = &test_setprop_scanmode_val, + .prop.len = sizeof(bt_scan_mode_t), + }, +}; + +static const struct generic_data bluetooth_setprop_scanmode_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = setprop_scanmode_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static uint32_t test_setprop_disctimeout_val = 120; + +static struct priority_property setprop_disctimeout_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + .prop.val = &test_setprop_disctimeout_val, + .prop.len = sizeof(test_setprop_disctimeout_val), + }, +}; + +static const struct generic_data bluetooth_setprop_disctimeout_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = setprop_disctimeout_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_bdaddr_t test_getprop_bdaddr_val = { {0x00} }; + +static struct priority_property getprop_bdaddr_props[] = { + { + .prop.type = BT_PROPERTY_BDADDR, + .prop.val = &test_getprop_bdaddr_val, + .prop.len = sizeof(test_getprop_bdaddr_val), + }, +}; + +static const struct generic_data bluetooth_getprop_bdaddr_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = getprop_bdaddr_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const char test_bdname[] = "test_bdname_setget"; + +static struct priority_property getprop_bdname_props[] = { + { + .prop.type = BT_PROPERTY_BDNAME, + .prop.val = &test_bdname, + .prop.len = sizeof(test_bdname) - 1, + }, +}; + +static const struct generic_data bluetooth_getprop_bdname_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = getprop_bdname_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static unsigned char setprop_uuids[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }; + +static struct priority_property setprop_uuid_prop[] = { + { + .prop.type = BT_PROPERTY_UUIDS, + .prop.val = &setprop_uuids, + .prop.len = sizeof(setprop_uuids), + }, +}; + +static const struct generic_data bluetooth_setprop_uuid_invalid_test = { + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static uint32_t setprop_class_of_device = 0; + +static struct priority_property setprop_cod_props[] = { + { + .prop.type = BT_PROPERTY_CLASS_OF_DEVICE, + .prop.val = &setprop_class_of_device, + .prop.len = sizeof(setprop_class_of_device), + }, +}; + +static const struct generic_data bluetooth_setprop_cod_invalid_test = { + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_device_type_t setprop_type_of_device = BT_DEVICE_DEVTYPE_DUAL; + +static struct priority_property setprop_tod_props[] = { + { + .prop.type = BT_PROPERTY_TYPE_OF_DEVICE, + .prop.val = &setprop_type_of_device, + .prop.len = sizeof(setprop_type_of_device), + }, +}; + +static const struct generic_data bluetooth_setprop_tod_invalid_test = { + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static int32_t setprop_remote_rssi = 0; + +static struct priority_property setprop_remote_rssi_props[] = { + { + .prop.type = BT_PROPERTY_REMOTE_RSSI, + .prop.val = &setprop_remote_rssi, + .prop.len = sizeof(setprop_remote_rssi), + }, +}; + +static const struct generic_data bluetooth_setprop_remote_rssi_invalid_test = { + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_service_record_t setprop_remote_service = { + .uuid = { {0x00} }, + .channel = 12, + .name = "bt_name", +}; + +static struct priority_property setprop_service_record_props[] = { + { + .prop.type = BT_PROPERTY_SERVICE_RECORD, + .prop.val = &setprop_remote_service, + .prop.len = sizeof(setprop_remote_service), + }, +}; + +static const struct generic_data + bluetooth_setprop_service_record_invalid_test = { + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_bdaddr_t setprop_bdaddr = { + .address = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +}; + +static struct priority_property setprop_bdaddr_props[] = { + { + .prop.type = BT_PROPERTY_BDADDR, + .prop.val = &setprop_bdaddr, + .prop.len = sizeof(setprop_bdaddr), + }, +}; + +static const struct generic_data bluetooth_setprop_bdaddr_invalid_test = { + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_scan_mode_t setprop_scanmode_connectable = BT_SCAN_MODE_CONNECTABLE; + +static struct priority_property setprop_scanmode_connectable_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .prop.val = &setprop_scanmode_connectable, + .prop.len = sizeof(setprop_scanmode_connectable), + }, +}; + +static const struct generic_data + bluetooth_setprop_scanmode_connectable_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = setprop_scanmode_connectable_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_bdaddr_t setprop_bonded_devices = { + .address = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }, +}; + +static struct priority_property setprop_bonded_devices_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, + .prop.val = &setprop_bonded_devices, + .prop.len = sizeof(setprop_bonded_devices), + }, +}; + +static const struct generic_data + bluetooth_setprop_bonded_devices_invalid_test = { + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static uint32_t getprop_cod = 0x00020c; + +static struct priority_property getprop_cod_props[] = { + { + .prop.type = BT_PROPERTY_CLASS_OF_DEVICE, + .prop.val = &getprop_cod, + .prop.len = sizeof(getprop_cod), + }, +}; + +static const struct generic_data bluetooth_getprop_cod_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = getprop_cod_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_device_type_t getprop_tod = BT_DEVICE_DEVTYPE_DUAL; + +static struct priority_property getprop_tod_props[] = { + { + .prop.type = BT_PROPERTY_TYPE_OF_DEVICE, + .prop.val = &getprop_tod, + .prop.len = sizeof(getprop_tod), + }, +}; + +static const struct generic_data bluetooth_getprop_tod_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = getprop_tod_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_scan_mode_t getprop_scanmode = BT_SCAN_MODE_NONE; + +static struct priority_property getprop_scanmode_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .prop.val = &getprop_scanmode, + .prop.len = sizeof(getprop_scanmode), + }, +}; + +static const struct generic_data bluetooth_getprop_scanmode_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = getprop_scanmode_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static uint32_t getprop_disctimeout_val = 120; + +static struct priority_property getprop_disctimeout_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + .prop.val = &getprop_disctimeout_val, + .prop.len = sizeof(getprop_disctimeout_val), + }, +}; + +static const struct generic_data bluetooth_getprop_disctimeout_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = getprop_disctimeout_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_uuid_t getprop_uuids = { + .uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, +}; + +static struct priority_property getprop_uuids_props[] = { + { + .prop.type = BT_PROPERTY_UUIDS, + .prop.val = &getprop_uuids, + .prop.len = sizeof(getprop_uuids), + }, +}; + +static const struct generic_data bluetooth_getprop_uuids_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = getprop_uuids_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static struct priority_property getprop_bondeddev_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, + .prop.val = NULL, + .prop.len = 0, + }, +}; + +static const struct generic_data bluetooth_getprop_bondeddev_success_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = getprop_bondeddev_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_scan_mode_t setprop_scanmode_none = BT_SCAN_MODE_NONE; + +static struct priority_property setprop_scanmode_none_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .prop.val = &setprop_scanmode_none, + .prop.len = sizeof(setprop_scanmode_none), + }, +}; + +static const struct generic_data + bluetooth_setprop_scanmode_none_success2_test = { + .expected_hal_cb.adapter_properties_cb = check_count_properties_cb, + .expected_properties_num = 1, + .expected_properties = setprop_scanmode_none_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bluetooth_discovery_start_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + discovery_start_success_cb, + .expected_cb_count = 1, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bluetooth_discovery_start_success2_test = { + .expected_hal_cb.discovery_state_changed_cb = discovery_start_done_cb, + .expected_cb_count = 1, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bluetooth_discovery_stop_success2_test = { + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bluetooth_discovery_stop_success_test = { + .expected_hal_cb.discovery_state_changed_cb = discovery_stop_success_cb, + .expected_cb_count = 2, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bluetooth_discovery_device_found_test = { + .expected_hal_cb.discovery_state_changed_cb = + discovery_device_found_state_changed_cb, + .expected_hal_cb.device_found_cb = discovery_device_found_cb, + .expected_cb_count = 3, + .expected_adapter_status = BT_STATUS_NOT_EXPECTED, +}; + +static const char remote_get_properties_bdname_val[] = "00:AA:01:01:00:00"; +static uint32_t remote_get_properties_cod_val = 0; +static bt_device_type_t remote_get_properties_tod_val = BT_DEVICE_DEVTYPE_BREDR; +static int32_t remote_get_properties_rssi_val = -60; + +static struct priority_property remote_getprops_props[] = { + { + .prop.type = BT_PROPERTY_BDNAME, + .prop.val = &remote_get_properties_bdname_val, + .prop.len = sizeof(remote_get_properties_bdname_val) - 1, + }, + { + .prop.type = BT_PROPERTY_UUIDS, + .prop.val = NULL, + .prop.len = 0, + }, + { + .prop.type = BT_PROPERTY_CLASS_OF_DEVICE, + .prop.val = &remote_get_properties_cod_val, + .prop.len = sizeof(remote_get_properties_cod_val), + }, + { + .prop.type = BT_PROPERTY_TYPE_OF_DEVICE, + .prop.val = &remote_get_properties_tod_val, + .prop.len = sizeof(remote_get_properties_tod_val), + }, + { + .prop.type = BT_PROPERTY_REMOTE_RSSI, + .prop.val = &remote_get_properties_rssi_val, + .prop.len = sizeof(remote_get_properties_rssi_val), + }, + { + .prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, + .prop.val = NULL, + .prop.len = 4, + }, +}; + +static const struct generic_data bt_dev_getprops_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_getprops_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties_num = 6, + .expected_properties = remote_getprops_props, + .expected_adapter_status = BT_STATUS_NOT_EXPECTED, +}; + +static const char remote_getprop_bdname_val[] = "00:AA:01:01:00:00"; + +static struct priority_property remote_getprop_bdname_props[] = { + { + .prop.type = BT_PROPERTY_BDNAME, + .prop.val = &remote_getprop_bdname_val, + .prop.len = sizeof(remote_getprop_bdname_val) - 1, + }, +}; + +static const struct generic_data bt_dev_getprop_bdname_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties_num = 1, + .expected_properties = remote_getprop_bdname_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static struct priority_property remote_getprop_uuids_props[] = { + { + .prop.type = BT_PROPERTY_UUIDS, + .prop.val = NULL, + .prop.len = 0, + }, +}; + +static const struct generic_data bt_dev_getprop_uuids_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties_num = 1, + .expected_properties = remote_getprop_uuids_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static uint32_t remote_getprop_cod_val = 0; + +static struct priority_property remote_getprop_cod_props[] = { + { + .prop.type = BT_PROPERTY_CLASS_OF_DEVICE, + .prop.val = &remote_getprop_cod_val, + .prop.len = sizeof(remote_getprop_cod_val), + }, +}; + +static const struct generic_data bt_dev_getprop_cod_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties_num = 1, + .expected_properties = remote_getprop_cod_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_device_type_t remote_getprop_tod_val = BT_DEVICE_DEVTYPE_BREDR; + +static struct priority_property remote_getprop_tod_props[] = { + { + .prop.type = BT_PROPERTY_TYPE_OF_DEVICE, + .prop.val = &remote_getprop_tod_val, + .prop.len = sizeof(remote_getprop_tod_val), + }, +}; + +static const struct generic_data bt_dev_getprop_tod_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties_num = 1, + .expected_properties = remote_getprop_tod_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static int32_t remote_getprop_rssi_val = -60; + +static struct priority_property remote_getprop_rssi_props[] = { + { + .prop.type = BT_PROPERTY_REMOTE_RSSI, + .prop.val = &remote_getprop_rssi_val, + .prop.len = sizeof(remote_getprop_rssi_val), + }, +}; + +static const struct generic_data bt_dev_getprop_rssi_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties_num = 1, + .expected_properties = remote_getprop_rssi_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static struct priority_property remote_getprop_timestamp_props[] = { + { + .prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, + .prop.val = NULL, + .prop.len = 4, + }, +}; + +static const struct generic_data bt_dev_getprop_timpestamp_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties_num = 1, + .expected_properties = remote_getprop_timestamp_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_bdaddr_t remote_getprop_bdaddr_val = { + .address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 } +}; + +static struct priority_property remote_getprop_bdaddr_props[] = { + { + .prop.type = BT_PROPERTY_BDADDR, + .prop.val = &remote_getprop_bdaddr_val, + .prop.len = sizeof(remote_getprop_bdaddr_val), + }, +}; + +static const struct generic_data bt_dev_getprop_bdaddr_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties = remote_getprop_bdaddr_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_service_record_t remote_getprop_servrec_val = { + .uuid = { {0x00} }, + .channel = 12, + .name = "bt_name", +}; + +static struct priority_property remote_getprop_servrec_props[] = { + { + .prop.type = BT_PROPERTY_SERVICE_RECORD, + .prop.val = &remote_getprop_servrec_val, + .prop.len = sizeof(remote_getprop_servrec_val), + }, +}; + +static const struct generic_data bt_dev_getprop_servrec_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties = remote_getprop_servrec_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_scan_mode_t remote_getprop_scanmode_val = BT_SCAN_MODE_CONNECTABLE; + +static struct priority_property remote_getprop_scanmode_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .prop.val = &remote_getprop_scanmode_val, + .prop.len = sizeof(remote_getprop_scanmode_val), + }, +}; + +static const struct generic_data bt_dev_getprop_scanmode_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties = remote_getprop_scanmode_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static struct priority_property remote_getprop_bondeddev_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, + .prop.val = NULL, + .prop.len = 0, + }, +}; + +static const struct generic_data bt_dev_getprop_bondeddev_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties = remote_getprop_bondeddev_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static uint32_t remote_getprop_disctimeout_val = 120; + +static struct priority_property remote_getprop_disctimeout_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + .prop.val = &remote_getprop_disctimeout_val, + .prop.len = sizeof(remote_getprop_disctimeout_val), + }, +}; + +static const struct generic_data bt_dev_getprop_disctimeout_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties = remote_getprop_disctimeout_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static struct priority_property remote_getprop_verinfo_props[] = { + { + .prop.type = BT_PROPERTY_REMOTE_VERSION_INFO, + .prop.val = NULL, + .prop.len = 0, + }, +}; + +static const struct generic_data bt_dev_getprop_verinfo_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties = remote_getprop_verinfo_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static struct priority_property remote_getprop_fname_props[] = { + { + .prop.type = BT_PROPERTY_REMOTE_VERSION_INFO, + .prop.val = NULL, + .prop.len = 0, + }, +}; + +static const struct generic_data bt_dev_getprop_fname_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties = remote_getprop_fname_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static const char remote_setprop_fname_val[] = "set_fname_test"; + +static struct priority_property remote_setprop_fname_props[] = { + { + .prop.type = BT_PROPERTY_REMOTE_FRIENDLY_NAME, + .prop.val = &remote_setprop_fname_val, + .prop.len = sizeof(remote_setprop_fname_val) - 1, + }, +}; + +static const struct generic_data bt_dev_setprop_fname_success_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_setprop_disc_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_device_found_cb, + .expected_hal_cb.remote_device_properties_cb = + remote_test_device_properties_cb, + .expected_cb_count = 3, + .expected_properties_num = 1, + .expected_properties = remote_setprop_fname_props, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const char remote_setprop_bdname_val[] = "setprop_bdname_fail"; + +static struct priority_property remote_setprop_bdname_props[] = { + { + .prop.type = BT_PROPERTY_BDNAME, + .prop.val = &remote_setprop_bdname_val, + .prop.len = sizeof(remote_setprop_bdname_val) - 1, + }, +}; + +static const struct generic_data bt_dev_setprop_bdname_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_bdname_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static struct priority_property remote_setprop_uuids_props[] = { + { + .prop.type = BT_PROPERTY_UUIDS, + .prop.val = NULL, + .prop.len = 0, + }, +}; + +static const struct generic_data bt_dev_setprop_uuids_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_uuids_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static uint32_t remote_setprop_cod_val = 0; + +static struct priority_property remote_setprop_cod_props[] = { + { + .prop.type = BT_PROPERTY_CLASS_OF_DEVICE, + .prop.val = &remote_setprop_cod_val, + .prop.len = sizeof(remote_setprop_cod_val), + }, +}; + +static const struct generic_data bt_dev_setprop_cod_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_cod_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_device_type_t remote_setprop_tod_val = BT_DEVICE_DEVTYPE_BREDR; + +static struct priority_property remote_setprop_tod_props[] = { + { + .prop.type = BT_PROPERTY_TYPE_OF_DEVICE, + .prop.val = &remote_setprop_tod_val, + .prop.len = sizeof(remote_setprop_tod_val), + }, +}; + +static const struct generic_data bt_dev_setprop_tod_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_tod_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static int32_t remote_setprop_rssi_val = -60; + +static struct priority_property remote_setprop_rssi_props[] = { + { + .prop.type = BT_PROPERTY_REMOTE_RSSI, + .prop.val = &remote_setprop_rssi_val, + .prop.len = sizeof(remote_setprop_rssi_val), + }, +}; + +static const struct generic_data bt_dev_setprop_rssi_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_rssi_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static int32_t remote_setprop_timestamp_val = 0xAB; + +static struct priority_property remote_setprop_timestamp_props[] = { + { + .prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, + .prop.val = (&remote_setprop_timestamp_val), + .prop.len = sizeof(remote_setprop_timestamp_val), + }, +}; + +static const struct generic_data bt_dev_setprop_timpestamp_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_timestamp_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_bdaddr_t remote_setprop_bdaddr_val = { + .address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 } +}; + +static struct priority_property remote_setprop_bdaddr_props[] = { + { + .prop.type = BT_PROPERTY_BDADDR, + .prop.val = &remote_setprop_bdaddr_val, + .prop.len = sizeof(remote_setprop_bdaddr_val), + }, +}; + +static const struct generic_data bt_dev_setprop_bdaddr_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_bdaddr_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_service_record_t remote_setprop_servrec_val = { + .uuid = { {0x00} }, + .channel = 12, + .name = "bt_name", +}; + +static struct priority_property remote_setprop_servrec_props[] = { + { + .prop.type = BT_PROPERTY_SERVICE_RECORD, + .prop.val = &remote_setprop_servrec_val, + .prop.len = sizeof(remote_setprop_servrec_val), + }, +}; + +static const struct generic_data bt_dev_setprop_servrec_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_servrec_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_scan_mode_t remote_setprop_scanmode_val = BT_SCAN_MODE_CONNECTABLE; + +static struct priority_property remote_setprop_scanmode_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .prop.val = &remote_setprop_scanmode_val, + .prop.len = sizeof(remote_setprop_scanmode_val), + }, +}; + +static const struct generic_data bt_dev_setprop_scanmode_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_scanmode_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static bt_bdaddr_t remote_setprop_bondeddev_val = { + .address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 } +}; + +static struct priority_property remote_setprop_bondeddev_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, + .prop.val = &remote_setprop_bondeddev_val, + .prop.len = sizeof(remote_setprop_bondeddev_val), + }, +}; + +static const struct generic_data bt_dev_setprop_bondeddev_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_bondeddev_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static uint32_t remote_setprop_disctimeout_val = 120; + +static struct priority_property remote_setprop_disctimeout_props[] = { + { + .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + .prop.val = &remote_setprop_disctimeout_val, + .prop.len = sizeof(remote_setprop_disctimeout_val), + }, +}; + +static const struct generic_data bt_dev_setprop_disctimeout_fail_test = { + .expected_hal_cb.discovery_state_changed_cb = + remote_discovery_state_changed_cb, + .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb, + .expected_cb_count = 3, + .expected_properties = remote_setprop_disctimeout_props, + .expected_adapter_status = BT_STATUS_FAIL, +}; + +static const struct generic_data bt_bond_create_pin_success_test = { + .expected_hal_cb.device_found_cb = bond_device_found_cb, + .expected_hal_cb.bond_state_changed_cb = + bond_test_bonded_state_changed_cb, + .expected_hal_cb.pin_request_cb = bond_create_pin_success_request_cb, + .expected_cb_count = 4, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bt_bond_create_pin_fail_test = { + .expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb, + .expected_hal_cb.bond_state_changed_cb = + bond_test_none_state_changed_cb, + .expected_hal_cb.pin_request_cb = bond_create_pin_fail_request_cb, + .expected_cb_count = 4, + .expected_adapter_status = MGMT_STATUS_AUTH_FAILED, +}; + +static const struct generic_data bt_bond_create_ssp_success_test = { + .expected_hal_cb.device_found_cb = bond_device_found_cb, + .expected_hal_cb.bond_state_changed_cb = + bond_test_bonded_state_changed_cb, + .expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb, + .expected_cb_count = 4, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bt_bond_create_ssp_fail_test = { + .expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb, + .expected_hal_cb.bond_state_changed_cb = + bond_test_none_state_changed_cb, + .expected_hal_cb.ssp_request_cb = bond_create_ssp_fail_request_cb, + .expected_cb_count = 4, + .expected_adapter_status = MGMT_STATUS_AUTH_FAILED, +}; + +static const struct generic_data bt_bond_create_no_disc_success_test = { + .expected_hal_cb.bond_state_changed_cb = + bond_test_bonded_state_changed_cb, + .expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb, + .expected_cb_count = 3, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bt_bond_create_bad_addr_success_test = { + .expected_adapter_status = MGMT_STATUS_CONNECT_FAILED, +}; + +static const struct generic_data bt_bond_cancel_success_test = { + .expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb, + .expected_hal_cb.bond_state_changed_cb = + bond_test_none_state_changed_cb, + .expected_hal_cb.ssp_request_cb = bond_cancel_success_ssp_request_cb, + .expected_cb_count = 4, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static const struct generic_data bt_bond_remove_success_test = { + .expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb, + .expected_hal_cb.bond_state_changed_cb = + bond_remove_success_state_changed_cb, + .expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb, + .expected_cb_count = 4, + .expected_adapter_status = BT_STATUS_SUCCESS, +}; + +static bt_callbacks_t bt_callbacks = { + .size = sizeof(bt_callbacks), + .adapter_state_changed_cb = adapter_state_changed_cb, + .adapter_properties_cb = adapter_properties_cb, + .remote_device_properties_cb = remote_device_properties_cb, + .device_found_cb = device_found_cb, + .discovery_state_changed_cb = discovery_state_changed_cb, + .pin_request_cb = pin_request_cb, + .ssp_request_cb = ssp_request_cb, + .bond_state_changed_cb = bond_state_changed_cb, + .acl_state_changed_cb = NULL, + .thread_evt_cb = NULL, + .dut_mode_recv_cb = NULL, + .le_test_mode_cb = NULL +}; + +static bool setup(struct test_data *data) +{ + const hw_module_t *module; + hw_device_t *device; + int signal_fd[2]; + char buf[1024]; + pid_t pid; + int len; + int err; + + if (pipe(signal_fd)) + return false; + + pid = fork(); + + if (pid < 0) { + close(signal_fd[0]); + close(signal_fd[1]); + return false; + } + + if (pid == 0) { + if (!tester_use_debug()) + fclose(stderr); + + close(signal_fd[0]); + emulator(signal_fd[1], data->mgmt_index); + exit(0); + } + + close(signal_fd[1]); + data->bluetoothd_pid = pid; + + len = read(signal_fd[0], buf, sizeof(buf)); + if (len <= 0 || strcmp(buf, EMULATOR_SIGNAL)) { + close(signal_fd[0]); + return false; + } + + close(signal_fd[0]); + + err = hw_get_module(BT_HARDWARE_MODULE_ID, &module); + if (err) + return false; + + err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device); + if (err) + return false; + + data->device = device; + + data->if_bluetooth = ((bluetooth_device_t *) + device)->get_bluetooth_interface(); + if (!data->if_bluetooth) + return false; + + return true; +} + +static void setup_base(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + + if (!setup(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_enabled_adapter(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + + if (!setup(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->enable(); + if (status != BT_STATUS_SUCCESS) + tester_setup_failed(); +} + +static void teardown(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + if (data->if_hid) { + data->if_hid->cleanup(); + data->if_hid = NULL; + } + + if (data->if_bluetooth) { + data->if_bluetooth->cleanup(); + data->if_bluetooth = NULL; + } + + /* Test result already known, no need to check further */ + data->test_checks_valid = false; + + if (data->expected_properties_list) + g_slist_free(data->expected_properties_list); + + data->device->close(data->device); + + if (!data->bluetoothd_pid) + tester_teardown_complete(); +} + +static void test_dummy(const void *test_data) +{ + tester_test_passed(); +} + +static void test_enable(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t adapter_status; + + uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu); + + init_test_conditions(data); + + bdaddr2android((const bdaddr_t *)bdaddr, + &enable_done_bdaddr_val.address); + + adapter_status = data->if_bluetooth->enable(); + check_expected_status(adapter_status); +} + +static void test_enable_done(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t adapter_status; + + uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu); + + init_test_conditions(data); + + bdaddr2android((const bdaddr_t *)bdaddr, + &enable_done_bdaddr_val.address); + + adapter_status = data->if_bluetooth->enable(); + check_expected_status(adapter_status); +} + +static void test_disable(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->disable(); + check_expected_status(adapter_status); +} + +static void test_setprop_bdname_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_bdname_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_scanmode_succes(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_scanmode_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_disctimeout_succes(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_disctimeout_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_getprop_bdaddr_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t prop = setprop_bdaddr_props[0].prop; + bt_status_t adapter_status; + uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu); + + init_test_conditions(data); + + bdaddr2android((const bdaddr_t *)bdaddr, + &test_getprop_bdaddr_val.address); + + adapter_status = data->if_bluetooth->get_adapter_property(prop.type); + check_expected_status(adapter_status); +} + +static void test_getprop_bdname_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(getprop_bdname_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + if (adapter_status != BT_STATUS_SUCCESS) { + tester_test_failed(); + return; + } + + adapter_status = data->if_bluetooth->get_adapter_property((*prop).type); + check_expected_status(adapter_status); +} +static void test_setprop_uuid_invalid(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_uuid_prop[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_cod_invalid(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_cod_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_tod_invalid(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + const bt_property_t *prop = &test->set_property; + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_rssi_invalid(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_remote_rssi_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_service_record_invalid(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_service_record_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_bdaddr_invalid(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_bdaddr_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_scanmode_connectable_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = + &(setprop_scanmode_connectable_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_setprop_bonded_devices_invalid(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_bonded_devices_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_getprop_cod_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t prop = setprop_cod_props[0].prop; + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->get_adapter_property(prop.type); + check_expected_status(adapter_status); +} + +static void test_getprop_tod_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t prop = setprop_tod_props[0].prop; + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->get_adapter_property(prop.type); + check_expected_status(adapter_status); +} + +static void test_getprop_scanmode_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t prop = setprop_scanmode_props[0].prop; + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->get_adapter_property(prop.type); + check_expected_status(adapter_status); +} + +static void test_getprop_disctimeout_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t prop = setprop_disctimeout_props[0].prop; + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->get_adapter_property(prop.type); + check_expected_status(adapter_status); +} + +static void test_getprop_uuids_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t prop = getprop_uuids_props[0].prop; + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->get_adapter_property(prop.type); + check_expected_status(adapter_status); +} + +static void test_getprop_bondeddev_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t prop = getprop_bondeddev_props[0].prop; + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->get_adapter_property(prop.type); + check_expected_status(adapter_status); +} + +static void test_setprop_scanmode_none_done(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_property_t *prop = &(setprop_scanmode_none_props[0].prop); + bt_status_t adapter_status; + + init_test_conditions(data); + + adapter_status = data->if_bluetooth->set_adapter_property(prop); + check_expected_status(adapter_status); +} + +static void test_discovery_start_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + + init_test_conditions(data); + + status = data->if_bluetooth->start_discovery(); + check_expected_status(status); +} + +static void test_discovery_stop_done(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + + init_test_conditions(data); + + status = data->if_bluetooth->cancel_discovery(); + check_expected_status(status); +} + +static void test_discovery_stop_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_discovery_start_done(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_discovery_device_found(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprops_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_bdname_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_uuids_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_cod_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_tod_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_rssi_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_timestamp_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_bdaddr_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_servrec_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_scanmode_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_bondeddev_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_disctimeout_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_verinfo_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_getprop_fname_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_fname_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_bdname_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_uuids_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_cod_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_tod_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_rssi_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_timestamp_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_bdaddr_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_servrec_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_scanmode_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_bondeddev_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void test_dev_setprop_disctimeout_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + init_test_conditions(data); + + data->if_bluetooth->start_discovery(); +} + +static void bond_device_auth_fail_callback(uint16_t index, uint16_t length, + const void *param, + void *user_data) +{ + const struct mgmt_ev_auth_failed *ev = param; + + check_expected_status(ev->status); +} + +static void test_bond_create_pin_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; + const void *pin = pair_device_pin; + uint8_t pin_len = 4; + + init_test_conditions(data); + + bthost_set_pin_code(bthost, pin, pin_len); + + data->if_bluetooth->start_discovery(); +} + +static void test_bond_create_pin_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; + const void *pin = pair_device_pin; + uint8_t pin_len = 4; + + init_test_conditions(data); + + mgmt_register(data->mgmt, MGMT_EV_AUTH_FAILED, data->mgmt_index, + bond_device_auth_fail_callback, data, + NULL); + + bthost_set_pin_code(bthost, pin, pin_len); + + data->if_bluetooth->start_discovery(); +} + +static void test_bond_create_ssp_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + init_test_conditions(data); + + bthost_write_ssp_mode(bthost, 0x01); + + data->if_bluetooth->start_discovery(); +} + +static void test_bond_create_ssp_fail(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + init_test_conditions(data); + + mgmt_register(data->mgmt, MGMT_EV_AUTH_FAILED, data->mgmt_index, + bond_device_auth_fail_callback, data, + NULL); + + bthost_write_ssp_mode(bthost, 0x01); + + data->if_bluetooth->start_discovery(); +} + +static void test_bond_create_no_disc_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t remote_addr; + bt_status_t status; + + init_test_conditions(data); + + bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address); + + bthost_write_ssp_mode(bthost, 0x01); + + status = data->if_bluetooth->create_bond(&remote_addr); + check_expected_status(status); +} + +static void test_bond_create_bad_addr_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_bdaddr_t bad_addr = { + .address = { 0x12, 0x34, 0x56, 0x78, 0x90, 0x12 } + }; + + init_test_conditions(data); + + mgmt_register(data->mgmt, MGMT_EV_CONNECT_FAILED, data->mgmt_index, + bond_device_auth_fail_callback, data, + NULL); + + data->if_bluetooth->create_bond(&bad_addr); +} + +static void test_bond_cancel_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + init_test_conditions(data); + + bthost_write_ssp_mode(bthost, 0x01); + + data->if_bluetooth->start_discovery(); +} + +static void test_bond_remove_success(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + init_test_conditions(data); + + bthost_write_ssp_mode(bthost, 0x01); + + data->if_bluetooth->start_discovery(); +} + +/* Test Socket HAL */ + +static gboolean adapter_socket_state_changed(gpointer user_data) +{ + struct bt_cb_data *cb_data = user_data; + + switch (cb_data->state) { + case BT_STATE_ON: + setup_powered_emulated_remote(); + break; + case BT_STATE_OFF: + tester_setup_failed(); + break; + default: + break; + } + + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void adapter_socket_state_changed_cb(bt_state_t state) +{ + struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1); + + cb_data->state = state; + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(adapter_socket_state_changed, cb_data); +} + +const bt_bdaddr_t bdaddr_dummy = { + .address = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55} +}; + +static const struct socket_data btsock_inv_param_socktype = { + .bdaddr = &bdaddr_dummy, + .sock_type = 0, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .expected_status = BT_STATUS_PARM_INVALID, +}; + +static const struct socket_data btsock_inv_param_socktype_l2cap = { + .bdaddr = &bdaddr_dummy, + .sock_type = BTSOCK_L2CAP, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .expected_status = BT_STATUS_UNSUPPORTED, +}; + +/* Test invalid: channel & uuid are both zeroes */ +static const struct socket_data btsock_inv_params_chan_uuid = { + .bdaddr = &bdaddr_dummy, + .sock_type = BTSOCK_RFCOMM, + .channel = 0, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .expected_status = BT_STATUS_PARM_INVALID, +}; + +static const struct socket_data btsock_success = { + .bdaddr = &bdaddr_dummy, + .sock_type = BTSOCK_RFCOMM, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .expected_status = BT_STATUS_SUCCESS, + .test_channel = false +}; + +static const struct socket_data btsock_success_check_chan = { + .sock_type = BTSOCK_RFCOMM, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .expected_status = BT_STATUS_SUCCESS, + .test_channel = true, +}; + +static const struct socket_data btsock_inv_param_bdaddr = { + .bdaddr = NULL, + .sock_type = BTSOCK_RFCOMM, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .expected_status = BT_STATUS_PARM_INVALID, +}; + +static const struct socket_data btsock_inv_listen_listen = { + .sock_type = BTSOCK_RFCOMM, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .expected_status = BT_STATUS_BUSY, + .test_channel = true, +}; + +static bt_callbacks_t bt_socket_callbacks = { + .size = sizeof(bt_callbacks), + .adapter_state_changed_cb = adapter_socket_state_changed_cb, + .adapter_properties_cb = NULL, + .remote_device_properties_cb = NULL, + .device_found_cb = NULL, + .discovery_state_changed_cb = NULL, + .pin_request_cb = NULL, + .ssp_request_cb = NULL, + .bond_state_changed_cb = NULL, + .acl_state_changed_cb = NULL, + .thread_evt_cb = NULL, + .dut_mode_recv_cb = NULL, + .le_test_mode_cb = NULL +}; + +static void setup_socket_interface(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + const void *sock; + + if (!setup(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_socket_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID); + if (!sock) { + tester_setup_failed(); + return; + } + + data->if_sock = sock; + + tester_setup_complete(); +} + +static void setup_socket_interface_enabled(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + const void *sock; + + if (!setup(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_socket_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID); + if (!sock) { + tester_setup_failed(); + return; + } + + data->if_sock = sock; + + status = data->if_bluetooth->enable(); + if (status != BT_STATUS_SUCCESS) + tester_setup_failed(); +} + +static void test_generic_listen(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct socket_data *test = data->test_data; + bt_status_t status; + int sock_fd = -1; + + status = data->if_sock->listen(test->sock_type, + test->service_name, test->service_uuid, + test->channel, &sock_fd, test->flags); + if (status != test->expected_status) { + tester_test_failed(); + goto clean; + } + + /* Check that file descriptor is valid */ + if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) { + tester_test_failed(); + return; + } + + if (status == BT_STATUS_SUCCESS && test->test_channel) { + int channel, len; + + len = read(sock_fd, &channel, sizeof(channel)); + if (len != sizeof(channel) || channel != test->channel) { + tester_test_failed(); + goto clean; + } + + tester_print("read correct channel: %d", channel); + } + + tester_test_passed(); + +clean: + if (sock_fd >= 0) + close(sock_fd); +} + +static void test_listen_close(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct socket_data *test = data->test_data; + bt_status_t status; + int sock_fd = -1; + + status = data->if_sock->listen(test->sock_type, + test->service_name, test->service_uuid, + test->channel, &sock_fd, test->flags); + if (status != test->expected_status) { + tester_warn("sock->listen() failed"); + tester_test_failed(); + goto clean; + } + + /* Check that file descriptor is valid */ + if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) { + tester_warn("sock_fd %d is not valid", sock_fd); + tester_test_failed(); + return; + } + + tester_print("Got valid sock_fd: %d", sock_fd); + + /* Now close sock_fd */ + close(sock_fd); + sock_fd = -1; + + /* Try to listen again */ + status = data->if_sock->listen(test->sock_type, + test->service_name, test->service_uuid, + test->channel, &sock_fd, test->flags); + if (status != test->expected_status) { + tester_warn("sock->listen() failed"); + tester_test_failed(); + goto clean; + } + + /* Check that file descriptor is valid */ + if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) { + tester_warn("sock_fd %d is not valid", sock_fd); + tester_test_failed(); + return; + } + + tester_print("Got valid sock_fd: %d", sock_fd); + + tester_test_passed(); + +clean: + if (sock_fd >= 0) + close(sock_fd); +} + +static void test_listen_listen(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct socket_data *test = data->test_data; + bt_status_t status; + int sock_fd1 = -1, sock_fd2 = -1; + + status = data->if_sock->listen(test->sock_type, + test->service_name, test->service_uuid, + test->channel, &sock_fd1, test->flags); + if (status != BT_STATUS_SUCCESS) { + tester_warn("sock->listen() failed"); + tester_test_failed(); + goto clean; + } + + status = data->if_sock->listen(test->sock_type, + test->service_name, test->service_uuid, + test->channel, &sock_fd2, test->flags); + if (status != test->expected_status) { + tester_warn("sock->listen() failed, status %d", status); + tester_test_failed(); + goto clean; + } + + tester_print("status after second listen(): %d", status); + + tester_test_passed(); + +clean: + if (sock_fd1 >= 0) + close(sock_fd1); + + if (sock_fd2 >= 0) + close(sock_fd2); +} + +static void test_generic_connect(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct socket_data *test = data->test_data; + bt_status_t status; + int sock_fd = -1; + + status = data->if_sock->connect(test->bdaddr, test->sock_type, + test->service_uuid, test->channel, + &sock_fd, test->flags); + if (status != test->expected_status) { + tester_test_failed(); + goto clean; + } + + /* Check that file descriptor is valid */ + if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) { + tester_test_failed(); + return; + } + + tester_test_passed(); + +clean: + if (sock_fd >= 0) + close(sock_fd); +} + +static gboolean socket_chan_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + int sock_fd = g_io_channel_unix_get_fd(io); + struct test_data *data = tester_get_data(); + const struct socket_data *test = data->test_data; + int channel, len; + + tester_print("%s", __func__); + + if (cond & G_IO_HUP) { + tester_warn("Socket %d hang up", sock_fd); + goto failed; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + tester_warn("Socket error: sock %d cond %d", sock_fd, cond); + goto failed; + } + + if (test->test_channel) { + len = read(sock_fd, &channel, sizeof(channel)); + if (len != sizeof(channel) || channel != test->channel) + goto failed; + + tester_print("read correct channel: %d", channel); + tester_test_passed(); + return FALSE; + } + +failed: + tester_test_failed(); + return FALSE; +} + +static void test_socket_real_connect(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct socket_data *test = data->test_data; + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + const uint8_t *client_bdaddr; + bt_bdaddr_t emu_bdaddr; + bt_status_t status; + int sock_fd = -1; + + client_bdaddr = hciemu_get_client_bdaddr(data->hciemu); + if (!client_bdaddr) { + tester_warn("No client bdaddr"); + tester_test_failed(); + return; + } + + bdaddr2android((bdaddr_t *) client_bdaddr, &emu_bdaddr); + + bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL); + + status = data->if_sock->connect(&emu_bdaddr, test->sock_type, + test->service_uuid, test->channel, + &sock_fd, test->flags); + if (status != test->expected_status) { + tester_test_failed(); + goto clean; + } + + /* Check that file descriptor is valid */ + if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) { + tester_test_failed(); + return; + } + + tester_print("status %d sock_fd %d", status, sock_fd); + + if (status == BT_STATUS_SUCCESS) { + GIOChannel *io; + + io = g_io_channel_unix_new(sock_fd); + g_io_channel_set_close_on_unref(io, TRUE); + + g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + socket_chan_cb, NULL); + + g_io_channel_unref(io); + } + + return; + +clean: + if (sock_fd >= 0) + close(sock_fd); +} + +static gboolean hidhost_connection_state(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct hidhost_generic_data *test = data->test_data; + struct hh_cb_data *cb_data = user_data; + + data->cb_count++; + + if (cb_data->state == BTHH_CONN_STATE_CONNECTED) + tester_setup_complete(); + + if (test && test->expected_hal_cb.connection_state_cb) + test->expected_hal_cb.connection_state_cb(&cb_data->bdaddr, + cb_data->state); + + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void hidhost_connection_state_cb(bt_bdaddr_t *bd_addr, + bthh_connection_state_t state) +{ + struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1); + + cb_data->state = state; + cb_data->bdaddr = *bd_addr; + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(hidhost_connection_state, cb_data); +} + +static gboolean hidhost_virual_unplug(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct hidhost_generic_data *test = data->test_data; + struct hh_cb_data *cb_data = user_data; + + data->cb_count++; + + if (test && test->expected_hal_cb.virtual_unplug_cb) + test->expected_hal_cb.virtual_unplug_cb(&cb_data->bdaddr, + cb_data->status); + + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void hidhost_virual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t status) +{ + struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1); + + cb_data->bdaddr = *bd_addr; + cb_data->status = status; + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(hidhost_virual_unplug, cb_data); +} + +static gboolean hidhost_hid_info(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct hidhost_generic_data *test = data->test_data; + struct hh_cb_data *cb_data = user_data; + + data->cb_count++; + + if (test && test->expected_hal_cb.hid_info_cb) + test->expected_hal_cb.hid_info_cb(&cb_data->bdaddr, + cb_data->hid_info); + + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void hidhost_hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid) +{ + struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1); + + cb_data->bdaddr = *bd_addr; + cb_data->hid_info = hid; + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(hidhost_hid_info, cb_data); +} + +static gboolean hidhost_protocol_mode(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct hidhost_generic_data *test = data->test_data; + struct hh_cb_data *cb_data = user_data; + + data->cb_count++; + + if (test && test->expected_hal_cb.protocol_mode_cb) + test->expected_hal_cb.protocol_mode_cb(&cb_data->bdaddr, + cb_data->status, cb_data->mode); + + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void hidhost_protocol_mode_cb(bt_bdaddr_t *bd_addr, + bthh_status_t status, + bthh_protocol_mode_t mode) +{ + struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1); + + cb_data->bdaddr = *bd_addr; + cb_data->status = status; + cb_data->mode = mode; + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(hidhost_protocol_mode, cb_data); +} + +static gboolean hidhost_get_report(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct hidhost_generic_data *test = data->test_data; + struct hh_cb_data *cb_data = user_data; + + data->cb_count++; + + if (test && test->expected_hal_cb.get_report_cb) + test->expected_hal_cb.get_report_cb(&cb_data->bdaddr, + cb_data->status, cb_data->report, cb_data->size); + + g_free(cb_data->report); + g_free(cb_data); + + g_atomic_int_dec_and_test(&scheduled_cbacks_num); + return FALSE; +} + +static void hidhost_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status, + uint8_t *report, int size) +{ + struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1); + + cb_data->bdaddr = *bd_addr; + cb_data->status = status; + cb_data->report = g_memdup(report, size); + cb_data->size = size; + + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(hidhost_get_report, cb_data); +} + +static bthh_callbacks_t bthh_callbacks = { + .size = sizeof(bthh_callbacks), + .connection_state_cb = hidhost_connection_state_cb, + .hid_info_cb = hidhost_hid_info_cb, + .protocol_mode_cb = hidhost_protocol_mode_cb, + .idle_time_cb = NULL, + .get_report_cb = hidhost_get_report_cb, + .virtual_unplug_cb = hidhost_virual_unplug_cb +}; + +static bool setup_hidhost(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + const void *hid; + + if (!setup(data)) + return false; + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + return false; + } + + hid = data->if_bluetooth->get_profile_interface(BT_PROFILE_HIDHOST_ID); + if (!hid) + return false; + + data->if_hid = hid; + + status = data->if_hid->init(&bthh_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_hid = NULL; + return false; + } + + return true; +} + +static void setup_hidhost_interface(const void *test_data) +{ + if (setup_hidhost(test_data)) + tester_setup_complete(); + else + tester_setup_failed(); +} + +#define HID_GET_REPORT_PROTOCOL 0x60 +#define HID_GET_BOOT_PROTOCOL 0x61 +#define HID_SET_REPORT_PROTOCOL 0x70 +#define HID_SET_BOOT_PROTOCOL 0x71 + +#define HID_SET_INPUT_REPORT 0x51 +#define HID_SET_OUTPUT_REPORT 0x52 +#define HID_SET_FEATURE_REPORT 0x53 + +#define HID_SEND_DATA 0xa2 + +#define HID_GET_INPUT_REPORT 0x49 +#define HID_GET_OUTPUT_REPORT 0x4a +#define HID_GET_FEATURE_REPORT 0x4b + +static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + uint8_t pdu[2] = { 0, 0 }; + uint16_t pdu_len = 0; + + pdu_len = 2; + pdu[0] = 0xa0; + pdu[1] = 0x00; + + bthost_send_cid(bthost, t_data->ctrl_handle, t_data->ctrl_cid, + (void *)pdu, pdu_len); +} + +static void hid_prepare_reply_report(const void *data, uint16_t len) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + uint8_t pdu[3] = { 0, 0, 0 }; + uint16_t pdu_len = 0; + + pdu_len = 3; + pdu[0] = 0xa2; + pdu[1] = 0x01; + pdu[2] = 0x00; + + bthost_send_cid(bthost, t_data->ctrl_handle, t_data->ctrl_cid, + (void *)pdu, pdu_len); +} + +static void hid_intr_cid_hook_cb(const void *data, uint16_t len, + void *user_data) +{ + uint8_t header = ((uint8_t *) data)[0]; + + switch (header) { + case HID_SEND_DATA: + tester_test_passed(); + break; + } +} + +static void hid_intr_connect_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + data->intr_handle = handle; + data->intr_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, hid_intr_cid_hook_cb, NULL); +} + +static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len, + void *user_data) +{ + uint8_t header = ((uint8_t *) data)[0]; + + switch (header) { + case HID_GET_REPORT_PROTOCOL: + case HID_GET_BOOT_PROTOCOL: + case HID_SET_REPORT_PROTOCOL: + case HID_SET_BOOT_PROTOCOL: + hid_prepare_reply_protocol_mode(data, len); + break; + case HID_GET_INPUT_REPORT: + case HID_GET_OUTPUT_REPORT: + case HID_GET_FEATURE_REPORT: + hid_prepare_reply_report(data, len); + break; + /* + * HID device doesnot reply for this commads, so reaching pdu's + * to hid device means assuming test passed + */ + case HID_SET_INPUT_REPORT: + case HID_SET_OUTPUT_REPORT: + case HID_SET_FEATURE_REPORT: + case HID_SEND_DATA: + tester_test_passed(); + break; + } +} + +static void hid_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + data->ctrl_handle = handle; + data->ctrl_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, hid_ctrl_cid_hook_cb, NULL); +} + +static const uint8_t did_req_pdu[] = { 0x06, /* PDU id */ + 0x00, 0x00, /* Transaction id */ + 0x00, 0x0f, /* Req length */ + 0x35, 0x03, /* Attributes length */ + 0x19, 0x12, 0x00, 0xff, 0xff, 0x35, 0x05, 0x0a, 0x00, + 0x00, 0xff, 0xff, 0x00 }; /* no continuation */ + +static const uint8_t did_rsp_pdu[] = { 0x07, /* PDU id */ + 0x00, 0x00, /* Transaction id */ + 0x00, 0x4f, /* Response length */ + 0x00, 0x4c, /* Attributes length */ + 0x35, 0x4a, 0x35, 0x48, 0x09, 0x00, 0x00, 0x0a, 0x00, + 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, + 0x12, 0x00, 0x09, 0x00, 0x05, 0x35, 0x03, 0x19, 0x10, + 0x02, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, + 0x12, 0x00, 0x09, 0x01, 0x03, 0x09, 0x02, 0x00, 0x09, + 0x01, 0x03, 0x09, 0x02, 0x01, 0x09, 0x1d, 0x6b, 0x09, + 0x02, 0x02, 0x09, 0x02, 0x46, 0x09, 0x02, 0x03, 0x09, + 0x05, 0x0e, 0x09, 0x02, 0x04, 0x28, 0x01, 0x09, 0x02, + 0x05, 0x09, 0x00, 0x02, + 0x00 }; /* no continuation */ + +static const uint8_t hid_rsp_pdu[] = { 0x07, /* PDU id */ + 0x00, 0x01, /* Transaction id */ + 0x01, 0x71, /* Response length */ + 0x01, 0x6E, /* Attributes length */ + 0x36, 0x01, 0x6b, 0x36, 0x01, 0x68, 0x09, 0x00, 0x00, + 0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, + 0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d, + 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35, + 0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x05, 0x35, 0x03, + 0x19, 0x10, 0x02, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, + 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09, + 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x24, + 0x09, 0x01, 0x00, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35, + 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x13, + 0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, 0x25, + 0x1e, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, 0x68, + 0x20, 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, + 0x68, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x20, 0x4d, + 0x35, 0x35, 0x35, 0x62, 0x09, 0x01, 0x01, 0x25, 0x0f, + 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, + 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x09, 0x01, 0x02, + 0x25, 0x08, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, + 0x68, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02, + 0x01, 0x09, 0x01, 0x11, 0x09, 0x02, 0x02, 0x08, 0x80, + 0x09, 0x02, 0x03, 0x08, 0x21, 0x09, 0x02, 0x04, 0x28, + 0x01, 0x09, 0x02, 0x05, 0x28, 0x01, 0x09, 0x02, 0x06, + 0x35, 0x74, 0x35, 0x72, 0x08, 0x22, 0x25, 0x6e, 0x05, + 0x01, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x02, 0x09, 0x01, + 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15, + 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x16, 0x01, 0xf8, + 0x26, 0xff, 0x07, 0x75, 0x0c, 0x95, 0x02, 0x81, 0x06, + 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, + 0x01, 0x81, 0x06, 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x81, + 0x06, 0x05, 0x09, 0x19, 0x09, 0x29, 0x10, 0x15, 0x00, + 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0xc0, + 0xc0, 0x06, 0x00, 0xff, 0x09, 0x01, 0xa1, 0x01, 0x85, + 0x10, 0x75, 0x08, 0x95, 0x06, 0x15, 0x00, 0x26, 0xff, + 0x00, 0x09, 0x01, 0x81, 0x00, 0x09, 0x01, 0x91, 0x00, + 0xc0, 0x09, 0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09, + 0x04, 0x09, 0x09, 0x01, 0x00, 0x09, 0x02, 0x08, 0x28, + 0x00, 0x09, 0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a, + 0x28, 0x01, 0x09, 0x02, 0x0b, 0x09, 0x01, 0x00, 0x09, + 0x02, 0x0c, 0x09, 0x0c, 0x80, 0x09, 0x02, 0x0d, 0x28, + 0x00, 0x09, 0x02, 0x0e, 0x28, 0x01, + 0x00 }; /* no continuation */ + +static void hid_sdp_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + + if (!memcmp(did_req_pdu, data, len)) { + bthost_send_cid(bthost, t_data->sdp_handle, t_data->sdp_cid, + did_rsp_pdu, sizeof(did_rsp_pdu)); + return; + } + + bthost_send_cid(bthost, t_data->sdp_handle, t_data->sdp_cid, + hid_rsp_pdu, sizeof(hid_rsp_pdu)); +} + +static void hid_sdp_search_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + data->sdp_handle = handle; + data->sdp_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, hid_sdp_cid_hook_cb, NULL); +} + +static void emu_powered_complete(uint16_t opcode, uint8_t status, + const void *param, uint8_t len, + void *user_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t bdaddr; + bt_status_t bt_status; + + switch (opcode) { + case BT_HCI_CMD_WRITE_SCAN_ENABLE: + case BT_HCI_CMD_LE_SET_ADV_ENABLE: + break; + default: + return; + } + + if (status) { + tester_setup_failed(); + return; + } + + data->cb_count = 0; + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + bt_status = data->if_hid->connect(&bdaddr); + if (bt_status != BT_STATUS_SUCCESS) + tester_setup_failed(); +} + +static void setup_hidhost_connect(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + + if (!setup_hidhost(test_data)) { + tester_setup_failed(); + return; + } + + bthost = hciemu_client_get_host(data->hciemu); + + /* Emulate SDP (PSM = 1) */ + bthost_add_l2cap_server(bthost, 1, hid_sdp_search_cb, NULL); + /* Emulate Control Channel (PSM = 17) */ + bthost_add_l2cap_server(bthost, 17, hid_ctrl_connect_cb, NULL); + /* Emulate Interrupt Channel (PSM = 19) */ + bthost_add_l2cap_server(bthost, 19, hid_intr_connect_cb, NULL); + + bthost_set_cmd_complete_cb(bthost, emu_powered_complete, data); + bthost_write_scan_enable(bthost, 0x03); +} + +static void hid_discon_cb(bt_bdaddr_t *bd_addr, bthh_connection_state_t state) +{ + if (state == BTHH_CONN_STATE_DISCONNECTED) + tester_test_passed(); +} + +static const struct hidhost_generic_data hidhost_test_disconnect = { + .expected_hal_cb.connection_state_cb = hid_discon_cb, +}; + +static void test_hidhost_disconnect(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t bdaddr; + bt_status_t bt_status; + + data->cb_count = 0; + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + bt_status = data->if_hid->disconnect(&bdaddr); + if (bt_status != BT_STATUS_SUCCESS) + tester_test_failed(); +} + +static void test_hidhost_virtual_unplug(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t bdaddr; + bt_status_t bt_status; + + data->cb_count = 0; + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + bt_status = data->if_hid->virtual_unplug(&bdaddr); + if (bt_status != BT_STATUS_SUCCESS) + tester_test_failed(); +} + +static void hid_protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t status, + bthh_protocol_mode_t mode) +{ + struct test_data *data = tester_get_data(); + const struct hidhost_generic_data *test = data->test_data; + + if (data->cb_count == test->expected_cb_count && + status == test->expected_status && + mode == test->expected_protocol_mode) + tester_test_passed(); + else + tester_test_failed(); +} + +static const struct hidhost_generic_data hidhost_test_get_protocol = { + .expected_hal_cb.protocol_mode_cb = hid_protocol_mode_cb, + .expected_cb_count = 1, + .expected_protocol_mode = BTHH_BOOT_MODE, + .expected_status = BTHH_OK, +}; + +static void test_hidhost_get_protocol(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t bdaddr; + bt_status_t bt_status; + + data->cb_count = 0; + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + bt_status = data->if_hid->get_protocol(&bdaddr, BTHH_REPORT_MODE); + if (bt_status != BT_STATUS_SUCCESS) + tester_test_failed(); +} + +static void test_hidhost_set_protocol(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t bdaddr; + bt_status_t bt_status; + + data->cb_count = 0; + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + bt_status = data->if_hid->set_protocol(&bdaddr, BTHH_REPORT_MODE); + if (bt_status != BT_STATUS_SUCCESS) + tester_test_failed(); +} + +static void test_hidhost_set_report(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t bdaddr; + bt_status_t bt_status; + char *buf = "010101"; + + data->cb_count = 0; + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + bt_status = data->if_hid->set_report(&bdaddr, BTHH_INPUT_REPORT, buf); + if (bt_status != BT_STATUS_SUCCESS) + tester_test_failed(); +} + +static void test_hidhost_send_data(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t bdaddr; + bt_status_t bt_status; + char *buf = "fe0201"; + + data->cb_count = 0; + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + bt_status = data->if_hid->send_data(&bdaddr, buf); + if (bt_status != BT_STATUS_SUCCESS) + tester_test_failed(); +} + +static void hid_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status, + uint8_t *report, int size) +{ + struct test_data *data = tester_get_data(); + const struct hidhost_generic_data *test = data->test_data; + + if (data->cb_count == test->expected_cb_count && + status == test->expected_status && + size == test->expected_report_size) + tester_test_passed(); + else + tester_test_failed(); +} + +static const struct hidhost_generic_data hidhost_test_get_report = { + .expected_hal_cb.get_report_cb = hid_get_report_cb, + .expected_cb_count = 1, + .expected_status = BTHH_OK, + .expected_report_size = 2, +}; + +static void test_hidhost_get_report(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + bt_bdaddr_t bdaddr; + bt_status_t bt_status; + + data->cb_count = 0; + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + bt_status = data->if_hid->get_report(&bdaddr, BTHH_INPUT_REPORT, 1, 20); + if (bt_status != BT_STATUS_SUCCESS) + tester_test_failed(); +} + +#define test_bredr(name, data, test_setup, test, test_teardown) \ + do { \ + struct test_data *user; \ + user = g_malloc0(sizeof(struct test_data)); \ + if (!user) \ + break; \ + user->hciemu_type = HCIEMU_TYPE_BREDR; \ + user->test_data = data; \ + tester_add_full(name, data, test_pre_setup, test_setup, \ + test, test_teardown, test_post_teardown, \ + 1, user, g_free); \ + } while (0) + +#define test_bredrle(name, data, test_setup, test, test_teardown) \ + do { \ + struct test_data *user; \ + user = g_malloc0(sizeof(struct test_data)); \ + if (!user) \ + break; \ + user->hciemu_type = HCIEMU_TYPE_BREDRLE; \ + user->test_data = data; \ + tester_add_full(name, data, test_pre_setup, test_setup, \ + test, test_teardown, test_post_teardown, \ + 3, user, g_free); \ + } while (0) + +int main(int argc, char *argv[]) +{ + snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); + + tester_init(&argc, &argv); + + test_bredrle("Bluetooth Init", NULL, setup_base, test_dummy, teardown); + + test_bredrle("Bluetooth Enable - Success", + &bluetooth_enable_success_test, + setup_base, test_enable, + teardown); + + test_bredrle("Bluetooth Enable - Success 2", + &bluetooth_enable_success2_test, + setup_enabled_adapter, + test_enable_done, teardown); + + test_bredrle("Bluetooth Disable - Success", + &bluetooth_disable_success_test, + setup_enabled_adapter, + test_disable, teardown); + + test_bredrle("Bluetooth Set BDNAME - Success", + &bluetooth_setprop_bdname_success_test, + setup_enabled_adapter, + test_setprop_bdname_success, teardown); + + test_bredrle("Bluetooth Set SCAN_MODE - Success", + &bluetooth_setprop_scanmode_success_test, + setup_enabled_adapter, + test_setprop_scanmode_succes, teardown); + + test_bredrle("Bluetooth Set DISCOVERY_TIMEOUT - Success", + &bluetooth_setprop_disctimeout_success_test, + setup_enabled_adapter, + test_setprop_disctimeout_succes, teardown); + + test_bredrle("Bluetooth Get BDADDR - Success", + &bluetooth_getprop_bdaddr_success_test, + setup_enabled_adapter, + test_getprop_bdaddr_success, teardown); + + test_bredrle("Bluetooth Get BDNAME - Success", + &bluetooth_getprop_bdname_success_test, + setup_enabled_adapter, + test_getprop_bdname_success, teardown); + + test_bredrle("Bluetooth Set UUID - Invalid", + &bluetooth_setprop_uuid_invalid_test, + setup_enabled_adapter, + test_setprop_uuid_invalid, teardown); + + test_bredrle("Bluetooth Set CLASS_OF_DEVICE - Invalid", + &bluetooth_setprop_cod_invalid_test, + setup_enabled_adapter, + test_setprop_cod_invalid, teardown); + + test_bredrle("Bluetooth Set TYPE_OF_DEVICE - Invalid", + &bluetooth_setprop_tod_invalid_test, + setup_enabled_adapter, + test_setprop_tod_invalid, teardown); + + test_bredrle("Bluetooth Set REMOTE_RSSI - Invalid", + &bluetooth_setprop_remote_rssi_invalid_test, + setup_enabled_adapter, + test_setprop_rssi_invalid, teardown); + + test_bredrle("Bluetooth Set SERVICE_RECORD - Invalid", + &bluetooth_setprop_service_record_invalid_test, + setup_enabled_adapter, + test_setprop_service_record_invalid, teardown); + + test_bredrle("Bluetooth Set BDADDR - Invalid", + &bluetooth_setprop_bdaddr_invalid_test, + setup_enabled_adapter, + test_setprop_bdaddr_invalid, teardown); + + test_bredrle("Bluetooth Set SCAN_MODE CONNECTABLE - Success", + &bluetooth_setprop_scanmode_connectable_success_test, + setup_enabled_adapter, + test_setprop_scanmode_connectable_success, teardown); + + test_bredrle("Bluetooth Set BONDED_DEVICES - Invalid", + &bluetooth_setprop_bonded_devices_invalid_test, + setup_enabled_adapter, + test_setprop_bonded_devices_invalid, teardown); + + test_bredrle("Bluetooth Get CLASS_OF_DEVICE - Success", + &bluetooth_getprop_cod_success_test, + setup_enabled_adapter, + test_getprop_cod_success, teardown); + + test_bredrle("Bluetooth Get TYPE_OF_DEVICE - Success", + &bluetooth_getprop_tod_success_test, + setup_enabled_adapter, + test_getprop_tod_success, teardown); + + test_bredrle("Bluetooth Get SCAN_MODE - Success", + &bluetooth_getprop_scanmode_success_test, + setup_enabled_adapter, + test_getprop_scanmode_success, teardown); + + test_bredrle("Bluetooth Get DISCOVERY_TIMEOUT - Success", + &bluetooth_getprop_disctimeout_success_test, + setup_enabled_adapter, + test_getprop_disctimeout_success, teardown); + + test_bredrle("Bluetooth Get UUIDS - Success", + &bluetooth_getprop_uuids_success_test, + setup_enabled_adapter, + test_getprop_uuids_success, teardown); + + test_bredrle("Bluetooth Get BONDED_DEVICES - Success", + &bluetooth_getprop_bondeddev_success_test, + setup_enabled_adapter, + test_getprop_bondeddev_success, teardown); + + test_bredrle("Bluetooth Set SCAN_MODE NONE - Success 2", + &bluetooth_setprop_scanmode_none_success2_test, + setup_enabled_adapter, + test_setprop_scanmode_none_done, teardown); + + test_bredrle("Bluetooth BR/EDR Discovery Start - Success", + &bluetooth_discovery_start_success_test, + setup_enabled_adapter, + test_discovery_start_success, teardown); + + test_bredrle("Bluetooth BR/EDR Discovery Start - Success 2", + &bluetooth_discovery_start_success2_test, + setup_enabled_adapter, + test_discovery_start_done, teardown); + + test_bredrle("Bluetooth BR/EDR Discovery Stop - Success", + &bluetooth_discovery_stop_success_test, + setup_enabled_adapter, + test_discovery_stop_success, teardown); + + test_bredrle("Bluetooth BR/EDR Discovery Stop - Success 2", + &bluetooth_discovery_stop_success2_test, + setup_enabled_adapter, + test_discovery_stop_done, teardown); + + test_bredr("Bluetooth BR/EDR Discovery Device Found", + &bluetooth_discovery_device_found_test, + setup_enabled_adapter, + test_discovery_device_found, teardown); + + test_bredr("Bluetooth Device Get Props - Success", + &bt_dev_getprops_success_test, + setup_enabled_adapter, + test_dev_getprops_success, teardown); + + test_bredr("Bluetooth Device Get BDNAME - Success", + &bt_dev_getprop_bdname_success_test, + setup_enabled_adapter, + test_dev_getprop_bdname_success, teardown); + + test_bredr("Bluetooth Device Get UUIDS - Success", + &bt_dev_getprop_uuids_success_test, + setup_enabled_adapter, + test_dev_getprop_uuids_success, teardown); + + test_bredr("Bluetooth Device Get COD - Success", + &bt_dev_getprop_cod_success_test, + setup_enabled_adapter, + test_dev_getprop_cod_success, teardown); + + test_bredr("Bluetooth Device Get TOD - Success", + &bt_dev_getprop_tod_success_test, + setup_enabled_adapter, + test_dev_getprop_tod_success, teardown); + + test_bredr("Bluetooth Device Get RSSI - Success", + &bt_dev_getprop_rssi_success_test, + setup_enabled_adapter, + test_dev_getprop_rssi_success, teardown); + + test_bredr("Bluetooth Device Get TIMESTAMP - Success", + &bt_dev_getprop_timpestamp_success_test, + setup_enabled_adapter, + test_dev_getprop_timestamp_success, teardown); + + test_bredr("Bluetooth Device Get BDADDR - Fail", + &bt_dev_getprop_bdaddr_fail_test, + setup_enabled_adapter, + test_dev_getprop_bdaddr_fail, teardown); + + test_bredr("Bluetooth Device Get SERVICE_RECORD - Fail", + &bt_dev_getprop_servrec_fail_test, + setup_enabled_adapter, + test_dev_getprop_servrec_fail, teardown); + + test_bredr("Bluetooth Device Get SCAN_MODE - Fail", + &bt_dev_getprop_scanmode_fail_test, + setup_enabled_adapter, + test_dev_getprop_scanmode_fail, teardown); + + test_bredr("Bluetooth Device Get BONDED_DEVICES - Fail", + &bt_dev_getprop_bondeddev_fail_test, + setup_enabled_adapter, + test_dev_getprop_bondeddev_fail, teardown); + + test_bredr("Bluetooth Device Get DISCOVERY_TIMEOUT - Fail", + &bt_dev_getprop_disctimeout_fail_test, + setup_enabled_adapter, + test_dev_getprop_disctimeout_fail, teardown); + + test_bredr("Bluetooth Device Get VERSION_INFO - Fail", + &bt_dev_getprop_verinfo_fail_test, + setup_enabled_adapter, + test_dev_getprop_verinfo_fail, teardown); + + test_bredr("Bluetooth Device Get FRIENDLY_NAME - Fail", + &bt_dev_getprop_fname_fail_test, + setup_enabled_adapter, + test_dev_getprop_fname_fail, teardown); + + test_bredr("Bluetooth Device Set FRIENDLY_NAME - Success", + &bt_dev_setprop_fname_success_test, + setup_enabled_adapter, + test_dev_setprop_fname_success, teardown); + + test_bredr("Bluetooth Device Set BDNAME - Fail", + &bt_dev_setprop_bdname_fail_test, + setup_enabled_adapter, + test_dev_setprop_bdname_fail, teardown); + + test_bredr("Bluetooth Device Set UUIDS - Fail", + &bt_dev_setprop_uuids_fail_test, + setup_enabled_adapter, + test_dev_setprop_uuids_fail, teardown); + + test_bredr("Bluetooth Device Set COD - Fail", + &bt_dev_setprop_cod_fail_test, + setup_enabled_adapter, + test_dev_setprop_cod_fail, teardown); + + test_bredr("Bluetooth Device Set TOD - Fail", + &bt_dev_setprop_tod_fail_test, + setup_enabled_adapter, + test_dev_setprop_tod_fail, teardown); + + test_bredr("Bluetooth Device Set RSSI - Fail", + &bt_dev_setprop_rssi_fail_test, + setup_enabled_adapter, + test_dev_setprop_rssi_fail, teardown); + + test_bredr("Bluetooth Device Set TIMESTAMP - Fail", + &bt_dev_setprop_timpestamp_fail_test, + setup_enabled_adapter, + test_dev_setprop_timestamp_fail, teardown); + + test_bredr("Bluetooth Device Set BDADDR - Fail", + &bt_dev_setprop_bdaddr_fail_test, + setup_enabled_adapter, + test_dev_setprop_bdaddr_fail, teardown); + + test_bredr("Bluetooth Device Set SERVICE_RECORD - Fail", + &bt_dev_setprop_servrec_fail_test, + setup_enabled_adapter, + test_dev_setprop_servrec_fail, teardown); + + test_bredr("Bluetooth Device Set SCAN_MODE - Fail", + &bt_dev_setprop_scanmode_fail_test, + setup_enabled_adapter, + test_dev_setprop_scanmode_fail, teardown); + + test_bredr("Bluetooth Device Set BONDED_DEVICES - Fail", + &bt_dev_setprop_bondeddev_fail_test, + setup_enabled_adapter, + test_dev_setprop_bondeddev_fail, teardown); + + test_bredr("Bluetooth Device Set DISCOVERY_TIMEOUT - Fail", + &bt_dev_setprop_disctimeout_fail_test, + setup_enabled_adapter, + test_dev_setprop_disctimeout_fail, teardown); + + test_bredr("Bluetooth Create Bond PIN - Success", + &bt_bond_create_pin_success_test, + setup_enabled_adapter, + test_bond_create_pin_success, teardown); + + test_bredr("Bluetooth Create Bond PIN - Bad PIN", + &bt_bond_create_pin_fail_test, + setup_enabled_adapter, + test_bond_create_pin_fail, teardown); + + test_bredr("Bluetooth Create Bond SSP - Success", + &bt_bond_create_ssp_success_test, + setup_enabled_adapter, + test_bond_create_ssp_success, teardown); + + test_bredr("Bluetooth Create Bond SSP - Negative reply", + &bt_bond_create_ssp_fail_test, + setup_enabled_adapter, + test_bond_create_ssp_fail, teardown); + + test_bredrle("Bluetooth Create Bond - No Discovery", + &bt_bond_create_no_disc_success_test, + setup_enabled_adapter, + test_bond_create_no_disc_success, teardown); + + test_bredrle("Bluetooth Create Bond - Bad Address", + &bt_bond_create_bad_addr_success_test, + setup_enabled_adapter, + test_bond_create_bad_addr_success, teardown); + + test_bredr("Bluetooth Cancel Bonding - Success", + &bt_bond_cancel_success_test, + setup_enabled_adapter, + test_bond_cancel_success, teardown); + + test_bredr("Bluetooth Remove Bond - Success", + &bt_bond_remove_success_test, + setup_enabled_adapter, + test_bond_remove_success, teardown); + + test_bredrle("Socket Init", NULL, setup_socket_interface, + test_dummy, teardown); + + test_bredrle("Socket Listen - Invalid: sock_type 0", + &btsock_inv_param_socktype, setup_socket_interface, + test_generic_listen, teardown); + + test_bredrle("Socket Listen - Invalid: sock_type L2CAP", + &btsock_inv_param_socktype_l2cap, + setup_socket_interface, test_generic_listen, teardown); + + test_bredrle("Socket Listen - Invalid: chan, uuid", + &btsock_inv_params_chan_uuid, + setup_socket_interface, test_generic_listen, teardown); + + test_bredrle("Socket Listen - Check returned fd valid", + &btsock_success, + setup_socket_interface, test_generic_listen, teardown); + + test_bredrle("Socket Listen - Check returned channel", + &btsock_success_check_chan, + setup_socket_interface, test_generic_listen, teardown); + + test_bredrle("Socket Listen - Close and Listen again", + &btsock_success_check_chan, + setup_socket_interface, test_listen_close, teardown); + + test_bredrle("Socket Listen - Invalid: double Listen", + &btsock_inv_listen_listen, + setup_socket_interface, test_listen_listen, teardown); + + test_bredrle("Socket Connect - Check returned fd valid", + &btsock_success, setup_socket_interface, + test_generic_connect, teardown); + + test_bredrle("Socket Connect - Invalid: sock_type 0", + &btsock_inv_param_socktype, setup_socket_interface, + test_generic_connect, teardown); + + test_bredrle("Socket Connect - Invalid: sock_type L2CAP", + &btsock_inv_param_socktype_l2cap, + setup_socket_interface, test_generic_connect, teardown); + + test_bredrle("Socket Connect - Invalid: chan, uuid", + &btsock_inv_params_chan_uuid, + setup_socket_interface, test_generic_connect, teardown); + + test_bredrle("Socket Connect - Invalid: bdaddr", + &btsock_inv_param_bdaddr, + setup_socket_interface, test_generic_connect, teardown); + + test_bredrle("Socket Connect - Check returned chan", + &btsock_success_check_chan, + setup_socket_interface_enabled, + test_socket_real_connect, teardown); + + test_bredrle("HIDHost Init", NULL, setup_hidhost_interface, + test_dummy, teardown); + + test_bredrle("HIDHost Connect Success", + NULL, setup_hidhost_connect, + test_dummy, teardown); + + test_bredrle("HIDHost Disconnect Success", + &hidhost_test_disconnect, setup_hidhost_connect, + test_hidhost_disconnect, teardown); + + test_bredrle("HIDHost VirtualUnplug Success", + &hidhost_test_disconnect, setup_hidhost_connect, + test_hidhost_virtual_unplug, teardown); + + test_bredrle("HIDHost GetProtocol Success", + &hidhost_test_get_protocol, setup_hidhost_connect, + test_hidhost_get_protocol, teardown); + + test_bredrle("HIDHost SetProtocol Success", + &hidhost_test_get_protocol, setup_hidhost_connect, + test_hidhost_set_protocol, teardown); + + test_bredrle("HIDHost GetReport Success", + &hidhost_test_get_report, setup_hidhost_connect, + test_hidhost_get_report, teardown); + + test_bredrle("HIDHost SetReport Success", + NULL, setup_hidhost_connect, + test_hidhost_set_report, teardown); + + test_bredrle("HIDHost SendData Success", + NULL, setup_hidhost_connect, + test_hidhost_send_data, teardown); + return tester_run(); +} diff --git a/android/audio-ipc-api.txt b/android/audio-ipc-api.txt new file mode 100644 index 0000000..f4a497d --- /dev/null +++ b/android/audio-ipc-api.txt @@ -0,0 +1,87 @@ +Bluetooth Audio Plugin +====================== + +The audio plugin happen to be in a different socket but all the rules for +HAL socket apply here as well, the abstract socket name is +"\0bluez_audio_socket" (tentative): + + .---Audio---. .--Android--. + | Plugin | | Daemon | + | | Command | | + | | --------------------------> | | + | | | | + | | <-------------------------- | | + | | Response | | + | | | | + | | | | + | | | | + '-----------' '-----------' + + + Audio HAL Daemon + ---------------------------------------------------- + + call dev->open() --> command 0x01 + return dev->open() <-- response 0x01 + + call dev->open_output_stream() --> command 0x03 + return dev->open_output_stream() <-- response 0x03 + + call stream->write() --> command 0x05 + return stream->write() <-- response 0x05 + + call stream->common.standby() --> command 0x06 + return stream->common.standby() <-- response 0x06 + + call dev->close_output_stream() --> command 0x04 + return dev->close_output_stream() <-- response 0x04 + + call dev->close() --> command 0x02 + return dev->close() <-- response 0x02 + +Audio Service (ID 0) +==================== + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Opcode 0x01 - Open Audio Endpoint commmand + + Command parameters: Service UUID (16 octets) + Codec ID (1 octet) + Number of codec presets (1 octet) + Codec capabilities length (1 octet) + Codec capabilities (variable) + Codec preset # length (1 octet) + Codec preset # configuration (variable) + ... + Response parameters: Endpoint ID (1 octet) + + Opcode 0x02 - Close Audio Endpoint command + + Command parameters: Endpoint ID (1 octet) + Response parameters: + + Opcode 0x03 - Open Stream command + + Command parameters: Endpoint ID (1 octet) + Response parameters: Outgoing MTU (2 octets) + Codec configuration length (1 octet) + Codec configuration (1 octet) + File descriptor (inline) + + Opcode 0x04 - Close Stream command + + Command parameters: Endpoint ID (1 octet) + Response parameters: + + Opcode 0x05 - Resume Stream command + + Command parameters: Endpoint ID (1 octet) + Response parameters: + + Opcode 0x06 - Suspend Stream command + + Command parameters: Endpoint ID (1 octet) + Response parameters: diff --git a/android/audio-msg.h b/android/audio-msg.h new file mode 100644 index 0000000..5981355 --- /dev/null +++ b/android/audio-msg.h @@ -0,0 +1,81 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 BLUEZ_AUDIO_MTU 1024 + +static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket"; + +#define AUDIO_SERVICE_ID 0 +#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID + +#define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS +#define AUDIO_STATUS_FAILED 0x01 + +#define AUDIO_OP_STATUS IPC_OP_STATUS + +#define AUDIO_OP_OPEN 0x01 +struct audio_preset { + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +struct audio_cmd_open { + uint8_t uuid[16]; + uint8_t codec; + uint8_t presets; + struct audio_preset preset[0]; +} __attribute__((packed)); + +struct audio_rsp_open { + uint8_t id; +} __attribute__((packed)); + +#define AUDIO_OP_CLOSE 0x02 +struct audio_cmd_close { + uint8_t id; +} __attribute__((packed)); + +#define AUDIO_OP_OPEN_STREAM 0x03 +struct audio_cmd_open_stream { + uint8_t id; +} __attribute__((packed)); + +struct audio_rsp_open_stream { + uint16_t mtu; + struct audio_preset preset[0]; +} __attribute__((packed)); + +#define AUDIO_OP_CLOSE_STREAM 0x04 +struct audio_cmd_close_stream { + uint8_t id; +} __attribute__((packed)); + +#define AUDIO_OP_RESUME_STREAM 0x05 +struct audio_cmd_resume_stream { + uint8_t id; +} __attribute__((packed)); + +#define AUDIO_OP_SUSPEND_STREAM 0x06 +struct audio_cmd_suspend_stream { + uint8_t id; +} __attribute__((packed)); diff --git a/android/audio_utils/resampler.c b/android/audio_utils/resampler.c new file mode 100644 index 0000000..ce30375 --- /dev/null +++ b/android/audio_utils/resampler.c @@ -0,0 +1,270 @@ +/* +** Copyright 2011, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include "hal-log.h" + +struct resampler { + struct resampler_itfe itfe; + SpeexResamplerState *speex_resampler; // handle on speex resampler + struct resampler_buffer_provider *provider; // buffer provider installed by client + uint32_t in_sample_rate; // input sampling rate in Hz + uint32_t out_sample_rate; // output sampling rate in Hz + uint32_t channel_count; // number of channels (interleaved) + int16_t *in_buf; // input buffer + size_t in_buf_size; // input buffer size + size_t frames_in; // number of frames in input buffer + size_t frames_rq; // cached number of output frames + size_t frames_needed; // minimum number of input frames to produce + // frames_rq output frames + int32_t speex_delay_ns; // delay introduced by speex resampler in ns +}; + + +//------------------------------------------------------------------------------ +// speex based resampler +//------------------------------------------------------------------------------ + +static void resampler_reset(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + rsmp->frames_in = 0; + rsmp->frames_rq = 0; + + if (rsmp != NULL && rsmp->speex_resampler != NULL) { + speex_resampler_reset_mem(rsmp->speex_resampler); + } +} + +static int32_t resampler_delay_ns(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate); + delay += rsmp->speex_delay_ns; + + return delay; +} + +// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount +// with the actual number of frames produced. +static int resampler_resample_from_provider(struct resampler_itfe *resampler, + int16_t *out, + size_t *outFrameCount) +{ + struct resampler *rsmp = (struct resampler *)resampler; + size_t framesRq; + size_t framesWr; + size_t inFrames; + + if (rsmp == NULL || out == NULL || outFrameCount == NULL) { + return -EINVAL; + } + if (rsmp->provider == NULL) { + *outFrameCount = 0; + return -ENOSYS; + } + + framesRq = *outFrameCount; + // update and cache the number of frames needed at the input sampling rate to produce + // the number of frames requested at the output sampling rate + if (framesRq != rsmp->frames_rq) { + rsmp->frames_needed = (framesRq * rsmp->in_sample_rate) / rsmp->out_sample_rate + 1; + rsmp->frames_rq = framesRq; + } + + framesWr = 0; + inFrames = 0; + while (framesWr < framesRq) { + size_t outFrames; + if (rsmp->frames_in < rsmp->frames_needed) { + struct resampler_buffer buf; + // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at + // least the number of frames needed to produce the number of frames requested at + // the output sampling rate + if (rsmp->in_buf_size < rsmp->frames_needed) { + rsmp->in_buf_size = rsmp->frames_needed; + rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf, + rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t)); + } + buf.frame_count = rsmp->frames_needed - rsmp->frames_in; + rsmp->provider->get_next_buffer(rsmp->provider, &buf); + if (buf.raw == NULL) { + break; + } + memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count, + buf.raw, + buf.frame_count * rsmp->channel_count * sizeof(int16_t)); + rsmp->frames_in += buf.frame_count; + rsmp->provider->release_buffer(rsmp->provider, &buf); + } + + outFrames = framesRq - framesWr; + inFrames = rsmp->frames_in; + if (rsmp->channel_count == 1) { + speex_resampler_process_int(rsmp->speex_resampler, + 0, + rsmp->in_buf, + (void *) &inFrames, + out + framesWr, + (void *) &outFrames); + } else { + speex_resampler_process_interleaved_int(rsmp->speex_resampler, + rsmp->in_buf, + (void *) &inFrames, + out + framesWr * rsmp->channel_count, + (void *) &outFrames); + } + framesWr += outFrames; + rsmp->frames_in -= inFrames; + + if ((framesWr != framesRq) && (rsmp->frames_in != 0)) + warn("ReSampler::resample() remaining %zd frames in and %zd out", + rsmp->frames_in, (framesRq - framesWr)); + } + if (rsmp->frames_in) { + memmove(rsmp->in_buf, + rsmp->in_buf + inFrames * rsmp->channel_count, + rsmp->frames_in * rsmp->channel_count * sizeof(int16_t)); + } + *outFrameCount = framesWr; + + return 0; +} + +static int resampler_resample_from_input(struct resampler_itfe *resampler, + int16_t *in, + size_t *inFrameCount, + int16_t *out, + size_t *outFrameCount) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + if (rsmp == NULL || in == NULL || inFrameCount == NULL || + out == NULL || outFrameCount == NULL) { + return -EINVAL; + } + if (rsmp->provider != NULL) { + *outFrameCount = 0; + return -ENOSYS; + } + + if (rsmp->channel_count == 1) { + speex_resampler_process_int(rsmp->speex_resampler, + 0, + in, + (void *) inFrameCount, + out, + (void *) outFrameCount); + } else { + speex_resampler_process_interleaved_int(rsmp->speex_resampler, + in, + (void *) inFrameCount, + out, + (void *) outFrameCount); + } + + DBG("resampler_resample_from_input() DONE in %zd out %zd", *inFrameCount, *outFrameCount); + + return 0; +} + +int create_resampler(uint32_t inSampleRate, + uint32_t outSampleRate, + uint32_t channelCount, + uint32_t quality, + struct resampler_buffer_provider* provider, + struct resampler_itfe **resampler) +{ + int error; + struct resampler *rsmp; + int frames; + + DBG("create_resampler() In SR %d Out SR %d channels %d", + inSampleRate, outSampleRate, channelCount); + + if (resampler == NULL) { + return -EINVAL; + } + + *resampler = NULL; + + if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) { + return -EINVAL; + } + + rsmp = (struct resampler *)calloc(1, sizeof(struct resampler)); + + rsmp->speex_resampler = speex_resampler_init(channelCount, + inSampleRate, + outSampleRate, + quality, + &error); + if (rsmp->speex_resampler == NULL) { + error("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error)); + free(rsmp); + return -ENODEV; + } + + rsmp->itfe.reset = resampler_reset; + rsmp->itfe.resample_from_provider = resampler_resample_from_provider; + rsmp->itfe.resample_from_input = resampler_resample_from_input; + rsmp->itfe.delay_ns = resampler_delay_ns; + + rsmp->provider = provider; + rsmp->in_sample_rate = inSampleRate; + rsmp->out_sample_rate = outSampleRate; + rsmp->channel_count = channelCount; + rsmp->in_buf = NULL; + rsmp->in_buf_size = 0; + + resampler_reset(&rsmp->itfe); + + frames = speex_resampler_get_input_latency(rsmp->speex_resampler); + rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate); + frames = speex_resampler_get_output_latency(rsmp->speex_resampler); + rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate); + + *resampler = &rsmp->itfe; + DBG("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p", + rsmp, &rsmp->itfe, rsmp->speex_resampler); + return 0; +} + +void release_resampler(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + if (rsmp == NULL) { + return; + } + + free(rsmp->in_buf); + + if (rsmp->speex_resampler != NULL) { + speex_resampler_destroy(rsmp->speex_resampler); + } + free(rsmp); +} diff --git a/android/audio_utils/resampler.h b/android/audio_utils/resampler.h new file mode 100644 index 0000000..0c7046f --- /dev/null +++ b/android/audio_utils/resampler.h @@ -0,0 +1,109 @@ +/* +** Copyright 2008, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_RESAMPLER_H +#define ANDROID_RESAMPLER_H + +#include +#include + +__BEGIN_DECLS + + +#define RESAMPLER_QUALITY_MAX 10 +#define RESAMPLER_QUALITY_MIN 0 +#define RESAMPLER_QUALITY_DEFAULT 4 +#define RESAMPLER_QUALITY_VOIP 3 +#define RESAMPLER_QUALITY_DESKTOP 5 + +struct resampler_buffer { + union { + void* raw; + short* i16; + int8_t* i8; + }; + size_t frame_count; +}; + +/* call back interface used by the resampler to get new data */ +struct resampler_buffer_provider +{ + /** + * get a new buffer of data: + * as input: buffer->frame_count is the number of frames requested + * as output: buffer->frame_count is the number of frames returned + * buffer->raw points to data returned + */ + int (*get_next_buffer)(struct resampler_buffer_provider *provider, + struct resampler_buffer *buffer); + /** + * release a consumed buffer of data: + * as input: buffer->frame_count is the number of frames released + * buffer->raw points to data released + */ + void (*release_buffer)(struct resampler_buffer_provider *provider, + struct resampler_buffer *buffer); +}; + +/* resampler interface */ +struct resampler_itfe { + /** + * reset resampler state + */ + void (*reset)(struct resampler_itfe *resampler); + /** + * resample input from buffer provider and output at most *outFrameCount to out buffer. + * *outFrameCount is updated with the actual number of frames produced. + */ + int (*resample_from_provider)(struct resampler_itfe *resampler, + int16_t *out, + size_t *outFrameCount); + /** + * resample at most *inFrameCount frames from in buffer and output at most + * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively + * with the number of frames remaining in input and written to output. + */ + int (*resample_from_input)(struct resampler_itfe *resampler, + int16_t *in, + size_t *inFrameCount, + int16_t *out, + size_t *outFrameCount); + /** + * return the latency introduced by the resampler in ns. + */ + int32_t (*delay_ns)(struct resampler_itfe *resampler); +}; + +/** + * create a resampler according to input parameters passed. + * If resampler_buffer_provider is not NULL only resample_from_provider() can be called. + * If resampler_buffer_provider is NULL only resample_from_input() can be called. + */ +int create_resampler(uint32_t inSampleRate, + uint32_t outSampleRate, + uint32_t channelCount, + uint32_t quality, + struct resampler_buffer_provider *provider, + struct resampler_itfe **); + +/** + * release resampler resources. + */ +void release_resampler(struct resampler_itfe *); + +__END_DECLS + +#endif // ANDROID_RESAMPLER_H diff --git a/android/avctp.c b/android/avctp.c new file mode 100644 index 0000000..be3fed1 --- /dev/null +++ b/android/avctp.c @@ -0,0 +1,1629 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011 Texas Instruments, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "src/log.h" +#include "src/uinput.h" + +#include "avctp.h" + +/* + * AV/C Panel 1.23, page 76: + * command with the pressed value is valid for two seconds + */ +#define AVC_PRESS_TIMEOUT 2 + +#define QUIRK_NO_RELEASE 1 << 0 + +/* 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 + +#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 avc_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 AVC_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 avc_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 AVC_HEADER_LENGTH 3 + +#else +#error "Unknown byte order" +#endif + +struct avctp_control_req { + struct avctp_pending_req *p; + uint8_t code; + uint8_t subunit; + uint8_t op; + struct iovec *iov; + int iov_cnt; + avctp_rsp_cb func; + void *user_data; +}; + +struct avctp_browsing_req { + struct avctp_pending_req *p; + struct iovec *iov; + int iov_cnt; + avctp_browsing_rsp_cb func; + void *user_data; +}; + +typedef int (*avctp_process_cb) (void *data); + +struct avctp_pending_req { + struct avctp_channel *chan; + uint8_t transaction; + guint timeout; + int err; + avctp_process_cb process; + void *data; + avctp_destroy_cb_t destroy; +}; + +struct avctp_channel { + struct avctp *session; + GIOChannel *io; + uint8_t transaction; + guint watch; + uint16_t imtu; + uint16_t omtu; + uint8_t *buffer; + GSList *handlers; + struct avctp_pending_req *p; + GQueue *queue; + GSList *processed; + guint process_id; + avctp_destroy_cb_t destroy; +}; + +struct key_pressed { + uint8_t op; + uint8_t *params; + size_t params_len; + guint timer; +}; + +struct avctp { + int uinput; + + unsigned int passthrough_id; + unsigned int unit_id; + unsigned int subunit_id; + + struct avctp_channel *control; + struct avctp_channel *browsing; + + struct avctp_passthrough_handler *handler; + + uint8_t key_quirks[256]; + struct key_pressed key; + uint16_t version; + + avctp_destroy_cb_t destroy; + void *data; +}; + +struct avctp_passthrough_handler { + avctp_passthrough_cb cb; + void *user_data; + unsigned int id; +}; + +struct avctp_pdu_handler { + uint8_t opcode; + avctp_control_pdu_cb cb; + void *user_data; + unsigned int id; +}; + +struct avctp_browsing_pdu_handler { + avctp_browsing_pdu_cb cb; + void *user_data; + unsigned int id; + avctp_destroy_cb_t destroy; +}; + +static struct { + const char *name; + uint8_t avc; + uint16_t uinput; +} key_map[] = { + { "SELECT", AVC_SELECT, KEY_SELECT }, + { "UP", AVC_UP, KEY_UP }, + { "DOWN", AVC_DOWN, KEY_DOWN }, + { "LEFT", AVC_LEFT, KEY_LEFT }, + { "RIGHT", AVC_RIGHT, KEY_RIGHT }, + { "ROOT MENU", AVC_ROOT_MENU, KEY_MENU }, + { "CONTENTS MENU", AVC_CONTENTS_MENU, KEY_PROGRAM }, + { "FAVORITE MENU", AVC_FAVORITE_MENU, KEY_FAVORITES }, + { "EXIT", AVC_EXIT, KEY_EXIT }, + { "ON DEMAND MENU", AVC_ON_DEMAND_MENU, KEY_MENU }, + { "APPS MENU", AVC_APPS_MENU, KEY_MENU }, + { "0", AVC_0, KEY_0 }, + { "1", AVC_1, KEY_1 }, + { "2", AVC_2, KEY_2 }, + { "3", AVC_3, KEY_3 }, + { "4", AVC_4, KEY_4 }, + { "5", AVC_5, KEY_5 }, + { "6", AVC_6, KEY_6 }, + { "7", AVC_7, KEY_7 }, + { "8", AVC_8, KEY_8 }, + { "9", AVC_9, KEY_9 }, + { "DOT", AVC_DOT, KEY_DOT }, + { "ENTER", AVC_ENTER, KEY_ENTER }, + { "CHANNEL UP", AVC_CHANNEL_UP, KEY_CHANNELUP }, + { "CHANNEL DOWN", AVC_CHANNEL_DOWN, KEY_CHANNELDOWN }, + { "CHANNEL PREVIOUS", AVC_CHANNEL_PREVIOUS, KEY_LAST }, + { "INPUT SELECT", AVC_INPUT_SELECT, KEY_CONFIG }, + { "INFO", AVC_INFO, KEY_INFO }, + { "HELP", AVC_HELP, KEY_HELP }, + { "POWER", AVC_POWER, KEY_POWER2 }, + { "VOLUME UP", AVC_VOLUME_UP, KEY_VOLUMEUP }, + { "VOLUME DOWN", AVC_VOLUME_DOWN, KEY_VOLUMEDOWN }, + { "MUTE", AVC_MUTE, KEY_MUTE }, + { "PLAY", AVC_PLAY, KEY_PLAYCD }, + { "STOP", AVC_STOP, KEY_STOPCD }, + { "PAUSE", AVC_PAUSE, KEY_PAUSECD }, + { "FORWARD", AVC_FORWARD, KEY_NEXTSONG }, + { "BACKWARD", AVC_BACKWARD, KEY_PREVIOUSSONG }, + { "RECORD", AVC_RECORD, KEY_RECORD }, + { "REWIND", AVC_REWIND, KEY_REWIND }, + { "FAST FORWARD", AVC_FAST_FORWARD, KEY_FASTFORWARD }, + { "LIST", AVC_LIST, KEY_LIST }, + { "F1", AVC_F1, KEY_F1 }, + { "F2", AVC_F2, KEY_F2 }, + { "F3", AVC_F3, KEY_F3 }, + { "F4", AVC_F4, KEY_F4 }, + { "F5", AVC_F5, KEY_F5 }, + { "F6", AVC_F6, KEY_F6 }, + { "F7", AVC_F7, KEY_F7 }, + { "F8", AVC_F8, KEY_F8 }, + { "F9", AVC_F9, KEY_F9 }, + { "RED", AVC_RED, KEY_RED }, + { "GREEN", AVC_GREEN, KEY_GREEN }, + { "BLUE", AVC_BLUE, KEY_BLUE }, + { "YELLOW", AVC_YELLOW, KEY_YELLOW }, + { NULL } +}; + +static gboolean process_queue(gpointer user_data); +static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, + uint8_t subunit, uint8_t *operands, + size_t operand_count, void *user_data); + +static int 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; + + do { + err = write(fd, &event, sizeof(event)); + } while (err < 0 && errno == EINTR); + + if (err < 0) { + err = -errno; + error("send_event: %s (%d)", strerror(-err), -err); + } + + return err; +} + +static void send_key(int fd, uint16_t key, int pressed) +{ + send_event(fd, EV_KEY, key, pressed); + send_event(fd, EV_SYN, SYN_REPORT, 0); +} + +static gboolean auto_release(gpointer user_data) +{ + struct avctp *session = user_data; + + session->key.timer = 0; + + DBG("AV/C: key press timeout"); + + send_key(session->uinput, session->key.op, 0); + + return FALSE; +} + +static ssize_t handle_panel_passthrough(struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avctp_passthrough_handler *handler = session->handler; + const char *status; + int pressed, i; + + if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) { + *code = AVC_CTYPE_REJECTED; + return operand_count; + } + + if (operand_count == 0) + goto done; + + if (operands[0] & 0x80) { + status = "released"; + pressed = 0; + } else { + status = "pressed"; + pressed = 1; + } + + if (session->key.timer == 0 && handler != NULL) { + if (handler->cb(session, operands[0] & 0x7F, + pressed, handler->user_data)) + goto done; + } + + if (session->uinput < 0) { + DBG("AV/C: uinput not initialized"); + *code = AVC_CTYPE_NOT_IMPLEMENTED; + return 0; + } + + for (i = 0; key_map[i].name != NULL; i++) { + uint8_t key_quirks; + + if ((operands[0] & 0x7F) != key_map[i].avc) + continue; + + DBG("AV/C: %s %s", key_map[i].name, status); + + key_quirks = session->key_quirks[key_map[i].avc]; + + if (key_quirks & QUIRK_NO_RELEASE) { + if (!pressed) { + DBG("AV/C: Ignoring release"); + break; + } + + DBG("AV/C: treating key press as press + release"); + send_key(session->uinput, key_map[i].uinput, 1); + send_key(session->uinput, key_map[i].uinput, 0); + break; + } + + if (pressed) { + if (session->key.timer > 0) { + g_source_remove(session->key.timer); + send_key(session->uinput, session->key.op, 0); + } + + session->key.op = key_map[i].uinput; + session->key.timer = g_timeout_add_seconds( + AVC_PRESS_TIMEOUT, + auto_release, + session); + } else if (session->key.timer > 0) { + g_source_remove(session->key.timer); + session->key.timer = 0; + } + + send_key(session->uinput, key_map[i].uinput, pressed); + break; + } + + if (key_map[i].name == NULL) { + DBG("AV/C: unknown button 0x%02X %s", + operands[0] & 0x7F, status); + *code = AVC_CTYPE_NOT_IMPLEMENTED; + return operand_count; + } + +done: + *code = AVC_CTYPE_ACCEPTED; + return operand_count; +} + +static ssize_t handle_unit_info(struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + if (*code != AVC_CTYPE_STATUS) { + *code = AVC_CTYPE_REJECTED; + return 0; + } + + *code = AVC_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) + operands[0] = 0x07; + if (operand_count >= 2) + operands[1] = AVC_SUBUNIT_PANEL << 3; + + DBG("reply to AVC_OP_UNITINFO"); + + return operand_count; +} + +static ssize_t handle_subunit_info(struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + if (*code != AVC_CTYPE_STATUS) { + *code = AVC_CTYPE_REJECTED; + return 0; + } + + *code = AVC_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 >= 2) + operands[1] = AVC_SUBUNIT_PANEL << 3; + + DBG("reply to AVC_OP_SUBUNITINFO"); + + return operand_count; +} + +static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode) +{ + for (; list; list = list->next) { + struct avctp_pdu_handler *handler = list->data; + + if (handler->opcode == opcode) + return handler; + } + + return NULL; +} + +static void pending_destroy(gpointer data, gpointer user_data) +{ + struct avctp_pending_req *req = data; + + if (req->destroy) + req->destroy(req->data); + + if (req->timeout > 0) + g_source_remove(req->timeout); + + g_free(req); +} + +static void avctp_channel_destroy(struct avctp_channel *chan) +{ + g_io_channel_shutdown(chan->io, TRUE, NULL); + g_io_channel_unref(chan->io); + + if (chan->watch) + g_source_remove(chan->watch); + + if (chan->p) + pending_destroy(chan->p, NULL); + + if (chan->process_id > 0) + g_source_remove(chan->process_id); + + if (chan->destroy) + chan->destroy(chan); + + g_free(chan->buffer); + g_queue_foreach(chan->queue, pending_destroy, NULL); + g_queue_free(chan->queue); + g_slist_foreach(chan->processed, pending_destroy, NULL); + g_slist_free(chan->processed); + g_slist_free_full(chan->handlers, g_free); + g_free(chan); +} + +static int avctp_send(struct avctp_channel *control, uint8_t transaction, + uint8_t cr, uint8_t code, + uint8_t subunit, uint8_t opcode, + const struct iovec *iov, int iov_cnt) +{ + struct avctp_header avctp; + struct avc_header avc; + struct msghdr msg; + int sk, err = 0; + struct iovec pdu[iov_cnt + 2]; + int i; + size_t len = sizeof(avctp) + sizeof(avc); + + DBG(""); + + pdu[0].iov_base = &avctp; + pdu[0].iov_len = sizeof(avctp); + pdu[1].iov_base = &avc; + pdu[1].iov_len = sizeof(avc); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 2].iov_base = iov[i].iov_base; + pdu[i + 2].iov_len = iov[i].iov_len; + len += iov[i].iov_len; + } + + if (control->omtu < len) + return -EOVERFLOW; + + sk = g_io_channel_unix_get_fd(control->io); + + memset(&avctp, 0, sizeof(avctp)); + + avctp.transaction = transaction; + avctp.packet_type = AVCTP_PACKET_SINGLE; + avctp.cr = cr; + avctp.pid = htons(AV_REMOTE_SVCLASS_ID); + + memset(&avc, 0, sizeof(avc)); + + avc.code = code; + avc.subunit_type = subunit; + avc.opcode = opcode; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = pdu; + msg.msg_iovlen = iov_cnt + 2; + + if (sendmsg(sk, &msg, 0) < 0) + err = -errno; + + return err; +} + +static int avctp_browsing_send(struct avctp_channel *browsing, + uint8_t transaction, uint8_t cr, + const struct iovec *iov, int iov_cnt) +{ + struct avctp_header avctp; + struct msghdr msg; + struct iovec pdu[iov_cnt + 1]; + int sk, err = 0; + int i; + size_t len = sizeof(avctp); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + len += iov[i].iov_len; + } + + pdu[0].iov_base = &avctp; + pdu[0].iov_len = sizeof(avctp); + + if (browsing->omtu < len) + return -EOVERFLOW; + + sk = g_io_channel_unix_get_fd(browsing->io); + + memset(&avctp, 0, sizeof(avctp)); + + avctp.transaction = transaction; + avctp.packet_type = AVCTP_PACKET_SINGLE; + avctp.cr = cr; + avctp.pid = htons(AV_REMOTE_SVCLASS_ID); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = pdu; + msg.msg_iovlen = iov_cnt + 1; + + if (sendmsg(sk, &msg, 0) < 0) + err = -errno; + + return err; +} + +static void control_req_destroy(void *data) +{ + struct avctp_control_req *req = data; + struct avctp_pending_req *p = req->p; + struct avctp *session = p->chan->session; + int i; + + if (p->err == 0 || req->func == NULL) + goto done; + + req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0, + req->user_data); + +done: + for (i = 0; i < req->iov_cnt; i++) + g_free(req->iov[i].iov_base); + + g_free(req->iov); + g_free(req); +} + +static void browsing_req_destroy(void *data) +{ + struct avctp_browsing_req *req = data; + struct avctp_pending_req *p = req->p; + struct avctp *session = p->chan->session; + int i; + + if (p->err == 0 || req->func == NULL) + goto done; + + req->func(session, NULL, 0, req->user_data); + +done: + for (i = 0; i < req->iov_cnt; i++) + g_free(req->iov[i].iov_base); + + g_free(req->iov); + g_free(req); +} + +static gboolean req_timeout(gpointer user_data) +{ + struct avctp_channel *chan = user_data; + struct avctp_pending_req *p = chan->p; + + DBG("transaction %u", p->transaction); + + p->timeout = 0; + p->err = -ETIMEDOUT; + + pending_destroy(p, NULL); + chan->p = NULL; + + if (chan->process_id == 0) + chan->process_id = g_idle_add(process_queue, chan); + + return FALSE; +} + +static int process_control(void *data) +{ + struct avctp_control_req *req = data; + struct avctp_pending_req *p = req->p; + + return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code, + req->subunit, req->op, req->iov, req->iov_cnt); +} + +static int process_browsing(void *data) +{ + struct avctp_browsing_req *req = data; + struct avctp_pending_req *p = req->p; + + return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND, + req->iov, req->iov_cnt); +} + +static gboolean process_queue(void *user_data) +{ + struct avctp_channel *chan = user_data; + struct avctp_pending_req *p = chan->p; + + chan->process_id = 0; + + if (p != NULL) + return FALSE; + + while ((p = g_queue_pop_head(chan->queue))) { + + if (p->process(p->data) == 0) + break; + + pending_destroy(p, NULL); + } + + if (p == NULL) + return FALSE; + + chan->p = p; + p->timeout = g_timeout_add_seconds(2, req_timeout, chan); + + return FALSE; + +} + +static void control_response(struct avctp_channel *control, + struct avctp_header *avctp, + struct avc_header *avc, + uint8_t *operands, + size_t operand_count) +{ + struct avctp_pending_req *p = control->p; + struct avctp_control_req *req; + GSList *l; + + if (p && p->transaction == avctp->transaction) { + control->processed = g_slist_prepend(control->processed, p); + + if (p->timeout > 0) { + g_source_remove(p->timeout); + p->timeout = 0; + } + + control->p = NULL; + + if (control->process_id == 0) + control->process_id = g_idle_add(process_queue, + control); + } + + for (l = control->processed; l; l = l->next) { + p = l->data; + req = p->data; + + if (p->transaction != avctp->transaction) + continue; + + if (req->func && req->func(control->session, avc->code, + avc->subunit_type, + operands, operand_count, + req->user_data)) + return; + + control->processed = g_slist_remove(control->processed, p); + pending_destroy(p, NULL); + + return; + } +} + +static void browsing_response(struct avctp_channel *browsing, + struct avctp_header *avctp, + uint8_t *operands, + size_t operand_count) +{ + struct avctp_pending_req *p = browsing->p; + struct avctp_browsing_req *req; + GSList *l; + + if (p && p->transaction == avctp->transaction) { + browsing->processed = g_slist_prepend(browsing->processed, p); + + if (p->timeout > 0) { + g_source_remove(p->timeout); + p->timeout = 0; + } + + browsing->p = NULL; + + if (browsing->process_id == 0) + browsing->process_id = g_idle_add(process_queue, + browsing); + } + + for (l = browsing->processed; l; l = l->next) { + p = l->data; + req = p->data; + + if (p->transaction != avctp->transaction) + continue; + + if (req->func && req->func(browsing->session, operands, + operand_count, req->user_data)) + return; + + browsing->processed = g_slist_remove(browsing->processed, p); + pending_destroy(p, NULL); + + return; + } +} + +static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avctp *session = data; + struct avctp_channel *browsing = session->browsing; + uint8_t *buf = browsing->buffer; + uint8_t *operands; + struct avctp_header *avctp; + int sock, ret, packet_size, operand_count; + struct avctp_browsing_pdu_handler *handler; + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) + goto failed; + + sock = g_io_channel_unix_get_fd(chan); + + ret = read(sock, buf, browsing->imtu); + if (ret <= 0) + goto failed; + + if (ret < AVCTP_HEADER_LENGTH) { + error("Too small AVCTP packet"); + goto failed; + } + + avctp = (struct avctp_header *) buf; + + if (avctp->packet_type != AVCTP_PACKET_SINGLE) { + error("Invalid packet type"); + goto failed; + } + + operands = buf + AVCTP_HEADER_LENGTH; + ret -= AVCTP_HEADER_LENGTH; + operand_count = ret; + + if (avctp->cr == AVCTP_RESPONSE) { + browsing_response(browsing, avctp, operands, operand_count); + return TRUE; + } + + packet_size = AVCTP_HEADER_LENGTH; + avctp->cr = AVCTP_RESPONSE; + + handler = g_slist_nth_data(browsing->handlers, 0); + if (handler == NULL) { + DBG("handler not found"); + /* FIXME: Add general reject */ + /* packet_size += avrcp_browsing_general_reject(operands); */ + goto send; + } + + ret = handler->cb(session, avctp->transaction, operands, operand_count, + handler->user_data); + if (ret < 0) { + if (ret == -EAGAIN) + return TRUE; + goto failed; + } + + packet_size += ret; + +send: + if (packet_size != 0) { + ret = write(sock, buf, packet_size); + if (ret != packet_size) + goto failed; + } + + return TRUE; + +failed: + DBG("AVCTP Browsing: disconnected"); + + if (session->browsing) { + avctp_channel_destroy(session->browsing); + session->browsing = NULL; + } + + return FALSE; +} + +static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + struct avctp *session = data; + struct avctp_channel *control = session->control; + uint8_t *buf = control->buffer; + uint8_t *operands, code, subunit; + struct avctp_header *avctp; + struct avc_header *avc; + int packet_size, operand_count, sock; + struct avctp_pdu_handler *handler; + ssize_t ret; + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) + goto failed; + + sock = g_io_channel_unix_get_fd(chan); + + ret = read(sock, buf, control->imtu); + if (ret <= 0) + goto failed; + + if (ret < AVCTP_HEADER_LENGTH) { + error("Too small AVCTP packet"); + goto failed; + } + + avctp = (struct avctp_header *) buf; + + ret -= AVCTP_HEADER_LENGTH; + if (ret < AVC_HEADER_LENGTH) { + error("Too small AVC packet"); + goto failed; + } + + avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH); + + ret -= AVC_HEADER_LENGTH; + + operands = (uint8_t *) avc + AVC_HEADER_LENGTH; + operand_count = ret; + + if (avctp->cr == AVCTP_RESPONSE) { + control_response(control, avctp, avc, operands, operand_count); + return TRUE; + } + + packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH; + avctp->cr = AVCTP_RESPONSE; + + if (avctp->packet_type != AVCTP_PACKET_SINGLE) { + avc->code = AVC_CTYPE_NOT_IMPLEMENTED; + goto done; + } + + if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) { + avctp->ipid = 1; + packet_size = AVCTP_HEADER_LENGTH; + goto done; + } + + handler = find_handler(control->handlers, avc->opcode); + if (!handler) { + DBG("handler not found for 0x%02x", avc->opcode); + avc->code = AVC_CTYPE_REJECTED; + goto done; + } + + code = avc->code; + subunit = avc->subunit_type; + + ret = handler->cb(session, avctp->transaction, &code, + &subunit, operands, operand_count, + handler->user_data); + if (ret < 0) { + if (ret == -EAGAIN) + return TRUE; + goto failed; + } + + packet_size += ret; + avc->code = code; + avc->subunit_type = subunit; + +done: + ret = write(sock, buf, packet_size); + if (ret != packet_size) + goto failed; + + return TRUE; + +failed: + DBG("AVCTP session %p got disconnected", session); + avctp_shutdown(session); + return FALSE; +} + +static int uinput_create(const 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); + 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); + return err; + } + + return fd; +} + +int avctp_init_uinput(struct avctp *session, const char *name, + const char *address) +{ + if (g_str_equal(name, "Nokia CK-20W")) { + session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE; + session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE; + session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE; + session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE; + } + + session->uinput = uinput_create(address); + if (session->uinput < 0) { + error("AVCTP: failed to init uinput for %s", address); + return session->uinput; + } + + return 0; +} + +static struct avctp_channel *avctp_channel_create(struct avctp *session, int fd, + size_t imtu, size_t omtu, + avctp_destroy_cb_t destroy) +{ + struct avctp_channel *chan; + + chan = g_new0(struct avctp_channel, 1); + chan->session = session; + chan->io = g_io_channel_unix_new(fd); + chan->queue = g_queue_new(); + chan->imtu = imtu; + chan->omtu = omtu; + chan->buffer = g_malloc0(MAX(imtu, omtu)); + chan->destroy = destroy; + + return chan; +} + +static void handler_free(void *data) +{ + struct avctp_browsing_pdu_handler *handler = data; + + if (handler->destroy) + handler->destroy(handler->user_data); + + g_free(data); +} + +static void avctp_destroy_browsing(void *data) +{ + struct avctp_channel *chan = data; + + g_slist_free_full(chan->handlers, handler_free); + + chan->handlers = NULL; +} + +static struct avctp_pending_req *pending_create(struct avctp_channel *chan, + avctp_process_cb process, + void *data, + avctp_destroy_cb_t destroy) +{ + struct avctp_pending_req *p; + GSList *l, *tmp; + + if (!chan->processed) + goto done; + + tmp = g_slist_copy(chan->processed); + + /* Find first unused transaction id */ + for (l = tmp; l; l = g_slist_next(l)) { + struct avctp_pending_req *req = l->data; + + if (req->transaction == chan->transaction) { + chan->transaction++; + chan->transaction %= 16; + tmp = g_slist_delete_link(tmp, l); + l = tmp; + } + } + + g_slist_free(tmp); + +done: + p = g_new0(struct avctp_pending_req, 1); + p->chan = chan; + p->transaction = chan->transaction; + p->process = process; + p->data = data; + p->destroy = destroy; + + chan->transaction++; + chan->transaction %= 16; + + return p; +} + +static int avctp_send_req(struct avctp *session, uint8_t code, uint8_t subunit, + uint8_t opcode, const struct iovec *iov, int iov_cnt, + avctp_rsp_cb func, void *user_data) +{ + struct avctp_channel *control = session->control; + struct avctp_pending_req *p; + struct avctp_control_req *req; + struct iovec *pdu; + int i; + + if (control == NULL) + return -ENOTCONN; + + pdu = g_new0(struct iovec, iov_cnt); + + for (i = 0; i < iov_cnt; i++) { + pdu[i].iov_len = iov[i].iov_len; + pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len); + } + + req = g_new0(struct avctp_control_req, 1); + req->code = code; + req->subunit = subunit; + req->op = opcode; + req->func = func; + req->iov = pdu; + req->iov_cnt = iov_cnt; + req->user_data = user_data; + + p = pending_create(control, process_control, req, control_req_destroy); + + req->p = p; + + g_queue_push_tail(control->queue, p); + + if (control->process_id == 0) + control->process_id = g_idle_add(process_queue, control); + + return 0; +} + +int avctp_send_browsing_req(struct avctp *session, + const struct iovec *iov, int iov_cnt, + avctp_browsing_rsp_cb func, void *user_data) +{ + struct avctp_channel *browsing = session->browsing; + struct avctp_pending_req *p; + struct avctp_browsing_req *req; + struct iovec *pdu; + int i; + + if (browsing == NULL) + return -ENOTCONN; + + pdu = g_new0(struct iovec, iov_cnt); + + for (i = 0; i < iov_cnt; i++) { + pdu[i].iov_len = iov[i].iov_len; + pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len); + } + + req = g_new0(struct avctp_browsing_req, 1); + req->func = func; + req->iov = pdu; + req->iov_cnt = iov_cnt; + req->user_data = user_data; + + p = pending_create(browsing, process_browsing, req, + browsing_req_destroy); + + req->p = p; + + g_queue_push_tail(browsing->queue, p); + + /* Connection did not complete, delay process of the request */ + if (browsing->watch == 0) + return 0; + + if (browsing->process_id == 0) + browsing->process_id = g_idle_add(process_queue, browsing); + + return 0; +} + +int avctp_send_browsing(struct avctp *session, uint8_t transaction, + const struct iovec *iov, int iov_cnt) +{ + struct avctp_channel *browsing = session->browsing; + + if (browsing == NULL) + return -ENOTCONN; + + return avctp_browsing_send(browsing, transaction, AVCTP_RESPONSE, + iov, iov_cnt); +} + +static const char *op2str(uint8_t op) +{ + int i; + + for (i = 0; key_map[i].name != NULL; i++) { + if ((op & 0x7F) == key_map[i].avc) + return key_map[i].name; + } + + return "UNKNOWN"; +} + +static int avctp_passthrough_press(struct avctp *session, uint8_t op, + uint8_t *params, size_t params_len) +{ + struct iovec iov[2]; + int iov_cnt; + uint8_t operands[2]; + + DBG("%s", op2str(op)); + + iov[0].iov_base = operands; + iov[0].iov_len = sizeof(operands); + + /* Button pressed */ + operands[0] = op & 0x7f; + + if (params_len > 0) { + iov[1].iov_base = params; + iov[1].iov_len = params_len; + iov_cnt = 2; + operands[1] = params_len; + } else { + iov_cnt = 1; + operands[1] = 0; + } + + return avctp_send_req(session, AVC_CTYPE_CONTROL, + AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH, + iov, iov_cnt, avctp_passthrough_rsp, NULL); +} + +static int avctp_passthrough_release(struct avctp *session, uint8_t op, + uint8_t *params, size_t params_len) +{ + struct iovec iov[2]; + int iov_cnt; + uint8_t operands[2]; + + DBG("%s", op2str(op)); + + iov[0].iov_base = operands; + iov[0].iov_len = sizeof(operands); + + /* Button released */ + operands[0] = op | 0x80; + + if (params_len > 0) { + iov[1].iov_base = params; + iov[1].iov_len = params_len; + iov_cnt = 2; + operands[1] = params_len; + } else { + iov_cnt = 1; + operands[1] = 0; + } + + return avctp_send_req(session, AVC_CTYPE_CONTROL, + AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH, + iov, iov_cnt, NULL, NULL); +} + +static gboolean repeat_timeout(gpointer user_data) +{ + struct avctp *session = user_data; + + avctp_passthrough_release(session, session->key.op, session->key.params, + session->key.params_len); + avctp_passthrough_press(session, session->key.op, session->key.params, + session->key.params_len); + + return TRUE; +} + +static void release_pressed(struct avctp *session) +{ + avctp_passthrough_release(session, session->key.op, session->key.params, + session->key.params_len); + + if (session->key.timer > 0) + g_source_remove(session->key.timer); + + session->key.timer = 0; +} + +static bool set_pressed(struct avctp *session, uint8_t op, uint8_t *params, + size_t params_len) +{ + if (session->key.timer > 0) { + if (session->key.op == op) + return TRUE; + release_pressed(session); + } + + if (op != AVC_FAST_FORWARD && op != AVC_REWIND) + return FALSE; + + session->key.op = op; + session->key.params = params; + session->key.params_len = params_len; + session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT, + repeat_timeout, + session); + + return TRUE; +} + +static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, + uint8_t subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + uint8_t *params; + size_t params_len; + + DBG("code 0x%02x operand_count %zd", code, operand_count); + + if (code != AVC_CTYPE_ACCEPTED) + return FALSE; + + if (operands[0] == AVC_VENDOR_UNIQUE) { + params = &operands[2]; + params_len = operand_count - 2; + } else { + params = NULL; + params_len = 0; + } + + if (set_pressed(session, operands[0], params, params_len)) + return FALSE; + + avctp_passthrough_release(session, operands[0], params, params_len); + + return FALSE; +} + +int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params, + size_t params_len) +{ + /* Auto release if key pressed */ + if (session->key.timer > 0) + release_pressed(session); + + return avctp_passthrough_press(session, op, params, params_len); +} + +int avctp_send_vendor(struct avctp *session, uint8_t transaction, + uint8_t code, uint8_t subunit, + const struct iovec *iov, int iov_cnt) +{ + struct avctp_channel *control = session->control; + + if (control == NULL) + return -ENOTCONN; + + return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit, + AVC_OP_VENDORDEP, iov, iov_cnt); +} + +int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit, + const struct iovec *iov, int iov_cnt, + avctp_rsp_cb func, void *user_data) +{ + return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP, iov, + iov_cnt, func, user_data); +} + +unsigned int avctp_register_passthrough_handler(struct avctp *session, + avctp_passthrough_cb cb, + void *user_data) +{ + struct avctp_channel *control = session->control; + struct avctp_passthrough_handler *handler; + static unsigned int id = 0; + + if (control == NULL || session->handler != NULL) + return 0; + + handler = g_new(struct avctp_passthrough_handler, 1); + handler->cb = cb; + handler->user_data = user_data; + handler->id = ++id; + + session->handler = handler; + + return handler->id; +} + +bool avctp_unregister_passthrough_handler(struct avctp *session, + unsigned int id) +{ + if (session->handler == NULL) + return false; + + if (session->handler->id != id) + return false; + + g_free(session->handler); + session->handler = NULL; + return true; +} + +unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode, + avctp_control_pdu_cb cb, + void *user_data) +{ + struct avctp_channel *control = session->control; + struct avctp_pdu_handler *handler; + static unsigned int id = 0; + + if (control == NULL) + return 0; + + handler = find_handler(control->handlers, opcode); + if (handler) + return 0; + + handler = g_new(struct avctp_pdu_handler, 1); + handler->opcode = opcode; + handler->cb = cb; + handler->user_data = user_data; + handler->id = ++id; + + control->handlers = g_slist_append(control->handlers, handler); + + return handler->id; +} + +unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, + avctp_browsing_pdu_cb cb, + void *user_data, + avctp_destroy_cb_t destroy) +{ + struct avctp_channel *browsing = session->browsing; + struct avctp_browsing_pdu_handler *handler; + static unsigned int id = 0; + + if (browsing == NULL) + return 0; + + if (browsing->handlers != NULL) + return 0; + + handler = g_new(struct avctp_browsing_pdu_handler, 1); + handler->cb = cb; + handler->user_data = user_data; + handler->id = ++id; + handler->destroy = destroy; + + browsing->handlers = g_slist_append(browsing->handlers, handler); + + return handler->id; +} + +bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id) +{ + struct avctp_channel *control = session->control; + GSList *l; + + if (!control) + return false; + + for (l = control->handlers; l; l = g_slist_next(l)) { + struct avctp_pdu_handler *handler = l->data; + + if (handler->id != id) + continue; + + control->handlers = g_slist_remove(control->handlers, handler); + g_free(handler); + return true; + } + + return false; +} + +bool avctp_unregister_browsing_pdu_handler(struct avctp *session, + unsigned int id) +{ + struct avctp_channel *browsing = session->browsing; + GSList *l; + + if (browsing == NULL) + return false; + + for (l = browsing->handlers; l; l = g_slist_next(l)) { + struct avctp_browsing_pdu_handler *handler = l->data; + + if (handler->id != id) + continue; + + browsing->handlers = g_slist_remove(browsing->handlers, + handler); + g_free(handler); + return true; + } + + return false; +} + +struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version) +{ + struct avctp *session; + struct avctp_channel *control; + GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + session = g_new0(struct avctp, 1); + session->version = version; + + control = avctp_channel_create(session, fd, imtu, omtu, NULL); + if (!control) { + g_free(session); + return NULL; + } + + session->uinput = -1; + session->control = control; + session->passthrough_id = avctp_register_pdu_handler(session, + AVC_OP_PASSTHROUGH, + handle_panel_passthrough, + NULL); + session->unit_id = avctp_register_pdu_handler(session, + AVC_OP_UNITINFO, + handle_unit_info, + NULL); + session->subunit_id = avctp_register_pdu_handler(session, + AVC_OP_SUBUNITINFO, + handle_subunit_info, + NULL); + + control->watch = g_io_add_watch(session->control->io, cond, + (GIOFunc) session_cb, session); + + return session; +} + +int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu, + size_t omtu) +{ + struct avctp_channel *browsing; + GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + if (session->browsing) + return -EISCONN; + + browsing = avctp_channel_create(session, fd, imtu, omtu, + avctp_destroy_browsing); + if (!browsing) + return -EINVAL; + + session->browsing = browsing; + browsing->watch = g_io_add_watch(session->browsing->io, cond, + (GIOFunc) session_browsing_cb, session); + + return 0; +} + +void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb, + void *user_data) +{ + session->destroy = cb; + session->data = user_data; +} + +void avctp_shutdown(struct avctp *session) +{ + if (!session) + return; + + if (session->browsing) + avctp_channel_destroy(session->browsing); + + if (session->control) + avctp_channel_destroy(session->control); + + if (session->destroy) + session->destroy(session->data); + + g_free(session->handler); + + if (session->key.timer > 0) + g_source_remove(session->key.timer); + + if (session->uinput >= 0) { + DBG("AVCTP: closing uinput"); + + ioctl(session->uinput, UI_DEV_DESTROY); + close(session->uinput); + session->uinput = -1; + } + + g_free(session); +} diff --git a/android/avctp.h b/android/avctp.h new file mode 100644 index 0000000..2b33858 --- /dev/null +++ b/android/avctp.h @@ -0,0 +1,179 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have 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 AVCTP_CONTROL_PSM 23 +#define AVCTP_BROWSING_PSM 27 + +#define AVC_MTU 512 + +/* ctype entries */ +#define AVC_CTYPE_CONTROL 0x0 +#define AVC_CTYPE_STATUS 0x1 +#define AVC_CTYPE_NOTIFY 0x3 +#define AVC_CTYPE_NOT_IMPLEMENTED 0x8 +#define AVC_CTYPE_ACCEPTED 0x9 +#define AVC_CTYPE_REJECTED 0xA +#define AVC_CTYPE_STABLE 0xC +#define AVC_CTYPE_CHANGED 0xD +#define AVC_CTYPE_INTERIM 0xF + +/* opcodes */ +#define AVC_OP_VENDORDEP 0x00 +#define AVC_OP_UNITINFO 0x30 +#define AVC_OP_SUBUNITINFO 0x31 +#define AVC_OP_PASSTHROUGH 0x7c + +/* subunits of interest */ +#define AVC_SUBUNIT_PANEL 0x09 + +/* operands in passthrough commands */ +#define AVC_SELECT 0x00 +#define AVC_UP 0x01 +#define AVC_DOWN 0x02 +#define AVC_LEFT 0x03 +#define AVC_RIGHT 0x04 +#define AVC_ROOT_MENU 0x09 +#define AVC_CONTENTS_MENU 0x0b +#define AVC_FAVORITE_MENU 0x0c +#define AVC_EXIT 0x0d +#define AVC_ON_DEMAND_MENU 0x0e +#define AVC_APPS_MENU 0x0f +#define AVC_0 0x20 +#define AVC_1 0x21 +#define AVC_2 0x22 +#define AVC_3 0x23 +#define AVC_4 0x24 +#define AVC_5 0x25 +#define AVC_6 0x26 +#define AVC_7 0x27 +#define AVC_8 0x28 +#define AVC_9 0x29 +#define AVC_DOT 0x2a +#define AVC_ENTER 0x2b +#define AVC_CHANNEL_UP 0x30 +#define AVC_CHANNEL_DOWN 0x31 +#define AVC_CHANNEL_PREVIOUS 0x32 +#define AVC_INPUT_SELECT 0x34 +#define AVC_INFO 0x35 +#define AVC_HELP 0x36 +#define AVC_PAGE_UP 0x37 +#define AVC_PAGE_DOWN 0x38 +#define AVC_LOCK 0x3a +#define AVC_POWER 0x40 +#define AVC_VOLUME_UP 0x41 +#define AVC_VOLUME_DOWN 0x42 +#define AVC_MUTE 0x43 +#define AVC_PLAY 0x44 +#define AVC_STOP 0x45 +#define AVC_PAUSE 0x46 +#define AVC_RECORD 0x47 +#define AVC_REWIND 0x48 +#define AVC_FAST_FORWARD 0x49 +#define AVC_EJECT 0x4a +#define AVC_FORWARD 0x4b +#define AVC_BACKWARD 0x4c +#define AVC_LIST 0x4d +#define AVC_F1 0x71 +#define AVC_F2 0x72 +#define AVC_F3 0x73 +#define AVC_F4 0x74 +#define AVC_F5 0x75 +#define AVC_F6 0x76 +#define AVC_F7 0x77 +#define AVC_F8 0x78 +#define AVC_F9 0x79 +#define AVC_RED 0x7a +#define AVC_GREEN 0x7b +#define AVC_BLUE 0x7c +#define AVC_YELLOW 0x7c + +#define AVC_VENDOR_UNIQUE 0x7e + +#define AVC_VENDOR_NEXT_GROUP 0x00 +#define AVC_VENDOR_PREV_GROUP 0x01 + +struct avctp; + +typedef bool (*avctp_passthrough_cb) (struct avctp *session, + uint8_t op, bool pressed, + void *user_data); +typedef ssize_t (*avctp_control_pdu_cb) (struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data); +typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code, + uint8_t subunit, uint8_t *operands, + size_t operand_count, void *user_data); +typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session, + uint8_t *operands, size_t operand_count, + void *user_data); +typedef ssize_t (*avctp_browsing_pdu_cb) (struct avctp *session, + uint8_t transaction, + uint8_t *operands, size_t operand_count, + void *user_data); + +typedef void (*avctp_destroy_cb_t) (void *user_data); + +struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version); +void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb, + void *user_data); + +int avctp_init_uinput(struct avctp *session, const char *name, + const char *address); +int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu, + size_t omtu); + +void avctp_shutdown(struct avctp *session); + +unsigned int avctp_register_passthrough_handler(struct avctp *session, + avctp_passthrough_cb cb, + void *user_data); +bool avctp_unregister_passthrough_handler(struct avctp *session, + unsigned int id); + +unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode, + avctp_control_pdu_cb cb, + void *user_data); +bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id); + +unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, + avctp_browsing_pdu_cb cb, + void *user_data, + avctp_destroy_cb_t destroy); +bool avctp_unregister_browsing_pdu_handler(struct avctp *session, + unsigned int id); + +int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params, + size_t params_len); +int avctp_send_vendor(struct avctp *session, uint8_t transaction, + uint8_t code, uint8_t subunit, + const struct iovec *iov, int iov_cnt); +int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit, + const struct iovec *iov, int iov_cnt, + avctp_rsp_cb func, void *user_data); +int avctp_send_browsing(struct avctp *session, uint8_t transaction, + const struct iovec *iov, int iov_cnt); +int avctp_send_browsing_req(struct avctp *session, + const struct iovec *iov, int iov_cnt, + avctp_browsing_rsp_cb func, void *user_data); diff --git a/android/avdtp.c b/android/avdtp.c index 5ae3afc..3d309b9 100644 --- a/android/avdtp.c +++ b/android/avdtp.c @@ -39,7 +39,7 @@ #include -#include "log.h" +#include "src/log.h" #include "avdtp.h" #define AVDTP_PSM 25 @@ -336,6 +336,12 @@ struct discover_callback { void *user_data; }; +struct disconnect_callback { + unsigned int id; + avdtp_disconnect_cb_t cb; + void *user_data; +}; + struct avdtp_stream { GIOChannel *io; uint16_t imtu; @@ -390,6 +396,10 @@ struct avdtp { struct discover_callback *discover; struct pending_req *req; + + GSList *disconnect; + + bool shutdown; }; static GSList *lseps = NULL; @@ -670,6 +680,9 @@ static void stream_free(void *data) if (stream->timer) g_source_remove(stream->timer); + if (stream->start_timer > 0) + g_source_remove(stream->start_timer); + if (stream->io) close_stream(stream); @@ -902,6 +915,11 @@ static void avdtp_sep_set_state(struct avdtp *session, session->streams = g_slist_remove(session->streams, stream); stream_free(stream); } + + if (session->io && session->shutdown && session->streams == NULL) { + int sock = g_io_channel_unix_get_fd(session->io); + shutdown(sock, SHUT_RDWR); + } } static void finalize_discovery(struct avdtp *session, int err) @@ -912,15 +930,17 @@ static void finalize_discovery(struct avdtp *session, int err) if (!discover) return; + session->discover = NULL; + avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err); if (discover->id > 0) g_source_remove(discover->id); - discover->cb(session, session->seps, err ? &avdtp_err : NULL, + if (discover->cb) + discover->cb(session, session->seps, err ? &avdtp_err : NULL, discover->user_data); g_free(discover); - session->discover = NULL; } static void release_stream(struct avdtp_stream *stream, struct avdtp *session) @@ -967,12 +987,22 @@ static void avdtp_free(void *data) g_slist_free_full(session->req_queue, pending_req_free); g_slist_free_full(session->prio_queue, pending_req_free); g_slist_free_full(session->seps, sep_free); + g_slist_free_full(session->disconnect, g_free); g_free(session->buf); g_free(session); } +static void process_disconnect(void *data) +{ + struct disconnect_callback *callback = data; + + callback->cb(callback->user_data); + + g_free(callback); +} + static void connection_lost(struct avdtp *session, int err) { DBG("Disconnected: %s (%d)", strerror(err), err); @@ -980,12 +1010,14 @@ static void connection_lost(struct avdtp *session, int err) g_slist_foreach(session->streams, (GFunc) release_stream, session); session->streams = NULL; + avdtp_ref(session); + finalize_discovery(session, err); - if (session->ref > 0) - return; + g_slist_free_full(session->disconnect, process_disconnect); + session->disconnect = NULL; - avdtp_free(session); + avdtp_unref(session); } void avdtp_unref(struct avdtp *session) @@ -1169,8 +1201,8 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction, goto failed; } - if (!sep->ind->get_capability(session, sep, get_all, &caps, - &err, sep->user_data)) + if (!sep->ind->get_capability(session, sep, &caps, &err, + sep->user_data)) goto failed; for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { @@ -1186,6 +1218,12 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction, g_free(cap); } + if (get_all && sep->delay_reporting) { + ptr[0] = AVDTP_DELAY_REPORTING; + ptr[1] = 0x00; + rsp_size += 2; + } + g_slist_free(caps); return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd, @@ -1259,11 +1297,15 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction, &stream->codec, &stream->delay_reporting); - /* Verify that the Media Transport capability's length = 0. Reject otherwise */ + /* + * 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) { + if (cap->category == AVDTP_MEDIA_TRANSPORT && + cap->length != 0) { err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT; goto failed_stream; } @@ -1333,7 +1375,7 @@ static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction, goto failed; } - for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { + for (l = sep->stream->caps, rsp_size = 0; l; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; if (rsp_size + cap->length + 2 > (int) sizeof(buf)) @@ -1703,10 +1745,14 @@ static gboolean avdtp_delayreport_cmd(struct avdtp *session, stream = sep->stream; - if (sep->state != AVDTP_STATE_CONFIGURED && - sep->state != AVDTP_STATE_STREAMING) { + switch (sep->state) { + case AVDTP_STATE_IDLE: + case AVDTP_STATE_ABORTING: + case AVDTP_STATE_CLOSING: err = AVDTP_BAD_STATE; goto failed; + default: + break; } stream->delay = ntohs(req->delay); @@ -1790,7 +1836,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, switch (header->packet_type) { case AVDTP_PKT_TYPE_SINGLE: if (size < sizeof(*single)) { - error("Received too small single packet (%zu bytes)", size); + error("Received too small single packet (%zu bytes)", + size); return PARSE_ERROR; } if (session->in.active) { @@ -1811,7 +1858,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, break; case AVDTP_PKT_TYPE_START: if (size < sizeof(*start)) { - error("Received too small start packet (%zu bytes)", size); + error("Received too small start packet (%zu bytes)", + size); return PARSE_ERROR; } if (session->in.active) { @@ -1855,7 +1903,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, break; case AVDTP_PKT_TYPE_END: if (size < sizeof(struct avdtp_continue_header)) { - error("Received too small end packet (%zu bytes)", size); + error("Received too small end packet (%zu bytes)", + size); return PARSE_ERROR; } if (!session->in.active) { @@ -2018,13 +2067,38 @@ failed: return FALSE; } +static int set_priority(int fd, int priority) +{ + int err; + + err = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, + sizeof(priority)); + if (err == 0 || errno == ENOTSOCK) + return 0; + + err = -errno; + error("setsockopt(SO_PRIORITY): %s (%d)", strerror(-err), -err); + + return err; +} + struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version) { struct avdtp *session; GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + int new_fd; + + new_fd = dup(fd); + if (new_fd < 0) { + error("dup(): %s (%d)", strerror(errno), errno); + return NULL; + } + + if (set_priority(new_fd, 6) < 0) + return NULL; session = g_new0(struct avdtp, 1); - session->io = g_io_channel_unix_new(fd); + session->io = g_io_channel_unix_new(new_fd); session->version = version; session->imtu = imtu; session->omtu = omtu; @@ -2044,6 +2118,66 @@ struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version) return avdtp_ref(session); } +unsigned int avdtp_add_disconnect_cb(struct avdtp *session, + avdtp_disconnect_cb_t cb, + void *user_data) +{ + struct disconnect_callback *callback; + static unsigned int id = 0; + + callback = g_new0(struct disconnect_callback, 1); + callback->id = ++id; + callback->cb = cb; + callback->user_data = user_data; + session->disconnect = g_slist_append(session->disconnect, callback); + + return id; +} + +gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id) +{ + GSList *l; + + for (l = session->disconnect; l; l = g_slist_next(l)) { + struct disconnect_callback *callback = l->data; + + if (callback->id != id) + continue; + + session->disconnect = g_slist_remove(session->disconnect, + callback); + g_free(callback); + return TRUE; + } + + return FALSE; +} + +void avdtp_shutdown(struct avdtp *session) +{ + GSList *l; + bool aborting = false; + + if (!session->io) + return; + + for (l = session->streams; l; l = g_slist_next(l)) { + struct avdtp_stream *stream = l->data; + + if (stream->abort_int || avdtp_abort(session, stream) == 0) + aborting = true; + } + + if (aborting) { + /* defer shutdown until all streams are aborted properly */ + session->shutdown = true; + } else { + int sock = g_io_channel_unix_get_fd(session->io); + + shutdown(sock, SHUT_RDWR); + } +} + static void queue_request(struct avdtp *session, struct pending_req *req, gboolean priority) { @@ -2128,7 +2262,7 @@ static int cancel_request(struct avdtp *session, int err) 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); + &averr, lsep->user_data); goto failed; case AVDTP_DISCOVER: error("Discover: %s (%d)", strerror(err), err); @@ -2214,6 +2348,11 @@ static int send_request(struct avdtp *session, gboolean priority, { struct pending_req *req; + if (size > 0 && !buffer) { + DBG("Invalid buffer %p", buffer); + return -EINVAL; + } + if (stream && stream->abort_int && signal_id != AVDTP_ABORT) { DBG("Unable to send requests while aborting"); return -EINVAL; @@ -2221,11 +2360,14 @@ static int send_request(struct avdtp *session, gboolean priority, 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; + if (size > 0) { + req->data = g_malloc(size); + memcpy(req->data, buffer, size); + } + return send_req(session, priority, req); } @@ -2324,9 +2466,9 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, } static gboolean avdtp_set_configuration_resp(struct avdtp *session, - struct avdtp_stream *stream, - struct avdtp_single_header *resp, - int size) + struct avdtp_stream *stream, + struct avdtp_single_header *resp, + int size) { struct avdtp_local_sep *sep = stream->lsep; @@ -2341,12 +2483,14 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session, static gboolean avdtp_reconfigure_resp(struct avdtp *session, struct avdtp_stream *stream, - struct avdtp_single_header *resp, int size) + struct avdtp_single_header *resp, + int size) { return TRUE; } -static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream, +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; @@ -2426,7 +2570,8 @@ static gboolean avdtp_delay_report_resp(struct avdtp *session, 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); + sep->cfm->delay_report(session, sep, stream, NULL, + sep->user_data); return TRUE; } @@ -2733,6 +2878,9 @@ gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, if (stream != stream->session->pending_open) return FALSE; + if (set_priority(fd, 5) < 0) + return FALSE; + io = g_io_channel_unix_new(fd); handle_transport_connect(stream->session, io, imtu, omtu); @@ -2799,13 +2947,19 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, { struct avdtp_service_capability *cap; - if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING) + if (category < AVDTP_MEDIA_TRANSPORT || + category > AVDTP_DELAY_REPORTING) + return NULL; + + if (length > 0 && !data) return NULL; cap = g_malloc(sizeof(struct avdtp_service_capability) + length); cap->category = category; cap->length = length; - memcpy(cap->data, data, length); + + if (length > 0) + memcpy(cap->data, data, length); return cap; } @@ -3186,7 +3340,7 @@ struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, sep->ind = ind; sep->cfm = cfm; sep->user_data = user_data; - sep->delay_reporting = TRUE; + sep->delay_reporting = delay_reporting; DBG("SEP %p registered: type:%d codec:%d seid:%d", sep, sep->info.type, sep->codec, sep->info.seid); @@ -3218,7 +3372,7 @@ const char *avdtp_strerror(struct avdtp_error *err) if (err->category == AVDTP_ERRNO) return strerror(err->err.posix_errno); - switch(err->err.error_code) { + switch (err->err.error_code) { case AVDTP_BAD_HEADER_FORMAT: return "Bad Header Format"; case AVDTP_BAD_LENGTH: diff --git a/android/avdtp.h b/android/avdtp.h index 9760875..7fdf597 100644 --- a/android/avdtp.h +++ b/android/avdtp.h @@ -156,12 +156,13 @@ struct avdtp_sep_cfm { 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 */ +/* + * 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, @@ -200,9 +201,17 @@ struct avdtp_sep_ind { typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data); +typedef void (*avdtp_disconnect_cb_t) (void *user_data); struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version); +unsigned int avdtp_add_disconnect_cb(struct avdtp *session, + avdtp_disconnect_cb_t cb, + void *user_data); +gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id); + +void avdtp_shutdown(struct avdtp *session); + void avdtp_unref(struct avdtp *session); struct avdtp *avdtp_ref(struct avdtp *session); diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c new file mode 100644 index 0000000..036867e --- /dev/null +++ b/android/avrcp-lib.c @@ -0,0 +1,2910 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include + +#include "lib/bluetooth.h" + +#include "src/shared/util.h" +#include "src/log.h" + +#include "avctp.h" +#include "avrcp-lib.h" + + +/* Packet types */ +#define AVRCP_PACKET_TYPE_SINGLE 0x00 +#define AVRCP_PACKET_TYPE_START 0x01 +#define AVRCP_PACKET_TYPE_CONTINUING 0x02 +#define AVRCP_PACKET_TYPE_END 0x03 + +#define AVRCP_CHARSET_UTF8 0x006a + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avrcp_header { + uint8_t company_id[3]; + uint8_t pdu_id; + uint8_t packet_type:2; + uint8_t rsvd:6; + uint16_t params_len; + uint8_t params[0]; +} __attribute__ ((packed)); +#define AVRCP_HEADER_LENGTH 7 + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avrcp_header { + uint8_t company_id[3]; + uint8_t pdu_id; + uint8_t rsvd:6; + uint8_t packet_type:2; + uint16_t params_len; + uint8_t params[0]; +} __attribute__ ((packed)); +#define AVRCP_HEADER_LENGTH 7 + +#else +#error "Unknown byte order" +#endif + +struct avrcp_browsing_header { + uint8_t pdu_id; + uint16_t params_len; + uint8_t params[0]; +} __attribute__ ((packed)); +#define AVRCP_BROWSING_HEADER_LENGTH 3 + +struct avrcp_control_handler { + uint8_t id; + uint8_t code; + uint8_t rsp; + ssize_t (*func) (struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, void *user_data); +}; + +struct avrcp_browsing_handler { + uint8_t id; + ssize_t (*func) (struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, void *user_data); +}; + +struct avrcp { + struct avctp *conn; + struct avrcp_player *player; + + const struct avrcp_control_handler *control_handlers; + void *control_data; + unsigned int control_id; + + const struct avrcp_passthrough_handler *passthrough_handlers; + void *passthrough_data; + unsigned int passthrough_id; + + const struct avrcp_browsing_handler *browsing_handlers; + void *browsing_data; + unsigned int browsing_id; + + avrcp_destroy_cb_t destroy; + void *destroy_data; +}; + +struct avrcp_player { + const struct avrcp_control_ind *ind; + const struct avrcp_control_cfm *cfm; + + void *user_data; +}; + +static inline uint32_t ntoh24(const uint8_t src[3]) +{ + return src[0] << 16 | src[1] << 8 | src[2]; +} + +static inline void hton24(uint8_t dst[3], uint32_t src) +{ + dst[0] = (src & 0xff0000) >> 16; + dst[1] = (src & 0x00ff00) >> 8; + dst[2] = (src & 0x0000ff); +} + +void avrcp_shutdown(struct avrcp *session) +{ + if (session->conn) { + if (session->control_id > 0) + avctp_unregister_pdu_handler(session->conn, + session->control_id); + if (session->passthrough_id > 0) + avctp_unregister_passthrough_handler(session->conn, + session->passthrough_id); + + /* clear destroy callback that would call shutdown again */ + avctp_set_destroy_cb(session->conn, NULL, NULL); + avctp_shutdown(session->conn); + } + + if (session->destroy) + session->destroy(session->destroy_data); + + g_free(session->player); + g_free(session); +} + +static struct avrcp_header *parse_pdu(uint8_t *operands, size_t operand_count) +{ + struct avrcp_header *pdu; + + if (!operands || operand_count < sizeof(*pdu)) { + error("AVRCP: packet too small (%zu bytes)", operand_count); + return NULL; + } + + pdu = (void *) operands; + pdu->params_len = ntohs(pdu->params_len); + + if (operand_count != pdu->params_len + sizeof(*pdu)) { + error("AVRCP: invalid parameter length (%u bytes)", + pdu->params_len); + return NULL; + } + + return pdu; +} + +static struct avrcp_browsing_header *parse_browsing_pdu(uint8_t *operands, + size_t operand_count) +{ + struct avrcp_browsing_header *pdu; + + if (!operands || operand_count < sizeof(*pdu)) { + error("AVRCP: packet too small (%zu bytes)", operand_count); + return NULL; + } + + pdu = (void *) operands; + pdu->params_len = ntohs(pdu->params_len); + + if (operand_count != pdu->params_len + sizeof(*pdu)) { + error("AVRCP: invalid parameter length (%u bytes)", + pdu->params_len); + return NULL; + } + + return pdu; +} + +static uint8_t errno2status(int err) +{ + switch (err) { + case -ENOSYS: + return AVRCP_STATUS_INVALID_COMMAND; + case -EINVAL: + return AVRCP_STATUS_INVALID_PARAM; + case 0: + return AVRCP_STATUS_SUCCESS; + case -ENOTDIR: + return AVRCP_STATUS_NOT_DIRECTORY; + case -EBADRQC: + return AVRCP_STATUS_INVALID_SCOPE; + case -ERANGE: + return AVRCP_STATUS_OUT_OF_BOUNDS; + case -ENOENT: + return AVRCP_STATUS_DOES_NOT_EXIST; + default: + return AVRCP_STATUS_INTERNAL_ERROR; + } +} + +static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction, + uint8_t *code, uint8_t *subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + const struct avrcp_control_handler *handler; + struct avrcp_header *pdu; + uint32_t company_id; + ssize_t ret; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + pdu = (void *) operands; + pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; + goto reject; + } + + company_id = ntoh24(pdu->company_id); + if (company_id != IEEEID_BTSIG) { + *code = AVC_CTYPE_NOT_IMPLEMENTED; + return 0; + } + + DBG("AVRCP PDU 0x%02X, len 0x%04X", pdu->pdu_id, pdu->params_len); + + pdu->packet_type = 0; + pdu->rsvd = 0; + + if (!session->control_handlers) + goto reject; + + for (handler = session->control_handlers; handler->id; handler++) { + if (handler->id == pdu->pdu_id) + break; + } + + if (handler->id != pdu->pdu_id || handler->code != *code) { + pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; + goto reject; + } + + if (!handler->func) { + pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; + goto reject; + } + + ret = handler->func(session, transaction, pdu->params_len, pdu->params, + session->control_data); + if (ret < 0) { + if (ret == -EAGAIN) + return ret; + pdu->params[0] = errno2status(ret); + goto reject; + } + + *code = handler->rsp; + pdu->params_len = htons(ret); + + return AVRCP_HEADER_LENGTH + ret; + +reject: + pdu->params_len = htons(1); + *code = AVC_CTYPE_REJECTED; + + return AVRCP_HEADER_LENGTH + 1; +} + +static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op, + bool pressed, void *user_data) +{ + struct avrcp *session = user_data; + const struct avrcp_passthrough_handler *handler; + + if (!session->passthrough_handlers) + return false; + + for (handler = session->passthrough_handlers; handler->func; + handler++) { + if (handler->op == op) + break; + } + + if (handler->func == NULL) + return false; + + return handler->func(session, pressed, session->passthrough_data); +} + +static void disconnect_cb(void *data) +{ + struct avrcp *session = data; + + session->conn = NULL; + + avrcp_shutdown(session); +} + +struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version) +{ + struct avrcp *session; + + session = g_new0(struct avrcp, 1); + + session->conn = avctp_new(fd, imtu, omtu, version); + if (!session->conn) { + g_free(session); + return NULL; + } + + session->passthrough_id = avctp_register_passthrough_handler( + session->conn, + handle_passthrough_pdu, + session); + session->control_id = avctp_register_pdu_handler(session->conn, + AVC_OP_VENDORDEP, + handle_vendordep_pdu, + session); + + avctp_set_destroy_cb(session->conn, disconnect_cb, session); + + return session; +} + +static ssize_t handle_browsing_pdu(struct avctp *conn, + uint8_t transaction, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + const struct avrcp_browsing_handler *handler; + struct avrcp_browsing_header *pdu; + int ret; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + pdu = (void *) operands; + pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; + goto reject; + } + + DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id, + pdu->params_len); + + if (!session->browsing_handlers) { + pdu->pdu_id = AVRCP_GENERAL_REJECT; + pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR; + goto reject; + } + + for (handler = session->browsing_handlers; handler->id; handler++) { + if (handler->id == pdu->pdu_id) + break; + } + + if (handler->id != pdu->pdu_id) { + pdu->pdu_id = AVRCP_GENERAL_REJECT; + pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; + goto reject; + } + + if (!handler->func) { + pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; + goto reject; + } + + ret = handler->func(session, transaction, pdu->params_len, pdu->params, + session->control_data); + if (ret < 0) { + if (ret == -EAGAIN) + return ret; + pdu->params[0] = errno2status(ret); + goto reject; + } + + pdu->params_len = htons(ret); + + return AVRCP_BROWSING_HEADER_LENGTH + ret; + +reject: + pdu->params_len = htons(1); + + return AVRCP_BROWSING_HEADER_LENGTH + 1; +} + +static void browsing_disconnect_cb(void *data) +{ + struct avrcp *session = data; + + session->browsing_id = 0; +} + +int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu, + size_t omtu) +{ + int err; + + err = avctp_connect_browsing(session->conn, fd, imtu, omtu); + if (err < 0) + return err; + + session->browsing_id = avctp_register_browsing_pdu_handler( + session->conn, + handle_browsing_pdu, + session, + browsing_disconnect_cb); + + return 0; +} + +void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb, + void *user_data) +{ + session->destroy = cb; + session->destroy_data = user_data; +} + +static ssize_t get_capabilities(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + if (!params || params_len != 1) + return -EINVAL; + + switch (params[0]) { + case CAP_COMPANY_ID: + params[1] = 1; + hton24(¶ms[2], IEEEID_BTSIG); + return 5; + case CAP_EVENTS_SUPPORTED: + if (!player->ind || !player->ind->get_capabilities) + return -ENOSYS; + return player->ind->get_capabilities(session, transaction, + player->user_data); + } + + return -EINVAL; +} + +static ssize_t list_attributes(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!player->ind || !player->ind->list_attributes) + return -ENOSYS; + + return player->ind->list_attributes(session, transaction, + player->user_data); +} + +static bool check_attributes(uint8_t number, const uint8_t *attrs) +{ + int i; + + for (i = 0; i < number; i++) { + if (attrs[i] > AVRCP_ATTRIBUTE_LAST || + attrs[i] == AVRCP_ATTRIBUTE_ILEGAL) + return false; + } + + return true; +} + +static ssize_t get_attribute_text(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!params || params_len != 1 + params[0]) + return -EINVAL; + + if (!check_attributes(params[0], ¶ms[1])) + return -EINVAL; + + if (!player->ind || !player->ind->get_attribute_text) + return -ENOSYS; + + return player->ind->get_attribute_text(session, transaction, params[0], + ¶ms[1], player->user_data); +} + +static ssize_t list_values(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!params || params_len != 1) + return -EINVAL; + + if (params[0] > AVRCP_ATTRIBUTE_LAST || + params[0] == AVRCP_ATTRIBUTE_ILEGAL) + return -EINVAL; + + if (!player->ind || !player->ind->list_values) + return -ENOSYS; + + return player->ind->list_values(session, transaction, params[0], + player->user_data); +} + +static bool check_value(uint8_t attr, uint8_t number, const uint8_t *values) +{ + int i; + + for (i = 0; i < number; i++) { + /* Check for invalid value */ + switch (attr) { + case AVRCP_ATTRIBUTE_EQUALIZER: + if (values[i] < AVRCP_EQUALIZER_OFF || + values[i] > AVRCP_EQUALIZER_ON) + return false; + case AVRCP_ATTRIBUTE_REPEAT_MODE: + if (values[i] < AVRCP_REPEAT_MODE_OFF || + values[i] > AVRCP_REPEAT_MODE_GROUP) + return false; + case AVRCP_ATTRIBUTE_SHUFFLE: + if (values[i] < AVRCP_SHUFFLE_OFF || + values[i] > AVRCP_SHUFFLE_GROUP) + return false; + case AVRCP_ATTRIBUTE_SCAN: + if (values[i] < AVRCP_SCAN_OFF || + values[i] > AVRCP_SCAN_GROUP) + return false; + } + } + + return true; +} + +static ssize_t get_value_text(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (params_len != 2 + params[1]) + return -EINVAL; + + if (params[0] > AVRCP_ATTRIBUTE_LAST || + params[0] == AVRCP_ATTRIBUTE_ILEGAL) + return -EINVAL; + + if (!check_value(params[0], params[1], ¶ms[2])) + return -EINVAL; + + if (!player->ind || !player->ind->get_value_text) + return -ENOSYS; + + return player->ind->get_value_text(session, transaction, params[0], + params[1], ¶ms[2], + player->user_data); +} + +static ssize_t get_value(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!params || params_len < 1 + params[0]) + return -EINVAL; + + if (!check_attributes(params[0], ¶ms[1])) + return -EINVAL; + + if (!player->ind || !player->ind->get_value) + return -ENOSYS; + + return player->ind->get_value(session, transaction, params[0], + ¶ms[1], player->user_data); +} + +static ssize_t set_value(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + int i; + + DBG(""); + + if (!params || params_len != params[0] * 2 + 1) + return -EINVAL; + + for (i = 0; i < params[0]; i++) { + uint8_t attr = params[i * 2 + 1]; + uint8_t val = params[i * 2 + 2]; + + if (!check_value(attr, 1, &val)) + return -EINVAL; + } + + if (!player->ind || !player->ind->set_value) + return -ENOSYS; + + return player->ind->set_value(session, transaction, params[0], + ¶ms[1], player->user_data); +} + +static ssize_t get_play_status(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!player->ind || !player->ind->get_play_status) + return -ENOSYS; + + return player->ind->get_play_status(session, transaction, + player->user_data); +} + +static ssize_t get_element_attributes(struct avrcp *session, + uint8_t transaction, + uint16_t params_len, + uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint64_t uid; + uint8_t number; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int i; + + DBG(""); + + if (!params || params_len != 9 + params[8] * 4) + return -EINVAL; + + uid = get_be64(params); + number = params[8]; + + for (i = 0; i < number; i++) { + attrs[i] = get_be32(¶ms[9 + i * 4]); + + if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || + attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + } + + if (!player->ind || !player->ind->get_element_attributes) + return -ENOSYS; + + return player->ind->get_element_attributes(session, transaction, uid, + number, attrs, + player->user_data); +} + +static ssize_t register_notification(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint32_t interval; + + DBG(""); + + if (!params || params_len != 5) + return -EINVAL; + + if (!player->ind || !player->ind->register_notification) + return -ENOSYS; + + interval = get_be32(¶ms[1]); + + return player->ind->register_notification(session, transaction, + params[0], interval, + player->user_data); +} + +static ssize_t set_volume(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t volume; + + DBG(""); + + if (!player->ind || !player->ind->set_volume) + return -ENOSYS; + + if (!params || params_len != sizeof(volume)) + return -EINVAL; + + volume = params[0] & 0x7f; + + return player->ind->set_volume(session, transaction, volume, + player->user_data); +} + +static ssize_t set_addressed(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint16_t id; + + DBG(""); + + if (!params || params_len != 2) + return -EINVAL; + + if (!player->ind || !player->ind->set_addressed) + return -ENOSYS; + + id = get_be16(params); + + return player->ind->set_addressed(session, transaction, id, + player->user_data); +} + +static const struct avrcp_control_handler player_handlers[] = { + { AVRCP_GET_CAPABILITIES, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_capabilities }, + { AVRCP_LIST_PLAYER_ATTRIBUTES, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + list_attributes }, + { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_attribute_text }, + { AVRCP_LIST_PLAYER_VALUES, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + list_values }, + { AVRCP_GET_PLAYER_VALUE_TEXT, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_value_text }, + { AVRCP_GET_CURRENT_PLAYER_VALUE, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_value }, + { AVRCP_SET_PLAYER_VALUE, + AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, + set_value }, + { AVRCP_GET_PLAY_STATUS, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_play_status }, + { AVRCP_GET_ELEMENT_ATTRIBUTES, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_element_attributes }, + { AVRCP_REGISTER_NOTIFICATION, + AVC_CTYPE_NOTIFY, AVC_CTYPE_INTERIM, + register_notification }, + { AVRCP_SET_ABSOLUTE_VOLUME, + AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, + set_volume }, + { AVRCP_SET_ADDRESSED_PLAYER, + AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, + set_addressed }, + { }, +}; + +static void avrcp_set_control_handlers(struct avrcp *session, + const struct avrcp_control_handler *handlers, + void *user_data) +{ + session->control_handlers = handlers; + session->control_data = user_data; +} + +static ssize_t get_folder_items(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t scope; + uint32_t start, end; + uint16_t number; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int i; + + DBG(""); + + if (!player->ind || !player->ind->get_folder_items) + return -ENOSYS; + + if (!params || params_len < 10) + return -EINVAL; + + scope = params[0]; + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EBADRQC; + + start = get_be32(¶ms[1]); + end = get_be32(¶ms[5]); + + if (start > end) + return -ERANGE; + + number = get_be16(¶ms[9]); + + for (i = 0; i < number; i++) { + attrs[i] = get_be32(¶ms[11 + i * 4]); + + if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || + attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + } + + return player->ind->get_folder_items(session, transaction, scope, start, + end, number, attrs, + player->user_data); +} + +static ssize_t change_path(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t direction; + uint16_t counter; + uint64_t uid; + + DBG(""); + + if (!player->ind || !player->ind->change_path) + return -ENOSYS; + + if (!params || params_len < 11) + return -EINVAL; + + counter = get_be16(¶ms[0]); + direction = params[2]; + uid = get_be64(¶ms[3]); + + return player->ind->change_path(session, transaction, counter, + direction, uid, player->user_data); +} + +static ssize_t get_item_attributes(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t scope; + uint64_t uid; + uint16_t counter; + uint8_t number; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int i; + + DBG(""); + + if (!player->ind || !player->ind->get_item_attributes) + return -ENOSYS; + + if (!params || params_len < 12) + return -EINVAL; + + scope = params[0]; + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EBADRQC; + + uid = get_be64(¶ms[1]); + counter = get_be16(¶ms[9]); + number = params[11]; + + for (i = 0; i < number; i++) { + attrs[i] = get_be32(¶ms[12 + i * 4]); + + if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || + attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + } + + return player->ind->get_item_attributes(session, transaction, scope, + uid, counter, number, attrs, + player->user_data); +} + +static ssize_t play_item(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t scope; + uint64_t uid; + uint16_t counter; + + DBG(""); + + if (!player->ind || !player->ind->play_item) + return -ENOSYS; + + if (!params || params_len < 11) + return -EINVAL; + + scope = params[0]; + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EBADRQC; + + uid = get_be64(¶ms[1]); + counter = get_be16(¶ms[9]); + + return player->ind->play_item(session, transaction, scope, uid, + counter, player->user_data); +} + +static ssize_t search(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + char *string; + uint16_t len; + int ret; + + DBG(""); + + if (!player->ind || !player->ind->search) + return -ENOSYS; + + if (!params || params_len < 4) + return -EINVAL; + + len = get_be16(¶ms[2]); + if (!len) + return -EINVAL; + + string = strndup((void *) ¶ms[4], len); + + ret = player->ind->search(session, transaction, string, + player->user_data); + + free(string); + + return ret; +} + +static ssize_t add_to_now_playing(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t scope; + uint64_t uid; + uint16_t counter; + + DBG(""); + + if (!player->ind || !player->ind->add_to_now_playing) + return -ENOSYS; + + if (!params || params_len < 11) + return -EINVAL; + + scope = params[0]; + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EBADRQC; + + uid = get_be64(¶ms[1]); + counter = get_be16(¶ms[9]); + + return player->ind->add_to_now_playing(session, transaction, scope, uid, + counter, player->user_data); +} + +static const struct avrcp_browsing_handler browsing_handlers[] = { + { AVRCP_GET_FOLDER_ITEMS, get_folder_items }, + { AVRCP_CHANGE_PATH, change_path }, + { AVRCP_GET_ITEM_ATTRIBUTES, get_item_attributes }, + { AVRCP_PLAY_ITEM, play_item }, + { AVRCP_SEARCH, search }, + { AVRCP_ADD_TO_NOW_PLAYING, add_to_now_playing }, + { }, +}; + +static void avrcp_set_browsing_handlers(struct avrcp *session, + const struct avrcp_browsing_handler *handlers, + void *user_data) +{ + session->browsing_handlers = handlers; + session->browsing_data = user_data; +} + +void avrcp_register_player(struct avrcp *session, + const struct avrcp_control_ind *ind, + const struct avrcp_control_cfm *cfm, + void *user_data) +{ + struct avrcp_player *player; + + player = g_new0(struct avrcp_player, 1); + player->ind = ind; + player->cfm = cfm; + player->user_data = user_data; + + avrcp_set_control_handlers(session, player_handlers, player); + avrcp_set_browsing_handlers(session, browsing_handlers, player); + session->player = player; +} + +void avrcp_set_passthrough_handlers(struct avrcp *session, + const struct avrcp_passthrough_handler *handlers, + void *user_data) +{ + session->passthrough_handlers = handlers; + session->passthrough_data = user_data; +} + +int avrcp_init_uinput(struct avrcp *session, const char *name, + const char *address) +{ + return avctp_init_uinput(session->conn, name, address); +} + +int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code, + uint8_t subunit, uint8_t pdu_id, + const struct iovec *iov, int iov_cnt) +{ + struct iovec pdu[iov_cnt + 1]; + struct avrcp_header hdr; + int i; + + memset(&hdr, 0, sizeof(hdr)); + + pdu[0].iov_base = &hdr; + pdu[0].iov_len = sizeof(hdr); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + hdr.params_len += iov[i].iov_len; + } + + hton24(hdr.company_id, IEEEID_BTSIG); + hdr.pdu_id = pdu_id; + hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE; + hdr.params_len = htons(hdr.params_len); + + return avctp_send_vendor(session->conn, transaction, code, subunit, + pdu, iov_cnt + 1); +} + +static int status2errno(uint8_t status) +{ + switch (status) { + case AVRCP_STATUS_INVALID_COMMAND: + return -ENOSYS; + case AVRCP_STATUS_INVALID_PARAM: + return -EINVAL; + case AVRCP_STATUS_SUCCESS: + return 0; + case AVRCP_STATUS_NOT_DIRECTORY: + return -ENOTDIR; + case AVRCP_STATUS_INVALID_SCOPE: + return -EBADRQC; + case AVRCP_STATUS_OUT_OF_BOUNDS: + return -ERANGE; + case AVRCP_STATUS_INTERNAL_ERROR: + case AVRCP_STATUS_INVALID_PLAYER_ID: + case AVRCP_STATUS_PLAYER_NOT_BROWSABLE: + case AVRCP_STATUS_NO_AVAILABLE_PLAYERS: + case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED: + return -EPERM; + default: + return -EPROTO; + } +} + +static int parse_status(struct avrcp_header *pdu) +{ + if (pdu->params_len < 1) + return -EPROTO; + + return status2errno(pdu->params[0]); +} + +static int parse_browsing_status(struct avrcp_browsing_header *pdu) +{ + if (pdu->params_len < 1) + return -EPROTO; + + return status2errno(pdu->params[0]); +} + +static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit, + uint8_t pdu_id, const struct iovec *iov, + int iov_cnt, avctp_rsp_cb func, + void *user_data) +{ + struct iovec pdu[iov_cnt + 1]; + struct avrcp_header hdr; + int i; + + memset(&hdr, 0, sizeof(hdr)); + + pdu[0].iov_base = &hdr; + pdu[0].iov_len = sizeof(hdr); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + hdr.params_len += iov[i].iov_len; + } + + hton24(hdr.company_id, IEEEID_BTSIG); + hdr.pdu_id = pdu_id; + hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE; + hdr.params_len = htons(hdr.params_len); + + return avctp_send_vendor_req(session->conn, code, subunit, pdu, + iov_cnt + 1, func, user_data); +} + +static int avrcp_send_browsing_req(struct avrcp *session, uint8_t pdu_id, + const struct iovec *iov, int iov_cnt, + avctp_browsing_rsp_cb func, + void *user_data) +{ + struct iovec pdu[iov_cnt + 1]; + struct avrcp_browsing_header hdr; + int i; + + memset(&hdr, 0, sizeof(hdr)); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + hdr.params_len += iov[i].iov_len; + } + + hdr.pdu_id = pdu_id; + hdr.params_len = htons(hdr.params_len); + + pdu[0].iov_base = &hdr; + pdu[0].iov_len = sizeof(hdr); + + return avctp_send_browsing_req(session->conn, pdu, iov_cnt + 1, + func, user_data); +} + +static int avrcp_send_browsing(struct avrcp *session, uint8_t transaction, + uint8_t pdu_id, const struct iovec *iov, + int iov_cnt) +{ + struct iovec pdu[iov_cnt + 1]; + struct avrcp_browsing_header hdr; + int i; + + memset(&hdr, 0, sizeof(hdr)); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + hdr.params_len += iov[i].iov_len; + } + + hdr.pdu_id = pdu_id; + hdr.params_len = htons(hdr.params_len); + + pdu[0].iov_base = &hdr; + pdu[0].iov_len = sizeof(hdr); + + return avctp_send_browsing(session->conn, transaction, pdu, + iov_cnt + 1); +} + +static gboolean get_capabilities_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint8_t *params = NULL; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_capabilities) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + if (pdu->params_len < 2) { + err = -EPROTO; + goto done; + } + + switch (pdu->params[0]) { + case CAP_COMPANY_ID: + case CAP_EVENTS_SUPPORTED: + break; + default: + err = -EPROTO; + goto done; + } + + number = pdu->params[1]; + + if (number > 0) + params = &pdu->params[2]; + + err = 0; + +done: + player->cfm->get_capabilities(session, err, number, params, + player->user_data); + + return FALSE; +} + + +int avrcp_get_capabilities(struct avrcp *session, uint8_t param) +{ + struct iovec iov; + + iov.iov_base = ¶m; + iov.iov_len = sizeof(param); + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_CAPABILITIES, &iov, 1, + get_capabilities_rsp, session); +} + +static gboolean register_notification_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t event = 0; + uint16_t value16; + uint32_t value32; + uint64_t value64; + uint8_t *params = NULL; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->register_notification) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + if (pdu->params_len < 1) { + err = -EPROTO; + goto done; + } + + event = pdu->params[0]; + + switch (event) { + case AVRCP_EVENT_STATUS_CHANGED: + case AVRCP_EVENT_VOLUME_CHANGED: + if (pdu->params_len != 2) { + err = -EPROTO; + goto done; + } + params = &pdu->params[1]; + break; + case AVRCP_EVENT_TRACK_CHANGED: + if (pdu->params_len != 9) { + err = -EPROTO; + goto done; + } + value64 = get_be64(&pdu->params[1]); + params = (uint8_t *) &value64; + break; + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + if (pdu->params_len != 5) { + err = -EPROTO; + goto done; + } + value32 = get_be32(&pdu->params[1]); + params = (uint8_t *) &value32; + break; + case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: + case AVRCP_EVENT_SETTINGS_CHANGED: + if (pdu->params_len < 2) { + err = -EPROTO; + goto done; + } + params = &pdu->params[1]; + break; + case AVRCP_EVENT_UIDS_CHANGED: + if (pdu->params_len != 3) { + err = -EPROTO; + goto done; + } + value16 = get_be16(&pdu->params[1]); + params = (uint8_t *) &value16; + break; + } + + err = 0; + +done: + return player->cfm->register_notification(session, err, code, event, + params, player->user_data); +} + +int avrcp_register_notification(struct avrcp *session, uint8_t event, + uint32_t interval) +{ + struct iovec iov; + uint8_t pdu[5]; + + pdu[0] = event; + put_be32(interval, &pdu[1]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_req(session, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL, + AVRCP_REGISTER_NOTIFICATION, &iov, 1, + register_notification_rsp, session); +} + +static gboolean list_attributes_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu = (void *) operands; + uint8_t number = 0; + uint8_t *attrs = NULL; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->list_attributes) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + number = pdu->params[0]; + if (number > 0) + attrs = &pdu->params[1]; + + err = 0; + +done: + player->cfm->list_attributes(session, err, number, attrs, + player->user_data); + + return FALSE; +} + +int avrcp_list_player_attributes(struct avrcp *session) +{ + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_LIST_PLAYER_ATTRIBUTES, NULL, 0, + list_attributes_rsp, session); +} + +static int parse_text_rsp(struct avrcp_header *pdu, uint8_t *number, + uint8_t *attrs, char **text) +{ + uint8_t *ptr; + uint16_t params_len; + int i; + + if (pdu->params_len < 1) + return -EPROTO; + + *number = pdu->params[0]; + if (*number > AVRCP_ATTRIBUTE_LAST) { + *number = 0; + return -EPROTO; + } + + params_len = pdu->params_len - 1; + for (i = 0, ptr = &pdu->params[1]; i < *number && params_len > 0; i++) { + uint8_t len; + + if (params_len < 4) + goto fail; + + attrs[i] = ptr[0]; + len = ptr[3]; + + params_len -= 4; + ptr += 4; + + if (len > params_len) + goto fail; + + if (len > 0) { + text[i] = g_strndup((const char *) &ptr[4], len); + params_len -= len; + ptr += len; + } + } + + if (i != *number) + goto fail; + + return 0; + +fail: + for (i -= 1; i >= 0; i--) + g_free(text[i]); + + *number = 0; + + return -EPROTO; +} + +static gboolean get_attribute_text_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; + char *text[AVRCP_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_attribute_text) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = parse_text_rsp(pdu, &number, attrs, text); + +done: + player->cfm->get_attribute_text(session, err, number, attrs, text, + player->user_data); + + return FALSE; +} + +int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number, + uint8_t *attrs) +{ + struct iovec iov[2]; + + if (!number || number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + iov[1].iov_base = attrs; + iov[1].iov_len = number; + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, iov, 2, + get_attribute_text_rsp, session); +} + +static gboolean list_values_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint8_t *values = NULL; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->list_values) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + number = pdu->params[0]; + if (number > 0) + values = &pdu->params[1]; + + err = 0; + +done: + player->cfm->list_values(session, err, number, values, + player->user_data); + + return FALSE; +} + +int avrcp_list_player_values(struct avrcp *session, uint8_t attr) +{ + struct iovec iov; + + iov.iov_base = &attr; + iov.iov_len = sizeof(attr); + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_LIST_PLAYER_VALUES, &iov, 1, + list_values_rsp, session); +} + +static gboolean get_value_text_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint8_t values[AVRCP_ATTRIBUTE_LAST]; + char *text[AVRCP_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_value_text) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = parse_text_rsp(pdu, &number, values, text); + +done: + player->cfm->get_value_text(session, err, number, values, text, + player->user_data); + + return FALSE; +} + +int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr, + uint8_t number, uint8_t *values) +{ + struct iovec iov[2]; + uint8_t pdu[2]; + + if (!number) + return -EINVAL; + + pdu[0] = attr; + pdu[1] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + iov[1].iov_base = values; + iov[1].iov_len = number; + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_PLAYER_VALUE_TEXT, iov, 2, + get_value_text_rsp, session); +} + +static int parse_value(struct avrcp_header *pdu, uint8_t *number, + uint8_t *attrs, uint8_t *values) +{ + int i; + + if (pdu->params_len < 1) + return -EPROTO; + + *number = pdu->params[0]; + + /* + * Check if PDU is big enough to hold the number of (attribute, value) + * tuples. + */ + if (*number > AVRCP_ATTRIBUTE_LAST || + 1 + *number * 2 != pdu->params_len) { + number = 0; + return -EPROTO; + } + + for (i = 0; i < *number; i++) { + attrs[i] = pdu->params[i * 2 + 1]; + values[i] = pdu->params[i * 2 + 2]; + } + + return 0; +} + +static gboolean get_value_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu = (void *) operands; + uint8_t number = 0; + uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; + uint8_t values[AVRCP_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_value) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = parse_value(pdu, &number, attrs, values); + +done: + player->cfm->get_value(session, err, number, attrs, values, + player->user_data); + + return FALSE; +} + +int avrcp_get_current_player_value(struct avrcp *session, uint8_t number, + uint8_t *attrs) + +{ + struct iovec iov[2]; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + iov[1].iov_base = attrs; + iov[1].iov_len = number; + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_CURRENT_PLAYER_VALUE, iov, 2, + get_value_rsp, session); +} + +static gboolean set_value_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->set_value) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = 0; + +done: + player->cfm->set_value(session, err, player->user_data); + + return FALSE; +} + +int avrcp_set_player_value(struct avrcp *session, uint8_t number, + uint8_t *attrs, uint8_t *values) +{ + struct iovec iov; + uint8_t pdu[2 * AVRCP_ATTRIBUTE_LAST + 1]; + int i; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + pdu[0] = number; + + for (i = 0; i < number; i++) { + pdu[i * 2 + 1] = attrs[i]; + pdu[i * 2 + 2] = values[i]; + } + + iov.iov_base = pdu; + iov.iov_len = 1 + number * 2; + + return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, + AVRCP_SET_PLAYER_VALUE, &iov, 1, + set_value_rsp, session); +} + +static gboolean get_play_status_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t status = 0; + uint32_t position = 0; + uint32_t duration = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_play_status) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + if (pdu->params_len < 5) { + err = -EPROTO; + goto done; + } + + duration = get_be32(&pdu->params[0]); + position = get_be32(&pdu->params[4]); + status = pdu->params[8]; + err = 0; + +done: + player->cfm->get_play_status(session, err, status, position, duration, + player->user_data); + + return FALSE; +} + +int avrcp_get_play_status(struct avrcp *session) +{ + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_PLAY_STATUS, NULL, 0, + get_play_status_rsp, session); +} + +static gboolean set_volume_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t value = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->set_volume) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + if (pdu->params_len < 1) { + err = -EPROTO; + goto done; + } + + value = pdu->params[0] & 0x7f; + err = 0; + +done: + player->cfm->set_volume(session, err, value, player->user_data); + + return FALSE; +} + +int avrcp_set_volume(struct avrcp *session, uint8_t volume) +{ + struct iovec iov; + + iov.iov_base = &volume; + iov.iov_len = sizeof(volume); + + return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, + AVRCP_SET_ABSOLUTE_VOLUME, &iov, 1, + set_volume_rsp, session); +} + +static int parse_attribute_list(uint8_t *params, uint16_t params_len, + uint8_t number, uint32_t *attrs, char **text) +{ + int i; + + if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EPROTO; + + for (i = 0; number > 0 && params_len > i; number--) { + uint16_t charset, len; + + if (params_len < 8) + goto fail; + + attrs[i] = get_be32(¶ms[i]); + i += sizeof(uint32_t); + + charset = get_be16(¶ms[i]); + i += sizeof(uint16_t); + + len = get_be16(¶ms[i]); + i += sizeof(uint16_t); + + if (len > params_len) + goto fail; + + if (charset == AVRCP_CHARSET_UTF8) + text[i] = g_strndup((const char *) ¶ms[i], len); + + i += len; + } + + return 0; + +fail: + for (i -= 1; i >= 0; i--) + g_free(text[i]); + + return -EPROTO; +} + +static int parse_elements(struct avrcp_header *pdu, uint8_t *number, + uint32_t *attrs, char **text) +{ + if (pdu->params_len < 1) + return -EPROTO; + + *number = pdu->params[0]; + if (*number > AVRCP_MEDIA_ATTRIBUTE_LAST) { + *number = 0; + return -EPROTO; + } + + return parse_attribute_list(&pdu->params[1], pdu->params_len - 1, + *number, attrs, text); +} + +static int parse_items(struct avrcp_browsing_header *pdu, uint8_t *number, + uint32_t *attrs, char **text) +{ + if (pdu->params_len < 2) + return -EPROTO; + + *number = pdu->params[1]; + if (*number > AVRCP_MEDIA_ATTRIBUTE_LAST) { + *number = 0; + return -EPROTO; + } + + return parse_attribute_list(&pdu->params[2], pdu->params_len - 2, + *number, attrs, text); +} + +static gboolean get_element_attributes_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + char *text[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_element_attributes) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = parse_elements(pdu, &number, attrs, text); + +done: + player->cfm->get_element_attributes(session, err, number, attrs, text, + player->user_data); + + return FALSE; +} + +int avrcp_get_element_attributes(struct avrcp *session) +{ + struct iovec iov; + uint8_t pdu[9]; + + /* This returns all attributes */ + memset(pdu, 0, sizeof(pdu)); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_ELEMENT_ATTRIBUTES, &iov, 1, + get_element_attributes_rsp, session); +} + +static gboolean set_addressed_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->set_addressed) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_status(pdu); + +done: + player->cfm->set_addressed(session, err, player->user_data); + + return FALSE; +} + +int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id) +{ + struct iovec iov; + uint8_t pdu[2]; + + put_be16(player_id, pdu); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, + AVRCP_SET_ADDRESSED_PLAYER, &iov, 1, + set_addressed_rsp, session); +} + +static gboolean set_browsed_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint16_t counter = 0; + uint32_t items = 0; + uint8_t depth = 0, count; + char **folders, *path = NULL; + int err; + size_t i; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->set_browsed) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + if (pdu->params_len < 10) { + err = -EPROTO; + goto done; + } + + counter = get_be16(&pdu->params[1]); + items = get_be32(&pdu->params[3]); + depth = pdu->params[9]; + + folders = g_new0(char *, depth + 2); + folders[0] = g_strdup("/Filesystem"); + + for (i = 10, count = 1; count - 1 < depth && i < pdu->params_len; + count++) { + uint8_t len; + + len = pdu->params[i++]; + + if (i + len > pdu->params_len || len == 0) { + g_strfreev(folders); + err = -EPROTO; + goto done; + } + + folders[count] = g_memdup(&pdu->params[i], len); + i += len; + } + + path = g_build_pathv("/", folders); + g_strfreev(folders); + +done: + player->cfm->set_browsed(session, err, counter, items, path, + player->user_data); + + return FALSE; +} + +int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id) +{ + struct iovec iov; + uint8_t pdu[2]; + + put_be16(player_id, pdu); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing_req(session, AVRCP_SET_BROWSED_PLAYER, + &iov, 1, set_browsed_rsp, session); +} + +static gboolean get_folder_items_rsp(struct avctp *conn, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint16_t counter = 0, number = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_folder_items) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + if (pdu->params_len < 5) { + err = -EPROTO; + goto done; + } + + counter = get_be16(&pdu->params[1]); + number = get_be16(&pdu->params[3]); + + /* FIXME: Add proper parsing for each item type */ + +done: + player->cfm->get_folder_items(session, err, counter, number, + &pdu->params[5], player->user_data); + + return FALSE; +} + +int avrcp_get_folder_items(struct avrcp *session, uint8_t scope, + uint32_t start, uint32_t end, uint8_t number, + uint32_t *attrs) +{ + + struct iovec iov[2]; + uint8_t pdu[10]; + int i; + + pdu[0] = scope; + put_be32(start, &pdu[1]); + put_be32(end, &pdu[5]); + pdu[9] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + if (!number) + return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS, + iov, 1, get_folder_items_rsp, + session); + + for (i = 0; i < number; i++) + put_be32(attrs[i], &attrs[i]); + + iov[1].iov_base = attrs; + iov[1].iov_len = number * sizeof(*attrs); + + return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS, + iov, 2, get_folder_items_rsp, session); +} + +static gboolean change_path_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint32_t items = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->change_path) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + if (pdu->params_len < 5) { + err = -EPROTO; + goto done; + } + + items = get_be32(&pdu->params[1]); + +done: + player->cfm->change_path(session, err, items, player->user_data); + + return FALSE; +} + +int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid, + uint16_t counter) +{ + struct iovec iov; + uint8_t pdu[11]; + + put_be16(counter, &pdu[0]); + pdu[2] = direction; + put_be64(uid, &pdu[3]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing_req(session, AVRCP_CHANGE_PATH, + &iov, 1, change_path_rsp, session); +} + +static gboolean get_item_attributes_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint8_t number = 0; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + char *text[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_item_attributes) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + err = parse_items(pdu, &number, attrs, text); + +done: + player->cfm->get_item_attributes(session, err, number, attrs, text, + player->user_data); + + return FALSE; +} + +int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope, + uint64_t uid, uint16_t counter, uint8_t number, + uint32_t *attrs) +{ + struct iovec iov[2]; + uint8_t pdu[12]; + int i; + + pdu[0] = scope; + put_be64(uid, &pdu[1]); + put_be16(counter, &pdu[9]); + pdu[11] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + if (!number) + return avrcp_send_browsing_req(session, + AVRCP_GET_ITEM_ATTRIBUTES, + iov, 1, get_item_attributes_rsp, + session); + + if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + + for (i = 0; i < number; i++) { + if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST || + attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL) + return -EINVAL; + put_be32(attrs[i], &attrs[i]); + } + + iov[1].iov_base = attrs; + iov[1].iov_len = number * sizeof(*attrs); + + return avrcp_send_browsing_req(session, AVRCP_GET_ITEM_ATTRIBUTES, + iov, 2, get_item_attributes_rsp, + session); +} + +static gboolean play_item_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->play_item) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + +done: + player->cfm->play_item(session, err, player->user_data); + + return FALSE; +} + +int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid, + uint16_t counter) +{ + struct iovec iov; + uint8_t pdu[11]; + + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EINVAL; + + pdu[0] = scope; + put_be64(uid, &pdu[1]); + put_be16(counter, &pdu[9]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing_req(session, AVRCP_PLAY_ITEM, &iov, 1, + play_item_rsp, session); +} + +static gboolean search_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint16_t counter = 0; + uint32_t items = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->search) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + if (pdu->params_len < 7) { + err = -EPROTO; + goto done; + } + + counter = get_be16(&pdu->params[1]); + items = get_be32(&pdu->params[3]); + + err = 0; + +done: + player->cfm->search(session, err, counter, items, player->user_data); + + return FALSE; +} + +int avrcp_search(struct avrcp *session, const char *string) +{ + struct iovec iov[2]; + uint8_t pdu[4]; + size_t len; + + if (!string) + return -EINVAL; + + len = strnlen(string, UINT8_MAX); + + put_be16(AVRCP_CHARSET_UTF8, &pdu[0]); + put_be16(len, &pdu[2]); + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + iov[1].iov_base = (void *) string; + iov[1].iov_len = len; + + return avrcp_send_browsing_req(session, AVRCP_SEARCH, iov, 2, + search_rsp, session); +} + +static gboolean add_to_now_playing_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->add_to_now_playing) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + +done: + player->cfm->add_to_now_playing(session, err, player->user_data); + + return FALSE; +} + +int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid, + uint16_t counter) +{ + struct iovec iov; + uint8_t pdu[11]; + + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EINVAL; + + pdu[0] = scope; + put_be64(uid, &pdu[1]); + put_be16(counter, &pdu[9]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing_req(session, AVRCP_ADD_TO_NOW_PLAYING, + &iov, 1, add_to_now_playing_rsp, + session); +} + +int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *events) +{ + uint8_t pdu[2]; + struct iovec iov[2]; + + if (number > AVRCP_EVENT_LAST) + return -EINVAL; + + pdu[0] = CAP_EVENTS_SUPPORTED; + pdu[1] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + iov[1].iov_base = events; + iov[1].iov_len = number; + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES, + iov, 2); +} + +int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs) +{ + struct iovec iov[2]; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + if (!number) + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES, + iov, 1); + + iov[1].iov_base = attrs; + iov[1].iov_len = number; + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES, + iov, 2); +} + +int avrcp_get_player_attribute_text_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *attrs, const char **text) +{ + struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2]; + uint8_t val[AVRCP_ATTRIBUTE_LAST][4]; + int i; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + for (i = 0; i < number; i++) { + uint8_t len = 0; + + if (attrs[i] > AVRCP_ATTRIBUTE_LAST || + attrs[i] == AVRCP_ATTRIBUTE_ILEGAL) + return -EINVAL; + + if (text[i]) + len = strlen(text[i]); + + val[i][0] = attrs[i]; + put_be16(AVRCP_CHARSET_UTF8, &val[i][1]); + val[i][3] = len; + + iov[i + 1].iov_base = val[i]; + iov[i + 1].iov_len = sizeof(val[i]); + + iov[i + 2].iov_base = (void *) text[i]; + iov[i + 2].iov_len = len; + } + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + iov, 1 + i * 2); +} + +int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *values) +{ + struct iovec iov[2]; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + iov[1].iov_base = values; + iov[1].iov_len = number; + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES, + iov, 2); +} + +int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction, + uint32_t position, uint32_t duration, + uint8_t status) +{ + struct iovec iov; + uint8_t pdu[9]; + + put_be32(duration, &pdu[0]); + put_be32(position, &pdu[4]); + pdu[8] = status; + + iov.iov_base = &pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS, + &iov, 1); +} + +int avrcp_get_player_values_text_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *values, const char **text) +{ + struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2]; + uint8_t val[AVRCP_ATTRIBUTE_LAST][4]; + int i; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + for (i = 0; i < number; i++) { + uint8_t len = 0; + + if (text[i]) + len = strlen(text[i]); + + val[i][0] = values[i]; + put_be16(AVRCP_CHARSET_UTF8, &val[i][1]); + val[i][3] = len; + + iov[i + 1].iov_base = val[i]; + iov[i + 1].iov_len = sizeof(val[i]); + + iov[i + 2].iov_base = (void *) text[i]; + iov[i + 2].iov_len = len; + } + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT, + iov, 1 + i * 2); +} + +int avrcp_get_current_player_value_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *attrs, uint8_t *values) +{ + struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST]; + uint8_t val[AVRCP_ATTRIBUTE_LAST][2]; + int i; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + for (i = 0; i < number; i++) { + val[i][0] = attrs[i]; + val[i][1] = values[i]; + + iov[i + 1].iov_base = val[i]; + iov[i + 1].iov_len = sizeof(val[i]); + } + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE, + iov, 1 + i); +} + +int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction) +{ + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_SET_PLAYER_VALUE, NULL, 0); +} + +int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction, + uint8_t *params, size_t params_len) +{ + struct iovec iov; + + iov.iov_base = params; + iov.iov_len = params_len; + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES, + &iov, 1); +} + +int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction, + uint8_t code, uint8_t *params, + size_t params_len) +{ + struct iovec iov; + + iov.iov_base = params; + iov.iov_len = params_len; + + return avrcp_send(session, transaction, code, + AVC_SUBUNIT_PANEL, AVRCP_REGISTER_NOTIFICATION, + &iov, 1); +} + +int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction, + uint8_t volume) +{ + struct iovec iov; + + if (volume > 127) + return -EINVAL; + + iov.iov_base = &volume; + iov.iov_len = sizeof(volume); + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_SET_ABSOLUTE_VOLUME, + &iov, 1); +} + +int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction, + uint8_t status) +{ + struct iovec iov; + + iov.iov_base = &status; + iov.iov_len = sizeof(status); + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER, + &iov, 1); +} + +int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint8_t number, + uint8_t *type, uint16_t *len, + uint8_t **params) +{ + struct iovec iov[UINT8_MAX * 2 + 1]; + uint8_t pdu[5]; + uint8_t item[UINT8_MAX][3]; + int i; + + pdu[0] = AVRCP_STATUS_SUCCESS; + put_be16(counter, &pdu[1]); + put_be16(number, &pdu[3]); + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + for (i = 0; i < number; i++) { + item[i][0] = type[i]; + put_be16(len[i], &item[i][1]); + + iov[i * 2 + 1].iov_base = item[i]; + iov[i * 2 + 1].iov_len = sizeof(item[i]); + + iov[i * 2 + 2].iov_base = params[i]; + iov[i * 2 + 2].iov_len = len[i]; + } + + return avrcp_send_browsing(session, transaction, AVRCP_GET_FOLDER_ITEMS, + iov, number * 2 + 1); +} + +int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction, + uint32_t items) +{ + struct iovec iov; + uint8_t pdu[5]; + + pdu[0] = AVRCP_STATUS_SUCCESS; + put_be32(items, &pdu[1]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing(session, transaction, AVRCP_CHANGE_PATH, + &iov, 1); +} + +int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint32_t *attrs, + const char **text) +{ + struct iovec iov[AVRCP_MEDIA_ATTRIBUTE_LAST * 2 + 1]; + uint8_t val[AVRCP_MEDIA_ATTRIBUTE_LAST][8]; + uint8_t pdu[2]; + int i; + + if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + + pdu[0] = AVRCP_STATUS_SUCCESS; + pdu[1] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + for (i = 0; i < number; i++) { + uint16_t len = 0; + + if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST || + attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL) + return -EINVAL; + + if (text[i]) + len = strlen(text[i]); + + put_be32(attrs[i], &val[i][0]); + put_be16(AVRCP_CHARSET_UTF8, &val[i][4]); + put_be16(len, &val[i][6]); + + iov[i + 1].iov_base = val[i]; + iov[i + 1].iov_len = sizeof(val[i]); + + iov[i + 2].iov_base = (void *) text[i]; + iov[i + 2].iov_len = len; + } + + return avrcp_send_browsing(session, transaction, + AVRCP_GET_ITEM_ATTRIBUTES, iov, + number * 2 + 1); +} + +int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction) +{ + struct iovec iov; + uint8_t pdu; + + pdu = AVRCP_STATUS_SUCCESS; + + iov.iov_base = &pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing(session, transaction, AVRCP_PLAY_ITEM, + &iov, 1); +} + +int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint32_t items) +{ + struct iovec iov; + uint8_t pdu[7]; + + pdu[0] = AVRCP_STATUS_SUCCESS; + put_be16(counter, &pdu[1]); + put_be32(items, &pdu[3]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing(session, transaction, AVRCP_SEARCH, + &iov, 1); +} + +int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction) +{ + struct iovec iov; + uint8_t pdu; + + pdu = AVRCP_STATUS_SUCCESS; + + iov.iov_base = &pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing(session, transaction, + AVRCP_ADD_TO_NOW_PLAYING, &iov, 1); +} + +int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op) +{ + uint8_t params[5]; + + if (!vendor) + return avctp_send_passthrough(session->conn, op, NULL, 0); + + hton24(params, vendor); + put_be16(op, ¶ms[3]); + + return avctp_send_passthrough(session->conn, AVC_VENDOR_UNIQUE, params, + sizeof(params)); +} diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h new file mode 100644 index 0000000..ee03958 --- /dev/null +++ b/android/avrcp-lib.h @@ -0,0 +1,336 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 + * + */ + +/* Control PDU ids */ +#define AVRCP_GET_CAPABILITIES 0x10 +#define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11 +#define AVRCP_LIST_PLAYER_VALUES 0x12 +#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13 +#define AVRCP_SET_PLAYER_VALUE 0x14 +#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15 +#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16 +#define AVRCP_DISPLAYABLE_CHARSET 0x17 +#define AVRCP_CT_BATTERY_STATUS 0x18 +#define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20 +#define AVRCP_GET_PLAY_STATUS 0x30 +#define AVRCP_REGISTER_NOTIFICATION 0x31 +#define AVRCP_REQUEST_CONTINUING 0x40 +#define AVRCP_ABORT_CONTINUING 0x41 +#define AVRCP_SET_ABSOLUTE_VOLUME 0x50 +#define AVRCP_SET_ADDRESSED_PLAYER 0x60 +#define AVRCP_SET_BROWSED_PLAYER 0x70 +#define AVRCP_GET_FOLDER_ITEMS 0x71 +#define AVRCP_CHANGE_PATH 0x72 +#define AVRCP_GET_ITEM_ATTRIBUTES 0x73 +#define AVRCP_PLAY_ITEM 0x74 +#define AVRCP_SEARCH 0x80 +#define AVRCP_ADD_TO_NOW_PLAYING 0x90 +#define AVRCP_GENERAL_REJECT 0xA0 + +/* Notification events */ +#define AVRCP_EVENT_STATUS_CHANGED 0x01 +#define AVRCP_EVENT_TRACK_CHANGED 0x02 +#define AVRCP_EVENT_TRACK_REACHED_END 0x03 +#define AVRCP_EVENT_TRACK_REACHED_START 0x04 +#define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05 +#define AVRCP_EVENT_SETTINGS_CHANGED 0x08 +#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a +#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b +#define AVRCP_EVENT_UIDS_CHANGED 0x0c +#define AVRCP_EVENT_VOLUME_CHANGED 0x0d +#define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED + +/* Status codes */ +#define AVRCP_STATUS_INVALID_COMMAND 0x00 +#define AVRCP_STATUS_INVALID_PARAM 0x01 +#define AVRCP_STATUS_PARAM_NOT_FOUND 0x02 +#define AVRCP_STATUS_INTERNAL_ERROR 0x03 +#define AVRCP_STATUS_SUCCESS 0x04 +#define AVRCP_STATUS_NOT_DIRECTORY 0x08 +#define AVRCP_STATUS_DOES_NOT_EXIST 0x09 +#define AVRCP_STATUS_INVALID_SCOPE 0x0a +#define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b +#define AVRCP_STATUS_INVALID_PLAYER_ID 0x11 +#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12 +#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS 0x15 +#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED 0x16 + +/* Capabilities for AVRCP_GET_CAPABILITIES pdu */ +#define CAP_COMPANY_ID 0x02 +#define CAP_EVENTS_SUPPORTED 0x03 + +/* Player Attributes */ +#define AVRCP_ATTRIBUTE_ILEGAL 0x00 +#define AVRCP_ATTRIBUTE_EQUALIZER 0x01 +#define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02 +#define AVRCP_ATTRIBUTE_SHUFFLE 0x03 +#define AVRCP_ATTRIBUTE_SCAN 0x04 +#define AVRCP_ATTRIBUTE_LAST AVRCP_ATTRIBUTE_SCAN + +/* equalizer values */ +#define AVRCP_EQUALIZER_OFF 0x01 +#define AVRCP_EQUALIZER_ON 0x02 + +/* repeat mode values */ +#define AVRCP_REPEAT_MODE_OFF 0x01 +#define AVRCP_REPEAT_MODE_SINGLE 0x02 +#define AVRCP_REPEAT_MODE_ALL 0x03 +#define AVRCP_REPEAT_MODE_GROUP 0x04 + +/* shuffle values */ +#define AVRCP_SHUFFLE_OFF 0x01 +#define AVRCP_SHUFFLE_ALL 0x02 +#define AVRCP_SHUFFLE_GROUP 0x03 + +/* scan values */ +#define AVRCP_SCAN_OFF 0x01 +#define AVRCP_SCAN_ALL 0x02 +#define AVRCP_SCAN_GROUP 0x03 + +/* media attributes */ +#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00 +#define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01 +#define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02 +#define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03 +#define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04 +#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05 +#define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06 +#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 +#define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_DURATION + +/* Media Scope */ +#define AVRCP_MEDIA_PLAYER_LIST 0x00 +#define AVRCP_MEDIA_PLAYER_VFS 0x01 +#define AVRCP_MEDIA_SEARCH 0x02 +#define AVRCP_MEDIA_NOW_PLAYING 0x03 + +/* Company IDs for vendor dependent commands */ +#define IEEEID_BTSIG 0x001958 + +struct avrcp; + +struct avrcp_control_ind { + int (*get_capabilities) (struct avrcp *session, uint8_t transaction, + void *user_data); + int (*list_attributes) (struct avrcp *session, uint8_t transaction, + void *user_data); + int (*get_attribute_text) (struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, + void *user_data); + int (*list_values) (struct avrcp *session, uint8_t transaction, + uint8_t attr, void *user_data); + int (*get_value_text) (struct avrcp *session, uint8_t transaction, + uint8_t attr, uint8_t number, + uint8_t *values, void *user_data); + int (*get_value) (struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, + void *user_data); + int (*set_value) (struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, + void *user_data); + int (*get_play_status) (struct avrcp *session, uint8_t transaction, + void *user_data); + int (*get_element_attributes) (struct avrcp *session, + uint8_t transaction, uint64_t uid, + uint8_t number, uint32_t *attrs, + void *user_data); + int (*register_notification) (struct avrcp *session, + uint8_t transaction, uint8_t event, + uint32_t interval, void *user_data); + int (*set_volume) (struct avrcp *session, uint8_t transaction, + uint8_t volume, void *user_data); + int (*set_addressed) (struct avrcp *session, uint8_t transaction, + uint16_t id, void *user_data); + int (*get_folder_items) (struct avrcp *session, uint8_t transaction, + uint8_t scope, uint32_t start, + uint32_t end, uint16_t number, + uint32_t *attrs, void *user_data); + int (*change_path) (struct avrcp *session, uint8_t transaction, + uint16_t counter, uint8_t direction, + uint64_t uid, void *user_data); + int (*get_item_attributes) (struct avrcp *session, uint8_t transaction, + uint8_t scope, uint64_t uid, + uint16_t counter, uint8_t number, + uint32_t *attrs, void *user_data); + int (*play_item) (struct avrcp *session, uint8_t transaction, + uint8_t scope, uint64_t uid, + uint16_t counter, void *user_data); + int (*search) (struct avrcp *session, uint8_t transaction, + const char *string, void *user_data); + int (*add_to_now_playing) (struct avrcp *session, uint8_t transaction, + uint8_t scope, uint64_t uid, + uint16_t counter, void *user_data); +}; + +struct avrcp_control_cfm { + void (*get_capabilities) (struct avrcp *session, int err, + uint8_t number, uint8_t *params, + void *user_data); + void (*list_attributes) (struct avrcp *session, int err, + uint8_t number, uint8_t *attrs, + void *user_data); + void (*get_attribute_text) (struct avrcp *session, int err, + uint8_t number, uint8_t *attrs, + char **text, void *user_data); + void (*list_values) (struct avrcp *session, int err, + uint8_t number, uint8_t *values, + void *user_data); + void (*get_value_text) (struct avrcp *session, int err, + uint8_t number, uint8_t *values, + char **text, void *user_data); + void (*get_value) (struct avrcp *session, int err, + uint8_t number, uint8_t *attrs, + uint8_t *values, void *user_data); + void (*set_value) (struct avrcp *session, int err, void *user_data); + void (*get_play_status) (struct avrcp *session, int err, + uint8_t status, uint32_t position, + uint32_t duration, void *user_data); + void (*get_element_attributes) (struct avrcp *session, int err, + uint8_t number, uint32_t *attrs, + char **text, void *user_data); + bool (*register_notification) (struct avrcp *session, int err, + uint8_t code, uint8_t event, + uint8_t *params, void *user_data); + void (*set_volume) (struct avrcp *session, int err, uint8_t volume, + void *user_data); + void (*set_addressed) (struct avrcp *session, int err, + void *user_data); + void (*set_browsed) (struct avrcp *session, int err, + uint16_t counter, uint32_t items, + char *path, void *user_data); + void (*get_folder_items) (struct avrcp *session, int err, + uint16_t counter, uint16_t number, + uint8_t *params, void *user_data); + void (*change_path) (struct avrcp *session, int err, + uint32_t items, void *user_data); + void (*get_item_attributes) (struct avrcp *session, int err, + uint8_t number, uint32_t *attrs, + char **text, void *user_data); + void (*play_item) (struct avrcp *session, int err, void *user_data); + void (*search) (struct avrcp *session, int err, uint16_t counter, + uint32_t items, void *user_data); + void (*add_to_now_playing) (struct avrcp *session, int err, + void *user_data); +}; + +struct avrcp_passthrough_handler { + uint8_t op; + bool (*func) (struct avrcp *session, bool pressed, void *user_data); +}; + +typedef void (*avrcp_destroy_cb_t) (void *user_data); + +struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version); +void avrcp_shutdown(struct avrcp *session); +void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb, + void *user_data); +int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu, + size_t omtu); + +void avrcp_register_player(struct avrcp *session, + const struct avrcp_control_ind *ind, + const struct avrcp_control_cfm *cfm, + void *user_data); +void avrcp_set_passthrough_handlers(struct avrcp *session, + const struct avrcp_passthrough_handler *handlers, + void *user_data); +int avrcp_init_uinput(struct avrcp *session, const char *name, + const char *address); +int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code, + uint8_t subunit, uint8_t pdu_id, + const struct iovec *iov, int iov_cnt); +int avrcp_get_capabilities(struct avrcp *session, uint8_t param); +int avrcp_register_notification(struct avrcp *session, uint8_t event, + uint32_t interval); +int avrcp_list_player_attributes(struct avrcp *session); +int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number, + uint8_t *attrs); +int avrcp_list_player_values(struct avrcp *session, uint8_t attr); +int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr, + uint8_t number, uint8_t *values); +int avrcp_set_player_value(struct avrcp *session, uint8_t number, + uint8_t *attrs, uint8_t *values); +int avrcp_get_current_player_value(struct avrcp *session, uint8_t number, + uint8_t *attrs); +int avrcp_get_play_status(struct avrcp *session); +int avrcp_set_volume(struct avrcp *session, uint8_t volume); +int avrcp_get_element_attributes(struct avrcp *session); +int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id); +int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id); +int avrcp_get_folder_items(struct avrcp *session, uint8_t scope, + uint32_t start, uint32_t end, uint8_t number, + uint32_t *attrs); +int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid, + uint16_t counter); +int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope, + uint64_t uid, uint16_t counter, uint8_t number, + uint32_t *attrs); +int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid, + uint16_t counter); +int avrcp_search(struct avrcp *session, const char *string); +int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid, + uint16_t counter); + +int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *events); +int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs); +int avrcp_get_player_attribute_text_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *attrs, const char **text); +int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *values); +int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction, + uint32_t position, uint32_t duration, + uint8_t status); +int avrcp_get_player_values_text_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *values, const char **text); +int avrcp_get_current_player_value_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *attrs, uint8_t *values); +int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction); +int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction, + uint8_t *params, size_t params_len); +int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction, + uint8_t code, uint8_t *params, + size_t params_len); +int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction, + uint8_t volume); +int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction, + uint8_t status); +int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint8_t number, + uint8_t *type, uint16_t *len, + uint8_t **params); +int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction, + uint32_t items); +int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint32_t *attrs, + const char **text); +int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction); +int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint32_t items); +int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction); + +int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op); diff --git a/android/avrcp.c b/android/avrcp.c new file mode 100644 index 0000000..caf9335 --- /dev/null +++ b/android/avrcp.c @@ -0,0 +1,1097 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include + +#include "btio/btio.h" +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/sdp-client.h" +#include "src/shared/util.h" +#include "src/log.h" + +#include "avctp.h" +#include "avrcp-lib.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "bluetooth.h" +#include "avrcp.h" +#include "utils.h" + +#define L2CAP_PSM_AVCTP 0x17 + +#define AVRCP_FEATURE_CATEGORY_1 0x0001 +#define AVRCP_FEATURE_CATEGORY_2 0x0002 +#define AVRCP_FEATURE_CATEGORY_3 0x0004 +#define AVRCP_FEATURE_CATEGORY_4 0x0008 + +static bdaddr_t adapter_addr; +static uint32_t record_id = 0; +static GSList *devices = NULL; +static GIOChannel *server = NULL; +static struct ipc *hal_ipc = NULL; + +struct avrcp_request { + struct avrcp_device *dev; + uint8_t pdu_id; + uint8_t event_id; + uint8_t transaction; +}; + +struct avrcp_device { + bdaddr_t dst; + uint16_t version; + uint16_t features; + struct avrcp *session; + GIOChannel *io; + GQueue *queue; +}; + +static struct avrcp_request *pop_request(uint8_t pdu_id, uint8_t event_id, + bool peek) +{ + GSList *l; + + for (l = devices; l; l = g_slist_next(l)) { + struct avrcp_device *dev = l->data; + GList *reqs = g_queue_peek_head_link(dev->queue); + int i; + + for (i = 0; reqs; reqs = g_list_next(reqs), i++) { + struct avrcp_request *req = reqs->data; + + if (req->pdu_id != pdu_id || req->event_id != event_id) + continue; + + if (!peek) + g_queue_pop_nth(dev->queue, i); + + return req; + } + } + + return NULL; +} + +static void handle_get_play_status(const void *buf, uint16_t len) +{ + const struct hal_cmd_avrcp_get_play_status *cmd = buf; + uint8_t status; + struct avrcp_request *req; + int ret; + + DBG(""); + + req = pop_request(AVRCP_GET_PLAY_STATUS, 0, false); + if (!req) { + status = HAL_STATUS_FAILED; + goto done; + } + + ret = avrcp_get_play_status_rsp(req->dev->session, req->transaction, + cmd->position, cmd->duration, + cmd->status); + if (ret < 0) { + status = HAL_STATUS_FAILED; + g_free(req); + goto done; + } + + status = HAL_STATUS_SUCCESS; + g_free(req); + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAY_STATUS, status); +} + +static void handle_list_player_attrs(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_LIST_PLAYER_ATTRS, HAL_STATUS_FAILED); +} + +static void handle_list_player_values(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_LIST_PLAYER_VALUES, HAL_STATUS_FAILED); +} + +static void handle_get_player_attrs(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_ATTRS, HAL_STATUS_FAILED); +} + +static void handle_get_player_attrs_text(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, HAL_STATUS_FAILED); +} + +static void handle_get_player_values_text(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, HAL_STATUS_FAILED); +} + +static size_t write_element_text(uint8_t id, uint8_t text_len, uint8_t *text, + uint8_t *pdu) +{ + uint16_t charset = 106; + size_t len = 0; + + put_be32(id, pdu); + pdu += 4; + len += 4; + + put_be16(charset, pdu); + pdu += 2; + len += 2; + + put_be16(text_len, pdu); + pdu += 2; + len += 2; + + memcpy(pdu, text, text_len); + len += text_len; + + return len; +} + +static void write_element_attrs(uint8_t *ptr, uint8_t number, uint8_t *pdu, + size_t *len) +{ + int i; + + *pdu = number; + pdu++; + *len += 1; + + for (i = 0; i < number; i++) { + struct hal_avrcp_player_setting_text *text = (void *) ptr; + size_t ret; + + ret = write_element_text(text->id, text->len, text->text, pdu); + + ptr += sizeof(*text) + text->len; + pdu += ret; + *len += ret; + } +} + +static void handle_get_element_attrs_text(const void *buf, uint16_t len) +{ + struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf; + uint8_t status; + struct avrcp_request *req; + uint8_t pdu[IPC_MTU]; + uint8_t *ptr; + size_t pdu_len; + int ret; + + DBG(""); + + req = pop_request(AVRCP_GET_ELEMENT_ATTRIBUTES, 0, false); + if (!req) { + status = HAL_STATUS_FAILED; + goto done; + } + + ptr = (uint8_t *) &cmd->values[0]; + pdu_len = 0; + write_element_attrs(ptr, cmd->number, pdu, &pdu_len); + + ret = avrcp_get_element_attrs_rsp(req->dev->session, req->transaction, + pdu, pdu_len); + if (ret < 0) { + status = HAL_STATUS_FAILED; + g_free(req); + goto done; + } + + status = HAL_STATUS_SUCCESS; + g_free(req); + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, status); +} + +static void handle_set_player_attrs_value(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, HAL_STATUS_FAILED); +} + +static void handle_register_notification(const void *buf, uint16_t len) +{ + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + uint8_t status; + struct avrcp_request *req; + uint8_t pdu[IPC_MTU]; + size_t pdu_len; + uint8_t code; + bool peek = false; + int ret; + + DBG(""); + + switch (cmd->type) { + case HAL_AVRCP_EVENT_TYPE_INTERIM: + code = AVC_CTYPE_INTERIM; + peek = true; + break; + case HAL_AVRCP_EVENT_TYPE_CHANGED: + code = AVC_CTYPE_CHANGED; + break; + default: + status = HAL_STATUS_FAILED; + goto done; + } + + req = pop_request(AVRCP_REGISTER_NOTIFICATION, cmd->event, peek); + if (!req) { + status = HAL_STATUS_FAILED; + goto done; + } + + pdu[0] = cmd->event; + pdu_len = 1; + + switch (cmd->event) { + case AVRCP_EVENT_STATUS_CHANGED: + case AVRCP_EVENT_TRACK_CHANGED: + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + memcpy(&pdu[1], cmd->data, cmd->len); + pdu_len += cmd->len; + break; + default: + status = HAL_STATUS_FAILED; + goto done; + } + + ret = avrcp_register_notification_rsp(req->dev->session, + req->transaction, code, + pdu, pdu_len); + if (ret < 0) { + status = HAL_STATUS_FAILED; + if (!peek) + g_free(req); + goto done; + } + + status = HAL_STATUS_SUCCESS; + if (!peek) + g_free(req); + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, status); +} + +static void handle_set_volume(const void *buf, uint16_t len) +{ + struct hal_cmd_avrcp_set_volume *cmd = (void *) buf; + struct avrcp_device *dev; + uint8_t status; + int ret; + + DBG(""); + + if (!devices) { + error("AVRCP: No device found to set volume"); + status = HAL_STATUS_FAILED; + goto done; + } + + /* + * Peek the first device since the HAL cannot really address a specific + * device it might mean there could only be one connected. + */ + dev = devices->data; + + ret = avrcp_set_volume(dev->session, cmd->value & 0x7f); + if (ret < 0) { + status = HAL_STATUS_FAILED; + goto done; + } + + status = HAL_STATUS_SUCCESS; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME, + status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_AVRCP_GET_PLAY_STATUS */ + { handle_get_play_status, false, + sizeof(struct hal_cmd_avrcp_get_play_status) }, + /* HAL_OP_AVRCP_LIST_PLAYER_ATTRS */ + { handle_list_player_attrs, true, + sizeof(struct hal_cmd_avrcp_list_player_attrs) }, + /* HAL_OP_AVRCP_LIST_PLAYER_VALUES */ + { handle_list_player_values, true, + sizeof(struct hal_cmd_avrcp_list_player_values) }, + /* HAL_OP_AVRCP_GET_PLAYER_ATTRS */ + { handle_get_player_attrs, true, + sizeof(struct hal_cmd_avrcp_get_player_attrs) }, + /* HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT */ + { handle_get_player_attrs_text, true, + sizeof(struct hal_cmd_avrcp_get_player_attrs_text) }, + /* HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT */ + { handle_get_player_values_text, true, + sizeof(struct hal_cmd_avrcp_get_player_values_text) }, + /* HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT */ + { handle_get_element_attrs_text, true, + sizeof(struct hal_cmd_avrcp_get_element_attrs_text) }, + /* HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE */ + { handle_set_player_attrs_value, true, + sizeof(struct hal_cmd_avrcp_set_player_attrs_value) }, + /* HAL_OP_AVRCP_REGISTER_NOTIFICATION */ + { handle_register_notification, true, + sizeof(struct hal_cmd_avrcp_register_notification) }, + /* HAL_OP_AVRCP_SET_VOLUME */ + { handle_set_volume, false, sizeof(struct hal_cmd_avrcp_set_volume) }, +}; + +static sdp_record_t *avrcp_record(void) +{ + 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_control, *proto_control[2]; + sdp_record_t *record; + sdp_data_t *psm, *version, *features; + uint16_t lp = L2CAP_PSM_AVCTP; + uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104; + uint16_t feat = (AVRCP_FEATURE_CATEGORY_1 | + AVRCP_FEATURE_CATEGORY_2 | + AVRCP_FEATURE_CATEGORY_3 | + AVRCP_FEATURE_CATEGORY_4); + + 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); + + /* Service Class ID List */ + sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &avrtg); + sdp_set_service_classes(record, svclass_id); + + /* Protocol Descriptor List */ + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto_control[0] = sdp_list_append(NULL, &l2cap); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto_control[0] = sdp_list_append(proto_control[0], psm); + apseq = sdp_list_append(NULL, proto_control[0]); + + sdp_uuid16_create(&avctp, AVCTP_UUID); + proto_control[1] = sdp_list_append(NULL, &avctp); + version = sdp_data_alloc(SDP_UINT16, &avctp_ver); + proto_control[1] = sdp_list_append(proto_control[1], version); + apseq = sdp_list_append(apseq, proto_control[1]); + + aproto_control = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto_control); + + /* Bluetooth Profile Descriptor List */ + sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); + profile[0].version = avrcp_ver; + pfseq = sdp_list_append(NULL, &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", NULL, NULL); + + sdp_data_free(psm); + sdp_data_free(version); + sdp_list_free(proto_control[0], NULL); + sdp_list_free(proto_control[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(aproto_control, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static void avrcp_device_free(void *data) +{ + struct avrcp_device *dev = data; + + if (dev->queue) { + g_queue_foreach(dev->queue, (GFunc) g_free, NULL); + g_queue_free(dev->queue); + } + + if (dev->session) + avrcp_shutdown(dev->session); + + if (dev->io) { + g_io_channel_shutdown(dev->io, FALSE, NULL); + g_io_channel_unref(dev->io); + } + + g_free(dev); +} + +static void avrcp_device_remove(struct avrcp_device *dev) +{ + devices = g_slist_remove(devices, dev); + avrcp_device_free(dev); +} + +static struct avrcp_device *avrcp_device_new(const bdaddr_t *dst) +{ + struct avrcp_device *dev; + + dev = g_new0(struct avrcp_device, 1); + bacpy(&dev->dst, dst); + devices = g_slist_prepend(devices, dev); + + return dev; +} + +static int device_cmp(gconstpointer s, gconstpointer user_data) +{ + const struct avrcp_device *dev = s; + const bdaddr_t *dst = user_data; + + return bacmp(&dev->dst, dst); +} + +static struct avrcp_device *avrcp_device_find(const bdaddr_t *dst) +{ + GSList *l; + + l = g_slist_find_custom(devices, dst, device_cmp); + if (!l) + return NULL; + + return l->data; +} + +static void disconnect_cb(void *data) +{ + struct avrcp_device *dev = data; + + DBG(""); + + dev->session = NULL; + + avrcp_device_remove(dev); +} + +static bool handle_fast_forward(struct avrcp *session, bool pressed, + void *user_data) +{ + struct hal_ev_avrcp_passthrough_cmd ev; + + DBG("pressed %s", pressed ? "true" : "false"); + + ev.id = AVC_FAST_FORWARD; + ev.state = pressed; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev); + + return true; +} + +static bool handle_rewind(struct avrcp *session, bool pressed, + void *user_data) +{ + struct hal_ev_avrcp_passthrough_cmd ev; + + DBG("pressed %s", pressed ? "true" : "false"); + + ev.id = AVC_REWIND; + ev.state = pressed; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev); + + return true; +} + +static const struct avrcp_passthrough_handler passthrough_handlers[] = { + { AVC_FAST_FORWARD, handle_fast_forward }, + { AVC_REWIND, handle_rewind }, + { }, +}; + +static int handle_get_capabilities_cmd(struct avrcp *session, + uint8_t transaction, void *user_data) +{ + uint8_t events[] = { AVRCP_EVENT_STATUS_CHANGED, + AVRCP_EVENT_TRACK_CHANGED, + AVRCP_EVENT_PLAYBACK_POS_CHANGED }; + + DBG(""); + + /* + * Android do not provide this info via HAL so the list most + * be hardcoded according to what RegisterNotification can + * actually handle + */ + avrcp_get_capabilities_rsp(session, transaction, sizeof(events), + events); + + return -EAGAIN; +} + +static void push_request(struct avrcp_device *dev, uint8_t pdu_id, + uint8_t event_id, uint8_t transaction) +{ + struct avrcp_request *req; + + req = g_new0(struct avrcp_request, 1); + req->dev = dev; + req->pdu_id = pdu_id; + req->event_id = event_id; + req->transaction = transaction; + + g_queue_push_tail(dev->queue, req); +} + +static int handle_get_play_status_cmd(struct avrcp *session, + uint8_t transaction, void *user_data) +{ + struct avrcp_device *dev = user_data; + + DBG(""); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_GET_PLAY_STATUS, 0, NULL); + + push_request(dev, AVRCP_GET_PLAY_STATUS, 0, transaction); + + return -EAGAIN; +} + +static int handle_get_element_attrs_cmd(struct avrcp *session, + uint8_t transaction, uint64_t uid, + uint8_t number, uint32_t *attrs, + void *user_data) +{ + struct avrcp_device *dev = user_data; + uint8_t buf[IPC_MTU]; + struct hal_ev_avrcp_get_element_attrs *ev = (void *) buf; + int i; + + DBG(""); + + ev->number = number; + /* Set everything in case of empty list */ + if (ev->number == 0) { + for (i = 0; i < HAL_AVRCP_MEDIA_ATTR_DURATION; i++) { + /* Skip 0x00 as the attributes start with 0x01 */ + ev->attrs[i] = i + 1; + } + ev->number = i; + goto done; + } + + for (i = 0; i < number; i++) + ev->attrs[i] = attrs[i]; + +done: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_GET_ELEMENT_ATTRS, + sizeof(*ev) + ev->number, ev); + + push_request(dev, AVRCP_GET_ELEMENT_ATTRIBUTES, 0, transaction); + + return -EAGAIN; + +} + +static int handle_register_notification_cmd(struct avrcp *session, + uint8_t transaction, + uint8_t event, + uint32_t interval, + void *user_data) +{ + struct avrcp_device *dev = user_data; + struct hal_ev_avrcp_register_notification ev; + + DBG(""); + + /* TODO: Add any missing events supported by Android */ + switch (event) { + case AVRCP_EVENT_STATUS_CHANGED: + case AVRCP_EVENT_TRACK_CHANGED: + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + break; + default: + return -EINVAL; + } + + ev.event = event; + ev.param = interval; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_REGISTER_NOTIFICATION, + sizeof(ev), &ev); + + push_request(dev, AVRCP_REGISTER_NOTIFICATION, event, transaction); + + return -EAGAIN; +} + +static const struct avrcp_control_ind control_ind = { + .get_capabilities = handle_get_capabilities_cmd, + .get_play_status = handle_get_play_status_cmd, + .get_element_attributes = handle_get_element_attrs_cmd, + .register_notification = handle_register_notification_cmd, +}; + +static bool handle_register_notification_rsp(struct avrcp *session, int err, + uint8_t code, uint8_t event, + uint8_t *params, + void *user_data) +{ + struct avrcp_device *dev = user_data; + struct hal_ev_avrcp_volume_changed ev; + + if (err < 0) { + error("AVRCP: %s", strerror(-err)); + return false; + } + + if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) + return false; + + if (event != AVRCP_EVENT_VOLUME_CHANGED) + return false; + + ev.type = code; + ev.volume = params[0] & 0x7f; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_VOLUME_CHANGED, + sizeof(ev), &ev); + + if (code == AVC_CTYPE_INTERIM) + return true; + + avrcp_register_notification(dev->session, event, 0); + return false; +} + +static void handle_get_capabilities_rsp(struct avrcp *session, int err, + uint8_t number, uint8_t *events, + void *user_data) +{ + struct avrcp_device *dev = user_data; + int i; + + if (err < 0) { + error("AVRCP: %s", strerror(-err)); + return; + } + + for (i = 0; i < number; i++) { + if (events[i] != AVRCP_EVENT_VOLUME_CHANGED) + continue; + + avrcp_register_notification(dev->session, events[i], 0); + break; + } + + return; +} + +static void handle_set_volume_rsp(struct avrcp *session, int err, + uint8_t value, void *user_data) +{ + struct hal_ev_avrcp_volume_changed ev; + + if (err < 0) { + ev.volume = 0; + ev.type = AVC_CTYPE_REJECTED; + goto done; + } + + ev.volume = value; + ev.type = AVC_CTYPE_ACCEPTED; + +done: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_VOLUME_CHANGED, + sizeof(ev), &ev); +} + +static const struct avrcp_control_cfm control_cfm = { + .get_capabilities = handle_get_capabilities_rsp, + .register_notification = handle_register_notification_rsp, + .set_volume = handle_set_volume_rsp, +}; + +static int avrcp_device_add_session(struct avrcp_device *dev, int fd, + uint16_t imtu, uint16_t omtu) +{ + struct hal_ev_avrcp_remote_features ev; + char address[18]; + + dev->session = avrcp_new(fd, imtu, omtu, dev->version); + if (!dev->session) + return -EINVAL; + + avrcp_set_destroy_cb(dev->session, disconnect_cb, dev); + avrcp_set_passthrough_handlers(dev->session, passthrough_handlers, + dev); + avrcp_register_player(dev->session, &control_ind, &control_cfm, dev); + + dev->queue = g_queue_new(); + + ba2str(&dev->dst, address); + + /* FIXME: get the real name of the device */ + avrcp_init_uinput(dev->session, "bluetooth", address); + + bdaddr2android(&dev->dst, ev.bdaddr); + ev.features = HAL_AVRCP_FEATURE_NONE; + + DBG("version 0x%02x", dev->version); + + if (dev->version < 0x0103) + goto done; + + ev.features |= HAL_AVRCP_FEATURE_METADATA; + + if (dev->version < 0x0104) + goto done; + + ev.features |= HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME; + + avrcp_get_capabilities(dev->session, CAP_EVENTS_SUPPORTED); + +done: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_REMOTE_FEATURES, + sizeof(ev), &ev); + + return 0; +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + struct avrcp_device *dev = user_data; + uint16_t imtu, omtu; + char address[18]; + GError *gerr = NULL; + int fd; + + if (err) { + error("%s", err->message); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_DEST, address, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + fd = g_io_channel_unix_get_fd(chan); + if (avrcp_device_add_session(dev, fd, imtu, omtu) < 0) { + avrcp_device_free(dev); + return; + } + + g_io_channel_set_close_on_unref(chan, FALSE); + + if (dev->io) { + g_io_channel_unref(dev->io); + dev->io = NULL; + } + + DBG("%s connected", address); +} + +static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb) +{ + GError *err = NULL; + + dev->io = bt_io_connect(cb, dev, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, L2CAP_PSM_AVCTP, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + return false; + } + + return true; +} + +static void search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct avrcp_device *dev = data; + sdp_list_t *list; + + DBG(""); + + if (err < 0) { + error("Unable to get AV_REMOTE_SVCLASS_ID SDP record: %s", + strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + error("No AVRCP records found"); + goto fail; + } + + for (list = recs; list; list = list->next) { + sdp_record_t *rec = list->data; + sdp_list_t *l; + sdp_profile_desc_t *desc; + int features; + + if (sdp_get_profile_descs(rec, &l) < 0) + continue; + + desc = l->data; + dev->version = desc->version; + + if (sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, + &features) == 0) + dev->features = features; + + sdp_list_free(l, free); + break; + } + + if (dev->io) { + GError *gerr = NULL; + if (!bt_io_accept(dev->io, connect_cb, dev, NULL, &gerr)) { + error("bt_io_accept: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + return; + } + + if (!avrcp_device_connect(dev, connect_cb)) { + error("Unable to connect to AVRCP"); + goto fail; + } + + return; + +fail: + avrcp_device_remove(dev); +} + +static int avrcp_device_search(struct avrcp_device *dev) +{ + uuid_t uuid; + + sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID); + + return bt_search_service(&adapter_addr, &dev->dst, &uuid, search_cb, + dev, NULL, 0); +} + +static void confirm_cb(GIOChannel *chan, gpointer data) +{ + struct avrcp_device *dev; + char address[18]; + bdaddr_t dst; + GError *err = NULL; + + bt_io_get(chan, &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); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + DBG("incoming connect from %s", address); + + dev = avrcp_device_find(&dst); + if (dev && dev->session) { + error("AVRCP: Refusing unexpected connect"); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + dev = avrcp_device_new(&dst); + if (avrcp_device_search(dev) < 0) { + error("AVRCP: Failed to search SDP details"); + avrcp_device_free(dev); + g_io_channel_shutdown(chan, TRUE, NULL); + } + + dev->io = g_io_channel_ref(chan); +} + +bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + GError *err = NULL; + sdp_record_t *rec; + + DBG(""); + + bacpy(&adapter_addr, addr); + + server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, L2CAP_PSM_AVCTP, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (!server) { + error("Failed to listen on AVDTP channel: %s", err->message); + g_error_free(err); + return false; + } + + rec = avrcp_record(); + if (!rec) { + error("Failed to allocate AVRCP record"); + goto fail; + } + + if (bt_adapter_add_record(rec, 0) < 0) { + error("Failed to register AVRCP record"); + sdp_record_free(rec); + goto fail; + } + record_id = rec->handle; + + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_AVRCP, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + return true; +fail: + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + + return false; +} + +void bt_avrcp_unregister(void) +{ + DBG(""); + + g_slist_free_full(devices, avrcp_device_free); + devices = NULL; + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_AVRCP); + hal_ipc = NULL; + + bt_adapter_remove_record(record_id); + record_id = 0; + + if (server) { + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + } +} + +void bt_avrcp_connect(const bdaddr_t *dst) +{ + struct avrcp_device *dev; + char addr[18]; + + DBG(""); + + if (avrcp_device_find(dst)) + return; + + dev = avrcp_device_new(dst); + if (avrcp_device_search(dev) < 0) { + error("AVRCP: Failed to search SDP details"); + avrcp_device_free(dev); + } + + ba2str(&dev->dst, addr); + DBG("connecting to %s", addr); +} + +void bt_avrcp_disconnect(const bdaddr_t *dst) +{ + struct avrcp_device *dev; + + DBG(""); + + dev = avrcp_device_find(dst); + if (!dev) + return; + + if (dev->session) { + avrcp_shutdown(dev->session); + return; + } + + avrcp_device_remove(dev); +} diff --git a/android/avrcp.h b/android/avrcp.h new file mode 100644 index 0000000..11e79b7 --- /dev/null +++ b/android/avrcp.h @@ -0,0 +1,28 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * 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 + * + */ + +bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_avrcp_unregister(void); + +void bt_avrcp_connect(const bdaddr_t *dst); +void bt_avrcp_disconnect(const bdaddr_t *dst); diff --git a/android/bite-rfcomm.txt b/android/bite-rfcomm.txt new file mode 100644 index 0000000..cf0e94a --- /dev/null +++ b/android/bite-rfcomm.txt @@ -0,0 +1,35 @@ + +BITE test results for RFCOMM + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TP/RFC/BV-01-C PASS +TP/RFC/BV-02-C PASS +TP/RFC/BV-03-C PASS +TP/RFC/BV-04-C PASS +TP/RFC/BV-05-C PASS +TP/RFC/BV-06-C PASS +TP/RFC/BV-07-C PASS +TP/RFC/BV-08-C PASS +TP/RFC/BV-09-C PASS +TP/RFC/BV-10-C PASS +TP/RFC/BV-11-C PASS +TP/RFC/BV-12-C PASS +TP/RFC/BV-13-C PASS +TP/RFC/BV-14-C PASS +TP/RFC/BV-15-C PASS +TP/RFC/BV-17-C PASS +TP/RFC/BV-19-C PASS +TP/RFC/BV-20-C PASS +TP/RFC/BV-21-C PASS +TP/RFC/BV-22-C PASS +TP/RFC/BV-23-C PASS +TP/RFC/BV-24-C PASS +------------------------------------------------------------------------------- diff --git a/android/bite-spp.txt b/android/bite-spp.txt new file mode 100644 index 0000000..1994b05 --- /dev/null +++ b/android/bite-spp.txt @@ -0,0 +1,19 @@ + +BITE test results for SPP + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TP/APP/BV-01-C PASS +TP/APP/BV-02-C PASS +TP/APP/BV-03-C PASS +TP/APP/BV-04-C PASS +TP/APP/BV-05-C PASS +TP/APP/BV-06-C PASS +------------------------------------------------------------------------------- diff --git a/android/bluetooth.c b/android/bluetooth.c index a3145cb..379d8ea 100644 --- a/android/bluetooth.c +++ b/android/bluetooth.c @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -27,45 +27,103 @@ #include #include +#include +#include +#include +#include +#include #include #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/mgmt.h" +#include "src/shared/util.h" #include "src/shared/mgmt.h" -#include "src/glib-helper.h" +#include "src/uuid-helper.h" #include "src/eir.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "src/sdpd.h" -#include "log.h" +#include "src/log.h" #include "hal-msg.h" +#include "ipc-common.h" #include "ipc.h" #include "utils.h" #include "bluetooth.h" +#define DEFAULT_ADAPTER_NAME "BlueZ for Android" + +#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode" + +#define SETTINGS_FILE ANDROID_STORAGEDIR"/settings" +#define DEVICES_FILE ANDROID_STORAGEDIR"/devices" +#define CACHE_FILE ANDROID_STORAGEDIR"/cache" + #define DEVICE_ID_SOURCE 0x0002 /* USB */ #define DEVICE_ID_VENDOR 0x1d6b /* Linux Foundation */ #define DEVICE_ID_PRODUCT 0x0247 /* BlueZ for Android */ +#define ADAPTER_MAJOR_CLASS 0x02 /* Phone */ +#define ADAPTER_MINOR_CLASS 0x03 /* Smartphone */ + /* Default to DisplayYesNo */ #define DEFAULT_IO_CAPABILITY 0x01 + /* Default discoverable timeout 120sec as in Android */ #define DEFAULT_DISCOVERABLE_TIMEOUT 120 -#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \ - + (sizeof(struct hal_property))) +#define DEVICES_CACHE_MAX 300 -static uint16_t option_index = MGMT_INDEX_NONE; +#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \ + + sizeof(struct hal_property)) #define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \ + sizeof(struct hal_property)) -/* This list contains addresses which are asked for records */ -static GSList *browse_reqs; -static struct mgmt *mgmt_if = NULL; +#define SCAN_TYPE_NONE 0 +#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR) +#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM)) +#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE) + +#define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC) + +struct device { + bdaddr_t bdaddr; + uint8_t bdaddr_type; + + bool le; + bool bredr; + + bool pairing; + + bool bredr_paired; + bool bredr_bonded; + bool le_paired; + bool le_bonded; + + char *name; + char *friendly_name; + + uint32_t class; + int32_t rssi; + + time_t bredr_seen; + time_t le_seen; + + GSList *uuids; + + bool found; /* if device is found in current discovery session */ + unsigned int confirm_id; /* mgtm command id if command pending */ +}; + +struct browse_req { + bdaddr_t bdaddr; + GSList *uuids; + int search_uuid; + int reconnect_attempt; +}; static struct { uint16_t index; @@ -76,8 +134,10 @@ static struct { char *name; uint32_t current_settings; + uint32_t supported_settings; - bool discovering; + uint8_t cur_discovery_type; + uint8_t exp_discovery_type; uint32_t discoverable_timeout; GSList *uuids; @@ -86,24 +146,13 @@ static struct { .dev_class = 0, .name = NULL, .current_settings = 0, - .discovering = false, + .supported_settings = 0, + .cur_discovery_type = SCAN_TYPE_NONE, + .exp_discovery_type = SCAN_TYPE_NONE, .discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT, .uuids = NULL, }; -struct device { - bdaddr_t bdaddr; - int bond_state; - char *name; -}; - -struct browse_req { - bdaddr_t bdaddr; - GSList *uuids; - int search_uuid; - int reconnect_attempt; -}; - static const uint16_t uuid_list[] = { L2CAP_UUID, PNP_INFO_SVCLASS_ID, @@ -111,27 +160,299 @@ static const uint16_t uuid_list[] = { 0 }; -static GSList *found_devices = NULL; -static GSList *devices = NULL; +static uint16_t option_index = MGMT_INDEX_NONE; +static struct mgmt *mgmt_if = NULL; + +static GSList *bonded_devices = NULL; +static GSList *cached_devices = NULL; -static void adapter_name_changed(const uint8_t *name) +static bt_le_device_found gatt_device_found_cb = NULL; +static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL; + +/* This list contains addresses which are asked for records */ +static GSList *browse_reqs; + +static struct ipc *hal_ipc = NULL; + +static void mgmt_debug(const char *str, void *user_data) { - struct hal_ev_adapter_props_changed *ev; - size_t len = strlen((const char *) name); - uint8_t buf[BASELEN_PROP_CHANGED + len]; + const char *prefix = user_data; + info("%s%s", prefix, str); +} - memset(buf, 0, sizeof(buf)); - ev = (void *) buf; +static void store_adapter_config(void) +{ + GKeyFile *key_file; + gsize length = 0; + char addr[18]; + char *data; + + key_file = g_key_file_new(); + + g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL); + + ba2str(&adapter.bdaddr, addr); + + g_key_file_set_string(key_file, "General", "Address", addr); + g_key_file_set_string(key_file, "General", "Name", adapter.name); + g_key_file_set_integer(key_file, "General", "DiscoverableTimeout", + adapter.discoverable_timeout); + + data = g_key_file_to_data(key_file, &length, NULL); + + g_file_set_contents(SETTINGS_FILE, data, length, NULL); + + g_free(data); + g_key_file_free(key_file); +} + +static void load_adapter_config(void) +{ + GError *gerr = NULL; + GKeyFile *key_file; + char *str; + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL); + + str = g_key_file_get_string(key_file, "General", "Address", NULL); + if (!str) { + g_key_file_free(key_file); + return; + } + + str2ba(str, &adapter.bdaddr); + g_free(str); + + adapter.name = g_key_file_get_string(key_file, "General", "Name", NULL); + + adapter.discoverable_timeout = g_key_file_get_integer(key_file, + "General", "DiscoverableTimeout", &gerr); + if (gerr) { + adapter.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT; + g_error_free(gerr); + gerr = NULL; + } + + g_key_file_free(key_file); +} + +static void store_device_info(struct device *dev, const char *path) +{ + GKeyFile *key_file; + char addr[18]; + gsize length = 0; + char **uuids = NULL; + char *str; + + ba2str(&dev->bdaddr, addr); + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, path, 0, NULL); + + g_key_file_set_boolean(key_file, addr, "BREDR", dev->bredr); + + if (dev->le) + g_key_file_set_integer(key_file, addr, "AddressType", + dev->bdaddr_type); + + g_key_file_set_string(key_file, addr, "Name", dev->name); + + if (dev->friendly_name) + g_key_file_set_string(key_file, addr, "FriendlyName", + dev->friendly_name); + else + g_key_file_remove_key(key_file, addr, "FriendlyName", NULL); + + if (dev->class) + g_key_file_set_integer(key_file, addr, "Class", dev->class); + else + g_key_file_remove_key(key_file, addr, "Class", NULL); + + if (dev->bredr_seen > dev->le_seen) + g_key_file_set_integer(key_file, addr, "Timestamp", + dev->bredr_seen); + else + g_key_file_set_integer(key_file, addr, "Timestamp", + dev->le_seen); + + if (dev->uuids) { + GSList *l; + int i; + + uuids = g_new0(char *, g_slist_length(dev->uuids) + 1); + + for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) { + int j; + uint8_t *u = l->data; + char *uuid_str = g_malloc0(33); + + for (j = 0; j < 16; j++) + sprintf(uuid_str + (j * 2), "%2.2X", u[j]); + + uuids[i] = uuid_str; + } + + g_key_file_set_string_list(key_file, addr, "Services", + (const char **)uuids, i); + } else { + g_key_file_remove_key(key_file, addr, "Services", NULL); + } + + str = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(path, str, length, NULL); + g_free(str); + + g_key_file_free(key_file); + g_strfreev(uuids); +} + +static void remove_device_info(struct device *dev, const char *path) +{ + GKeyFile *key_file; + gsize length = 0; + char addr[18]; + char *str; + + ba2str(&dev->bdaddr, addr); + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, path, 0, NULL); + + g_key_file_remove_group(key_file, addr, NULL); + + str = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(path, str, length, NULL); + g_free(str); + + g_key_file_free(key_file); +} + +static int device_match(gconstpointer a, gconstpointer b) +{ + const struct device *dev = a; + const bdaddr_t *bdaddr = b; + + return bacmp(&dev->bdaddr, bdaddr); +} + +static struct device *find_device(const bdaddr_t *bdaddr) +{ + GSList *l; + + l = g_slist_find_custom(bonded_devices, bdaddr, device_match); + if (l) + return l->data; + + l = g_slist_find_custom(cached_devices, bdaddr, device_match); + if (l) + return l->data; + + return NULL; +} + +static void free_device(struct device *dev) +{ + if (dev->confirm_id) + mgmt_cancel(mgmt_if, dev->confirm_id); + + g_free(dev->name); + g_free(dev->friendly_name); + g_slist_free_full(dev->uuids, g_free); + g_free(dev); +} + +static void cache_device(struct device *new_dev) +{ + struct device *dev; + GSList *l; + + l = g_slist_find(cached_devices, new_dev); + if (l) { + cached_devices = g_slist_remove(cached_devices, new_dev); + goto cache; + } + + if (g_slist_length(cached_devices) < DEVICES_CACHE_MAX) + goto cache; + + l = g_slist_last(cached_devices); + dev = l->data; + + cached_devices = g_slist_remove(cached_devices, dev); + remove_device_info(dev, CACHE_FILE); + free_device(dev); + +cache: + cached_devices = g_slist_prepend(cached_devices, new_dev); + store_device_info(new_dev, CACHE_FILE); +} + +static struct device *create_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type) +{ + struct device *dev; + char addr[18]; + + ba2str(bdaddr, addr); + DBG("%s", addr); + + dev = g_new0(struct device, 1); + + bacpy(&dev->bdaddr, bdaddr); + + if (bdaddr_type == BDADDR_BREDR) { + dev->bredr = true; + dev->bredr_seen = time(NULL); + } else { + dev->le = true; + dev->bdaddr_type = bdaddr_type; + dev->le_seen = time(NULL); + } + + /* + * Use address for name, will be change if one is present + * eg. in EIR or set by set_property. + */ + dev->name = g_strdup(addr); + + return dev; +} + +static struct device *get_device(const bdaddr_t *bdaddr, uint8_t type) +{ + struct device *dev; + + dev = find_device(bdaddr); + if (dev) + return dev; + + dev = create_device(bdaddr, type); + + cache_device(dev); + + return dev; +} + +static void send_adapter_property(uint8_t type, uint16_t len, const void *val) +{ + uint8_t buf[BASELEN_PROP_CHANGED + len]; + struct hal_ev_adapter_props_changed *ev = (void *) buf; - ev->num_props = 1; ev->status = HAL_STATUS_SUCCESS; - ev->props[0].type = HAL_PROP_ADAPTER_NAME; - /* Android expects value without NULL terminator */ + ev->num_props = 1; + ev->props[0].type = type; ev->props[0].len = len; - memcpy(ev->props->val, name, len); + memcpy(ev->props[0].val, val, len); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf); +} - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, - sizeof(buf), ev); +static void adapter_name_changed(const uint8_t *name) +{ + /* Android expects string value without NULL terminator */ + send_adapter_property(HAL_PROP_ADAPTER_NAME, + strlen((const char *) name), name); } static void adapter_set_name(const uint8_t *name) @@ -144,6 +465,8 @@ static void adapter_set_name(const uint8_t *name) g_free(adapter.name); adapter.name = g_strdup((const char *) name); + store_adapter_config(); + adapter_name_changed(name); } @@ -171,8 +494,8 @@ static void powered_changed(void) DBG("%u", ev.state); - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_STATE_CHANGED, - sizeof(ev), &ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev); } static uint8_t settings2scan_mode(void) @@ -193,39 +516,19 @@ static uint8_t settings2scan_mode(void) static void scan_mode_changed(void) { - uint8_t buf[BASELEN_PROP_CHANGED + 1]; - struct hal_ev_adapter_props_changed *ev = (void *) buf; - uint8_t *mode; - - ev->num_props = 1; - ev->status = HAL_STATUS_SUCCESS; - - ev->props[0].type = HAL_PROP_ADAPTER_SCAN_MODE; - ev->props[0].len = 1; + uint8_t mode; - mode = ev->props[0].val; - *mode = settings2scan_mode(); + mode = settings2scan_mode(); - DBG("mode %u", *mode); + DBG("mode %u", mode); - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, - sizeof(buf), buf); + send_adapter_property(HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode); } static void adapter_class_changed(void) { - uint8_t buf[BASELEN_PROP_CHANGED + sizeof(uint32_t)]; - struct hal_ev_adapter_props_changed *ev = (void *) buf; - - ev->num_props = 1; - ev->status = HAL_STATUS_SUCCESS; - - ev->props[0].type = HAL_PROP_ADAPTER_CLASS; - ev->props[0].len = sizeof(uint32_t); - memcpy(ev->props->val, &adapter.dev_class, sizeof(uint32_t)); - - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, - sizeof(buf), buf); + send_adapter_property(HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class), + &adapter.dev_class); } static void settings_changed(uint32_t settings) @@ -242,7 +545,6 @@ static void settings_changed(uint32_t settings) if (changed_mask & MGMT_SETTING_POWERED) powered_changed(); - scan_mode_mask = MGMT_SETTING_CONNECTABLE | MGMT_SETTING_DISCOVERABLE; @@ -265,7 +567,7 @@ static void new_settings_callback(uint16_t index, uint16_t length, return; } - settings = bt_get_le32(param); + settings = get_le32(param); DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings, settings); @@ -304,16 +606,36 @@ static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length, static void store_link_key(const bdaddr_t *dst, const uint8_t *key, uint8_t type, uint8_t pin_length) { - /* TODO store link key */ + GKeyFile *key_file; + char key_str[33]; + gsize length = 0; + char addr[18]; + char *data; + int i; -} + key_file = g_key_file_new(); -static int bdaddr_cmp(gconstpointer a, gconstpointer b) -{ - const bdaddr_t *bda = a; - const bdaddr_t *bdb = b; + if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { + g_key_file_free(key_file); + return; + } + + ba2str(dst, addr); - return bacmp(bdb, bda); + DBG("%s type %u pin_len %u", addr, type, pin_length); + + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", key[i]); + + g_key_file_set_string(key_file, addr, "LinkKey", key_str); + g_key_file_set_integer(key_file, addr, "LinkKeyType", type); + g_key_file_set_integer(key_file, addr, "LinkKeyPinLength", pin_length); + + data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(DEVICES_FILE, data, length, NULL); + g_free(data); + + g_key_file_free(key_file); } static void send_bond_state_change(const bdaddr_t *addr, uint8_t status, @@ -325,91 +647,159 @@ static void send_bond_state_change(const bdaddr_t *addr, uint8_t status, ev.state = state; bdaddr2android(addr, ev.bdaddr); - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_BOND_STATE_CHANGED, - sizeof(ev), &ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev); } -static void cache_device_name(const bdaddr_t *addr, const char *name) +static void update_bredr_state(struct device *dev, bool pairing, bool paired, + bool bonded) { - struct device *dev = NULL; - GSList *l; - - l = g_slist_find_custom(devices, addr, bdaddr_cmp); - if (l) - dev = l->data; + if (pairing == dev->pairing && paired == dev->bredr_paired && + bonded == dev->bredr_bonded) + return; - if (!dev) { - dev = g_new0(struct device, 1); - bacpy(&dev->bdaddr, addr); - dev->bond_state = HAL_BOND_STATE_NONE; - devices = g_slist_prepend(devices, dev); + /* avoid unpairing device on incoming pairing request */ + if (pairing && dev->bredr_paired) + goto done; + + /* avoid unpairing device if pairing failed */ + if (!pairing && !paired && dev->pairing && dev->bredr_paired) + goto done; + + if (paired && !dev->le_paired) { + cached_devices = g_slist_remove(cached_devices, dev); + bonded_devices = g_slist_prepend(bonded_devices, dev); + remove_device_info(dev, CACHE_FILE); + store_device_info(dev, DEVICES_FILE); + } else if (!paired && !dev->le_paired) { + bonded_devices = g_slist_remove(bonded_devices, dev); + remove_device_info(dev, DEVICES_FILE); + cache_device(dev); } - if (!g_strcmp0(dev->name, name)) + dev->bredr_paired = paired; + dev->bredr_bonded = bonded; + +done: + dev->pairing = pairing; +} + +static void update_le_state(struct device *dev, bool pairing, bool paired, + bool bonded) +{ + if (pairing == dev->pairing && paired == dev->le_paired && + bonded == dev->le_bonded) return; - g_free(dev->name); - dev->name = g_strdup(name); - /*TODO: Do some real caching here */ + /* avoid unpairing device on incoming pairing request */ + if (pairing && dev->le_paired) + goto done; + + /* avoid unpairing device if pairing failed */ + if (!pairing && !paired && dev->pairing && dev->le_paired) + goto done; + + if (paired && !dev->bredr_paired) { + cached_devices = g_slist_remove(cached_devices, dev); + bonded_devices = g_slist_prepend(bonded_devices, dev); + remove_device_info(dev, CACHE_FILE); + store_device_info(dev, DEVICES_FILE); + } else if (!paired && !dev->bredr_paired) { + bonded_devices = g_slist_remove(bonded_devices, dev); + remove_device_info(dev, DEVICES_FILE); + cache_device(dev); + } + + dev->le_paired = paired; + dev->le_bonded = bonded; + +done: + dev->pairing = pairing; } -static void set_device_bond_state(const bdaddr_t *addr, uint8_t status, - int state) { +static uint8_t device_bond_state(struct device *dev) +{ + if (dev->pairing) + return HAL_BOND_STATE_BONDING; - struct device *dev = NULL; - GSList *l; + /* + * We are checking for paired here instead of bonded as HAL API is + * using BOND state also if there was no bonding pairing. + */ + if (dev->bredr_paired || dev->le_paired) + return HAL_BOND_STATE_BONDED; - l = g_slist_find_custom(devices, addr, bdaddr_cmp); - if (l) - dev = l->data; + return HAL_BOND_STATE_NONE; +} - if (!dev) { - dev = g_new0(struct device, 1); - bacpy(&dev->bdaddr, addr); - dev->bond_state = HAL_BOND_STATE_NONE; - devices = g_slist_prepend(devices, dev); - } +static void update_device_state(struct device *dev, uint8_t addr_type, + uint8_t status, bool pairing, bool paired, + bool bonded) +{ + uint8_t old_bond, new_bond; - if (dev->bond_state != state) { - dev->bond_state = state; - send_bond_state_change(&dev->bdaddr, status, state); - } + old_bond = device_bond_state(dev); + + if (addr_type == BDADDR_BREDR) + update_bredr_state(dev, pairing, paired, bonded); + else + update_le_state(dev, pairing, paired, bonded); + + new_bond = device_bond_state(dev); + + if (old_bond != new_bond) + send_bond_state_change(&dev->bdaddr, status, new_bond); } -static void browse_req_free(struct browse_req *req) +static void send_device_property(const bdaddr_t *bdaddr, uint8_t type, + uint16_t len, const void *val) { - g_slist_free_full(req->uuids, g_free); - g_free(req); + uint8_t buf[BASELEN_REMOTE_DEV_PROP + len]; + struct hal_ev_remote_device_props *ev = (void *) buf; + + ev->status = HAL_STATUS_SUCCESS; + bdaddr2android(bdaddr, ev->bdaddr); + ev->num_props = 1; + ev->props[0].type = type; + ev->props[0].len = len; + memcpy(ev->props[0].val, val, len); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf); } -static void fill_uuids(GSList *list, void *buf) +static void send_device_uuids_notif(struct device *dev) { - for (; list; list = g_slist_next(list)) { - memcpy(buf, list->data, sizeof(uint128_t)); - buf += sizeof(uint128_t); + uint8_t buf[sizeof(uint128_t) * g_slist_length(dev->uuids)]; + uint8_t *ptr = buf; + GSList *l; + + for (l = dev->uuids; l; l = g_slist_next(l)) { + memcpy(ptr, l->data, sizeof(uint128_t)); + ptr += sizeof(uint128_t); } + + send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_UUIDS, sizeof(buf), + buf); } -static void remote_uuids_callback(struct browse_req *req) +static void set_device_uuids(struct device *dev, GSList *uuids) { - struct hal_ev_remote_device_props *ev; - int len; - - len = sizeof(*ev) + sizeof(struct hal_property) + (sizeof(uint128_t) * - g_slist_length(req->uuids)); - ev = g_malloc(len); + g_slist_free_full(dev->uuids, g_free); + dev->uuids = uuids; - ev->status = HAL_STATUS_SUCCESS; - bdaddr2android(&req->bdaddr, &ev->bdaddr); - ev->num_props = 1; - ev->props[0].type = HAL_PROP_DEVICE_UUIDS; - ev->props[0].len = sizeof(uint128_t) * g_slist_length(req->uuids); - fill_uuids(req->uuids, ev->props[0].val); + if (dev->le_paired || dev->bredr_paired) + store_device_info(dev, DEVICES_FILE); + else + store_device_info(dev, CACHE_FILE); - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS, - len, ev); + send_device_uuids_notif(dev); +} - g_free(ev); +static void browse_req_free(struct browse_req *req) +{ + g_slist_free_full(req->uuids, g_free); + g_free(req); } static int uuid_128_cmp(gconstpointer a, gconstpointer b) @@ -469,10 +859,13 @@ static void update_records(struct browse_req *req, sdp_list_t *recs) static void browse_cb(sdp_list_t *recs, int err, gpointer user_data) { struct browse_req *req = user_data; + struct device *dev; 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 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) { if (err == -ECONNRESET && req->reconnect_attempt < 1) { req->search_uuid--; @@ -488,12 +881,16 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data) if (uuid_list[req->search_uuid]) { sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid, - browse_cb, user_data, NULL); + browse_cb, user_data, NULL, 0); return; } done: - remote_uuids_callback(req); + dev = find_device(&req->bdaddr); + if (dev) { + set_device_uuids(dev, req->uuids); + req->uuids = NULL; + } browse_reqs = g_slist_remove(browse_reqs, req); browse_req_free(req); @@ -513,16 +910,16 @@ static uint8_t browse_remote_sdp(const bdaddr_t *addr) uuid_t uuid; if (g_slist_find_custom(browse_reqs, addr, req_cmp)) - return HAL_STATUS_DONE; + return HAL_STATUS_SUCCESS; req = g_new0(struct browse_req, 1); bacpy(&req->bdaddr, addr); sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); if (bt_search_service(&adapter.bdaddr, - &req->bdaddr, &uuid, browse_cb, req, NULL) < 0) { + &req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) { browse_req_free(req); - return false; + return HAL_STATUS_FAILED; } browse_reqs = g_slist_append(browse_reqs, req); @@ -535,6 +932,7 @@ static void new_link_key_callback(uint16_t index, uint16_t length, { const struct mgmt_ev_new_link_key *ev = param; const struct mgmt_addr_info *addr = &ev->key.addr; + struct device *dev; char dst[18]; if (length < sizeof(*ev)) { @@ -553,6 +951,13 @@ static void new_link_key_callback(uint16_t index, uint16_t length, return; } + dev = find_device(&ev->key.addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, + true, !!ev->store_hint); + if (ev->store_hint) { const struct mgmt_link_key_info *key = &ev->key; @@ -560,53 +965,15 @@ static void new_link_key_callback(uint16_t index, uint16_t length, key->pin_len); } - set_device_bond_state(&addr->bdaddr, HAL_STATUS_SUCCESS, - HAL_BOND_STATE_BONDED); - browse_remote_sdp(&addr->bdaddr); } -static const char *get_device_name(const bdaddr_t *addr) -{ - GSList *l; - - l = g_slist_find_custom(devices, addr, bdaddr_cmp); - if (l) { - struct device *dev = l->data; - return dev->name; - } - - return NULL; -} - -static void send_remote_device_name_prop(const bdaddr_t *bdaddr) +static uint8_t get_device_name(struct device *dev) { - struct hal_ev_remote_device_props *ev; - const char *name; - size_t ev_len; - char dst[18]; - - /* Use cached name or bdaddr string */ - name = get_device_name(bdaddr); - if (!name) { - ba2str(bdaddr, dst); - name = dst; - } - - ev_len = BASELEN_REMOTE_DEV_PROP + strlen(name); - ev = g_malloc0(ev_len); + send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_NAME, + strlen(dev->name), dev->name); - ev->status = HAL_STATUS_SUCCESS; - bdaddr2android(bdaddr, ev->bdaddr); - ev->num_props = 1; - ev->props[0].type = HAL_PROP_DEVICE_NAME; - ev->props[0].len = strlen(name); - memcpy(&ev->props[0].val, name, strlen(name)); - - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS, - ev_len, ev); - - g_free(ev); + return HAL_STATUS_SUCCESS; } static void pin_code_request_callback(uint16_t index, uint16_t length, @@ -614,6 +981,7 @@ static void pin_code_request_callback(uint16_t index, uint16_t length, { const struct mgmt_ev_pin_code_request *ev = param; struct hal_ev_pin_request hal_ev; + struct device *dev; char dst[18]; if (length < sizeof(*ev)) { @@ -623,39 +991,41 @@ static void pin_code_request_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, dst); - /* Workaround for Android Bluetooth.apk issue: send remote - * device property */ - send_remote_device_name_prop(&ev->addr.bdaddr); + dev = get_device(&ev->addr.bdaddr, BDADDR_BREDR); + + /* + * Workaround for Android Bluetooth.apk issue: send remote + * device property + */ + get_device_name(dev); - set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS, - HAL_BOND_STATE_BONDING); + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, + false, false); DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure); - /* TODO CoD of remote devices should probably be cached - * Name we already send in remote device prop */ + /* Name already sent in remote device prop */ memset(&hal_ev, 0, sizeof(hal_ev)); bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr); + hal_ev.class_of_dev = dev->class; - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST, + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST, sizeof(hal_ev), &hal_ev); } -static void send_ssp_request(const bdaddr_t *addr, uint8_t variant, +static void send_ssp_request(struct device *dev, uint8_t variant, uint32_t passkey) { struct hal_ev_ssp_request ev; - /* It is ok to have empty name and CoD of remote devices here since - * those information has been already provided on device_connected event - * or during device scaning. Android will use that instead. - */ - memset(&ev, 0, sizeof(ev)); - bdaddr2android(addr, ev.bdaddr); + bdaddr2android(&dev->bdaddr, ev.bdaddr); + memcpy(ev.name, dev->name, strlen(dev->name)); + ev.class_of_dev = dev->class; + ev.pairing_variant = variant; ev.passkey = passkey; - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST, + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST, sizeof(ev), &ev); } @@ -663,6 +1033,7 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_user_confirm_request *ev = param; + struct device *dev; char dst[18]; if (length < sizeof(*ev)) { @@ -673,20 +1044,24 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, dst); DBG("%s confirm_hint %u", dst, ev->confirm_hint); - set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS, - HAL_BOND_STATE_BONDING); + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, + false, false); if (ev->confirm_hint) - send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONSENT, 0); + send_ssp_request(dev, HAL_SSP_VARIANT_CONSENT, 0); else - send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONFIRM, - ev->value); + send_ssp_request(dev, HAL_SSP_VARIANT_CONFIRM, ev->value); } static void user_passkey_request_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_user_passkey_request *ev = param; + struct device *dev; char dst[18]; if (length < sizeof(*ev)) { @@ -697,16 +1072,22 @@ static void user_passkey_request_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, dst); DBG("%s", dst); - set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS, - HAL_BOND_STATE_BONDING); + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, + false, false); - send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_ENTRY, 0); + send_ssp_request(dev, HAL_SSP_VARIANT_ENTRY, 0); } static void user_passkey_notify_callback(uint16_t index, uint16_t length, - const void *param, void *user_data) + const void *param, + void *user_data) { const struct mgmt_ev_passkey_notify *ev = param; + struct device *dev; char dst[18]; if (length < sizeof(*ev)) { @@ -721,60 +1102,165 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length, if (ev->entered) return; - set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS, - HAL_BOND_STATE_BONDING); + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, + false, false); + + send_ssp_request(dev, HAL_SSP_VARIANT_NOTIF, ev->passkey); +} + +static void clear_device_found(gpointer data, gpointer user_data) +{ + struct device *dev = data; + + dev->found = false; +} + +static uint8_t get_supported_discovery_type(void) +{ + uint8_t type = SCAN_TYPE_NONE; + + if (adapter.current_settings & MGMT_SETTING_BREDR) + type |= SCAN_TYPE_BREDR; + + if (adapter.current_settings & MGMT_SETTING_LE) + type |= SCAN_TYPE_LE; + + return type; +} + +static bool start_discovery(uint8_t type) +{ + struct mgmt_cp_start_discovery cp; + + cp.type = get_supported_discovery_type() & type; + + DBG("type=0x%x", cp.type); + + if (cp.type == SCAN_TYPE_NONE) + return false; + + if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) > 0) + return true; + + error("Failed to start discovery"); + + return false; +} + +/* + * Send discovery state change event only if it is related to dual type + * discovery session (triggered by start/cancel discovery commands) + */ +static void check_discovery_state(uint8_t new_type, uint8_t old_type) +{ + struct hal_ev_discovery_state_changed ev; + + DBG("%u %u", new_type, old_type); + + if (new_type == get_supported_discovery_type()) { + g_slist_foreach(bonded_devices, clear_device_found, NULL); + g_slist_foreach(cached_devices, clear_device_found, NULL); + ev.state = HAL_DISCOVERY_STATE_STARTED; + goto done; + } + + if (old_type != get_supported_discovery_type()) + return; + + ev.state = HAL_DISCOVERY_STATE_STOPPED; - send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_NOTIF, - ev->passkey); +done: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(ev), &ev); } static void mgmt_discovering_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_discovering *ev = param; - struct hal_ev_discovery_state_changed cp; + uint8_t type; if (length < sizeof(*ev)) { error("Too small discovering event"); return; } - DBG("hci%u type %u discovering %u", index, ev->type, - ev->discovering); + DBG("type %u discovering %u", ev->type, ev->discovering); - if (adapter.discovering == !!ev->discovering) + if (!!adapter.cur_discovery_type == !!ev->discovering) return; - adapter.discovering = !!ev->discovering; + type = ev->discovering ? ev->type : SCAN_TYPE_NONE; - DBG("new discovering state %u", ev->discovering); + check_discovery_state(type, adapter.cur_discovery_type); - if (adapter.discovering) { - cp.state = HAL_DISCOVERY_STATE_STARTED; - } else { - g_slist_free_full(found_devices, g_free); - found_devices = NULL; + adapter.cur_discovery_type = type; + + if (ev->discovering) + return; + + /* One shot notification about discovery stopped */ + if (gatt_discovery_stopped_cb) { + gatt_discovery_stopped_cb(); + gatt_discovery_stopped_cb = NULL; + } + + type = adapter.exp_discovery_type; + adapter.exp_discovery_type = SCAN_TYPE_NONE; + + if (type == SCAN_TYPE_NONE && gatt_device_found_cb) + type = SCAN_TYPE_LE; + + if (type != SCAN_TYPE_NONE) + start_discovery(type); +} + +static void confirm_device_name_cb(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_confirm_name *rp = param; + struct device *dev; - cp.state = HAL_DISCOVERY_STATE_STOPPED; + DBG("Confirm name status: %s (0x%02x)", mgmt_errstr(status), status); + + if (length < sizeof(*rp)) { + error("Wrong size of confirm name response"); + return; } - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp); + dev = find_device(&rp->addr.bdaddr); + if (!dev) + return; + + dev->confirm_id = 0; } -static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type) +static unsigned int confirm_device_name(const bdaddr_t *addr, uint8_t addr_type, + bool resolve_name) { struct mgmt_cp_confirm_name cp; + unsigned int res; memset(&cp, 0, sizeof(cp)); bacpy(&cp.addr.bdaddr, addr); cp.addr.type = addr_type; - if (mgmt_reply(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index, - sizeof(cp), &cp, NULL, NULL, NULL) == 0) + if (!resolve_name) + cp.name_known = 1; + + res = mgmt_send(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index, + sizeof(cp), &cp, confirm_device_name_cb, + NULL, NULL); + if (!res) error("Failed to send confirm name request"); -} + return res; +} static int fill_hal_prop(void *buf, uint8_t type, uint16_t len, const void *val) @@ -783,99 +1269,266 @@ static int fill_hal_prop(void *buf, uint8_t type, uint16_t len, prop->type = type; prop->len = len; - memcpy(prop->val, val, len); + + if (len) + memcpy(prop->val, val, len); return sizeof(*prop) + len; } -static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type, - int8_t rssi, bool confirm, - const uint8_t *data, uint8_t data_len) +static uint8_t get_device_android_type(struct device *dev) { - uint8_t buf[BLUEZ_HAL_MTU]; - bool new_dev = false; - struct eir_data eir; - uint8_t *num_prop; - uint8_t opcode; - int size = 0; + if (dev->bredr && dev->le) + return HAL_TYPE_DUAL; + + if (dev->le) + return HAL_TYPE_LE; + + return HAL_TYPE_BREDR; +} + +uint8_t bt_get_device_android_type(const bdaddr_t *addr) +{ + struct device *dev; + + dev = get_device(addr, BDADDR_BREDR); + + return get_device_android_type(dev); +} + +const char *bt_get_adapter_name(void) +{ + return adapter.name; +} + +bool bt_device_is_bonded(const bdaddr_t *bdaddr) +{ + if (g_slist_find_custom(bonded_devices, bdaddr, device_match)) + return true; + + return false; +} + +static bool rssi_above_threshold(int old, int new) +{ + /* only 8 dBm or more */ + return abs(old - new) >= 8; +} + +static void update_new_device(struct device *dev, int8_t rssi, + const struct eir_data *eir) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_device_found *ev = (void *) buf; + bdaddr_t android_bdaddr; + uint8_t android_type; + int size; memset(buf, 0, sizeof(buf)); - memset(&eir, 0, sizeof(eir)); - eir_parse(&eir, data, data_len); + if (adapter.cur_discovery_type) + dev->found = true; - if (!g_slist_find_custom(found_devices, bdaddr, bdaddr_cmp)) { - bdaddr_t *new_bdaddr; - char addr[18]; + size = sizeof(*ev); + + bdaddr2android(&dev->bdaddr, &android_bdaddr); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR, + sizeof(android_bdaddr), + &android_bdaddr); + ev->num_props++; - new_bdaddr = g_new0(bdaddr_t, 1); - bacpy(new_bdaddr, bdaddr); + android_type = get_device_android_type(dev); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, + sizeof(android_type), &android_type); + ev->num_props++; - found_devices = g_slist_prepend(found_devices, new_bdaddr); + if (eir->class) + dev->class = eir->class; - ba2str(new_bdaddr, addr); - DBG("New device found: %s", addr); + if (dev->class) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, + sizeof(dev->class), &dev->class); + ev->num_props++; + } - new_dev = true; + if (rssi && rssi_above_threshold(dev->rssi, rssi)) + dev->rssi = rssi; + + if (dev->rssi) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, + sizeof(dev->rssi), &dev->rssi); + ev->num_props++; } - if (new_dev) { - struct hal_ev_device_found *ev = (void *) buf; - bdaddr_t android_bdaddr; + if (eir->name && strlen(eir->name)) { + g_free(dev->name); + dev->name = g_strdup(eir->name); + } - size += sizeof(*ev); + if (dev->name) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, + strlen(dev->name), dev->name); + ev->num_props++; + + /* when updating name also send stored friendly name */ + if (dev->friendly_name) { + size += fill_hal_prop(buf + size, + HAL_PROP_DEVICE_FRIENDLY_NAME, + strlen(dev->friendly_name), + dev->friendly_name); + ev->num_props++; + } + } - num_prop = &ev->num_props; - opcode = HAL_EV_DEVICE_FOUND; + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND, + size, buf); +} - bdaddr2android(bdaddr, &android_bdaddr); +static void update_device(struct device *dev, int8_t rssi, + const struct eir_data *eir, uint8_t bdaddr_type) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_remote_device_props *ev = (void *) buf; + uint8_t old_type, new_type; + int size; - size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR, - sizeof(android_bdaddr), &android_bdaddr); - (*num_prop)++; - } else { - struct hal_ev_remote_device_props *ev = (void *) buf; + memset(buf, 0, sizeof(buf)); - size += sizeof(*ev); + size = sizeof(*ev); - num_prop = &ev->num_props; - opcode = HAL_EV_REMOTE_DEVICE_PROPS; + ev->status = HAL_STATUS_SUCCESS; + bdaddr2android(&dev->bdaddr, ev->bdaddr); + + old_type = get_device_android_type(dev); + + if (bdaddr_type == BDADDR_BREDR) { + dev->bredr = true; + } else { + dev->le = true; + dev->bdaddr_type = bdaddr_type; + } + + new_type = get_device_android_type(dev); - ev->status = HAL_STATUS_SUCCESS; - bdaddr2android(bdaddr, ev->bdaddr); + if (old_type != new_type) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, + sizeof(new_type), &new_type); + ev->num_props++; } - if (eir.class) { + if (eir->class && dev->class != eir->class) { + dev->class = eir->class; size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, - sizeof(eir.class), &eir.class); - (*num_prop)++; + sizeof(dev->class), &dev->class); + ev->num_props++; } - if (rssi) { + if (rssi && rssi_above_threshold(dev->rssi, rssi)) { + dev->rssi = rssi; size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, - sizeof(rssi), &rssi); - (*num_prop)++; + sizeof(dev->rssi), &dev->rssi); + ev->num_props++; } - if (eir.name) { - cache_device_name(bdaddr, eir.name); - + if (eir->name && strlen(eir->name) && strcmp(dev->name, eir->name)) { + g_free(dev->name); + dev->name = g_strdup(eir->name); size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, - strlen(eir.name), eir.name); - (*num_prop)++; + strlen(dev->name), dev->name); + ev->num_props++; + + /* when updating name also send stored friendly name */ + if (dev->friendly_name) { + size += fill_hal_prop(buf + size, + HAL_PROP_DEVICE_FRIENDLY_NAME, + strlen(dev->friendly_name), + dev->friendly_name); + ev->num_props++; + } + } + + if (ev->num_props) + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, size, buf); +} + +static bool is_new_device(const struct device *dev, unsigned int flags) +{ + if (dev->found) + return false; + + if (dev->bredr_paired || dev->le_paired) + return false; + + if (dev->bdaddr_type != BDADDR_BREDR && + !(flags & (EIR_LIM_DISC | EIR_GEN_DISC))) + return false; + + return true; +} + +static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type, + int8_t rssi, bool confirm, + const uint8_t *data, uint8_t data_len) +{ + struct eir_data eir; + struct device *dev; + + memset(&eir, 0, sizeof(eir)); + + eir_parse(&eir, data, data_len); + + dev = get_device(bdaddr, bdaddr_type); + + if (bdaddr_type == BDADDR_BREDR) + dev->bredr_seen = time(NULL); + else + dev->le_seen = time(NULL); + + /* + * Device found event needs to be send also for known device if this is + * new discovery session. Otherwise framework will ignore it. + */ + if (is_new_device(dev, eir.flags)) + update_new_device(dev, rssi, &eir); + else + update_device(dev, rssi, &eir, bdaddr_type); + + eir_data_free(&eir); + + /* Notify Gatt if its registered for LE events */ + if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) { + bool discoverable; + + discoverable = eir.flags & (EIR_LIM_DISC | EIR_GEN_DISC); + + gatt_device_found_cb(bdaddr, bdaddr_type, rssi, data_len, data, + discoverable); } - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, opcode, size, buf); + if (!dev->bredr_paired && !dev->le_paired) + cache_device(dev); if (confirm) { char addr[18]; + bool resolve_name = true; ba2str(bdaddr, addr); - info("Device %s needs name confirmation.", addr); - confirm_device_name(bdaddr, bdaddr_type); - } - eir_data_free(&eir); + /* + * Don't need to confirm name if we have it already in cache + * Just check if device name is different than bdaddr + */ + if (g_strcmp0(dev->name, addr)) { + get_device_name(dev); + resolve_name = false; + } + + info("Device %s needs name confirmation (resolve_name=%d)", + addr, resolve_name); + dev->confirm_id = confirm_device_name(bdaddr, bdaddr_type, + resolve_name); + } } static void mgmt_device_found_event(uint16_t index, uint16_t length, @@ -935,12 +1588,13 @@ static void mgmt_device_connected_event(uint16_t index, uint16_t length, hal_ev.state = HAL_ACL_STATE_CONNECTED; bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr); - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED, - sizeof(hal_ev), &hal_ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev); } static void mgmt_device_disconnected_event(uint16_t index, uint16_t length, - const void *param, void *user_data) + const void *param, + void *user_data) { const struct mgmt_ev_device_disconnected *ev = param; struct hal_ev_acl_state_changed hal_ev; @@ -954,8 +1608,8 @@ static void mgmt_device_disconnected_event(uint16_t index, uint16_t length, hal_ev.state = HAL_ACL_STATE_DISCONNECTED; bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr); - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED, - sizeof(hal_ev), &hal_ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev); } static uint8_t status_mgmt2hal(uint8_t mgmt) @@ -984,79 +1638,213 @@ static void mgmt_connect_failed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_connect_failed *ev = param; + struct device *dev; + + if (length < sizeof(*ev)) { + error("Too short connect failed event (%u bytes)", length); + return; + } DBG(""); - /* In case security mode 3 pairing we will get connect failed event - * in case e.g wrong PIN code entered. Let's check if device is - * bonding, if so update bond state */ - set_device_bond_state(&ev->addr.bdaddr, status_mgmt2hal(ev->status), - HAL_BOND_STATE_NONE); + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + /* + * In case security mode 3 pairing we will get connect failed event + * in case e.g wrong PIN code entered. Let's check if device is + * bonding, if so update bond state + */ + + if (!dev->pairing) + return; + + update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status), + false, false, false); } static void mgmt_auth_failed_event(uint16_t index, uint16_t length, const void *param, void *user_data) { const struct mgmt_ev_auth_failed *ev = param; + struct device *dev; + + if (length < sizeof(*ev)) { + error("Too small auth failed mgmt event (%u bytes)", length); + return; + } DBG(""); - set_device_bond_state(&ev->addr.bdaddr, status_mgmt2hal(ev->status), - HAL_BOND_STATE_NONE); + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + if (!dev->pairing) + return; + + update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status), + false, false, false); } static void mgmt_device_unpaired_event(uint16_t index, uint16_t length, const void *param, void *user_data) { + const struct mgmt_ev_device_unpaired *ev = param; + struct device *dev; + + if (length < sizeof(*ev)) { + error("Too small device unpaired event (%u bytes)", length); + return; + } + DBG(""); + + /* TODO should device be disconnected ? */ + + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, false, + false, false); } -static void register_mgmt_handlers(void) +static void store_ltk(const bdaddr_t *dst, uint8_t bdaddr_type, bool master, + const uint8_t *key, uint8_t key_type, uint8_t enc_size, + uint16_t ediv, uint64_t rand) { - mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index, - new_settings_callback, NULL, NULL); - - mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index, - mgmt_dev_class_changed_event, NULL, NULL); + const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s; + GKeyFile *key_file; + char key_str[33]; + gsize length = 0; + char addr[18]; + char *data; + int i; - mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index, - mgmt_local_name_changed_event, NULL, NULL); + key_file = g_key_file_new(); + if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { + g_key_file_free(key_file); + return; + } - mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index, - new_link_key_callback, NULL, NULL); + ba2str(dst, addr); - mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index, - pin_code_request_callback, NULL, NULL); + key_s = master ? "LongTermKey" : "SlaveLongTermKey"; + keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType"; + encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize"; + ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv"; + rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand"; - mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index, - user_confirm_request_callback, NULL, NULL); + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", key[i]); - mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index, - user_passkey_request_callback, NULL, NULL); + g_key_file_set_string(key_file, addr, key_s, key_str); - mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index, - user_passkey_notify_callback, NULL, NULL); + g_key_file_set_integer(key_file, addr, keytype_s, key_type); - mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index, - mgmt_discovering_event, NULL, NULL); + g_key_file_set_integer(key_file, addr, encsize_s, enc_size); - mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index, - mgmt_device_found_event, NULL, NULL); + g_key_file_set_integer(key_file, addr, ediv_s, ediv); - mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index, - mgmt_device_connected_event, NULL, NULL); + g_key_file_set_uint64(key_file, addr, rand_s, rand); - mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index, - mgmt_device_disconnected_event, NULL, NULL); + data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(DEVICES_FILE, data, length, NULL); + g_free(data); - mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index, - mgmt_connect_failed_event, NULL, NULL); + g_key_file_free(key_file); +} + +static void new_long_term_key_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_long_term_key *ev = param; + struct device *dev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small long term key event (%u bytes)", length); + return; + } + + ba2str(&ev->key.addr.bdaddr, dst); + + DBG("new LTK for %s type %u enc_size %u store_hint %u", + dst, ev->key.type, ev->key.enc_size, ev->store_hint); + + dev = find_device(&ev->key.addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, + true, !!ev->store_hint); + + if (ev->store_hint) { + const struct mgmt_ltk_info *key = &ev->key; + uint16_t ediv; + uint64_t rand; + + ediv = le16_to_cpu(key->ediv); + rand = le64_to_cpu(key->rand); + + store_ltk(&key->addr.bdaddr, key->addr.type, key->master, + key->val, key->type, key->enc_size, ediv, rand); + } + + /* TODO browse services here? */ +} + +static void register_mgmt_handlers(void) +{ + mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index, + new_settings_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index, + mgmt_dev_class_changed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index, + mgmt_local_name_changed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index, + new_link_key_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index, + pin_code_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index, + user_confirm_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index, + user_passkey_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index, + user_passkey_notify_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index, + mgmt_discovering_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index, + mgmt_device_found_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index, + mgmt_device_connected_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index, + mgmt_device_disconnected_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index, + mgmt_connect_failed_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index, mgmt_auth_failed_event, NULL, NULL); mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index, mgmt_device_unpaired_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index, + new_long_term_key_event, NULL, NULL); } static void load_link_keys_complete(uint8_t status, uint16_t length, @@ -1117,18 +1905,39 @@ static void load_link_keys(GSList *keys, bt_bluetooth_ready cb) } } -/* output uint128 is in host order */ -static void uuid16_to_uint128(uint16_t uuid, uint128_t *u128) +static void load_ltks(GSList *ltks) { - uuid_t uuid16, uuid128; + struct mgmt_cp_load_long_term_keys *cp; + struct mgmt_ltk_info *ltk; + size_t ltk_count, cp_size; + GSList *l; + + ltk_count = g_slist_length(ltks); - sdp_uuid16_create(&uuid16, uuid); - sdp_uuid16_to_uuid128(&uuid128, &uuid16); + DBG("ltks %zu", ltk_count); - ntoh128(&uuid128.value.uuid128, u128); + cp_size = sizeof(*cp) + (ltk_count * sizeof(*ltk)); + + cp = g_malloc0(cp_size); + + /* + * Even if the list of stored keys is empty, it is important to load + * an empty list into the kernel. That way it is ensured that no old + * keys from a previous daemon are present. + */ + cp->key_count = htobs(ltk_count); + + for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++) + memcpy(ltk, ltks->data, sizeof(*ltk)); + + if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index, + cp_size, cp, NULL, NULL, NULL) == 0) + error("Failed to load LTKs"); + + g_free(cp); } -static uint8_t get_uuids(void) +static uint8_t get_adapter_uuids(void) { struct hal_ev_adapter_props_changed *ev; GSList *list = adapter.uuids; @@ -1136,7 +1945,6 @@ static uint8_t get_uuids(void) int len = uuid_count * sizeof(uint128_t); uint8_t buf[BASELEN_PROP_CHANGED + len]; uint8_t *p; - int i; memset(buf, 0, sizeof(buf)); ev = (void *) buf; @@ -1149,20 +1957,15 @@ static uint8_t get_uuids(void) p = ev->props->val; for (; list; list = g_slist_next(list)) { - uint16_t uuid = GPOINTER_TO_UINT(list->data); - uint128_t uint128; + uuid_t *uuid = list->data; - uuid16_to_uint128(uuid, &uint128); - - /* Android expects swapped bytes in uuid */ - for (i = 0; i < 16; i++) - p[15 - i] = uint128.data[i]; + memcpy(p, &uuid->value.uuid128, sizeof(uint128_t)); p += sizeof(uint128_t); } - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, - sizeof(buf), ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev); return HAL_STATUS_SUCCESS; } @@ -1178,15 +1981,15 @@ static void remove_uuid_complete(uint8_t status, uint16_t length, mgmt_dev_class_changed_event(adapter.index, length, param, NULL); - get_uuids(); + get_adapter_uuids(); } -static void remove_uuid(uint16_t uuid) +static void remove_uuid(uuid_t *uuid) { uint128_t uint128; struct mgmt_cp_remove_uuid cp; - uuid16_to_uint128(uuid, &uint128); + ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128); htob128(&uint128, (uint128_t *) cp.uuid); mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp, @@ -1204,17 +2007,17 @@ static void add_uuid_complete(uint8_t status, uint16_t length, mgmt_dev_class_changed_event(adapter.index, length, param, NULL); - get_uuids(); + get_adapter_uuids(); } -static void add_uuid(uint8_t svc_hint, uint16_t uuid) +static void add_uuid(uint8_t svc_hint, uuid_t *uuid) { uint128_t uint128; struct mgmt_cp_add_uuid cp; - uuid16_to_uint128(uuid, &uint128); - + ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128); htob128(&uint128, (uint128_t *) cp.uuid); + cp.svc_hint = svc_hint; mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp, @@ -1223,22 +2026,21 @@ static void add_uuid(uint8_t svc_hint, uint16_t uuid) int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint) { - uint16_t uuid; + uuid_t *uuid; - /* TODO support all types? */ - if (rec->svclass.type != SDP_UUID16) { - warn("Ignoring unsupported UUID type"); - return -EINVAL; - } + uuid = sdp_uuid_to_uuid128(&rec->svclass); - uuid = rec->svclass.value.uuid16; + if (g_slist_find_custom(adapter.uuids, uuid, sdp_uuid_cmp)) { + char uuid_str[32]; - if (g_slist_find(adapter.uuids, GUINT_TO_POINTER(uuid))) { - DBG("UUID 0x%x already added", uuid); + sdp_uuid2strn(uuid, uuid_str, sizeof(uuid_str)); + DBG("UUID %s already added", uuid_str); + + bt_free(uuid); return -EALREADY; } - adapter.uuids = g_slist_prepend(adapter.uuids, GUINT_TO_POINTER(uuid)); + adapter.uuids = g_slist_prepend(adapter.uuids, uuid); add_uuid(svc_hint, uuid); @@ -1249,20 +2051,21 @@ void bt_adapter_remove_record(uint32_t handle) { sdp_record_t *rec; GSList *uuid_found; - uint16_t uuid; rec = sdp_record_find(handle); if (!rec) return; - uuid = rec->svclass.value.uuid16; - - uuid_found = g_slist_find(adapter.uuids, GUINT_TO_POINTER(uuid)); + uuid_found = g_slist_find_custom(adapter.uuids, &rec->svclass, + sdp_uuid_cmp); if (uuid_found) { + uuid_t *uuid = uuid_found->data; + remove_uuid(uuid); - adapter.uuids = g_slist_remove(adapter.uuids, - uuid_found->data); + adapter.uuids = g_slist_remove(adapter.uuids, uuid); + + free(uuid); } remove_record_from_server(handle); @@ -1373,7 +2176,7 @@ static uint8_t set_adapter_name(const uint8_t *name, uint16_t len) return HAL_STATUS_FAILED; } -static uint8_t set_discoverable_timeout(const void *buf, uint16_t len) +static uint8_t set_adapter_discoverable_timeout(const void *buf, uint16_t len) { const uint32_t *timeout = buf; @@ -1384,13 +2187,20 @@ static uint8_t set_discoverable_timeout(const void *buf, uint16_t len) return HAL_STATUS_FAILED; } - /* Android handles discoverable timeout in Settings app. + /* + * Android handles discoverable timeout in Settings app. * There is no need to use kernel feature for that. - * Just need to store this value here */ + * Just need to store this value here + */ - /* TODO: This should be in some storage */ memcpy(&adapter.discoverable_timeout, timeout, sizeof(uint32_t)); + store_adapter_config(); + + send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT, + sizeof(adapter.discoverable_timeout), + &adapter.discoverable_timeout); + return HAL_STATUS_SUCCESS; } @@ -1404,12 +2214,296 @@ static void clear_uuids(void) sizeof(cp), &cp, NULL, NULL, NULL); } +static struct device *create_device_from_info(GKeyFile *key_file, + const char *peer) +{ + struct device *dev; + uint8_t type; + bdaddr_t bdaddr; + char **uuids; + char *str; + + /* BREDR if not present */ + type = g_key_file_get_integer(key_file, peer, "AddressType", NULL); + + str2ba(peer, &bdaddr); + dev = create_device(&bdaddr, type); + + if (type != BDADDR_BREDR) + dev->bredr = g_key_file_get_boolean(key_file, peer, "BREDR", + NULL); + + str = g_key_file_get_string(key_file, peer, "LinkKey", NULL); + if (str) { + g_free(str); + dev->bredr_paired = true; + dev->bredr_bonded = true; + } + + str = g_key_file_get_string(key_file, peer, "LongTermKey", NULL); + if (str) { + g_free(str); + dev->le_paired = true; + dev->le_bonded = true; + } + + str = g_key_file_get_string(key_file, peer, "SlaveLongTermKey", NULL); + if (str) { + g_free(str); + dev->le_paired = true; + dev->le_bonded = true; + } + + str = g_key_file_get_string(key_file, peer, "Name", NULL); + if (str) { + g_free(dev->name); + dev->name = str; + } + + str = g_key_file_get_string(key_file, peer, "FriendlyName", NULL); + if (str) { + g_free(dev->friendly_name); + dev->friendly_name = str; + } + + dev->class = g_key_file_get_integer(key_file, peer, "Class", NULL); + + if (dev->bredr) + dev->bredr_seen = g_key_file_get_integer(key_file, peer, + "Timestamp", + NULL); + else + dev->le_seen = g_key_file_get_integer(key_file, peer, + "Timestamp", NULL); + + uuids = g_key_file_get_string_list(key_file, peer, "Services", NULL, + NULL); + if (uuids) { + char **uuid; + + for (uuid = uuids; *uuid; uuid++) { + uint8_t *u = g_malloc0(16); + int i; + + for (i = 0; i < 16; i++) + sscanf((*uuid) + (i * 2), "%02hhX", &u[i]); + + dev->uuids = g_slist_append(dev->uuids, u); + } + + g_strfreev(uuids); + } + + return dev; +} + +static struct mgmt_link_key_info *get_key_info(GKeyFile *key_file, + const char *peer) +{ + struct mgmt_link_key_info *info = NULL; + char *str; + unsigned int i; + + str = g_key_file_get_string(key_file, peer, "LinkKey", NULL); + if (!str || strlen(str) != 32) + goto failed; + + info = g_new0(struct mgmt_link_key_info, 1); + + str2ba(peer, &info->addr.bdaddr); + + info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL); + + for (i = 0; i < sizeof(info->val); i++) + sscanf(str + (i * 2), "%02hhX", &info->val[i]); + + info->type = g_key_file_get_integer(key_file, peer, "LinkKeyType", + NULL); + info->pin_len = g_key_file_get_integer(key_file, peer, + "LinkKeyPinLength", NULL); + +failed: + g_free(str); + + return info; +} + +static struct mgmt_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer, + bool master) +{ + const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s; + struct mgmt_ltk_info *info = NULL; + char *key; + unsigned int i; + + key_s = master ? "LongTermKey" : "SlaveLongTermKey"; + keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType"; + encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize"; + ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv"; + rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand"; + + key = g_key_file_get_string(key_file, peer, key_s, NULL); + if (!key || strlen(key) != 32) + goto failed; + + info = g_new0(struct mgmt_ltk_info, 1); + + str2ba(peer, &info->addr.bdaddr); + + info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL); + + for (i = 0; i < sizeof(info->val); i++) + sscanf(key + (i * 2), "%02hhX", &info->val[i]); + + info->type = g_key_file_get_integer(key_file, peer, keytype_s, NULL); + + info->enc_size = g_key_file_get_integer(key_file, peer, encsize_s, + NULL); + + info->rand = g_key_file_get_uint64(key_file, peer, rand_s, NULL); + info->rand = cpu_to_le64(info->rand); + + info->ediv = g_key_file_get_integer(key_file, peer, ediv_s, NULL); + info->ediv = cpu_to_le16(info->ediv); + + info->master = master; + +failed: + g_free(key); + + return info; +} + +static time_t device_timestamp(const struct device *dev) +{ + if (dev->bredr && dev->le) { + if (dev->le_seen > dev->bredr_seen) + return dev->le_seen; + + return dev->bredr_seen; + } + + if (dev->bredr) + return dev->bredr_seen; + + return dev->le_seen; +} + +static int device_timestamp_cmp(gconstpointer a, gconstpointer b) +{ + const struct device *deva = a; + const struct device *devb = b; + + return device_timestamp(deva) < device_timestamp(devb); +} + +static void load_devices_cache(void) +{ + GKeyFile *key_file; + gchar **devs; + gsize len = 0; + unsigned int i; + + key_file = g_key_file_new(); + + g_key_file_load_from_file(key_file, CACHE_FILE, 0, NULL); + + devs = g_key_file_get_groups(key_file, &len); + + for (i = 0; i < len; i++) { + struct device *dev; + + dev = create_device_from_info(key_file, devs[i]); + cached_devices = g_slist_prepend(cached_devices, dev); + } + + cached_devices = g_slist_sort(cached_devices, device_timestamp_cmp); + + g_strfreev(devs); + g_key_file_free(key_file); +} + +static void load_devices_info(bt_bluetooth_ready cb) +{ + GKeyFile *key_file; + gchar **devs; + gsize len = 0; + unsigned int i; + GSList *keys = NULL; + GSList *ltks = NULL; + + key_file = g_key_file_new(); + + g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL); + + devs = g_key_file_get_groups(key_file, &len); + + for (i = 0; i < len; i++) { + struct mgmt_link_key_info *key_info; + struct mgmt_ltk_info *ltk_info; + struct mgmt_ltk_info *slave_ltk_info; + struct device *dev; + + key_info = get_key_info(key_file, devs[i]); + ltk_info = get_ltk_info(key_file, devs[i], true); + slave_ltk_info = get_ltk_info(key_file, devs[i], false); + + if (!key_info && !ltk_info && !slave_ltk_info) { + error("Failed to load keys for %s, skipping", devs[i]); + + continue; + } + + if (key_info) + keys = g_slist_prepend(keys, key_info); + + if (ltk_info) + ltks = g_slist_prepend(ltks, ltk_info); + + if (slave_ltk_info) + ltks = g_slist_prepend(ltks, slave_ltk_info); + + dev = create_device_from_info(key_file, devs[i]); + + bonded_devices = g_slist_prepend(bonded_devices, dev); + } + + load_ltks(ltks); + g_slist_free_full(ltks, g_free); + + load_link_keys(keys, cb); + g_slist_free_full(keys, g_free); + + g_strfreev(devs); + g_key_file_free(key_file); +} + +static void set_adapter_class(void) +{ + struct mgmt_cp_set_dev_class cp; + + memset(&cp, 0, sizeof(cp)); + + /* + * kernel assign the major and minor numbers straight to dev_class[0] + * and dev_class[1] without considering the proper bit shifting. + */ + cp.major = ADAPTER_MAJOR_CLASS & 0x1f; + cp.minor = ADAPTER_MINOR_CLASS << 2; + + if (mgmt_send(mgmt_if, MGMT_OP_SET_DEV_CLASS, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) > 0) + return; + + error("Failed to set class of device"); +} + static void read_info_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_read_info *rp = param; bt_bluetooth_ready cb = user_data; - uint32_t missing_settings, supported_settings; + uint32_t missing_settings; int err; DBG(""); @@ -1433,35 +2527,53 @@ static void read_info_complete(uint8_t status, uint16_t length, goto failed; } + load_adapter_config(); + + if (!bacmp(&adapter.bdaddr, BDADDR_ANY)) { + bacpy(&adapter.bdaddr, &rp->bdaddr); + adapter.name = g_strdup(DEFAULT_ADAPTER_NAME); + store_adapter_config(); + } else if (bacmp(&adapter.bdaddr, &rp->bdaddr)) { + error("Bluetooth address mismatch"); + err = -ENODEV; + goto failed; + } + + if (g_strcmp0(adapter.name, (const char *) rp->name)) + set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name)); + + set_adapter_class(); + /* Store adapter information */ - bacpy(&adapter.bdaddr, &rp->bdaddr); adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) | (rp->dev_class[2] << 16); - adapter.name = g_strdup((const char *) rp->name); - supported_settings = btohs(rp->supported_settings); + adapter.supported_settings = btohs(rp->supported_settings); adapter.current_settings = btohs(rp->current_settings); - /* TODO: Read discoverable timeout from storage here */ - /* TODO: Register all event notification handlers */ register_mgmt_handlers(); clear_uuids(); - load_link_keys(NULL, cb); - set_io_capability(); set_device_id(); - missing_settings = adapter.current_settings ^ supported_settings; + missing_settings = adapter.current_settings ^ + adapter.supported_settings; if (missing_settings & MGMT_SETTING_SSP) set_mode(MGMT_OP_SET_SSP, 0x01); + if (missing_settings & MGMT_SETTING_SECURE_CONN) + set_mode(MGMT_OP_SET_SECURE_CONN, 0x01); + if (missing_settings & MGMT_SETTING_PAIRABLE) set_mode(MGMT_OP_SET_PAIRABLE, 0x01); + load_devices_info(cb); + load_devices_cache(); + return; failed: @@ -1605,7 +2717,7 @@ failed: cb(-EIO, NULL); } -bool bt_bluetooth_start(int index, bt_bluetooth_ready cb) +bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb) { DBG("index %d", index); @@ -1615,6 +2727,9 @@ bool bt_bluetooth_start(int index, bt_bluetooth_ready cb) return false; } + if (mgmt_dbg) + mgmt_set_debug(mgmt_if, mgmt_debug, "mgmt_if: ", NULL); + if (mgmt_send(mgmt_if, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL, read_version_complete, cb, NULL) == 0) { error("Error sending READ_VERSION mgmt command"); @@ -1686,25 +2801,18 @@ static bool set_discoverable(uint8_t mode, uint16_t timeout) return false; } -static uint8_t get_address(void) +static uint8_t get_adapter_address(void) { - uint8_t buf[BASELEN_PROP_CHANGED + sizeof(bdaddr_t)]; - struct hal_ev_adapter_props_changed *ev = (void *) buf; - - ev->num_props = 1; - ev->status = HAL_STATUS_SUCCESS; + uint8_t buf[6]; - ev->props[0].type = HAL_PROP_ADAPTER_ADDR; - ev->props[0].len = sizeof(bdaddr_t); - bdaddr2android(&adapter.bdaddr, ev->props[0].val); + bdaddr2android(&adapter.bdaddr, buf); - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, - sizeof(buf), buf); + send_adapter_property(HAL_PROP_ADAPTER_ADDR, sizeof(buf), buf); return HAL_STATUS_SUCCESS; } -static uint8_t get_name(void) +static uint8_t get_adapter_name(void) { if (!adapter.name) return HAL_STATUS_FAILED; @@ -1714,8 +2822,7 @@ static uint8_t get_name(void) return HAL_STATUS_SUCCESS; } - -static uint8_t get_class(void) +static uint8_t get_adapter_class(void) { DBG(""); @@ -1724,34 +2831,42 @@ static uint8_t get_class(void) return HAL_STATUS_SUCCESS; } -static uint8_t get_type(void) +static uint8_t settings2type(void) { - DBG("Not implemented"); + bool bredr, le; - /* TODO: Add implementation */ + bredr = adapter.current_settings & MGMT_SETTING_BREDR; + le = adapter.current_settings & MGMT_SETTING_LE; - return HAL_STATUS_FAILED; -} + if (bredr && le) + return HAL_TYPE_DUAL; -static uint8_t get_service(void) -{ - DBG("Not implemented"); + if (bredr && !le) + return HAL_TYPE_BREDR; - /* TODO: Add implementation */ + if (!bredr && le) + return HAL_TYPE_LE; - return HAL_STATUS_FAILED; + return 0; } -static uint8_t get_scan_mode(void) +static uint8_t get_adapter_type(void) { + uint8_t type; + DBG(""); - scan_mode_changed(); + type = settings2type(); + + if (!type) + return HAL_STATUS_FAILED; + + send_adapter_property(HAL_PROP_ADAPTER_TYPE, sizeof(type), &type); return HAL_STATUS_SUCCESS; } -static uint8_t get_devices(void) +static uint8_t get_adapter_service_rec(void) { DBG("Not implemented"); @@ -1760,24 +2875,41 @@ static uint8_t get_devices(void) return HAL_STATUS_FAILED; } -static uint8_t get_discoverable_timeout(void) +static uint8_t get_adapter_scan_mode(void) { - struct hal_ev_adapter_props_changed *ev; - uint8_t buf[BASELEN_PROP_CHANGED + sizeof(uint32_t)]; + DBG(""); - memset(buf, 0, sizeof(buf)); - ev = (void *) buf; + scan_mode_changed(); - ev->num_props = 1; - ev->status = HAL_STATUS_SUCCESS; + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_adapter_bonded_devices(void) +{ + uint8_t buf[sizeof(bdaddr_t) * g_slist_length(bonded_devices)]; + int i = 0; + GSList *l; - ev->props[0].type = HAL_PROP_ADAPTER_DISC_TIMEOUT; - ev->props[0].len = sizeof(uint32_t); - memcpy(&ev->props[0].val, &adapter.discoverable_timeout, - sizeof(uint32_t)); + DBG(""); + + for (l = bonded_devices; l; l = g_slist_next(l)) { + struct device *dev = l->data; + + bdaddr2android(&dev->bdaddr, buf + (i * sizeof(bdaddr_t))); + i++; + } + + send_adapter_property(HAL_PROP_ADAPTER_BONDED_DEVICES, + i * sizeof(bdaddr_t), buf); + + return HAL_STATUS_SUCCESS; +} - ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, - sizeof(buf), ev); +static uint8_t get_adapter_discoverable_timeout(void) +{ + send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT, + sizeof(adapter.discoverable_timeout), + &adapter.discoverable_timeout); return HAL_STATUS_SUCCESS; } @@ -1789,94 +2921,285 @@ static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len) switch (cmd->type) { case HAL_PROP_ADAPTER_ADDR: - status = get_address(); + status = get_adapter_address(); break; case HAL_PROP_ADAPTER_NAME: - status = get_name(); + status = get_adapter_name(); break; case HAL_PROP_ADAPTER_UUIDS: - status = get_uuids(); + status = get_adapter_uuids(); break; case HAL_PROP_ADAPTER_CLASS: - status = get_class(); + status = get_adapter_class(); break; case HAL_PROP_ADAPTER_TYPE: - status = get_type(); + status = get_adapter_type(); break; case HAL_PROP_ADAPTER_SERVICE_REC: - status = get_service(); + status = get_adapter_service_rec(); break; case HAL_PROP_ADAPTER_SCAN_MODE: - status = get_scan_mode(); + status = get_adapter_scan_mode(); break; case HAL_PROP_ADAPTER_BONDED_DEVICES: - status = get_devices(); + status = get_adapter_bonded_devices(); break; case HAL_PROP_ADAPTER_DISC_TIMEOUT: - status = get_discoverable_timeout(); + status = get_adapter_discoverable_timeout(); break; default: status = HAL_STATUS_FAILED; break; } - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, status); -} + if (status != HAL_STATUS_SUCCESS) + error("Failed to get adapter property (type %u status %u)", + cmd->type, status); -static void get_properties(void) -{ - get_address(); - get_name(); - get_uuids(); - get_class(); - get_type(); - get_service(); - get_scan_mode(); - get_devices(); - get_discoverable_timeout(); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, + status); } -static bool start_discovery(void) +static void get_adapter_properties(void) { - struct mgmt_cp_start_discovery cp; - uint8_t type = 1 << BDADDR_BREDR; + uint8_t buf[IPC_MTU]; + struct hal_ev_adapter_props_changed *ev = (void *) buf; + uint8_t bonded[g_slist_length(bonded_devices) * sizeof(bdaddr_t)]; + uint128_t uuids[g_slist_length(adapter.uuids)]; + uint8_t android_bdaddr[6]; + uint8_t type, mode; + int size, i; + GSList *l; - if (adapter.current_settings & type) - cp.type = type; - else - cp.type = 0; + size = sizeof(*ev); - DBG("type=0x%x", type); + ev->status = HAL_STATUS_SUCCESS; + ev->num_props = 0; - if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index, - sizeof(cp), &cp, NULL, NULL, NULL) > 0) - return true; + bdaddr2android(&adapter.bdaddr, &android_bdaddr); + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_ADDR, + sizeof(android_bdaddr), android_bdaddr); + ev->num_props++; - error("Failed to start discovery"); - return false; + if (adapter.name) { + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_NAME, + strlen(adapter.name), adapter.name); + ev->num_props++; + } + + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_CLASS, + sizeof(adapter.dev_class), &adapter.dev_class); + ev->num_props++; + + type = settings2type(); + if (type) { + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_TYPE, + sizeof(type), &type); + ev->num_props++; + } + + mode = settings2scan_mode(); + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_SCAN_MODE, + sizeof(mode), &mode); + ev->num_props++; + + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_DISC_TIMEOUT, + sizeof(adapter.discoverable_timeout), + &adapter.discoverable_timeout); + ev->num_props++; + + for (i = 0, l = bonded_devices; l; l = g_slist_next(l), i++) { + struct device *dev = l->data; + + bdaddr2android(&dev->bdaddr, bonded + (i * sizeof(bdaddr_t))); + } + + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_BONDED_DEVICES, + sizeof(bonded), bonded); + ev->num_props++; + + for (i = 0, l = adapter.uuids; l; l = g_slist_next(l), i++) { + uuid_t *uuid = l->data; + + memcpy(&uuids[i], &uuid->value.uuid128, sizeof(uint128_t)); + } + + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_UUIDS, sizeof(uuids), + uuids); + ev->num_props++; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, size, buf); } -static bool stop_discovery(void) +static void cancel_pending_confirm_name(gpointer data, gpointer user_data) +{ + struct device *dev = data; + + mgmt_cancel(mgmt_if, dev->confirm_id); + dev->confirm_id = 0; +} + +static bool stop_discovery(uint8_t type) { struct mgmt_cp_stop_discovery cp; - uint8_t type = 1 << BDADDR_BREDR; - if (adapter.current_settings & type) - cp.type = type; - else - cp.type = 0; + cp.type = get_supported_discovery_type() & type; - DBG("type=0x%x", type); + DBG("type=0x%x", cp.type); + + if (cp.type == SCAN_TYPE_NONE) + return false; + + /* Lets drop all confirm name request as we don't need it anymore */ + g_slist_foreach(cached_devices, cancel_pending_confirm_name, NULL); if (mgmt_send(mgmt_if, MGMT_OP_STOP_DISCOVERY, adapter.index, sizeof(cp), &cp, NULL, NULL, NULL) > 0) return true; - error("Failed to start discovery"); + error("Failed to stop discovery"); + return false; +} + +struct adv_user_data { + bt_le_set_advertising_done cb; + void *user_data; +}; + +static void set_advertising_cb(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct adv_user_data *data = user_data; + + DBG(""); + + if (status) + error("Failed to set adverising %s (0x%02x))", + mgmt_errstr(status), status); + + data->cb(status, data->user_data); +} + +bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb, + void *user_data) +{ + struct adv_user_data *data; + uint8_t adv = advertising ? 0x01 : 0x00; + + data = new0(struct adv_user_data, 1); + if (!data) + return false; + + data->cb = cb; + data->user_data = user_data; + + if (mgmt_send(mgmt_if, MGMT_OP_SET_ADVERTISING, adapter.index, + sizeof(adv), &adv, set_advertising_cb, data, free) > 0) + return true; + + error("Failed to set advertising"); + free(data); + return false; +} + +bool bt_le_discovery_stop(bt_le_discovery_stopped cb) +{ + if (adapter.cur_discovery_type != SCAN_TYPE_LE) { + if (cb) + cb(); + + gatt_device_found_cb = NULL; + + return true; + } + + if (!stop_discovery(SCAN_TYPE_LE)) + return false; + + gatt_device_found_cb = NULL; + gatt_discovery_stopped_cb = cb; + adapter.exp_discovery_type = SCAN_TYPE_NONE; + + return true; +} + +bool bt_le_discovery_start(bt_le_device_found cb) +{ + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) + return false; + + /* If core is discovering, don't bother */ + if (adapter.cur_discovery_type != SCAN_TYPE_NONE) { + gatt_device_found_cb = cb; + return true; + } + + if (start_discovery(SCAN_TYPE_LE)) { + gatt_device_found_cb = cb; + return true; + } + return false; } -static uint8_t set_scan_mode(const void *buf, uint16_t len) +struct read_rssi_user_data { + bt_read_device_rssi_done cb; + void *user_data; +}; + +static void read_device_rssi_cb(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_get_conn_info *rp = param; + struct read_rssi_user_data *data = user_data; + + DBG(""); + + if (status) + error("Failed to get conn info: %s (0x%02x))", + mgmt_errstr(status), status); + + if (length < sizeof(*rp)) { + error("Wrong size of get conn info response"); + return; + } + + data->cb(status, &rp->addr.bdaddr, rp->rssi, data->user_data); +} + +bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb, + void *user_data) +{ + struct device *dev; + struct read_rssi_user_data *data; + struct mgmt_cp_get_conn_info cp; + + dev = find_device(addr); + if (!dev) + return false; + + memcpy(&cp.addr.bdaddr, addr, sizeof(cp.addr.bdaddr)); + cp.addr.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type; + + data = new0(struct read_rssi_user_data, 1); + if (!data) + return false; + + data->cb = cb; + data->user_data = user_data; + + if (!mgmt_send(mgmt_if, MGMT_OP_GET_CONN_INFO, adapter.index, + sizeof(cp), &cp, read_device_rssi_cb, data, free)) { + free(data); + error("Failed to get conn info"); + return false; + } + + return true; +} + +static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len) { const uint8_t *mode = buf; bool conn, disc, cur_conn, cur_disc; @@ -1936,7 +3259,7 @@ done: /* Android expects property changed callback */ scan_mode_changed(); - return HAL_STATUS_DONE; + return HAL_STATUS_SUCCESS; } static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len) @@ -1953,13 +3276,13 @@ static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len) switch (cmd->type) { case HAL_PROP_ADAPTER_SCAN_MODE: - status = set_scan_mode(cmd->val, cmd->len); + status = set_adapter_scan_mode(cmd->val, cmd->len); break; case HAL_PROP_ADAPTER_NAME: status = set_adapter_name(cmd->val, cmd->len); break; case HAL_PROP_ADAPTER_DISC_TIMEOUT: - status = set_discoverable_timeout(cmd->val, cmd->len); + status = set_adapter_discoverable_timeout(cmd->val, cmd->len); break; default: DBG("Unhandled property type 0x%x", cmd->type); @@ -1967,35 +3290,77 @@ static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len) break; } - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, status); + if (status != HAL_STATUS_SUCCESS) + error("Failed to set adapter property (type %u status %u)", + cmd->type, status); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, + status); } static void pair_device_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_pair_device *rp = param; + struct device *dev; DBG("status %u", status); - /* On success bond state change will be send when new link key event - * is received */ + /* + * On success bond state change will be send when new link key or LTK + * event is received + */ if (status == MGMT_STATUS_SUCCESS) return; - set_device_bond_state(&rp->addr.bdaddr, status_mgmt2hal(status), - HAL_BOND_STATE_NONE); + dev = find_device(&rp->addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, rp->addr.type, status_mgmt2hal(status), false, + false, false); +} + +static uint8_t select_device_bearer(struct device *dev) +{ + if (dev->bredr && dev->le) { + if (dev->le_seen > dev->bredr_seen) + return dev->bdaddr_type; + + return BDADDR_BREDR; + } + + return dev->bredr ? BDADDR_BREDR : dev->bdaddr_type; +} + +static bool device_is_paired(struct device *dev, uint8_t addr_type) +{ + if (addr_type == BDADDR_BREDR) + return dev->bredr_paired; + + return dev->le_paired; } static void handle_create_bond_cmd(const void *buf, uint16_t len) { const struct hal_cmd_create_bond *cmd = buf; + struct device *dev; uint8_t status; struct mgmt_cp_pair_device cp; cp.io_cap = DEFAULT_IO_CAPABILITY; - cp.addr.type = BDADDR_BREDR; android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr); + /* type is used only as fallback when device is not in cache */ + dev = get_device(&cp.addr.bdaddr, BDADDR_BREDR); + + cp.addr.type = select_device_bearer(dev); + + if (device_is_paired(dev, cp.addr.type)) { + status = HAL_STATUS_FAILED; + goto fail; + } + if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp), &cp, pair_device_complete, NULL, NULL) == 0) { status = HAL_STATUS_FAILED; @@ -2004,63 +3369,107 @@ static void handle_create_bond_cmd(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; - set_device_bond_state(&cp.addr.bdaddr, HAL_STATUS_SUCCESS, - HAL_BOND_STATE_BONDING); + update_device_state(dev, cp.addr.type, HAL_STATUS_SUCCESS, true, false, + false); fail: - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, + status); } static void handle_cancel_bond_cmd(const void *buf, uint16_t len) { const struct hal_cmd_cancel_bond *cmd = buf; struct mgmt_addr_info cp; + struct device *dev; uint8_t status; - cp.type = BDADDR_BREDR; android2bdaddr(cmd->bdaddr, &cp.bdaddr); - if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index, - sizeof(cp), &cp, NULL, NULL, NULL) > 0) - status = HAL_STATUS_SUCCESS; - else + dev = find_device(&cp.bdaddr); + if (!dev) { status = HAL_STATUS_FAILED; + goto failed; + } + + cp.type = select_device_bearer(dev); + + if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, + adapter.index, sizeof(cp), &cp, + NULL, NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, status); + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, + status); } static void unpair_device_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_unpair_device *rp = param; + struct device *dev; DBG("status %u", status); - if (status != MGMT_STATUS_SUCCESS) + if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_NOT_PAIRED) + return; + + dev = find_device(&rp->addr.bdaddr); + if (!dev) return; - set_device_bond_state(&rp->addr.bdaddr, HAL_STATUS_SUCCESS, - HAL_BOND_STATE_NONE); + update_device_state(dev, rp->addr.type, HAL_STATUS_SUCCESS, false, + false, false); } static void handle_remove_bond_cmd(const void *buf, uint16_t len) { const struct hal_cmd_remove_bond *cmd = buf; struct mgmt_cp_unpair_device cp; + struct device *dev; uint8_t status; cp.disconnect = 1; - cp.addr.type = BDADDR_BREDR; android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr); - if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, - sizeof(cp), &cp, unpair_device_complete, - NULL, NULL) > 0) - status = HAL_STATUS_SUCCESS; - else + dev = find_device(&cp.addr.bdaddr); + if (!dev) { status = HAL_STATUS_FAILED; + goto failed; + } + + if (dev->le_paired) { + cp.addr.type = dev->bdaddr_type; + + if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, + sizeof(cp), &cp, unpair_device_complete, + NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + } + + if (dev->bredr_paired) { + cp.addr.type = BDADDR_BREDR; + + if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, + sizeof(cp), &cp, unpair_device_complete, + NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + } + + status = HAL_STATUS_SUCCESS; - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, status); +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, + status); } static void handle_pin_reply_cmd(const void *buf, uint16_t len) @@ -2111,7 +3520,8 @@ static void handle_pin_reply_cmd(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, + status); } static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, bool accept) @@ -2199,7 +3609,8 @@ static void handle_ssp_reply_cmd(const void *buf, uint16_t len) break; } - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, + status); } static void handle_get_remote_services_cmd(const void *buf, uint16_t len) @@ -2212,31 +3623,185 @@ static void handle_get_remote_services_cmd(const void *buf, uint16_t len) status = browse_remote_sdp(&addr); - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES, - status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICES, status); +} + +static uint8_t get_device_uuids(struct device *dev) +{ + send_device_uuids_notif(dev); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_class(struct device *dev) +{ + send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_CLASS, + sizeof(dev->class), &dev->class); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_type(struct device *dev) +{ + uint8_t type = get_device_android_type(dev); + + send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TYPE, + sizeof(type), &type); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_service_rec(struct device *dev) +{ + DBG("Not implemented"); + + /* TODO */ + + return HAL_STATUS_FAILED; +} + +static uint8_t get_device_friendly_name(struct device *dev) +{ + if (!dev->friendly_name) + return HAL_STATUS_FAILED; + + send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_FRIENDLY_NAME, + strlen(dev->friendly_name), dev->friendly_name); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_rssi(struct device *dev) +{ + if (!dev->rssi) + return HAL_STATUS_FAILED; + + send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_RSSI, + sizeof(dev->rssi), &dev->rssi); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_version_info(struct device *dev) +{ + DBG("Not implemented"); + + /* TODO */ + + return HAL_STATUS_FAILED; +} + +static uint8_t get_device_timestamp(struct device *dev) +{ + uint32_t timestamp; + + timestamp = device_timestamp(dev); + + send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TIMESTAMP, + sizeof(timestamp), ×tamp); + + return HAL_STATUS_SUCCESS; +} + +static void get_remote_device_props(struct device *dev) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_remote_device_props *ev = (void *) buf; + uint128_t uuids[g_slist_length(dev->uuids)]; + uint8_t android_type; + uint32_t timestamp; + int size, i; + GSList *l; + + memset(buf, 0, sizeof(buf)); + + size = sizeof(*ev); + + ev->status = HAL_STATUS_SUCCESS; + bdaddr2android(&dev->bdaddr, ev->bdaddr); + + android_type = get_device_android_type(dev); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, + sizeof(android_type), &android_type); + ev->num_props++; + + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, + sizeof(dev->class), &dev->class); + ev->num_props++; + + if (dev->rssi) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, + sizeof(dev->rssi), &dev->rssi); + ev->num_props++; + } + + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, + strlen(dev->name), dev->name); + ev->num_props++; + + if (dev->friendly_name) { + size += fill_hal_prop(buf + size, + HAL_PROP_DEVICE_FRIENDLY_NAME, + strlen(dev->friendly_name), + dev->friendly_name); + ev->num_props++; + } + + for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) + memcpy(&uuids[i], l->data, sizeof(uint128_t)); + + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_UUIDS, sizeof(uuids), + uuids); + ev->num_props++; + + timestamp = get_device_timestamp(dev); + + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TIMESTAMP, + sizeof(timestamp), ×tamp); + ev->num_props++; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, size, buf); +} + +static void send_bonded_devices_props(void) +{ + GSList *l; + + for (l = bonded_devices; l; l = g_slist_next(l)) { + struct device *dev = l->data; + + get_remote_device_props(dev); + } } static void handle_enable_cmd(const void *buf, uint16_t len) { uint8_t status; - /* Framework expects all properties to be emitted while - * enabling adapter */ - get_properties(); + /* + * Framework expects all properties to be emitted while enabling + * adapter + */ + get_adapter_properties(); + + /* Sent also properties of bonded devices */ + send_bonded_devices_props(); if (adapter.current_settings & MGMT_SETTING_POWERED) { - status = HAL_STATUS_DONE; - goto failed; + status = HAL_STATUS_SUCCESS; + goto reply; } if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) { status = HAL_STATUS_FAILED; - goto failed; + goto reply; } status = HAL_STATUS_SUCCESS; -failed: - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status); +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status); } static void handle_disable_cmd(const void *buf, uint16_t len) @@ -2244,48 +3809,143 @@ static void handle_disable_cmd(const void *buf, uint16_t len) uint8_t status; if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { - status = HAL_STATUS_DONE; - goto failed; + status = HAL_STATUS_SUCCESS; + goto reply; } + /* Cancel all pending requests. Need it in case of ongoing paring */ + mgmt_cancel_index(mgmt_if, adapter.index); + if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) { status = HAL_STATUS_FAILED; - goto failed; + goto reply; } status = HAL_STATUS_SUCCESS; -failed: - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status); +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status); } static void handle_get_adapter_props_cmd(const void *buf, uint16_t len) { - get_properties(); + get_adapter_properties(); - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS, - HAL_STATUS_SUCCESS); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_ADAPTER_PROPS, HAL_STATUS_SUCCESS); } static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len) { - /* TODO */ + const struct hal_cmd_get_remote_device_props *cmd = buf; + struct device *dev; + uint8_t status; + bdaddr_t addr; - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROPS, - HAL_STATUS_FAILED); + android2bdaddr(cmd->bdaddr, &addr); + + dev = find_device(&addr); + if (!dev) { + status = HAL_STATUS_INVALID; + goto failed; + } + + get_remote_device_props(dev); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROPS, status); } static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len) { + const struct hal_cmd_get_remote_device_prop *cmd = buf; + struct device *dev; + uint8_t status; + bdaddr_t addr; + + android2bdaddr(cmd->bdaddr, &addr); + + dev = find_device(&addr); + if (!dev) { + status = HAL_STATUS_INVALID; + goto failed; + } + + switch (cmd->type) { + case HAL_PROP_DEVICE_NAME: + status = get_device_name(dev); + break; + case HAL_PROP_DEVICE_UUIDS: + status = get_device_uuids(dev); + break; + case HAL_PROP_DEVICE_CLASS: + status = get_device_class(dev); + break; + case HAL_PROP_DEVICE_TYPE: + status = get_device_type(dev); + break; + case HAL_PROP_DEVICE_SERVICE_REC: + status = get_device_service_rec(dev); + break; + case HAL_PROP_DEVICE_FRIENDLY_NAME: + status = get_device_friendly_name(dev); + break; + case HAL_PROP_DEVICE_RSSI: + status = get_device_rssi(dev); + break; + case HAL_PROP_DEVICE_VERSION_INFO: + status = get_device_version_info(dev); + break; + case HAL_PROP_DEVICE_TIMESTAMP: + status = get_device_timestamp(dev); + break; + default: + status = HAL_STATUS_FAILED; + break; + } + + if (status != HAL_STATUS_SUCCESS) + error("Failed to get device property (type %u status %u)", + cmd->type, status); + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROP, status); +} + +static uint8_t set_device_friendly_name(struct device *dev, const uint8_t *val, + uint16_t len) +{ + DBG(""); + + g_free(dev->friendly_name); + dev->friendly_name = g_strndup((const char *) val, len); + + if (dev->bredr_paired || dev->le_paired) + store_device_info(dev, DEVICES_FILE); + else + store_device_info(dev, CACHE_FILE); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t set_device_version_info(struct device *dev) +{ + DBG("Not implemented"); + /* TODO */ - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROP, - HAL_STATUS_FAILED); + return HAL_STATUS_FAILED; } static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len) { const struct hal_cmd_set_remote_device_prop *cmd = buf; + struct device *dev; uint8_t status; + bdaddr_t addr; if (len != sizeof(*cmd) + cmd->len) { error("Invalid set remote device prop cmd (0x%x), terminating", @@ -2294,74 +3954,199 @@ static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len) return; } - /* TODO */ + android2bdaddr(cmd->bdaddr, &addr); + + dev = find_device(&addr); + if (!dev) { + status = HAL_STATUS_INVALID; + goto failed; + } switch (cmd->type) { + case HAL_PROP_DEVICE_FRIENDLY_NAME: + status = set_device_friendly_name(dev, cmd->val, cmd->len); + break; + case HAL_PROP_DEVICE_VERSION_INFO: + status = set_device_version_info(dev); + break; default: - DBG("Unhandled property type 0x%x", cmd->type); status = HAL_STATUS_FAILED; break; } - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP, - status); + if (status != HAL_STATUS_SUCCESS) + error("Failed to set device property (type %u status %u)", + cmd->type, status); + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_REMOTE_DEVICE_PROP, status); } static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len) { /* TODO */ - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICE_REC, - HAL_STATUS_FAILED); + error("get_remote_service_record not supported"); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICE_REC, HAL_STATUS_FAILED); } static void handle_start_discovery_cmd(const void *buf, uint16_t len) { uint8_t status; - if (adapter.discovering) { - status = HAL_STATUS_DONE; - goto failed; - } - if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { status = HAL_STATUS_NOT_READY; goto failed; } - if (!start_discovery()) { - status = HAL_STATUS_FAILED; - goto failed; + switch (adapter.cur_discovery_type) { + case SCAN_TYPE_DUAL: + case SCAN_TYPE_BREDR: + break; + case SCAN_TYPE_NONE: + if (!start_discovery(SCAN_TYPE_DUAL)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case SCAN_TYPE_LE: + if (get_supported_discovery_type() == SCAN_TYPE_LE) + break; + + if (!stop_discovery(SCAN_TYPE_LE)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + adapter.exp_discovery_type = SCAN_TYPE_DUAL; + break; } status = HAL_STATUS_SUCCESS; + failed: - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, + status); } static void handle_cancel_discovery_cmd(const void *buf, uint16_t len) { uint8_t status; - if (!adapter.discovering) { - status = HAL_STATUS_DONE; - goto failed; - } - if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { status = HAL_STATUS_NOT_READY; goto failed; } - if (!stop_discovery()) { + switch (adapter.cur_discovery_type) { + case SCAN_TYPE_NONE: + break; + case SCAN_TYPE_LE: + if (get_supported_discovery_type() != SCAN_TYPE_LE) + break; + + if (gatt_device_found_cb) { + status = HAL_STATUS_BUSY; + goto failed; + } + + if (!stop_discovery(SCAN_TYPE_LE)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case SCAN_TYPE_DUAL: + case SCAN_TYPE_BREDR: + if (!stop_discovery(SCAN_TYPE_DUAL)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + adapter.exp_discovery_type = gatt_device_found_cb ? + SCAN_TYPE_LE : SCAN_TYPE_NONE; + break; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, + status); +} + +static void handle_dut_mode_conf_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_dut_mode_conf *cmd = buf; + char path[FILENAME_MAX]; + uint8_t status; + int fd, ret; + + DBG("enable %u", cmd->enable); + + snprintf(path, sizeof(path), DUT_MODE_FILE, adapter.index); + + fd = open(path, O_WRONLY); + if (fd < 0) { status = HAL_STATUS_FAILED; goto failed; } - status = HAL_STATUS_SUCCESS; + if (cmd->enable) + ret = write(fd, "1", sizeof("1")); + else + ret = write(fd, "0", sizeof("0")); + + if (ret < 0) + status = HAL_STATUS_FAILED; + else + status = HAL_STATUS_SUCCESS; + + close(fd); failed: - ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF, + status); +} + +static void handle_dut_mode_send_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_dut_mode_send *cmd = buf; + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid dut mode send cmd, terminating"); + raise(SIGTERM); + return; + } + + error("dut_mode_send not supported (cmd opcode %u)", cmd->opcode); + + /* TODO */ + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, + HAL_STATUS_FAILED); +} + +static void handle_le_test_mode_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_le_test_mode *cmd = buf; + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid le test mode cmd, terminating"); + raise(SIGTERM); + return; + } + + error("le_test_mode not supported (cmd opcode %u)", cmd->opcode); + + /* TODO */ + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE, + HAL_STATUS_FAILED); } static const struct ipc_handler cmd_handlers[] = { @@ -2406,19 +4191,91 @@ static const struct ipc_handler cmd_handlers[] = { { handle_pin_reply_cmd, false, sizeof(struct hal_cmd_pin_reply) }, /* HAL_OP_SSP_REPLY */ { handle_ssp_reply_cmd, false, sizeof(struct hal_cmd_ssp_reply) }, + /* HAL_OP_DUT_MODE_CONF */ + { handle_dut_mode_conf_cmd, false, + sizeof(struct hal_cmd_dut_mode_conf) }, + /* HAL_OP_DUT_MODE_SEND */ + { handle_dut_mode_send_cmd, true, + sizeof(struct hal_cmd_dut_mode_send) }, + /* HAL_OP_LE_TEST_MODE */ + { handle_le_test_mode_cmd, true, sizeof(struct hal_cmd_le_test_mode) }, }; -void bt_bluetooth_register(void) +bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode) { - DBG(""); + uint32_t missing_settings; + + DBG("mode 0x%x", mode); + + missing_settings = adapter.current_settings ^ + adapter.supported_settings; + + switch (mode) { + case HAL_MODE_DEFAULT: + if (missing_settings & MGMT_SETTING_BREDR) + set_mode(MGMT_OP_SET_BREDR, 0x01); + + if (missing_settings & MGMT_SETTING_LE) + set_mode(MGMT_OP_SET_LE, 0x01); + break; + case HAL_MODE_LE: + /* Fail if controller does not support LE */ + if (!(adapter.supported_settings & MGMT_SETTING_LE)) { + error("LE Mode not supported by controller"); + return false; + } + + /* If LE it is not yet enabled then enable it */ + if (!(adapter.current_settings & MGMT_SETTING_LE)) + set_mode(MGMT_OP_SET_LE, 0x01); - ipc_register(HAL_SERVICE_ID_BLUETOOTH, cmd_handlers, + /* Disable BR/EDR if it is enabled */ + if (adapter.current_settings & MGMT_SETTING_BREDR) + set_mode(MGMT_OP_SET_BREDR, 0x00); + break; + case HAL_MODE_BREDR: + /* Fail if controller does not support BR/EDR */ + if (!(adapter.supported_settings & MGMT_SETTING_BREDR)) { + error("BR/EDR Mode not supported"); + return false; + } + + /* Enable BR/EDR if it is not enabled */ + if (missing_settings & MGMT_SETTING_BREDR) + set_mode(MGMT_OP_SET_BREDR, 0x01); + + /* + * According to Core Spec 4.0 host should not disable LE in + * controller if it was enabled (Vol 2. Part E. 7.3.79). + * Core Spec 4.1 removed this limitation and chips seem to be + * handling this just fine anyway. + */ + if (adapter.current_settings & MGMT_SETTING_LE) + set_mode(MGMT_OP_SET_LE, 0x00); + break; + default: + error("Unknown mode 0x%x", mode); + return false; + } + + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); + + return true; } void bt_bluetooth_unregister(void) { DBG(""); - ipc_unregister(HAL_SERVICE_ID_CORE); + g_slist_free_full(bonded_devices, (GDestroyNotify) free_device); + bonded_devices = NULL; + + g_slist_free_full(cached_devices, (GDestroyNotify) free_device); + cached_devices = NULL; + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE); + hal_ipc = NULL; } diff --git a/android/bluetooth.h b/android/bluetooth.h index 86872ee..6a3e766 100644 --- a/android/bluetooth.h +++ b/android/bluetooth.h @@ -2,37 +2,56 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr); -bool bt_bluetooth_start(int index, bt_bluetooth_ready cb); +bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb); typedef void (*bt_bluetooth_stopped)(void); bool bt_bluetooth_stop(bt_bluetooth_stopped cb); void bt_bluetooth_cleanup(void); -void bt_bluetooth_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len); - -void bt_bluetooth_register(void); +bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode); void bt_bluetooth_unregister(void); int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint); void bt_adapter_remove_record(uint32_t handle); + +typedef void (*bt_le_device_found)(const bdaddr_t *addr, uint8_t addr_type, + int rssi, uint16_t eir_len, + const void *eir, bool discoverable); +bool bt_le_discovery_start(bt_le_device_found cb); + +typedef void (*bt_le_discovery_stopped)(void); +bool bt_le_discovery_stop(bt_le_discovery_stopped cb); + +typedef void (*bt_le_set_advertising_done)(uint8_t status, void *user_data); +bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb, + void *user_data); + +uint8_t bt_get_device_android_type(const bdaddr_t *addr); +const char *bt_get_adapter_name(void); +bool bt_device_is_bonded(const bdaddr_t *bdaddr); + +typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr, + int8_t rssi, void *user_data); +bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb, + void *user_data); diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c new file mode 100644 index 0000000..57f97f4 --- /dev/null +++ b/android/bluetoothd-snoop.c @@ -0,0 +1,251 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include +#if defined(ANDROID) +#include +#endif + +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/mgmt.h" + +#include "monitor/mainloop.h" +#include "src/shared/btsnoop.h" + +#define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log" + +#define MAX_PACKET_SIZE (1486 + 4) + +static struct btsnoop *snoop = NULL; +static uint8_t monitor_buf[MAX_PACKET_SIZE]; +static int monitor_fd = -1; + +static void signal_callback(int signum, void *user_data) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + mainloop_quit(); + break; + } +} + +static uint32_t get_flags_from_opcode(uint16_t opcode) +{ + switch (opcode) { + case BTSNOOP_OPCODE_NEW_INDEX: + case BTSNOOP_OPCODE_DEL_INDEX: + break; + case BTSNOOP_OPCODE_COMMAND_PKT: + return 0x02; + case BTSNOOP_OPCODE_EVENT_PKT: + return 0x03; + case BTSNOOP_OPCODE_ACL_TX_PKT: + return 0x00; + case BTSNOOP_OPCODE_ACL_RX_PKT: + return 0x01; + case BTSNOOP_OPCODE_SCO_TX_PKT: + case BTSNOOP_OPCODE_SCO_RX_PKT: + break; + } + + return 0xff; +} + +static void data_callback(int fd, uint32_t events, void *user_data) +{ + unsigned char control[32]; + struct mgmt_hdr hdr; + struct msghdr msg; + struct iovec iov[2]; + + if (events & (EPOLLERR | EPOLLHUP)) { + mainloop_remove_fd(monitor_fd); + return; + } + + iov[0].iov_base = &hdr; + iov[0].iov_len = MGMT_HDR_SIZE; + iov[1].iov_base = monitor_buf; + iov[1].iov_len = sizeof(monitor_buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + while (true) { + struct cmsghdr *cmsg; + struct timeval *tv = NULL; + struct timeval ctv; + uint16_t opcode, index, pktlen; + uint32_t flags; + ssize_t len; + + len = recvmsg(monitor_fd, &msg, MSG_DONTWAIT); + if (len < 0) + break; + + if (len < MGMT_HDR_SIZE) + break; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + + if (cmsg->cmsg_type == SCM_TIMESTAMP) { + memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv)); + tv = &ctv; + } + } + + opcode = btohs(hdr.opcode); + index = btohs(hdr.index); + pktlen = btohs(hdr.len); + + if (index) + continue; + + flags = get_flags_from_opcode(opcode); + if (flags != 0xff) + btsnoop_write(snoop, tv, flags, monitor_buf, pktlen); + } +} + +static int open_monitor(const char *path) +{ + struct sockaddr_hci addr; + int opt = 1; + + snoop = btsnoop_create(path, BTSNOOP_TYPE_HCI); + if (!snoop) + return -1; + + monitor_fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); + if (monitor_fd < 0) + goto failed; + + memset(&addr, 0, sizeof(addr)); + addr.hci_family = AF_BLUETOOTH; + addr.hci_dev = HCI_DEV_NONE; + addr.hci_channel = HCI_CHANNEL_MONITOR; + + if (bind(monitor_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + goto failed_close; + + if (setsockopt(monitor_fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) + < 0) + goto failed_close; + + mainloop_add_fd(monitor_fd, EPOLLIN, data_callback, NULL, NULL); + + return 0; + +failed_close: + close(monitor_fd); + monitor_fd = -1; + +failed: + btsnoop_unref(snoop); + snoop = NULL; + + return -1; +} + +static void close_monitor(void) +{ + btsnoop_unref(snoop); + snoop = NULL; + + close(monitor_fd); + monitor_fd = -1; +} + +static void set_capabilities(void) +{ +#if defined(ANDROID) + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + + /* + * CAP_NET_RAW: for snooping + * CAP_DAC_READ_SEARCH: override path search permissions + */ + cap.effective = cap.permitted = + CAP_TO_MASK(CAP_NET_RAW) | + CAP_TO_MASK(CAP_DAC_READ_SEARCH); + cap.inheritable = 0; + + /* TODO: Move to cap_set_proc once bionic support it */ + if (capset(&header, &cap) < 0) + exit(EXIT_FAILURE); +#endif +} + +int main(int argc, char *argv[]) +{ + const char *path; + sigset_t mask; + + set_capabilities(); + + if (argc > 1) + path = argv[1]; + else + path = DEFAULT_SNOOP_FILE; + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + if (!strcmp(DEFAULT_SNOOP_FILE, path)) + rename(DEFAULT_SNOOP_FILE, DEFAULT_SNOOP_FILE ".old"); + + if (open_monitor(path) < 0) { + printf("Failed to start bluetoothd_snoop\n"); + return EXIT_FAILURE; + } + + mainloop_run(); + + close_monitor(); + + return EXIT_SUCCESS; +} diff --git a/android/client/haltest.c b/android/client/haltest.c index 7154d27..d86fe49 100644 --- a/android/client/haltest.c +++ b/android/client/haltest.c @@ -31,16 +31,18 @@ #include "history.h" const struct interface *interfaces[] = { + &audio_if, + &sco_if, &bluetooth_if, &av_if, -#if PLATFORM_SDK_VERSION > 17 + &rc_if, &gatt_if, &gatt_client_if, &gatt_server_if, -#endif &hf_if, &hh_if, &pan_if, + &hl_if, &sock_if, NULL }; @@ -254,12 +256,12 @@ static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size) static void process_line(char *line_buffer) { - char *argv[10]; + char *argv[50]; int argc; int i = 0; struct method *m; - argc = command_line_to_argv(line_buffer, argv, 10); + argc = command_line_to_argv(line_buffer, argv, 50); if (argc < 1) return; @@ -383,20 +385,24 @@ static void init(void) static const char * const inames[] = { BT_PROFILE_HANDSFREE_ID, BT_PROFILE_ADVANCED_AUDIO_ID, + BT_PROFILE_AV_RC_ID, BT_PROFILE_HEALTH_ID, BT_PROFILE_HIDHOST_ID, BT_PROFILE_PAN_ID, -#if PLATFORM_SDK_VERSION > 17 BT_PROFILE_GATT_ID, -#endif BT_PROFILE_SOCKETS_ID }; const struct method *m; const char *argv[4]; - char init_line[] = "bluetooth init"; + char init_audio[] = "audio init"; + char init_sco[] = "sco init"; + char init_bt[] = "bluetooth init"; uint32_t i; - process_line(init_line); + process_line(init_audio); + process_line(init_sco); + process_line(init_bt); + m = get_interface_method("bluetooth", "get_profile_interface"); for (i = 0; i < NELEM(inames); ++i) { @@ -405,7 +411,7 @@ static void init(void) } /* Init what is available to init */ - for (i = 1; i < NELEM(interfaces) - 1; ++i) { + for (i = 2; i < NELEM(interfaces) - 1; ++i) { m = get_interface_method(interfaces[i]->name, "init"); if (m != NULL) m->func(2, argv); diff --git a/android/client/history.c b/android/client/history.c index ee285da..3dae27a 100644 --- a/android/client/history.c +++ b/android/client/history.c @@ -22,9 +22,7 @@ #include "history.h" -/* - * Very simple history storage for easy usage of tool - */ +/* Very simple history storage for easy usage of tool */ #define HISTORY_DEPTH 20 #define LINE_SIZE 100 diff --git a/android/client/hwmodule.c b/android/client/hwmodule.c deleted file mode 100644 index 80e7475..0000000 --- a/android/client/hwmodule.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2013 Intel Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - -#include "hardware/hardware.h" - -int hw_get_module(const char *id, const struct hw_module_t **module) -{ - extern struct hw_module_t HAL_MODULE_INFO_SYM; - - *module = &HAL_MODULE_INFO_SYM; - - return 0; -} diff --git a/android/client/if-audio.c b/android/client/if-audio.c new file mode 100644 index 0000000..6a48025 --- /dev/null +++ b/android/client/if-audio.c @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "if-main.h" +#include "../hal-utils.h" +#include "pthread.h" +#include "unistd.h" +#include + +audio_hw_device_t *if_audio = NULL; +static struct audio_stream_out *stream_out = NULL; + +static size_t buffer_size = 0; +static pthread_t play_thread = 0; +static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER; + +enum state { + STATE_STOPPED, + STATE_STOPPING, + STATE_PLAYING, + STATE_SUSPENDED, + STATE_MAX +}; + +SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)") + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_MONO), + DELEMENT(AUDIO_CHANNEL_OUT_STEREO), + DELEMENT(AUDIO_CHANNEL_OUT_QUAD), + DELEMENT(AUDIO_CHANNEL_OUT_SURROUND), + DELEMENT(AUDIO_CHANNEL_OUT_5POINT1), + DELEMENT(AUDIO_CHANNEL_OUT_7POINT1), + DELEMENT(AUDIO_CHANNEL_OUT_ALL), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), +ENDMAP + +SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)") + DELEMENT(AUDIO_FORMAT_DEFAULT), + DELEMENT(AUDIO_FORMAT_PCM), + DELEMENT(AUDIO_FORMAT_MP3), + DELEMENT(AUDIO_FORMAT_AMR_NB), + DELEMENT(AUDIO_FORMAT_AMR_WB), + DELEMENT(AUDIO_FORMAT_AAC), + DELEMENT(AUDIO_FORMAT_HE_AAC_V1), + DELEMENT(AUDIO_FORMAT_HE_AAC_V2), + DELEMENT(AUDIO_FORMAT_VORBIS), + DELEMENT(AUDIO_FORMAT_MAIN_MASK), + DELEMENT(AUDIO_FORMAT_SUB_MASK), + DELEMENT(AUDIO_FORMAT_PCM_16_BIT), + DELEMENT(AUDIO_FORMAT_PCM_8_BIT), + DELEMENT(AUDIO_FORMAT_PCM_32_BIT), + DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT), +ENDMAP + +static int current_state = STATE_STOPPED; + +#define SAMPLERATE 44100 +static short sample[SAMPLERATE]; +static uint16_t sample_pos; + +static void init_p(int argc, const char **argv) +{ + int err; + const hw_module_t *module; + audio_hw_device_t *device; + + err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, + AUDIO_HARDWARE_MODULE_ID_A2DP, &module); + if (err) { + haltest_error("hw_get_module_by_class returned %d\n", err); + return; + } + + err = audio_hw_device_open(module, &device); + if (err) { + haltest_error("audio_hw_device_open returned %d\n", err); + return; + } + + if_audio = device; +} + +static int feed_from_file(short *buffer, void *data) +{ + FILE *in = data; + return fread(buffer, buffer_size, 1, in); +} + +static int feed_from_generator(short *buffer, void *data) +{ + size_t i = 0; + float volume = 0.5; + float *freq = data; + float f = 1; + + if (freq) + f = *freq; + + /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/ + for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) { + if (sample_pos >= SAMPLERATE) + sample_pos = sample_pos % SAMPLERATE; + + /* Use the same sample for both channels */ + buffer[i++] = sample[sample_pos] * volume; + buffer[i++] = sample[sample_pos] * volume; + + sample_pos += f; + } + + return buffer_size; +} + +static void prepare_sample(void) +{ + int x; + double s; + + haltest_info("Preparing audio sample...\n"); + + for (x = 0; x < SAMPLERATE; x++) { + /* prepare sinusoidal 1Hz sample */ + s = (2.0 * 3.14159) * ((double)x / SAMPLERATE); + s = sin(s); + + /* remap <-1, 1> to signed 16bit PCM range */ + sample[x] = s * 32767; + } + + sample_pos = 0; +} + +static void *playback_thread(void *data) +{ + int (*filbuff_cb) (short*, void*); + short buffer[buffer_size / sizeof(short)]; + size_t len = 0; + ssize_t w_len = 0; + FILE *in = data; + void *cb_data = NULL; + float freq = 440.0; + + /* Use file or fall back to generator */ + if (in) { + filbuff_cb = feed_from_file; + cb_data = in; + } else { + prepare_sample(); + filbuff_cb = feed_from_generator; + cb_data = &freq; + } + + pthread_mutex_lock(&state_mutex); + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); + + do { + pthread_mutex_lock(&state_mutex); + + if (current_state == STATE_STOPPING) { + pthread_mutex_unlock(&state_mutex); + break; + } else if (current_state == STATE_SUSPENDED) { + pthread_mutex_unlock(&state_mutex); + usleep(500); + continue; + } + + pthread_mutex_unlock(&state_mutex); + + len = filbuff_cb(buffer, cb_data); + + pthread_mutex_lock(&outstream_mutex); + if (!stream_out) { + pthread_mutex_unlock(&outstream_mutex); + break; + } + + w_len = stream_out->write(stream_out, buffer, buffer_size); + pthread_mutex_unlock(&outstream_mutex); + } while (len && w_len > 0); + + if (in) + fclose(in); + + pthread_mutex_lock(&state_mutex); + current_state = STATE_STOPPED; + pthread_mutex_unlock(&state_mutex); + + haltest_info("Done playing.\n"); + + return NULL; +} + +static void play_p(int argc, const char **argv) +{ + const char *fname = NULL; + FILE *in = NULL; + + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_error("Invalid audio file path.\n"); + haltest_info("Using sound generator.\n"); + } else { + fname = argv[2]; + in = fopen(fname, "r"); + + if (in == NULL) { + haltest_error("Cannot open file: %s\n", fname); + return; + } + haltest_info("Playing file: %s\n", fname); + } + + if (buffer_size == 0) { + haltest_error("Invalid buffer size. Was stream_out opened?\n"); + goto fail; + } + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + haltest_error("Already playing or stream suspended!\n"); + pthread_mutex_unlock(&state_mutex); + goto fail; + } + pthread_mutex_unlock(&state_mutex); + + if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) { + haltest_error("Cannot create playback thread!\n"); + goto fail; + } + + return; +fail: + if (in) + fclose(in); +} + +static void stop_p(int argc, const char **argv) +{ + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) { + pthread_mutex_unlock(&state_mutex); + return; + } + + current_state = STATE_STOPPING; + pthread_mutex_unlock(&state_mutex); + + pthread_mutex_lock(&outstream_mutex); + stream_out->common.standby(&stream_out->common); + pthread_mutex_unlock(&outstream_mutex); +} + +static void open_output_stream_p(int argc, const char **argv) +{ + int err; + + RETURN_IF_NULL(if_audio); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_PLAYING) { + haltest_error("Already playing!\n"); + pthread_mutex_unlock(&state_mutex); + return; + } + pthread_mutex_unlock(&state_mutex); + + err = if_audio->open_output_stream(if_audio, + 0, + AUDIO_DEVICE_OUT_ALL_A2DP, + AUDIO_OUTPUT_FLAG_NONE, + NULL, + &stream_out); + if (err < 0) { + haltest_error("open output stream returned %d\n", err); + return; + } + + buffer_size = stream_out->common.get_buffer_size(&stream_out->common); + if (buffer_size == 0) + haltest_error("Invalid buffer size received!\n"); + else + haltest_info("Using buffer size: %zu\n", buffer_size); +} + +static void close_output_stream_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + stop_p(argc, argv); + + haltest_info("Waiting for playback thread...\n"); + pthread_join(play_thread, NULL); + + if_audio->close_output_stream(if_audio, stream_out); + + stream_out = NULL; + buffer_size = 0; +} + +static void cleanup_p(int argc, const char **argv) +{ + int err; + + RETURN_IF_NULL(if_audio); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + pthread_mutex_unlock(&state_mutex); + close_output_stream_p(0, NULL); + } else { + pthread_mutex_unlock(&state_mutex); + } + + err = audio_hw_device_close(if_audio); + if (err < 0) { + haltest_error("audio_hw_device_close returned %d\n", err); + return; + } + + if_audio = NULL; +} + +static void suspend_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_PLAYING) { + pthread_mutex_unlock(&state_mutex); + return; + } + current_state = STATE_SUSPENDED; + pthread_mutex_unlock(&state_mutex); + + pthread_mutex_lock(&outstream_mutex); + stream_out->common.standby(&stream_out->common); + pthread_mutex_unlock(&outstream_mutex); +} + +static void resume_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_SUSPENDED) + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); +} + +static void get_latency_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + haltest_info("Output audio stream latency: %d\n", + stream_out->get_latency(stream_out)); +} + +static void get_buffer_size_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + haltest_info("Current output buffer size: %zu\n", + stream_out->common.get_buffer_size(&stream_out->common)); +} + +static void get_channels_p(int argc, const char **argv) +{ + audio_channel_mask_t channels; + + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + channels = stream_out->common.get_channels(&stream_out->common); + + haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels)); +} + +static void get_format_p(int argc, const char **argv) +{ + audio_format_t format; + + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + format = stream_out->common.get_format(&stream_out->common); + + haltest_info("Format: %s\n", audio_format_t2str(format)); +} + +static void get_sample_rate_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + haltest_info("Current sample rate: %d\n", + stream_out->common.get_sample_rate(&stream_out->common)); +} + +static void get_parameters_p(int argc, const char **argv) +{ + const char *keystr; + + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_info("No keys given.\n"); + keystr = ""; + } else { + keystr = argv[2]; + } + + haltest_info("Current parameters: %s\n", + stream_out->common.get_parameters(&stream_out->common, + keystr)); +} + +static void set_parameters_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_error("No key=value; pairs given.\n"); + return; + } + + stream_out->common.set_parameters(&stream_out->common, argv[2]); +} + +static void set_sample_rate_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + if (argc < 3) + return; + + stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2])); +} + +static void init_check_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + + haltest_info("Init check result: %d\n", if_audio->init_check(if_audio)); +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHOD(cleanup), + STD_METHOD(open_output_stream), + STD_METHOD(close_output_stream), + STD_METHODH(play, ""), + STD_METHOD(stop), + STD_METHOD(suspend), + STD_METHOD(resume), + STD_METHOD(get_latency), + STD_METHOD(get_buffer_size), + STD_METHOD(get_channels), + STD_METHOD(get_format), + STD_METHOD(get_sample_rate), + STD_METHODH(get_parameters, ""), + STD_METHODH(set_parameters, ""), + STD_METHODH(set_sample_rate, ""), + STD_METHOD(init_check), + END_METHOD +}; + +const struct interface audio_if = { + .name = "audio", + .methods = methods +}; diff --git a/android/client/if-av.c b/android/client/if-av.c index 0470e0d..8d1f69b 100644 --- a/android/client/if-av.c +++ b/android/client/if-av.c @@ -80,7 +80,7 @@ static void connect_p(int argc, const char **argv) { bt_bdaddr_t addr; - RETURN_IF_NULL(if_hh); + RETURN_IF_NULL(if_av); VERIFY_ADDR_ARG(2, &addr); EXEC(if_av->connect, &addr); @@ -101,7 +101,7 @@ static void disconnect_p(int argc, const char **argv) { bt_bdaddr_t addr; - RETURN_IF_NULL(if_hh); + RETURN_IF_NULL(if_av); VERIFY_ADDR_ARG(2, &addr); EXEC(if_av->disconnect, &addr); diff --git a/android/client/if-bt.c b/android/client/if-bt.c index 0cd43db..4d6ff83 100644 --- a/android/client/if-bt.c +++ b/android/client/if-bt.c @@ -80,9 +80,7 @@ static void dump_properties(int num_properties, bt_property_t *properties) } } -/* - * Cache for remote devices, stored in sorted array - */ +/* Cache for remote devices, stored in sorted array */ static bt_bdaddr_t *remote_devices = NULL; static int remote_devices_cnt = 0; static int remote_devices_capacity = 0; @@ -307,13 +305,11 @@ static void dut_mode_recv_cb(uint16_t opcode, uint8_t *buf, uint8_t len) haltest_info("%s\n", __func__); } -#if PLATFORM_SDK_VERSION > 17 static void le_test_mode_cb(bt_status_t status, uint16_t num_packets) { - haltest_info("%s %s %d\n", __func__, bt_state_t2str(status), + haltest_info("%s %s %d\n", __func__, bt_status_t2str(status), num_packets); } -#endif static bt_callbacks_t bt_callbacks = { .size = sizeof(bt_callbacks), @@ -328,9 +324,7 @@ static bt_callbacks_t bt_callbacks = { .acl_state_changed_cb = acl_state_changed_cb, .thread_evt_cb = thread_evt_cb, .dut_mode_recv_cb = dut_mode_recv_cb, -#if PLATFORM_SDK_VERSION > 17 .le_test_mode_cb = le_test_mode_cb -#endif }; static void init_p(int argc, const char **argv) @@ -470,9 +464,7 @@ static void set_adapter_property_p(int argc, const char **argv) EXEC(if_bluetooth->set_adapter_property, &property); } -/* - * This function is to be used for completion methods that need only address - */ +/* This function is to be used for completion methods that need only address */ static void complete_addr_c(int argc, const char **argv, enum_func *enum_func, void **user) { @@ -547,9 +539,7 @@ static void set_remote_device_property_p(int argc, const char **argv) EXEC(if_bluetooth->set_remote_device_property, &addr, &property); } -/* - * For now uuid is not autocompleted. Use routine for complete_addr_c - */ +/* For now uuid is not autocompleted. Use routine for complete_addr_c */ #define get_remote_service_record_c complete_addr_c static void get_remote_service_record_p(int argc, const char **argv) @@ -726,10 +716,8 @@ static void get_profile_interface_c(int argc, const char **argv, BT_PROFILE_SOCKETS_ID, BT_PROFILE_HIDHOST_ID, BT_PROFILE_PAN_ID, -#if PLATFORM_SDK_VERSION > 17 BT_PROFILE_GATT_ID, BT_PROFILE_AV_RC_ID, -#endif NULL }; @@ -743,7 +731,6 @@ static void get_profile_interface_p(int argc, const char **argv) { const char *id; const void **pif = NULL; - const void *dummy = NULL; RETURN_IF_NULL(if_bluetooth); if (argc <= 2) { @@ -758,19 +745,17 @@ static void get_profile_interface_p(int argc, const char **argv) else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_ID, id) == 0) pif = (const void **) &if_av; else if (strcmp(BT_PROFILE_HEALTH_ID, id) == 0) - pif = &dummy; /* TODO: change when if_hl is there */ + pif = (const void **) &if_hl; else if (strcmp(BT_PROFILE_SOCKETS_ID, id) == 0) pif = (const void **) &if_sock; else if (strcmp(BT_PROFILE_HIDHOST_ID, id) == 0) pif = (const void **) &if_hh; else if (strcmp(BT_PROFILE_PAN_ID, id) == 0) pif = (const void **) &if_pan; -#if PLATFORM_SDK_VERSION > 17 else if (strcmp(BT_PROFILE_AV_RC_ID, id) == 0) - pif = &dummy; /* TODO: change when if_rc is there */ + pif = (const void **) &if_rc; else if (strcmp(BT_PROFILE_GATT_ID, id) == 0) pif = (const void **) &if_gatt; -#endif else haltest_error("%s is not correct for get_profile_interface\n", id); @@ -797,6 +782,32 @@ static void dut_mode_configure_p(int argc, const char **argv) EXEC(if_bluetooth->dut_mode_configure, mode); } +static void dut_mode_send_p(int argc, const char **argv) +{ + haltest_error("not implemented\n"); +} + +static void le_test_mode_p(int argc, const char **argv) +{ + haltest_error("not implemented\n"); +} + +static void config_hci_snoop_log_p(int argc, const char **argv) +{ + uint8_t mode; + + RETURN_IF_NULL(if_bluetooth); + + if (argc <= 2) { + haltest_error("No mode specified\n"); + return; + } + + mode = strtol(argv[2], NULL, 0); + + EXEC(if_bluetooth->config_hci_snoop_log, mode); +} + static struct method methods[] = { STD_METHOD(init), STD_METHOD(cleanup), @@ -820,6 +831,9 @@ static struct method methods[] = { STD_METHODCH(ssp_reply, "
1|0 []"), STD_METHODCH(get_profile_interface, ""), STD_METHODH(dut_mode_configure, ""), + STD_METHOD(dut_mode_send), + STD_METHOD(le_test_mode), + STD_METHODH(config_hci_snoop_log, ""), END_METHOD }; diff --git a/android/client/if-gatt.c b/android/client/if-gatt.c index bb53952..252e89d 100644 --- a/android/client/if-gatt.c +++ b/android/client/if-gatt.c @@ -22,20 +22,14 @@ const btgatt_interface_t *if_gatt = NULL; -/* In version 19 some callback were changed. +/* + * In version 19 some callback were changed. * btgatt_char_id_t -> btgatt_gatt_id_t * bt_uuid_t -> btgatt_gatt_id_t */ -#if PLATFORM_SDK_VERSION > 18 #define str2btgatt_descr_id_t str2btgatt_gatt_id_t #define btgatt_descr_id_t2str btgatt_gatt_id_t2str #define btgatt_descr_id_t btgatt_gatt_id_t -#else -#define btgatt_descr_id_t2str gatt_uuid_t2str -#define str2btgatt_descr_id_t(a, b) gatt_str2bt_uuid_t(a, -1, b) -#define btgatt_gatt_id_t btgatt_char_id_t -#define btgatt_descr_id_t bt_uuid_t -#endif #define MAX_CHAR_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11) #define MAX_SRVC_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11 + 1 + 11) @@ -70,6 +64,9 @@ const btgatt_interface_t *if_gatt = NULL; #define VERIFY_CLIENT_IF(n, v) VERIFY_INT_ARG(n, v, "No client_if specified\n") #define VERIFY_SERVER_IF(n, v) VERIFY_INT_ARG(n, v, "No server_if specified\n") #define VERIFY_CONN_ID(n, v) VERIFY_INT_ARG(n, v, "No conn_if specified\n") +#define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n") +#define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n") +#define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n") #define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n") #define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v) @@ -436,7 +433,7 @@ static void gattc_disconnect_cb(int conn_id, int status, int client_if, */ static void gattc_search_complete_cb(int conn_id, int status) { - haltest_info("%s: conn_id=%d status=%s\n", __func__, conn_id, status); + haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); } /* Reports GATT services on a remote device */ @@ -583,6 +580,13 @@ static void gattc_read_remote_rssi_cb(int client_if, bt_bdaddr_t *bda, int rssi, client_if, bt_bdaddr_t2str(bda, buf), rssi, status); } +/* Callback invoked in response to listen */ +static void gattc_listen_cb(int status, int client_if) +{ + haltest_info("%s: client_if=%d status=%d\n", __func__, client_if, + status); +} + static const btgatt_client_callbacks_t btgatt_client_callbacks = { .register_client_cb = gattc_register_client_cb, .scan_result_cb = gattc_scan_result_cb, @@ -600,7 +604,8 @@ static const btgatt_client_callbacks_t btgatt_client_callbacks = { .read_descriptor_cb = gattc_read_descriptor_cb, .write_descriptor_cb = gattc_write_descriptor_cb, .execute_write_cb = gattc_execute_write_cb, - .read_remote_rssi_cb = gattc_read_remote_rssi_cb + .read_remote_rssi_cb = gattc_read_remote_rssi_cb, + .listen_cb = gattc_listen_cb, }; /* BT-GATT Server callbacks */ @@ -861,7 +866,7 @@ static void scan_p(int argc, const char **argv) VERIFY_CLIENT_IF(2, client_if); /* start */ - if (argc >= 3) + if (argc >= 4) start = atoi(argv[3]); EXEC(if_gatt->client->scan, client_if, start); @@ -929,6 +934,27 @@ static void disconnect_p(int argc, const char **argv) EXEC(if_gatt->client->disconnect, client_if, &bd_addr, conn_id); } +/* listen */ + +/* Same completion as unregister for now, start stop is not auto completed */ +#define listen_c unregister_client_c + +static void listen_p(int argc, const char **argv) +{ + int client_if; + int start = 1; + + RETURN_IF_NULL(if_gatt); + + VERIFY_CLIENT_IF(2, client_if); + + /* start */ + if (argc >= 4) + start = atoi(argv[3]); + + EXEC(if_gatt->client->listen, client_if, start); +} + /* refresh */ static void refresh_c(int argc, const char **argv, enum_func *enum_func, @@ -968,19 +994,21 @@ static void search_service_c(int argc, const char **argv, enum_func *enum_func, static void search_service_p(int argc, const char **argv) { int conn_id; - bt_uuid_t filter_uuid; RETURN_IF_NULL(if_gatt); VERIFY_CONN_ID(2, conn_id); /* uuid */ - if (argc <= 3) - memset(&filter_uuid, 0, sizeof(bt_uuid_t)); - else - gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid); + if (argc <= 3) { + EXEC(if_gatt->client->search_service, conn_id, NULL); + + } else { + bt_uuid_t filter_uuid; - EXEC(if_gatt->client->search_service, conn_id, &filter_uuid); + gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid); + EXEC(if_gatt->client->search_service, conn_id, &filter_uuid); + } } /* get_included_service */ @@ -1410,6 +1438,7 @@ static struct method client_methods[] = { STD_METHODCH(get_device_type, ""), STD_METHODCH(test_command, " [u1] [u2] [u3] [u4] [u5]"), + STD_METHODCH(listen, " [1|0]"), END_METHOD }; @@ -1725,7 +1754,38 @@ static void gatts_send_indication_p(int argc, const char *argv[]) static void gatts_send_response_p(int argc, const char *argv[]) { - haltest_warn("%s is not implemented yet\n", __func__); + int conn_id; + int trans_id; + int status; + btgatt_response_t data; + + memset(&data, 0, sizeof(data)); + + RETURN_IF_NULL(if_gatt); + + VERIFY_CONN_ID(2, conn_id); + VERIFY_TRANS_ID(3, trans_id); + VERIFY_STATUS(4, status); + VERIFY_HANDLE(5, data.attr_value.handle); + VERIFY_OFFSET(6, data.attr_value.offset); + + data.attr_value.auth_req = 0; + data.attr_value.len = 0; + + if (argc <= 7) { + haltest_error("No data specified\n"); + return; + } + + data.attr_value.len = strlen(argv[7]); + scan_field(argv[7], data.attr_value.len, data.attr_value.value, + sizeof(data.attr_value.value)); + + + haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id, + status); + + EXEC(if_gatt->server->send_response, conn_id, trans_id, status, &data); } #define GATTS_METHODH(n, h) METHOD(#n, gatts_##n##_p, NULL, h) @@ -1741,14 +1801,16 @@ static struct method server_methods[] = { " "), GATTS_METHODCH(add_characteristic, " "), - GATTS_METHODCH(add_descriptor, " "), + GATTS_METHODCH(add_descriptor, + " "), GATTS_METHODCH(start_service, " "), GATTS_METHODCH(stop_service, " "), GATTS_METHODCH(delete_service, " "), GATTS_METHODH(send_indication, " []"), - GATTS_METHODH(send_response, " "), + GATTS_METHODH(send_response, + " []"), END_METHOD }; diff --git a/android/client/if-hh.c b/android/client/if-hh.c index b8ebc8e..df3fd30 100644 --- a/android/client/if-hh.c +++ b/android/client/if-hh.c @@ -128,9 +128,7 @@ static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, bthh_protocol_mode_t2str(mode)); } -/* - * Callback for get/set_idle_time api. - */ +/* Callback for get/set_idle_time api. */ static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate) { @@ -178,7 +176,7 @@ static void init_p(int argc, const char **argv) /* connect */ -static void connect_c(int argc, const const char **argv, enum_func *enum_func, +static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { @@ -229,6 +227,9 @@ static void virtual_unplug_p(int argc, const char **argv) /* set_info */ +/* Same completion as connect_c */ +#define set_info_c connect_c + static void set_info_p(int argc, const char **argv) { bt_bdaddr_t addr; @@ -236,15 +237,20 @@ static void set_info_p(int argc, const char **argv) RETURN_IF_NULL(if_hh); VERIFY_ADDR_ARG(2, &addr); - /* TODO: set_info does not seem to be called anywhere */ + memset(&hid_info, 0, sizeof(hid_info)); + + /* + * This command is intentionally not supported. See comment from + * bt_hid_info() in android/hidhost.c + */ EXEC(if_hh->set_info, &addr, hid_info); } /* get_protocol */ -static void get_protocol_c(int argc, const const char **argv, - enum_func *enum_func, void **user) +static void get_protocol_c(int argc, const char **argv, enum_func *enum_func, + void **user) { if (argc == 3) { *user = connected_device_addr; @@ -296,8 +302,8 @@ static void set_protocol_p(int argc, const char **argv) /* get_report */ -static void get_report_c(int argc, const const char **argv, - enum_func *enum_func, void **user) +static void get_report_c(int argc, const char **argv, enum_func *enum_func, + void **user) { if (argc == 3) { *user = connected_device_addr; @@ -341,8 +347,8 @@ static void get_report_p(int argc, const char **argv) /* set_report */ -static void set_report_c(int argc, const const char **argv, - enum_func *enum_func, void **user) +static void set_report_c(int argc, const char **argv, enum_func *enum_func, + void **user) { if (argc == 3) { *user = connected_device_addr; @@ -377,7 +383,7 @@ static void set_report_p(int argc, const char **argv) /* send_data */ -static void send_data_c(int argc, const const char **argv, enum_func *enum_func, +static void send_data_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { @@ -416,7 +422,7 @@ static struct method methods[] = { STD_METHODCH(connect, ""), STD_METHODCH(disconnect, ""), STD_METHODCH(virtual_unplug, ""), - STD_METHOD(set_info), + STD_METHODCH(set_info, ""), STD_METHODCH(get_protocol, " "), STD_METHODCH(set_protocol, " "), STD_METHODCH(get_report, " "), diff --git a/android/client/if-hl.c b/android/client/if-hl.c new file mode 100644 index 0000000..557a205 --- /dev/null +++ b/android/client/if-hl.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include + +#include "if-main.h" +#include "pollhandler.h" +#include "../hal-utils.h" + +SINTMAP(bthl_mdep_role_t, -1, "(unknown)") + DELEMENT(BTHL_MDEP_ROLE_SOURCE), + DELEMENT(BTHL_MDEP_ROLE_SINK), +ENDMAP + +SINTMAP(bthl_channel_type_t, -1, "(unknown)") + DELEMENT(BTHL_CHANNEL_TYPE_RELIABLE), + DELEMENT(BTHL_CHANNEL_TYPE_STREAMING), + DELEMENT(BTHL_CHANNEL_TYPE_ANY), +ENDMAP + +SINTMAP(bthl_app_reg_state_t, -1, "(unknown)") + DELEMENT(BTHL_APP_REG_STATE_REG_SUCCESS), + DELEMENT(BTHL_APP_REG_STATE_REG_FAILED), + DELEMENT(BTHL_APP_REG_STATE_DEREG_SUCCESS), + DELEMENT(BTHL_APP_REG_STATE_DEREG_FAILED), +ENDMAP + +SINTMAP(bthl_channel_state_t, -1, "(unknown)") + DELEMENT(BTHL_CONN_STATE_CONNECTING), + DELEMENT(BTHL_CONN_STATE_CONNECTED), + DELEMENT(BTHL_CONN_STATE_DISCONNECTING), + DELEMENT(BTHL_CONN_STATE_DISCONNECTED), + DELEMENT(BTHL_CONN_STATE_DESTROYED), +ENDMAP + +const bthl_interface_t *if_hl = NULL; + +static void app_reg_state_cb(int app_id, bthl_app_reg_state_t state) +{ + haltest_info("%s: app_id=%d app_reg_state=%s\n", __func__, + app_id, bthl_app_reg_state_t2str(state)); +} + +static void channel_state_cb(int app_id, bt_bdaddr_t *bd_addr, + int mdep_cfg_index, int channel_id, + bthl_channel_state_t state, int fd) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: app_id=%d bd_addr=%s mdep_cfg_index=%d\n" + "channel_id=%d channel_state=%s fd=%d\n", __func__, + app_id, bt_bdaddr_t2str(bd_addr, addr), mdep_cfg_index, + channel_id, bthl_channel_state_t2str(state), fd); +} + +static bthl_callbacks_t hl_cbacks = { + .size = sizeof(hl_cbacks), + .app_reg_state_cb = app_reg_state_cb, + .channel_state_cb = channel_state_cb, +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hl); + + EXEC(if_hl->init, &hl_cbacks); +} + +/* register_application */ + +static void register_application_p(int argc, const char **argv) +{ + bthl_reg_param_t reg; + uint16_t mdep_argc_init, mdep_argc_off; + int app_id = -1; + int i; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No app name is specified\n"); + return; + } + + if (argc <= 3) { + haltest_error("No provider is specified\n"); + return; + } + + if (argc <= 4) { + haltest_error("No service name is specified\n"); + return; + } + + if (argc <= 5) { + haltest_error("No service description is specified\n"); + return; + } + + if (argc <= 6) { + haltest_error("No num of mdeps is specified\n"); + return; + } + + memset(®, 0, sizeof(reg)); + + if (argc != ((atoi(argv[6]) * 4) + 7)) { + haltest_error("mdep cfg argumetns are not proper\n"); + return; + } + + reg.application_name = argv[2]; + + if (strcmp("-", argv[3])) + reg.provider_name = argv[3]; + + if (strcmp("-", argv[4])) + reg.srv_name = argv[4]; + + if (strcmp("-", argv[5])) + reg.srv_desp = argv[5]; + + reg.number_of_mdeps = atoi(argv[6]); + + reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t)); + mdep_argc_init = 7; + + for (i = 0; i < reg.number_of_mdeps; i++) { + mdep_argc_off = mdep_argc_init + (4 * i); + reg.mdep_cfg[i].mdep_role = + str2bthl_mdep_role_t(argv[mdep_argc_off]); + reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]); + reg.mdep_cfg[i].channel_type = + str2bthl_channel_type_t(argv[mdep_argc_off + 2]); + + if (!strcmp("-", argv[mdep_argc_off + 3])) { + reg.mdep_cfg[i].mdep_description = NULL; + continue; + } + + reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3]; + } + + EXEC(if_hl->register_application, ®, &app_id); + + free(reg.mdep_cfg); +} + +/* unregister_application */ + +static void unregister_application_p(int argc, const char **argv) +{ + uint32_t app_id; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No app id is specified"); + return; + } + + app_id = (uint32_t) atoi(argv[2]); + + EXEC(if_hl->unregister_application, app_id); +} + +/* connect_channel */ + +static void connect_channel_p(int argc, const char **argv) +{ + uint32_t app_id, mdep_cfg_index; + int channel_id = -1; + bt_bdaddr_t bd_addr; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No app id is specified"); + return; + } + + VERIFY_ADDR_ARG(3, &bd_addr); + + if (argc <= 4) { + haltest_error("No mdep cfg index is specified"); + return; + } + + app_id = (uint32_t) atoi(argv[2]); + mdep_cfg_index = (uint32_t) atoi(argv[4]); + + EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index, + &channel_id); +} + +/* destroy_channel */ + +static void destroy_channel_p(int argc, const char **argv) +{ + uint32_t channel_id; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No channel id is specified"); + return; + } + + channel_id = (uint32_t) atoi(argv[2]); + + EXEC(if_hl->destroy_channel, channel_id); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hl); + + EXECV(if_hl->cleanup); + if_hl = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODH(register_application, + " \n" + "\n" + "[[] [] [] []]" + "..."), + STD_METHODH(unregister_application, ""), + STD_METHODH(connect_channel, " "), + STD_METHODH(destroy_channel, ""), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface hl_if = { + .name = "hl", + .methods = methods +}; diff --git a/android/client/if-main.h b/android/client/if-main.h index a83f48b..88da0c7 100644 --- a/android/client/if-main.h +++ b/android/client/if-main.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -36,26 +37,26 @@ #include #include -#if PLATFORM_SDK_VERSION > 17 #include #include #include #include #include -#endif + +extern audio_hw_device_t *if_audio; /* Interfaces from hal that can be populated during application lifetime */ extern const bt_interface_t *if_bluetooth; extern const btav_interface_t *if_av; +extern const btrc_interface_t *if_rc; extern const bthf_interface_t *if_hf; extern const bthh_interface_t *if_hh; extern const btpan_interface_t *if_pan; +extern const bthl_interface_t *if_hl; extern const btsock_interface_t *if_sock; -#if PLATFORM_SDK_VERSION > 17 extern const btgatt_interface_t *if_gatt; extern const btgatt_server_interface_t *if_gatt_server; extern const btgatt_client_interface_t *if_gatt_client; -#endif /* * Structure defines top level interfaces that can be used in test tool @@ -66,17 +67,19 @@ struct interface { struct method *methods; /* methods available for this interface */ }; +extern const struct interface audio_if; +extern const struct interface sco_if; extern const struct interface bluetooth_if; extern const struct interface av_if; -#if PLATFORM_SDK_VERSION > 17 +extern const struct interface rc_if; extern const struct interface gatt_if; extern const struct interface gatt_client_if; extern const struct interface gatt_server_if; -#endif extern const struct interface pan_if; extern const struct interface sock_if; extern const struct interface hf_if; extern const struct interface hh_if; +extern const struct interface hl_if; /* Interfaces that will show up in tool (first part of command line) */ extern const struct interface *interfaces[]; @@ -124,13 +127,12 @@ struct method { const char *help; }; -int haltest_error(const char *format, ...); -int haltest_info(const char *format, ...); -int haltest_warn(const char *format, ...); +int haltest_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); +int haltest_info(const char *format, ...)__attribute__((format(printf, 1, 2))); +int haltest_warn(const char *format, ...)__attribute__((format(printf, 1, 2))); -/* - * Enumerator for discovered devices, to be used as tab completion enum_func - */ +/* Enumerator for discovered devices, to be used as tab completion enum_func */ const char *enum_devices(void *v, int i); const char *interface_name(void *v, int i); const char *command_name(void *v, int i); @@ -147,8 +149,12 @@ const struct method *get_interface_method(const char *iname, /* Helper macro for executing function on interface and printing BT_STATUS */ #define EXEC(f, ...) \ { \ - int err = f(__VA_ARGS__); \ - haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \ + if (f) { \ + int err = f(__VA_ARGS__); \ + haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \ + } else { \ + haltest_info("%s is NULL\n", #f); \ + } \ } /* Helper macro for executing void function on interface */ diff --git a/android/client/if-pan.c b/android/client/if-pan.c index a11f2a3..bdb36cc 100644 --- a/android/client/if-pan.c +++ b/android/client/if-pan.c @@ -80,7 +80,7 @@ static void init_p(int argc, const char **argv) /* enable */ -static void enable_c(int argc, const const char **argv, enum_func *enum_func, +static void enable_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { @@ -121,7 +121,7 @@ static void get_local_role_p(int argc, const char **argv) /* connect */ -static void connect_c(int argc, const const char **argv, enum_func *enum_func, +static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) { if (argc == 3) { @@ -165,8 +165,8 @@ static void connect_p(int argc, const char **argv) /* disconnect */ -static void disconnect_c(int argc, const const char **argv, - enum_func *enum_func, void **user) +static void disconnect_c(int argc, const char **argv, enum_func *enum_func, + void **user) { if (argc == 3) { *user = last_used_addr; diff --git a/android/client/if-rc.c b/android/client/if-rc.c new file mode 100644 index 0000000..cdf6311 --- /dev/null +++ b/android/client/if-rc.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include + +#include "if-main.h" +#include "pollhandler.h" +#include "../hal-utils.h" + +const btrc_interface_t *if_rc = NULL; + +SINTMAP(btrc_play_status_t, -1, "(unknown)") + DELEMENT(BTRC_PLAYSTATE_STOPPED), + DELEMENT(BTRC_PLAYSTATE_PLAYING), + DELEMENT(BTRC_PLAYSTATE_PAUSED), + DELEMENT(BTRC_PLAYSTATE_FWD_SEEK), + DELEMENT(BTRC_PLAYSTATE_REV_SEEK), + DELEMENT(BTRC_PLAYSTATE_ERROR), +ENDMAP + +SINTMAP(btrc_media_attr_t, -1, "(unknown)") + DELEMENT(BTRC_MEDIA_ATTR_TITLE), + DELEMENT(BTRC_MEDIA_ATTR_ARTIST), + DELEMENT(BTRC_MEDIA_ATTR_ALBUM), + DELEMENT(BTRC_MEDIA_ATTR_TRACK_NUM), + DELEMENT(BTRC_MEDIA_ATTR_NUM_TRACKS), + DELEMENT(BTRC_MEDIA_ATTR_GENRE), + DELEMENT(BTRC_MEDIA_ATTR_PLAYING_TIME), +ENDMAP + +SINTMAP(btrc_status_t, -1, "(unknown)") + DELEMENT(BTRC_STS_BAD_CMD), + DELEMENT(BTRC_STS_BAD_PARAM), + DELEMENT(BTRC_STS_NOT_FOUND), + DELEMENT(BTRC_STS_INTERNAL_ERR), + DELEMENT(BTRC_STS_NO_ERROR), +ENDMAP + +SINTMAP(btrc_event_id_t, -1, "(unknown)") + DELEMENT(BTRC_EVT_PLAY_STATUS_CHANGED), + DELEMENT(BTRC_EVT_TRACK_CHANGE), + DELEMENT(BTRC_EVT_TRACK_REACHED_END), + DELEMENT(BTRC_EVT_TRACK_REACHED_START), + DELEMENT(BTRC_EVT_PLAY_POS_CHANGED), + DELEMENT(BTRC_EVT_APP_SETTINGS_CHANGED), +ENDMAP + +SINTMAP(btrc_notification_type_t, -1, "(unknown)") + DELEMENT(BTRC_NOTIFICATION_TYPE_INTERIM), + DELEMENT(BTRC_NOTIFICATION_TYPE_CHANGED), +ENDMAP + +static char last_addr[MAX_ADDR_STR_LEN]; + +static void remote_features_cb(bt_bdaddr_t *bd_addr, + btrc_remote_features_t features) +{ + haltest_info("%s: remote_bd_addr=%s features=%u\n", __func__, + bt_bdaddr_t2str(bd_addr, last_addr), features); +} + +static void get_play_status_cb(void) +{ + haltest_info("%s\n", __func__); +} + +static void list_player_app_attr_cb(void) +{ + haltest_info("%s\n", __func__); +} + +static void list_player_app_values_cb(btrc_player_attr_t attr_id) +{ + haltest_info("%s, attr_id=%d\n", __func__, attr_id); +} + +static void get_player_app_value_cb(uint8_t num_attr, + btrc_player_attr_t *p_attrs) +{ + int i; + + haltest_info("%s, num_attr=%d\n", __func__, num_attr); + + for (i = 0; i < num_attr; i++) + haltest_info("attribute=%u\n", p_attrs[i]); +} + +static void get_player_app_attrs_text_cb(uint8_t num_attr, + btrc_player_attr_t *p_attrs) +{ + int i; + + haltest_info("%s, num_attr=%d\n", __func__, num_attr); + + for (i = 0; i < num_attr; i++) + haltest_info("attribute=%u\n", p_attrs[i]); + +} + +static void get_player_app_values_text_cb(uint8_t attr_id, uint8_t num_val, + uint8_t *p_vals) +{ + haltest_info("%s, attr_id=%d num_val=%d values=%p\n", __func__, + attr_id, num_val, p_vals); +} + +static void set_player_app_value_cb(btrc_player_settings_t *p_vals) +{ + int i; + + haltest_info("%s, num_attr=%u\n", __func__, p_vals->num_attr); + + for (i = 0; i < p_vals->num_attr; i++) + haltest_info("attr id=%u, values=%u\n", p_vals->attr_ids[i], + p_vals->attr_values[i]); +} + +static void get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *attrs) +{ + uint8_t i; + + haltest_info("%s, num_of_attributes=%d\n", __func__, num_attr); + + for (i = 0; i < num_attr; i++) + haltest_info("attr id=%s\n", btrc_media_attr_t2str(attrs[i])); +} + +static void register_notification_cb(btrc_event_id_t event_id, uint32_t param) +{ + haltest_info("%s, event=%u param=%u\n", __func__, event_id, param); +} + +static void volume_change_cb(uint8_t volume, uint8_t ctype) +{ + haltest_info("%s, volume=%d ctype=%d\n", __func__, volume, ctype); +} + +static void passthrough_cmd_cb(int id, int key_state) +{ + haltest_info("%s, id=%d key_state=%d\n", __func__, id, key_state); +} + +static btrc_callbacks_t rc_cbacks = { + .size = sizeof(rc_cbacks), + .remote_features_cb = remote_features_cb, + .get_play_status_cb = get_play_status_cb, + .list_player_app_attr_cb = list_player_app_attr_cb, + .list_player_app_values_cb = list_player_app_values_cb, + .get_player_app_value_cb = get_player_app_value_cb, + .get_player_app_attrs_text_cb = get_player_app_attrs_text_cb, + .get_player_app_values_text_cb = get_player_app_values_text_cb, + .set_player_app_value_cb = set_player_app_value_cb, + .get_element_attr_cb = get_element_attr_cb, + .register_notification_cb = register_notification_cb, + .volume_change_cb = volume_change_cb, + .passthrough_cmd_cb = passthrough_cmd_cb, +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_rc); + + EXEC(if_rc->init, &rc_cbacks); +} + +/* get_play_status_rsp */ + +static void get_play_status_rsp_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btrc_play_status_t); + *enum_func = enum_defines; + } +} + +static void get_play_status_rsp_p(int argc, const char **argv) +{ + btrc_play_status_t play_status; + uint32_t song_len, song_pos; + + RETURN_IF_NULL(if_rc); + + if (argc <= 2) { + haltest_error("No play status specified"); + return; + } + + if (argc <= 3) { + haltest_error("No song length specified"); + return; + } + + if (argc <= 4) { + haltest_error("No song position specified"); + return; + } + + play_status = str2btrc_play_status_t(argv[2]); + song_len = (uint32_t) atoi(argv[3]); + song_pos = (uint32_t) atoi(argv[4]); + + EXEC(if_rc->get_play_status_rsp, play_status, song_len, song_pos); +} + +/* get_element_attr_rsp */ + +static void get_element_attr_rsp_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 4) { + *user = TYPE_ENUM(btrc_media_attr_t); + *enum_func = enum_defines; + } +} + +static void get_element_attr_rsp_p(int argc, const char **argv) +{ + uint8_t num_attr; + btrc_element_attr_val_t attrs; + + RETURN_IF_NULL(if_rc); + + if (argc <= 2) { + haltest_error("No number of attributes specified"); + return; + } + + if (argc <= 4) { + haltest_error("No attr id and value specified"); + return; + } + + num_attr = (uint8_t) atoi(argv[2]); + attrs.attr_id = str2btrc_media_attr_t(argv[3]); + strcpy((char *)attrs.text, argv[4]); + + EXEC(if_rc->get_element_attr_rsp, num_attr, &attrs); +} + +/* set_volume */ + +static void set_volume_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ +} + +static void set_volume_p(int argc, const char **argv) +{ + uint8_t volume; + + RETURN_IF_NULL(if_rc); + + if (argc <= 2) { + haltest_error("No volume specified"); + return; + } + + volume = (uint8_t) atoi(argv[2]); + + EXEC(if_rc->set_volume, volume); +} + +/* set_player_app_value_rsp */ + +static void set_player_app_value_rsp_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btrc_status_t); + *enum_func = enum_defines; + } +} + +static void set_player_app_value_rsp_p(int argc, const char **argv) +{ + btrc_status_t rsp_status; + + RETURN_IF_NULL(if_rc); + + if (argc <= 2) { + haltest_error("No response status specified"); + return; + } + + rsp_status = str2btrc_status_t(argv[2]); + + EXEC(if_rc->set_player_app_value_rsp, rsp_status); +} + +/* register_notification_rsp */ + +static void register_notification_rsp_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btrc_event_id_t); + *enum_func = enum_defines; + } + + if (argc == 4) { + *user = TYPE_ENUM(btrc_notification_type_t); + *enum_func = enum_defines; + } +} + +static void register_notification_rsp_p(int argc, const char **argv) +{ + btrc_event_id_t event_id; + btrc_notification_type_t type; + btrc_register_notification_t reg; + uint32_t song_pos; + uint64_t track; + + RETURN_IF_NULL(if_rc); + + memset(®, 0, sizeof(reg)); + event_id = str2btrc_event_id_t(argv[2]); + type = str2btrc_notification_type_t(argv[3]); + + switch (event_id) { + case BTRC_EVT_PLAY_STATUS_CHANGED: + reg.play_status = str2btrc_play_status_t(argv[4]); + break; + + case BTRC_EVT_TRACK_CHANGE: + track = strtoull(argv[5], NULL, 10); + memcpy(reg.track, &track, sizeof(btrc_uid_t)); + break; + + case BTRC_EVT_TRACK_REACHED_END: + case BTRC_EVT_TRACK_REACHED_START: + break; + + case BTRC_EVT_PLAY_POS_CHANGED: + song_pos = strtoul(argv[4], NULL, 10); + memcpy(®.song_pos, &song_pos, sizeof(uint32_t)); + break; + + case BTRC_EVT_APP_SETTINGS_CHANGED: + haltest_error("not supported"); + return; + } + + EXEC(if_rc->register_notification_rsp, event_id, type, ®); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_rc); + + EXECV(if_rc->cleanup); + if_rc = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(get_play_status_rsp, + " "), + STD_METHODCH(get_element_attr_rsp, " "), + STD_METHODCH(set_player_app_value_rsp, ""), + STD_METHODCH(set_volume, ""), + STD_METHODCH(register_notification_rsp, + " \n" + "BTRC_EVT_PLAY_STATUS_CHANGED \n" + "BTRC_EVT_TRACK_CHANGE \n" + "BTRC_EVT_TRACK_REACHED_END \n" + "BTRC_EVT_TRACK_REACHED_START \n" + "BTRC_EVT_PLAY_POS_CHANGED \n"), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface rc_if = { + .name = "rc", + .methods = methods +}; diff --git a/android/client/if-sco.c b/android/client/if-sco.c new file mode 100644 index 0000000..b7f5a80 --- /dev/null +++ b/android/client/if-sco.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "if-main.h" +#include "../hal-utils.h" +#include "pthread.h" +#include "unistd.h" +#include + +audio_hw_device_t *if_audio_sco = NULL; +static struct audio_stream_out *stream_out = NULL; + +static size_t buffer_size = 0; +static pthread_t play_thread = 0; +static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER; + +enum state { + STATE_STOPPED, + STATE_STOPPING, + STATE_PLAYING, + STATE_SUSPENDED, + STATE_MAX +}; + +SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)") + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_MONO), + DELEMENT(AUDIO_CHANNEL_OUT_STEREO), + DELEMENT(AUDIO_CHANNEL_OUT_QUAD), + DELEMENT(AUDIO_CHANNEL_OUT_SURROUND), + DELEMENT(AUDIO_CHANNEL_OUT_5POINT1), + DELEMENT(AUDIO_CHANNEL_OUT_7POINT1), + DELEMENT(AUDIO_CHANNEL_OUT_ALL), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), +ENDMAP + +SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)") + DELEMENT(AUDIO_FORMAT_DEFAULT), + DELEMENT(AUDIO_FORMAT_PCM), + DELEMENT(AUDIO_FORMAT_MP3), + DELEMENT(AUDIO_FORMAT_AMR_NB), + DELEMENT(AUDIO_FORMAT_AMR_WB), + DELEMENT(AUDIO_FORMAT_AAC), + DELEMENT(AUDIO_FORMAT_HE_AAC_V1), + DELEMENT(AUDIO_FORMAT_HE_AAC_V2), + DELEMENT(AUDIO_FORMAT_VORBIS), + DELEMENT(AUDIO_FORMAT_MAIN_MASK), + DELEMENT(AUDIO_FORMAT_SUB_MASK), + DELEMENT(AUDIO_FORMAT_PCM_16_BIT), + DELEMENT(AUDIO_FORMAT_PCM_8_BIT), + DELEMENT(AUDIO_FORMAT_PCM_32_BIT), + DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT), +ENDMAP + +static int current_state = STATE_STOPPED; + +#define SAMPLERATE 44100 +static short sample[SAMPLERATE]; +static uint16_t sample_pos; + +static void init_p(int argc, const char **argv) +{ + int err; + const hw_module_t *module; + audio_hw_device_t *device; + + err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, "sco", &module); + if (err) { + haltest_error("hw_get_module_by_class returned %d\n", err); + return; + } + + err = audio_hw_device_open(module, &device); + if (err) { + haltest_error("audio_hw_device_open returned %d\n", err); + return; + } + + if_audio_sco = device; +} + +static int feed_from_file(short *buffer, void *data) +{ + FILE *in = data; + return fread(buffer, buffer_size, 1, in); +} + +static int feed_from_generator(short *buffer, void *data) +{ + size_t i = 0; + float volume = 0.5; + float *freq = data; + float f = 1; + + if (freq) + f = *freq; + + /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/ + for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) { + if (sample_pos >= SAMPLERATE) + sample_pos = sample_pos % SAMPLERATE; + + /* Use the same sample for both channels */ + buffer[i++] = sample[sample_pos] * volume; + buffer[i++] = sample[sample_pos] * volume; + + sample_pos += f; + } + + return buffer_size; +} + +static void prepare_sample(void) +{ + int x; + double s; + + haltest_info("Preparing audio sample...\n"); + + for (x = 0; x < SAMPLERATE; x++) { + /* prepare sinusoidal 1Hz sample */ + s = (2.0 * 3.14159) * ((double)x / SAMPLERATE); + s = sin(s); + + /* remap <-1, 1> to signed 16bit PCM range */ + sample[x] = s * 32767; + } + + sample_pos = 0; +} + +static void *playback_thread(void *data) +{ + int (*filbuff_cb) (short*, void*); + short buffer[buffer_size / sizeof(short)]; + size_t len = 0; + ssize_t w_len = 0; + FILE *in = data; + void *cb_data = NULL; + float freq = 440.0; + + /* Use file or fall back to generator */ + if (in) { + filbuff_cb = feed_from_file; + cb_data = in; + } else { + prepare_sample(); + filbuff_cb = feed_from_generator; + cb_data = &freq; + } + + pthread_mutex_lock(&state_mutex); + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); + + do { + pthread_mutex_lock(&state_mutex); + + if (current_state == STATE_STOPPING) { + haltest_info("Detected stopping\n"); + pthread_mutex_unlock(&state_mutex); + break; + } else if (current_state == STATE_SUSPENDED) { + pthread_mutex_unlock(&state_mutex); + usleep(500); + continue; + } + + pthread_mutex_unlock(&state_mutex); + + len = filbuff_cb(buffer, cb_data); + + pthread_mutex_lock(&outstream_mutex); + if (!stream_out) { + pthread_mutex_unlock(&outstream_mutex); + break; + } + + w_len = stream_out->write(stream_out, buffer, buffer_size); + pthread_mutex_unlock(&outstream_mutex); + } while (len && w_len > 0); + + if (in) + fclose(in); + + pthread_mutex_lock(&state_mutex); + current_state = STATE_STOPPED; + pthread_mutex_unlock(&state_mutex); + + haltest_info("Done playing.\n"); + + return NULL; +} + +static void play_p(int argc, const char **argv) +{ + const char *fname = NULL; + FILE *in = NULL; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_error("Invalid audio file path.\n"); + haltest_info("Using sound generator.\n"); + } else { + fname = argv[2]; + in = fopen(fname, "r"); + + if (in == NULL) { + haltest_error("Cannot open file: %s\n", fname); + return; + } + haltest_info("Playing file: %s\n", fname); + } + + if (buffer_size == 0) { + haltest_error("Invalid buffer size. Was stream_out opened?\n"); + goto fail; + } + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + haltest_error("Already playing or stream suspended!\n"); + pthread_mutex_unlock(&state_mutex); + goto fail; + } + pthread_mutex_unlock(&state_mutex); + + if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) { + haltest_error("Cannot create playback thread!\n"); + goto fail; + } + + return; +fail: + if (in) + fclose(in); +} + +static void stop_p(int argc, const char **argv) +{ + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) { + pthread_mutex_unlock(&state_mutex); + return; + } + + current_state = STATE_STOPPING; + pthread_mutex_unlock(&state_mutex); + + pthread_mutex_lock(&outstream_mutex); + stream_out->common.standby(&stream_out->common); + pthread_mutex_unlock(&outstream_mutex); + + haltest_info("Ended %s\n", __func__); +} + +static void open_output_stream_p(int argc, const char **argv) +{ + int err; + + RETURN_IF_NULL(if_audio_sco); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_PLAYING) { + haltest_error("Already playing!\n"); + pthread_mutex_unlock(&state_mutex); + return; + } + pthread_mutex_unlock(&state_mutex); + + err = if_audio_sco->open_output_stream(if_audio_sco, + 0, + AUDIO_DEVICE_OUT_ALL_SCO, + AUDIO_OUTPUT_FLAG_NONE, + NULL, + &stream_out); + if (err < 0) { + haltest_error("open output stream returned %d\n", err); + return; + } + + buffer_size = stream_out->common.get_buffer_size(&stream_out->common); + if (buffer_size == 0) + haltest_error("Invalid buffer size received!\n"); + else + haltest_info("Using buffer size: %zu\n", buffer_size); +} + +static void close_output_stream_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + stop_p(argc, argv); + + haltest_info("Waiting for playback thread...\n"); + pthread_join(play_thread, NULL); + + if_audio_sco->close_output_stream(if_audio_sco, stream_out); + + stream_out = NULL; + buffer_size = 0; +} + +static void cleanup_p(int argc, const char **argv) +{ + int err; + + RETURN_IF_NULL(if_audio_sco); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + pthread_mutex_unlock(&state_mutex); + close_output_stream_p(0, NULL); + } else { + pthread_mutex_unlock(&state_mutex); + } + + err = audio_hw_device_close(if_audio_sco); + if (err < 0) { + haltest_error("audio_hw_device_close returned %d\n", err); + return; + } + + if_audio_sco = NULL; +} + +static void suspend_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_PLAYING) { + pthread_mutex_unlock(&state_mutex); + return; + } + current_state = STATE_SUSPENDED; + pthread_mutex_unlock(&state_mutex); + + pthread_mutex_lock(&outstream_mutex); + stream_out->common.standby(&stream_out->common); + pthread_mutex_unlock(&outstream_mutex); +} + +static void resume_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_SUSPENDED) + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); +} + +static void get_latency_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + haltest_info("Output audio stream latency: %d\n", + stream_out->get_latency(stream_out)); +} + +static void get_buffer_size_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + haltest_info("Current output buffer size: %zu\n", + stream_out->common.get_buffer_size(&stream_out->common)); +} + +static void get_channels_p(int argc, const char **argv) +{ + audio_channel_mask_t channels; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + channels = stream_out->common.get_channels(&stream_out->common); + + haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels)); +} + +static void get_format_p(int argc, const char **argv) +{ + audio_format_t format; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + format = stream_out->common.get_format(&stream_out->common); + + haltest_info("Format: %s\n", audio_format_t2str(format)); +} + +static void get_sample_rate_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + haltest_info("Current sample rate: %d\n", + stream_out->common.get_sample_rate(&stream_out->common)); +} + +static void get_parameters_p(int argc, const char **argv) +{ + const char *keystr; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_info("No keys given.\n"); + keystr = ""; + } else { + keystr = argv[2]; + } + + haltest_info("Current parameters: %s\n", + stream_out->common.get_parameters(&stream_out->common, + keystr)); +} + +static void set_parameters_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_error("No key=value; pairs given.\n"); + return; + } + + stream_out->common.set_parameters(&stream_out->common, argv[2]); +} + +static void set_sample_rate_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (argc < 3) + return; + + stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2])); +} + +static void init_check_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + + haltest_info("Init check result: %d\n", + if_audio_sco->init_check(if_audio_sco)); +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHOD(cleanup), + STD_METHOD(open_output_stream), + STD_METHOD(close_output_stream), + STD_METHODH(play, ""), + STD_METHOD(stop), + STD_METHOD(suspend), + STD_METHOD(resume), + STD_METHOD(get_latency), + STD_METHOD(get_buffer_size), + STD_METHOD(get_channels), + STD_METHOD(get_format), + STD_METHOD(get_sample_rate), + STD_METHODH(get_parameters, ""), + STD_METHODH(set_parameters, ""), + STD_METHODH(set_sample_rate, ""), + STD_METHOD(init_check), + END_METHOD +}; + +const struct interface sco_if = { + .name = "sco", + .methods = methods +}; diff --git a/android/client/if-sock.c b/android/client/if-sock.c index 5394a5d..20a1fc8 100644 --- a/android/client/if-sock.c +++ b/android/client/if-sock.c @@ -46,7 +46,8 @@ static const char * const uuids[] = { static void receive_from_client(struct pollfd *pollfd) { char buf[16]; - /* Buffer for lines: + /* + * Buffer for lines: * 41 42 43 20 20 00 31 32 00 07 04 00 00 00 00 00 ABC .12..... */ char outbuf[sizeof(buf) * 4 + 2]; @@ -154,21 +155,19 @@ static void read_accepted(int fd) for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { - int *descs; int count; if (cmsgptr->cmsg_level != SOL_SOCKET || cmsgptr->cmsg_type != SCM_RIGHTS) continue; - descs = (int *) CMSG_DATA(cmsgptr); + memcpy(&accepted_fd, CMSG_DATA(cmsgptr), sizeof(accepted_fd)); count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int)); if (count != 1) haltest_error("Failed to accept descriptors count=%d\n", count); - accepted_fd = descs[0]; break; } @@ -189,7 +188,7 @@ static void client_connected(struct pollfd *pollfd) read_accepted(pollfd->fd); } -/** listen */ +/* listen */ static void listen_c(int argc, const char **argv, enum_func *enum_func, void **user) @@ -209,7 +208,7 @@ static void listen_p(int argc, const char **argv) const char *service_name; bt_uuid_t service_uuid; int channel; - int sock_fd; + int sock_fd = -1; int flags; RETURN_IF_NULL(if_sock); @@ -261,7 +260,7 @@ static void listen_p(int argc, const char **argv) } } -/** connect */ +/* connect */ static void connect_c(int argc, const char **argv, enum_func *enum_func, void **user) @@ -283,7 +282,7 @@ static void connect_p(int argc, const char **argv) btsock_type_t type; bt_uuid_t uuid; int channel; - int sock_fd; + int sock_fd = -1; int flags; /* Address */ diff --git a/android/client/tabcompletion.c b/android/client/tabcompletion.c index f965620..cf3fd80 100644 --- a/android/client/tabcompletion.c +++ b/android/client/tabcompletion.c @@ -113,12 +113,10 @@ struct command_completion_args { enum_func func; /* enumerating function */ void *user; /* argument to enumerating function */ short_help help; /* help function */ - void *user_help; /* additional data (used by short_help) */ + const char *user_help; /* additional data (used by short_help) */ }; -/* - * complete command line - */ +/* complete command line */ static void tab_completion(struct command_completion_args *args) { const char *name = args->typed; @@ -142,10 +140,7 @@ static void tab_completion(struct command_completion_args *args) continue; } - /* - * Prefix matches next time - * reduce prefix to common part - */ + /* Prefix matches next time reduce prefix to common part */ for (j = 0; prefix[j] != 0 && prefix[j] == enum_name[j];) ++j; @@ -289,10 +284,9 @@ static void method_help(struct command_completion_args *args) if (eb != NULL) haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy, - arg1_size, arg1, - sb - (const char *) args->user_help, - args->user_help, - bold, eb - sb, sb, normal, eb); + arg1_size, arg1, (int) (sb - args->user_help), + args->user_help, bold, (int) (eb - sb), + sb, normal, eb); else haltest_info("%s %-*s%s\n", args->arg->ntcopy, arg1_size, arg1, args->user_help); @@ -336,7 +330,7 @@ static void param_completion(int argc, const split_arg_t *arg, if (args.func != NULL) { args.typed = argv[argc - 1]; args.help = method_help; - args.user_help = method ? (void *) method->help : NULL; + args.user_help = method ? method->help : NULL; tab_completion(&args); } diff --git a/android/cutils/properties.h b/android/cutils/properties.h index 7951585..8096b19 100644 --- a/android/cutils/properties.h +++ b/android/cutils/properties.h @@ -1,24 +1,58 @@ /* - * Copyright (C) 2006 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * BlueZ - Bluetooth protocol stack for Linux * - * http://www.apache.org/licenses/LICENSE-2.0 + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * 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 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ +#include #include #include #include #include +#define PROPERTY_VALUE_MAX 32 + +#define BLUETOOTH_MODE_PROPERTY_NAME "persist.sys.bluetooth.mode" + +static inline int property_get(const char *key, char *value, + const char *default_value) +{ + const char *prop = NULL; + + if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_NAME)) + prop = getenv("BLUETOOTH_MODE"); + + if (!prop) + prop = default_value; + + if (prop) { + strncpy(value, prop, PROPERTY_VALUE_MAX); + + value[PROPERTY_VALUE_MAX - 1] = '\0'; + + return strlen(value); + } + + return 0; +} + /* property_set: returns 0 on success, < 0 on failure */ static inline int property_set(const char *key, const char *value) diff --git a/android/gatt.c b/android/gatt.c new file mode 100644 index 0000000..8e0d72a --- /dev/null +++ b/android/gatt.c @@ -0,0 +1,5226 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include + +#include "ipc.h" +#include "ipc-common.h" +#include "lib/sdp.h" +#include "lib/uuid.h" +#include "bluetooth.h" +#include "gatt.h" +#include "src/log.h" +#include "hal-msg.h" +#include "utils.h" +#include "src/shared/util.h" +#include "src/shared/queue.h" +#include "src/shared/gatt-db.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "btio/btio.h" + +/* set according to Android bt_gatt_client.h */ +#define GATT_MAX_ATTR_LEN 600 + +#define GATT_SUCCESS 0x00000000 +#define GATT_FAILURE 0x00000101 + +#define BASE_UUID16_OFFSET 12 + +static const uint8_t BLUETOOTH_UUID[] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +typedef enum { + DEVICE_DISCONNECTED = 0, + DEVICE_CONNECT_INIT, /* connection procedure initiated */ + DEVICE_CONNECT_READY, /* dev found during LE scan */ + DEVICE_CONNECTED, /* connection has been established */ +} gatt_device_state_t; + +static const char const *device_state_str[] = { + "DISCONNECTED", + "CONNECT INIT", + "CONNECT READY", + "CONNECTED", +}; + +typedef enum { + APP_CLIENT, + APP_SERVER, +} gatt_app_type_t; + +struct pending_trans_data { + unsigned int id; + uint8_t opcode; +}; + +struct gatt_app { + int32_t id; + uint8_t uuid[16]; + + gatt_app_type_t type; + + /* Valid for client applications */ + struct queue *notifications; + + /* Transaction data valid for server application */ + struct pending_trans_data trans_id; +}; + +struct element_id { + bt_uuid_t uuid; + uint8_t instance; +}; + +struct descriptor { + struct element_id id; + uint16_t handle; +}; + +struct characteristic { + struct element_id id; + struct gatt_char ch; + uint16_t end_handle; + + struct queue *descriptors; +}; + +struct service { + struct element_id id; + struct gatt_primary prim; + struct gatt_included incl; + + bool primary; + + struct queue *chars; + struct queue *included; /* Valid only for primary services */ + bool incl_search_done; +}; + +struct notification_data { + struct hal_gatt_srvc_id service; + struct hal_gatt_gatt_id ch; + struct app_connection *conn; + guint notif_id; + guint ind_id; + int ref; +}; + +struct gatt_device { + bdaddr_t bdaddr; + uint8_t bdaddr_type; + + gatt_device_state_t state; + + GAttrib *attrib; + GIOChannel *att_io; + struct queue *services; + bool partial_srvc_search; + + bool notify_services_changed; + + guint watch_id; + guint server_id; + + int ref; + int conn_cnt; + + struct queue *pending_requests; +}; + +struct app_connection { + struct gatt_device *device; + struct gatt_app *app; + int32_t id; +}; + +static struct ipc *hal_ipc = NULL; +static bdaddr_t adapter_addr; +static bool scanning = false; +static unsigned int advertising_cnt = 0; + +static struct queue *gatt_apps = NULL; +static struct queue *gatt_devices = NULL; +static struct queue *app_connections = NULL; + +static struct queue *listen_apps = NULL; +static struct gatt_db *gatt_db = NULL; + +static GIOChannel *listening_io = NULL; + +static void bt_le_discovery_stop_cb(void); + +static bool is_bluetooth_uuid(const uint8_t *uuid) +{ + int i; + + for (i = 0; i < 16; i++) { + /* ignore minimal uuid (16) value */ + if (i == 12 || i == 13) + continue; + + if (uuid[i] != BLUETOOTH_UUID[i]) + return false; + } + + return true; +} + +static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst) +{ + if (is_bluetooth_uuid(uuid)) { + /* copy 16 bit uuid value from full android 128bit uuid */ + dst->type = BT_UUID16; + dst->value.u16 = (uuid[13] << 8) + uuid[12]; + } else { + int i; + + dst->type = BT_UUID128; + for (i = 0; i < 16; i++) + dst->value.u128.data[i] = uuid[15 - i]; + } +} + +static void uuid2android(const bt_uuid_t *src, uint8_t *uuid) +{ + bt_uuid_t uu128; + uint8_t i; + + if (src->type != BT_UUID128) { + bt_uuid_to_uuid128(src, &uu128); + src = &uu128; + } + + for (i = 0; i < 16; i++) + uuid[15 - i] = src->value.u128.data[i]; +} + +static void hal_srvc_id_to_element_id(const struct hal_gatt_srvc_id *from, + struct element_id *to) +{ + to->instance = from->inst_id; + android2uuid(from->uuid, &to->uuid); +} + +static void element_id_to_hal_srvc_id(const struct element_id *from, + uint8_t primary, + struct hal_gatt_srvc_id *to) +{ + to->is_primary = primary; + to->inst_id = from->instance; + uuid2android(&from->uuid, to->uuid); +} + +static void hal_gatt_id_to_element_id(const struct hal_gatt_gatt_id *from, + struct element_id *to) +{ + to->instance = from->inst_id; + android2uuid(from->uuid, &to->uuid); +} + +static void element_id_to_hal_gatt_id(const struct element_id *from, + struct hal_gatt_gatt_id *to) +{ + to->inst_id = from->instance; + uuid2android(&from->uuid, to->uuid); +} + +static void destroy_characteristic(void *data) +{ + struct characteristic *chars = data; + + if (!chars) + return; + + queue_destroy(chars->descriptors, free); + free(chars); +} + +static void destroy_service(void *data) +{ + struct service *srvc = data; + + if (!srvc) + return; + + queue_destroy(srvc->chars, destroy_characteristic); + + /* + * Included services we keep on two queues. + * 1. On the same queue with primary services. + * 2. On the queue inside primary service. + * So we need to free service memory only once but we need to destroy + * two queues + */ + if (srvc->primary) + queue_destroy(srvc->included, NULL); + + free(srvc); +} + +static bool match_app_by_uuid(const void *data, const void *user_data) +{ + const uint8_t *exp_uuid = user_data; + const struct gatt_app *client = data; + + return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid)); +} + +static bool match_app_by_id(const void *data, const void *user_data) +{ + int32_t exp_id = PTR_TO_INT(user_data); + const struct gatt_app *client = data; + + return client->id == exp_id; +} + +static struct gatt_app *find_app_by_id(int32_t id) +{ + return queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(id)); +} + +static bool match_by_value(const void *data, const void *user_data) +{ + return data == user_data; +} + +static bool match_device_by_bdaddr(const void *data, const void *user_data) +{ + const struct gatt_device *dev = data; + const bdaddr_t *addr = user_data; + + return !bacmp(&dev->bdaddr, addr); +} + +static bool match_device_by_state(const void *data, const void *user_data) +{ + const struct gatt_device *dev = data; + + if (dev->state != PTR_TO_UINT(user_data)) + return false; + + return true; +} + +static bool match_pending_device(const void *data, const void *user_data) +{ + const struct gatt_device *dev = data; + + if ((dev->state == DEVICE_CONNECT_INIT) || + (dev->state == DEVICE_CONNECT_READY)) + return true; + + return false; +} + +static bool match_connection_by_id(const void *data, const void *user_data) +{ + const struct app_connection *conn = data; + const int32_t id = PTR_TO_INT(user_data); + + return conn->id == id; +} + +static bool match_connection_by_device_and_app(const void *data, + const void *user_data) +{ + const struct app_connection *conn = data; + const struct app_connection *match = user_data; + + return conn->device == match->device && conn->app == match->app; +} + +static struct app_connection *find_connection_by_id(int32_t conn_id) +{ + return queue_find(app_connections, match_connection_by_id, + INT_TO_PTR(conn_id)); +} + +static bool match_connection_by_device(const void *data, const void *user_data) +{ + const struct app_connection *conn = data; + const struct gatt_device *dev = user_data; + + return conn->device == dev; +} + +static bool match_connection_by_app(const void *data, const void *user_data) +{ + const struct app_connection *conn = data; + const struct gatt_app *app = user_data; + + return conn->app == app; +} + +static struct gatt_device *find_device_by_addr(const bdaddr_t *addr) +{ + return queue_find(gatt_devices, match_device_by_bdaddr, (void *)addr); +} + +static struct gatt_device *find_pending_device() +{ + return queue_find(gatt_devices, match_pending_device, NULL); +} + +static struct gatt_device *find_device_by_state(uint32_t state) +{ + return queue_find(gatt_devices, match_device_by_state, + UINT_TO_PTR(state)); +} + +static bool match_srvc_by_element_id(const void *data, const void *user_data) +{ + const struct element_id *exp_id = user_data; + const struct service *service = data; + + if (service->id.instance == exp_id->instance) + return !bt_uuid_cmp(&service->id.uuid, &exp_id->uuid); + + return false; +} + +static bool match_srvc_by_higher_inst_id(const void *data, + const void *user_data) +{ + const struct service *s = data; + uint8_t inst_id = PTR_TO_INT(user_data); + + /* For now we match inst_id as it is unique */ + return inst_id < s->id.instance; +} + +static bool match_srvc_by_bt_uuid(const void *data, const void *user_data) +{ + const bt_uuid_t *exp_uuid = user_data; + const struct service *service = data; + + return !bt_uuid_cmp(exp_uuid, &service->id.uuid); +} + +static bool match_srvc_by_range(const void *data, const void *user_data) +{ + const struct service *srvc = data; + const struct att_range *range = user_data; + + return !memcmp(&srvc->prim.range, range, sizeof(srvc->prim.range)); +} + +static bool match_char_by_higher_inst_id(const void *data, + const void *user_data) +{ + const struct characteristic *ch = data; + uint8_t inst_id = PTR_TO_INT(user_data); + + /* For now we match inst_id as it is unique, we'll match uuids later */ + return inst_id < ch->id.instance; +} + +static bool match_descr_by_element_id(const void *data, const void *user_data) +{ + const struct element_id *exp_id = user_data; + const struct descriptor *descr = data; + + if (exp_id->instance == descr->id.instance) + return !bt_uuid_cmp(&descr->id.uuid, &exp_id->uuid); + + return false; +} + +static bool match_descr_by_higher_inst_id(const void *data, + const void *user_data) +{ + const struct descriptor *descr = data; + uint8_t instance = PTR_TO_INT(user_data); + + /* For now we match instance as it is unique */ + return instance < descr->id.instance; +} + +static bool match_notification(const void *a, const void *b) +{ + const struct notification_data *a1 = a; + const struct notification_data *b1 = b; + + if (a1->conn != b1->conn) + return false; + + if (memcmp(&a1->ch, &b1->ch, sizeof(a1->ch))) + return false; + + if (memcmp(&a1->service, &b1->service, sizeof(a1->service))) + return false; + + return true; +} + +static bool match_char_by_element_id(const void *data, const void *user_data) +{ + const struct element_id *exp_id = user_data; + const struct characteristic *chars = data; + + if (exp_id->instance == chars->id.instance) + return !bt_uuid_cmp(&chars->id.uuid, &exp_id->uuid); + + return false; +} + +static void destroy_notification(void *data) +{ + struct notification_data *notification = data; + struct gatt_app *app; + + if (--notification->ref) + return; + + app = notification->conn->app; + queue_remove_if(app->notifications, match_notification, notification); + free(notification); +} + +static void unregister_notification(void *data) +{ + struct notification_data *notification = data; + struct gatt_device *dev = notification->conn->device; + + /* + * No device means it was already disconnected and client cleanup was + * triggered afterwards, but once client unregisters, device stays if + * used by others. Then just unregister single handle. + */ + if (!queue_find(gatt_devices, match_by_value, dev)) + return; + + if (notification->notif_id && dev) + g_attrib_unregister(dev->attrib, notification->notif_id); + + if (notification->ind_id && dev) + g_attrib_unregister(dev->attrib, notification->ind_id); +} + +static void device_set_state(struct gatt_device *dev, uint32_t state) +{ + char bda[18]; + + ba2str(&dev->bdaddr, bda); + DBG("gatt: Device %s state changed %s -> %s", bda, + device_state_str[dev->state], device_state_str[state]); + + dev->state = state; +} + +static void connection_cleanup(struct gatt_device *device) +{ + if (device->watch_id) { + g_source_remove(device->watch_id); + device->watch_id = 0; + } + + if (device->att_io) { + g_io_channel_shutdown(device->att_io, FALSE, NULL); + g_io_channel_unref(device->att_io); + device->att_io = NULL; + } + + if (device->server_id > 0) + g_attrib_unregister(device->attrib, device->server_id); + + if (device->attrib) { + GAttrib *attrib = device->attrib; + device->attrib = NULL; + g_attrib_cancel_all(attrib); + g_attrib_unref(attrib); + } + + /* + * If device was in connection_pending or connectable state we + * search device list if we should stop the scan. + */ + if (!scanning && (device->state == DEVICE_CONNECT_INIT || + device->state == DEVICE_CONNECT_READY)) { + if (!find_pending_device()) + bt_le_discovery_stop(NULL); + } + + /* If device is not bonded service cache should be refreshed */ + if (!bt_device_is_bonded(&device->bdaddr)) + queue_remove_all(device->services, NULL, NULL, destroy_service); + + device_set_state(device, DEVICE_DISCONNECTED); +} + +static void destroy_gatt_app(void *data) +{ + struct gatt_app *app = data; + + /* + * First we want to get all notifications and unregister them. + * We don't pass unregister_notification to queue_destroy, + * because destroy notification performs operations on queue + * too. So remove all elements and then destroy queue. + */ + + if (app->type == APP_CLIENT) + while (queue_peek_head(app->notifications)) { + struct notification_data *notification; + + notification = queue_pop_head(app->notifications); + unregister_notification(notification); + } + + queue_destroy(app->notifications, free); + + free(app); +} + +static int register_app(const uint8_t *uuid, gatt_app_type_t app_type) +{ + static int32_t application_id = 1; + struct gatt_app *app; + + if (queue_find(gatt_apps, match_app_by_uuid, (void *) uuid)) { + error("gatt: app uuid is already on list"); + return 0; + } + + app = new0(struct gatt_app, 1); + if (!app) { + error("gatt: Cannot allocate memory for registering app"); + return 0; + } + + app->type = app_type; + + if (app->type == APP_CLIENT) { + app->notifications = queue_new(); + if (!app->notifications) { + error("gatt: couldn't allocate notifications queue"); + destroy_gatt_app(app); + return 0; + } + } + + memcpy(app->uuid, uuid, sizeof(app->uuid)); + + app->id = application_id++; + + if (!queue_push_head(gatt_apps, app)) { + error("gatt: Cannot push app on the list"); + destroy_gatt_app(app); + return 0; + } + + if ((app->type == APP_SERVER) && + !queue_push_tail(listen_apps, INT_TO_PTR(app->id))) { + error("gatt: Cannot push server on the list"); + destroy_gatt_app(app); + return 0; + } + + return app->id; +} + +static void handle_client_register(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_register *cmd = buf; + struct hal_ev_gatt_client_register_client ev; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + ev.client_if = register_app(cmd->uuid, APP_CLIENT); + + if (ev.client_if) + ev.status = GATT_SUCCESS; + else + ev.status = GATT_FAILURE; + + /* We should send notification with given in cmd UUID */ + memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER, + HAL_STATUS_SUCCESS); +} + +static void send_client_disconnection_notify(struct app_connection *connection, + int32_t status) +{ + struct hal_ev_gatt_client_disconnect ev; + + ev.client_if = connection->app->id; + ev.conn_id = connection->id; + ev.status = status; + + bdaddr2android(&connection->device->bdaddr, &ev.bda); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev); +} + +static void send_client_connection_notify(struct app_connection *connection, + int32_t status) +{ + struct hal_ev_gatt_client_connect ev; + + ev.client_if = connection->app->id; + ev.conn_id = connection->id; + ev.status = status; + + bdaddr2android(&connection->device->bdaddr, &ev.bda); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT, + sizeof(ev), &ev); +} + +static void send_server_connection_notify(struct app_connection *connection, + bool connected) +{ + struct hal_ev_gatt_server_connection ev; + + ev.server_if = connection->app->id; + ev.conn_id = connection->id; + ev.connected = connected; + + bdaddr2android(&connection->device->bdaddr, &ev.bdaddr); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev); +} + +static void send_app_disconnect_notify(struct app_connection *connection, + int32_t status) +{ + if (connection->app->type == APP_CLIENT) + send_client_disconnection_notify(connection, status); + else + send_server_connection_notify(connection, !!status); +} + +static void send_app_connect_notify(struct app_connection *connection, + int32_t status) +{ + if (connection->app->type == APP_CLIENT) + send_client_connection_notify(connection, status); + else + send_server_connection_notify(connection, !status); +} + +static void disconnect_notify_by_device(void *data, void *user_data) +{ + struct app_connection *conn = data; + struct gatt_device *dev = user_data; + + if (dev != conn->device) + return; + + if (dev->state == DEVICE_CONNECTED) + send_app_disconnect_notify(conn, GATT_SUCCESS); + else if (dev->state == DEVICE_CONNECT_INIT || + dev->state == DEVICE_CONNECT_READY) + send_app_connect_notify(conn, GATT_FAILURE); +} + +#define READ_INIT -3 +#define READ_PENDING -2 +#define READ_FAILED -1 + +struct pending_request { + uint16_t handle; + int length; + uint8_t *value; + uint16_t offset; + + uint8_t *filter_value; + uint16_t filter_vlen; +}; + +static void destroy_pending_request(void *data) +{ + struct pending_request *entry = data; + + free(entry->value); + free(entry->filter_value); + free(entry); +} + +static void destroy_device(void *data) +{ + struct gatt_device *dev = data; + + if (!dev) + return; + + queue_destroy(dev->services, destroy_service); + queue_destroy(dev->pending_requests, destroy_pending_request); + + free(dev); +} + +static struct gatt_device *device_ref(struct gatt_device *device) +{ + if (!device) + return NULL; + + device->ref++; + + return device; +} + +static void device_unref(struct gatt_device *device) +{ + if (!device) + return; + + if (--device->ref) + return; + + destroy_device(device); +} + +static void destroy_connection(void *data) +{ + struct app_connection *conn = data; + + if (!queue_find(gatt_devices, match_by_value, conn->device)) + goto cleanup; + + conn->device->conn_cnt--; + if (conn->device->conn_cnt == 0) + connection_cleanup(conn->device); + +cleanup: + device_unref(conn->device); + free(conn); +} + +static void device_disconnect_clients(struct gatt_device *dev) +{ + /* Notify disconnection to all clients */ + queue_foreach(app_connections, disconnect_notify_by_device, dev); + + /* Remove all clients by given device's */ + queue_remove_all(app_connections, match_connection_by_device, dev, + destroy_connection); +} + +static void send_client_primary_notify(void *data, void *user_data) +{ + struct hal_ev_gatt_client_search_result ev; + struct service *p = data; + int32_t conn_id = PTR_TO_INT(user_data); + + /* In service queue we will have also included services */ + if (!p->primary) + return; + + ev.conn_id = conn_id; + element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id); + + uuid2android(&p->id.uuid, ev.srvc_id.uuid); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev); +} + +static struct service *create_service(uint8_t id, bool primary, char *uuid, + void *data) +{ + struct service *s; + + s = new0(struct service, 1); + if (!s) { + error("gatt: Cannot allocate memory for gatt_primary"); + return NULL; + } + + s->chars = queue_new(); + if (!s->chars) { + error("gatt: Cannot allocate memory for char cache"); + free(s); + return NULL; + } + + if (bt_string_to_uuid(&s->id.uuid, uuid) < 0) { + error("gatt: Cannot convert string to uuid"); + queue_destroy(s->chars, NULL); + free(s); + return NULL; + } + + s->id.instance = id; + + /* Put primary service to our local list */ + s->primary = primary; + if (s->primary) { + memcpy(&s->prim, data, sizeof(s->prim)); + } else { + memcpy(&s->incl, data, sizeof(s->incl)); + return s; + } + + /* For primary service allocate queue for included services */ + s->included = queue_new(); + if (!s->included) { + queue_destroy(s->chars, NULL); + free(s); + return NULL; + } + + return s; +} + +static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type, + int rssi, uint16_t eir_len, + const void *eir, + bool discoverable) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_scan_result *ev = (void *) buf; + struct gatt_device *dev; + char bda[18]; + + if (!scanning || !discoverable) + goto connect; + + ba2str(addr, bda); + DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir); + + bdaddr2android(addr, ev->bda); + ev->rssi = rssi; + ev->len = eir_len; + + memcpy(ev->adv_data, eir, ev->len); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_SCAN_RESULT, + sizeof(*ev) + ev->len, ev); + +connect: + dev = find_device_by_addr(addr); + if (!dev || (dev->state != DEVICE_CONNECT_INIT)) + return; + + device_set_state(dev, DEVICE_CONNECT_READY); + dev->bdaddr_type = addr_type; + + /* + * We are ok to perform connect now. Stop discovery + * and once it is stopped continue with creating ACL + */ + bt_le_discovery_stop(bt_le_discovery_stop_cb); +} + +static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct gatt_device *dev = user_data; + int sock, err = 0; + socklen_t len; + + sock = g_io_channel_unix_get_fd(io); + len = sizeof(err); + if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len)) + DBG("%s (%d)", strerror(err), err); + + device_disconnect_clients(dev); + + return FALSE; +} + +struct connect_data { + struct gatt_device *dev; + int32_t status; +}; + +static void send_app_connect_notifications(void *data, void *user_data) +{ + struct app_connection *conn = data; + struct connect_data *con_data = user_data; + + if (conn->device == con_data->dev) + send_app_connect_notify(conn, con_data->status); +} + +static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data); + +static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) +{ + struct gatt_device *dev = user_data; + struct connect_data data; + uint32_t status; + GAttrib *attrib; + + if (dev->state != DEVICE_CONNECT_READY) { + error("gatt: Device not in a connecting state!?"); + g_io_channel_shutdown(io, TRUE, NULL); + return; + } + + g_io_channel_unref(dev->att_io); + dev->att_io = NULL; + + if (gerr) { + error("gatt: connection failed %s", gerr->message); + device_set_state(dev, DEVICE_DISCONNECTED); + status = GATT_FAILURE; + goto reply; + } + + attrib = g_attrib_new(io); + if (!attrib) { + error("gatt: unable to create new GAttrib instance"); + device_set_state(dev, DEVICE_DISCONNECTED); + status = GATT_FAILURE; + goto reply; + } + + dev->attrib = attrib; + dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + disconnected_cb, dev); + + dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS, + GATTRIB_ALL_HANDLES, + att_handler, dev, NULL); + if (dev->server_id == 0) + error("gatt: Could not attach to server"); + + device_set_state(dev, DEVICE_CONNECTED); + + status = GATT_SUCCESS; + +reply: + data.dev = dev; + data.status = status; + queue_foreach(app_connections, send_app_connect_notifications, &data); + device_unref(dev); + + /* Check if we should restart scan */ + if (scanning) + bt_le_discovery_start(le_device_found_handler); + + /* FIXME: What to do if discovery won't start here. */ +} + +static int connect_le(struct gatt_device *dev) +{ + BtIOSecLevel sec_level; + GIOChannel *io; + GError *gerr = NULL; + char addr[18]; + + ba2str(&dev->bdaddr, addr); + + /* There is one connection attempt going on */ + if (dev->att_io) { + info("gatt: connection to dev %s is ongoing", addr); + return -EALREADY; + } + + DBG("Connection attempt to: %s", addr); + + /* TODO: If we are bonded then we should use higier sec level */ + sec_level = BT_IO_SEC_LOW; + + /* + * This connection will help us catch any PDUs that comes before + * pairing finishes + */ + io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, + &adapter_addr, + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, + BT_IO_OPT_DEST_BDADDR, &dev->bdaddr, + BT_IO_OPT_DEST_TYPE, dev->bdaddr_type, + BT_IO_OPT_CID, ATT_CID, + BT_IO_OPT_SEC_LEVEL, sec_level, + BT_IO_OPT_INVALID); + if (!io) { + error("gatt: Failed bt_io_connect(%s): %s", addr, + gerr->message); + g_error_free(gerr); + return -EIO; + } + + /* Keep this, so we can cancel the connection */ + dev->att_io = io; + + return 0; +} + +static void handle_client_scan(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_scan *cmd = buf; + uint8_t status; + void *registered; + + DBG("new state %d", cmd->start); + + registered = find_app_by_id(cmd->client_if); + if (!registered) { + error("gatt: Client not registered"); + status = HAL_STATUS_FAILED; + goto reply; + } + + /* Turn off scan */ + if (!cmd->start) { + DBG("Stopping LE SCAN"); + + if (scanning) { + bt_le_discovery_stop(NULL); + scanning = false; + } + + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* Reply success if we already do scan */ + if (scanning) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* Turn on scan */ + if (!bt_le_discovery_start(le_device_found_handler)) { + error("gatt: LE scan switch failed"); + status = HAL_STATUS_FAILED; + goto reply; + } + scanning = true; + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN, + status); +} + +static int connect_next_dev(void) +{ + struct gatt_device *dev; + + DBG(""); + + dev = find_device_by_state(DEVICE_CONNECT_READY); + if (!dev) + return -ENODEV; + + return connect_le(dev); +} + +static void bt_le_discovery_stop_cb(void) +{ + DBG(""); + + /* Check now if there is any device ready to connect */ + if (connect_next_dev() < 0) + bt_le_discovery_start(le_device_found_handler); +} + +static struct gatt_device *create_device(const bdaddr_t *addr) +{ + struct gatt_device *dev; + + dev = new0(struct gatt_device, 1); + if (!dev) + return NULL; + + bacpy(&dev->bdaddr, addr); + + dev->services = queue_new(); + if (!dev->services) { + error("gatt: Failed to allocate memory for client"); + destroy_device(dev); + return NULL; + } + + dev->pending_requests = queue_new(); + if (!dev->pending_requests) { + error("gatt: Failed to allocate memory for client"); + destroy_device(dev); + return NULL; + } + + if (!queue_push_head(gatt_devices, dev)) { + error("gatt: Cannot push device to queue"); + destroy_device(dev); + return NULL; + } + + return device_ref(dev); +} + +static struct app_connection *create_connection(struct gatt_device *device, + struct gatt_app *app) +{ + struct app_connection *new_conn; + static int32_t last_conn_id = 1; + + /* Check if already connected */ + new_conn = new0(struct app_connection, 1); + if (!new_conn) + return NULL; + + /* Make connection id unique to connection record (app, device) pair */ + new_conn->app = app; + new_conn->id = last_conn_id++; + + if (!queue_push_head(app_connections, new_conn)) { + error("gatt: Cannot push client on the client queue!?"); + + free(new_conn); + return NULL; + } + + new_conn->device = device_ref(device); + new_conn->device->conn_cnt++; + + return new_conn; +} + +static void trigger_disconnection(struct app_connection *connection) +{ + /* Notify client */ + if (queue_remove(app_connections, connection)) + send_app_disconnect_notify(connection, GATT_SUCCESS); + + destroy_connection(connection); +} + +static void app_disconnect_devices(struct gatt_app *client) +{ + struct app_connection *conn; + + /* find every connection for client record and trigger disconnect */ + conn = queue_remove_if(app_connections, match_connection_by_app, + client); + while (conn) { + trigger_disconnection(conn); + + conn = queue_remove_if(app_connections, + match_connection_by_app, client); + } +} + +static bool trigger_connection(struct app_connection *connection) +{ + switch (connection->device->state) { + case DEVICE_DISCONNECTED: + device_set_state(connection->device, DEVICE_CONNECT_INIT); + break; + case DEVICE_CONNECTED: + send_app_connect_notify(connection, GATT_SUCCESS); + break; + default: + break; + } + + /* after state change trigger discovering */ + if (!scanning && (connection->device->state == DEVICE_CONNECT_INIT)) + if (!bt_le_discovery_start(le_device_found_handler)) { + error("gatt: Could not start scan"); + + return false; + } + + return true; +} + +static void handle_client_unregister(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_unregister *cmd = buf; + uint8_t status; + struct gatt_app *cl; + + DBG(""); + + cl = queue_remove_if(gatt_apps, match_app_by_id, + INT_TO_PTR(cmd->client_if)); + if (!cl) { + error("gatt: client_if=%d not found", cmd->client_if); + status = HAL_STATUS_FAILED; + } else { + /* + * Check if there is any connect request or connected device + * for this client. If so, remove this client from those lists. + */ + app_disconnect_devices(cl); + destroy_gatt_app(cl); + status = HAL_STATUS_SUCCESS; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_UNREGISTER, status); +} + +static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id) +{ + struct app_connection conn_match; + struct gatt_device *dev = NULL; + struct gatt_app *app; + + /* Check if app is registered */ + app = find_app_by_id(app_id); + if (!app) { + error("gatt: Client id %d not found", app_id); + return NULL; + } + + /* Check if device is known */ + dev = find_device_by_addr(addr); + if (!dev) { + error("gatt: Client id %d not found", app_id); + return NULL; + } + + conn_match.device = dev; + conn_match.app = app; + + return queue_find(app_connections, match_connection_by_device_and_app, + &conn_match); +} + +static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr) +{ + struct app_connection conn_match; + struct app_connection *conn; + struct gatt_device *device; + struct gatt_app *app; + + DBG(""); + + app = find_app_by_id(app_id); + if (!app) + return HAL_STATUS_FAILED; + + device = find_device_by_addr(addr); + if (!device) { + device = create_device(addr); + if (!device) + return HAL_STATUS_FAILED; + } + + conn_match.device = device; + conn_match.app = app; + + conn = queue_find(app_connections, match_connection_by_device_and_app, + &conn_match); + if (!conn) { + conn = create_connection(device, app); + if (!conn) + return HAL_STATUS_NOMEM; + } + + if (!trigger_connection(conn)) + return HAL_STATUS_FAILED; + + return HAL_STATUS_SUCCESS; +} + +static void handle_client_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_connect *cmd = buf; + uint8_t status; + bdaddr_t addr; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &addr); + + /* TODO handle is_direct flag */ + + status = handle_connect(cmd->client_if, &addr); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT, + status); +} + +static void handle_client_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_disconnect *cmd = buf; + struct app_connection *conn; + uint8_t status; + + DBG(""); + + /* TODO: should we care to match also bdaddr when conn_id is unique? */ + conn = find_connection_by_id(cmd->conn_id); + if (conn) + trigger_disconnection(conn); + + status = HAL_STATUS_SUCCESS; + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_DISCONNECT, status); +} + +static void send_client_listen_notify(int32_t id, int32_t status) +{ + struct hal_ev_gatt_client_listen ev; + + /* Server if because of typo in android headers */ + ev.server_if = id; + ev.status = status; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_LISTEN, + sizeof(ev), &ev); +} + +struct listen_data { + int32_t client_id; + bool start; +}; + +static void set_advertising_cb(uint8_t status, void *user_data) +{ + struct listen_data *l = user_data; + + send_client_listen_notify(l->client_id, status); + + /* In case of success update advertising state*/ + if (!status) + advertising_cnt = l->start ? 1 : 0; + + /* + * Let's remove client from the list in two cases + * 1. Start failed + * 2. Stop succeed + */ + if ((l->start && status) || (!l->start && !status)) + queue_remove(listen_apps, INT_TO_PTR(l->client_id)); + + free(l); +} + +static void handle_client_listen(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_listen *cmd = buf; + uint8_t status; + struct listen_data *data; + bool req_sent = false; + void *listening_client; + + DBG(""); + + if (!find_app_by_id(cmd->client_if)) { + error("gatt: Client not registered"); + status = HAL_STATUS_FAILED; + goto reply; + } + + listening_client = queue_find(listen_apps, match_by_value, + INT_TO_PTR(cmd->client_if)); + /* Start listening */ + if (cmd->start) { + if (listening_client) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + if (!queue_push_tail(listen_apps, + INT_TO_PTR(cmd->client_if))) { + error("gatt: Could not put client on listen queue"); + status = HAL_STATUS_FAILED; + goto reply; + } + + /* If listen is already on just return success*/ + if (advertising_cnt > 0) { + advertising_cnt++; + status = HAL_STATUS_SUCCESS; + goto reply; + } + } else { + /* Stop listening. Check if client was listening */ + if (!listening_client) { + error("gatt: This client %d does not listen", + cmd->client_if); + status = HAL_STATUS_FAILED; + goto reply; + } + + /* + * In case there is more listening clients don't stop + * advertising + */ + if (advertising_cnt > 1) { + advertising_cnt--; + queue_remove(listen_apps, + INT_TO_PTR(cmd->client_if)); + status = HAL_STATUS_SUCCESS; + goto reply; + } + } + + data = new0(struct listen_data, 1); + if (!data) { + error("gatt: Could not allocate memory for listen data"); + status = HAL_STATUS_NOMEM; + goto reply; + } + + data->client_id = cmd->client_if; + data->start = cmd->start; + + if (!bt_le_set_advertising(cmd->start, set_advertising_cb, data)) { + error("gatt: Could not set advertising"); + status = HAL_STATUS_FAILED; + free(data); + goto reply; + } + + /* + * Use this flag to keep in mind that we are waiting for callback with + * result + */ + req_sent = true; + + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN, + status); + + /* In case of early success or error, just send notification up */ + if (!req_sent) { + int32_t gatt_status = status == HAL_STATUS_SUCCESS ? + GATT_SUCCESS : GATT_FAILURE; + send_client_listen_notify(cmd->client_if, gatt_status); + } +} + +static void handle_client_refresh(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_refresh *cmd = buf; + struct gatt_device *dev; + uint8_t status; + bdaddr_t bda; + + /* + * This is Android's framework hidden API call. It seams that no + * notification is expected and Bluedroid silently updates device's + * cache under the hood. As we use lazy caching ,we can just clear the + * cache and we're done. + */ + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &bda); + dev = find_device_by_addr(&bda); + if (!dev) { + status = HAL_STATUS_FAILED; + goto done; + } + + queue_remove_all(dev->services, NULL, NULL, destroy_service); + + status = HAL_STATUS_SUCCESS; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH, + status); +} + +struct discover_srvc_data { + bt_uuid_t uuid; + struct app_connection *conn; +}; + +static void send_client_search_complete_notify(int32_t status, int32_t conn_id) +{ + struct hal_ev_gatt_client_search_complete ev; + + ev.status = status; + ev.conn_id = conn_id; + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev); +} + +static void discover_srvc_all_cb(uint8_t status, GSList *services, + void *user_data) +{ + struct discover_srvc_data *cb_data = user_data; + struct gatt_device *dev = cb_data->conn->device; + int32_t gatt_status; + GSList *l; + /* + * There might be multiply services with same uuid. Therefore make sure + * each primary service one has unique instance_id + */ + uint8_t instance_id = queue_length(dev->services); + + DBG("Status %d", status); + + if (status) { + error("gatt: Discover all primary services failed: %s", + att_ecode2str(status)); + gatt_status = GATT_FAILURE; + goto reply; + } + + if (!services) { + info("gatt: No primary services found"); + gatt_status = GATT_SUCCESS; + goto reply; + } + + for (l = services; l; l = l->next) { + struct gatt_primary *prim = l->data; + struct service *p; + + if (queue_find(dev->services, match_srvc_by_range, + &prim->range)) + continue; + + p = create_service(instance_id++, true, prim->uuid, prim); + if (!p) + continue; + + if (!queue_push_tail(dev->services, p)) { + error("gatt: Cannot push primary service to the list"); + free(p); + continue; + } + + DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s", + prim->range.start, prim->range.end, prim->uuid); + } + + /* + * Send all found services notifications - first cache, + * then send notifies + */ + queue_foreach(dev->services, send_client_primary_notify, + INT_TO_PTR(cb_data->conn->id)); + + /* Full search service scanning was performed */ + dev->partial_srvc_search = false; + gatt_status = GATT_SUCCESS; + +reply: + send_client_search_complete_notify(gatt_status, cb_data->conn->id); + free(cb_data); +} + +static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges, + void *user_data) +{ + struct discover_srvc_data *cb_data = user_data; + struct gatt_primary prim; + struct service *s; + int32_t gatt_status; + struct gatt_device *dev = cb_data->conn->device; + uint8_t instance_id = queue_length(dev->services); + + DBG("Status %d", status); + + if (status) { + error("gatt: Discover pri srvc filtered by uuid failed: %s", + att_ecode2str(status)); + gatt_status = GATT_FAILURE; + goto reply; + } + + if (!ranges) { + info("gatt: No primary services searched by uuid found"); + gatt_status = GATT_SUCCESS; + goto reply; + } + + bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid)); + /* + * If multiple instances of the same service (as identified by UUID) + * exist, the first instance of the service is returned. + */ + memcpy(&prim.range, ranges->data, sizeof(prim.range)); + + s = create_service(instance_id++, true, prim.uuid, &prim); + if (!s) { + gatt_status = GATT_FAILURE; + goto reply; + } + + if (!queue_push_tail(dev->services, s)) { + error("gatt: Cannot push primary service to the list"); + gatt_status = GATT_FAILURE; + goto reply; + } + + send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id)); + + DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s", + prim.range.start, prim.range.end, prim.uuid); + + /* Partial search service scanning was performed */ + dev->partial_srvc_search = true; + gatt_status = GATT_SUCCESS; + +reply: + send_client_search_complete_notify(gatt_status, cb_data->conn->id); + free(cb_data); +} + +static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid) +{ + struct discover_srvc_data *cb_data = + new0(struct discover_srvc_data, 1); + + if (!cb_data) { + error("gatt: Cannot allocate cb data"); + return 0; + } + + cb_data->conn = conn; + + if (uuid) { + memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid)); + return gatt_discover_primary(conn->device->attrib, uuid, + discover_srvc_by_uuid_cb, cb_data); + } + + return gatt_discover_primary(conn->device->attrib, NULL, + discover_srvc_all_cb, cb_data); +} + +static void handle_client_search_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_search_service *cmd = buf; + struct app_connection *conn; + uint8_t status; + struct service *s; + bt_uuid_t uuid; + guint srvc_search_success; + + DBG(""); + + if (len != sizeof(*cmd) + (cmd->filtered ? 16 : 0)) { + error("Invalid search service size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + conn = find_connection_by_id(cmd->conn_id); + if (!conn) { + error("gatt: dev with conn_id=%d not found", cmd->conn_id); + + status = HAL_STATUS_FAILED; + goto reply; + } + + if (conn->device->state != DEVICE_CONNECTED) { + char bda[18]; + + ba2str(&conn->device->bdaddr, bda); + error("gatt: device %s not connected", bda); + + status = HAL_STATUS_FAILED; + goto reply; + } + + if (cmd->filtered) + android2uuid(cmd->filter_uuid, &uuid); + + /* Services not cached yet */ + if (queue_isempty(conn->device->services)) { + if (cmd->filtered) + srvc_search_success = search_dev_for_srvc(conn, &uuid); + else + srvc_search_success = search_dev_for_srvc(conn, NULL); + + if (!srvc_search_success) { + status = HAL_STATUS_FAILED; + goto reply; + } + + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* Search in cached services for given service */ + if (cmd->filtered) { + /* Search in cache for service by uuid */ + s = queue_find(conn->device->services, match_srvc_by_bt_uuid, + &uuid); + + if (s) { + send_client_primary_notify(s, INT_TO_PTR(conn->id)); + } else { + if (!search_dev_for_srvc(conn, &uuid)) + status = HAL_STATUS_FAILED; + + status = HAL_STATUS_SUCCESS; + goto reply; + } + } else { + /* Refresh service cache if only partial search was performed */ + if (conn->device->partial_srvc_search) + srvc_search_success = search_dev_for_srvc(conn, NULL); + else + queue_foreach(conn->device->services, + send_client_primary_notify, + INT_TO_PTR(cmd->conn_id)); + } + + send_client_search_complete_notify(GATT_SUCCESS, conn->id); + + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status); +} + +static void send_client_incl_service_notify(const struct element_id *srvc_id, + const struct service *incl, + int32_t conn_id) +{ + struct hal_ev_gatt_client_get_inc_service ev; + + memset(&ev, 0, sizeof(ev)); + + ev.conn_id = conn_id; + + element_id_to_hal_srvc_id(srvc_id, 1, &ev.srvc_id); + + if (incl) { + element_id_to_hal_srvc_id(&incl->id, 0, &ev.incl_srvc_id); + ev.status = GATT_SUCCESS; + } else { + ev.status = GATT_FAILURE; + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT , + HAL_EV_GATT_CLIENT_GET_INC_SERVICE, + sizeof(ev), &ev); +} + +struct get_included_data { + struct service *prim; + struct app_connection *conn; +}; + +static int get_inst_id_of_prim_services(const struct gatt_device *dev) +{ + struct service *s = queue_peek_tail(dev->services); + + if (s) + return s->id.instance; + + return -1; +} + +static void get_included_cb(uint8_t status, GSList *included, void *user_data) +{ + struct get_included_data *data = user_data; + struct app_connection *conn = data->conn; + struct service *service = data->prim; + struct service *incl = NULL; + int instance_id; + + DBG(""); + + free(data); + + if (status) { + error("gatt: no included services found"); + return; + } + + /* Remember that we already search included services.*/ + service->incl_search_done = true; + + /* + * There might be multiply services with same uuid. Therefore make sure + * each service has unique instance id. Let's take the latest instance + * id of primary service and start iterate included services from this + * point. + */ + instance_id = get_inst_id_of_prim_services(conn->device); + if (instance_id < 0) + goto failed; + + for (; included; included = included->next) { + struct gatt_included *included_service = included->data; + + incl = create_service(++instance_id, false, + included_service->uuid, + included_service); + if (!incl) + continue; + + /* + * Lets keep included service on two queues. + * 1. on services queue together with primary service + * 2. on special queue inside primary service + */ + if (!queue_push_tail(service->included, incl) || + !queue_push_tail(conn->device->services, incl)) { + error("gatt: Cannot push incl service to the list"); + destroy_service(incl); + continue; + } + } + + /* + * Notify upper layer about first included service. + * Android framework will iterate for next one. + */ + incl = queue_peek_head(service->included); + +failed: + send_client_incl_service_notify(&service->id, incl, conn->id); +} + +static bool search_included_services(struct app_connection *connection, + struct service *service) +{ + struct get_included_data *data; + + data = new0(struct get_included_data, 1); + if (!data) { + error("gatt: failed to allocate memory for included_data"); + return false; + } + + data->prim = service; + data->conn = connection; + + gatt_find_included(connection->device->attrib, + service->prim.range.start, + service->prim.range.end, get_included_cb, data); + return true; +} + +static bool find_service(int32_t conn_id, struct element_id *service_id, + struct app_connection **connection, + struct service **service) +{ + struct service *srvc; + struct app_connection *conn; + + conn = find_connection_by_id(conn_id); + if (!conn) { + error("gatt: conn_id=%d not found", conn_id); + return false; + } + + srvc = queue_find(conn->device->services, match_srvc_by_element_id, + service_id); + if (!srvc) { + error("gatt: Service with inst_id: %d not found", + service_id->instance); + return false; + } + + *connection = conn; + *service = srvc; + + return true; +} + +static void handle_client_get_included_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_get_included_service *cmd = buf; + struct app_connection *conn; + struct service *prim_service; + struct service *incl_service = NULL; + struct element_id match_id; + struct element_id srvc_id; + uint8_t status; + + DBG(""); + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + + if (len != sizeof(*cmd) + + (cmd->continuation ? sizeof(cmd->incl_srvc_id[0]) : 0)) { + error("Invalid get incl services size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); + if (!find_service(cmd->conn_id, &match_id, &conn, &prim_service)) { + status = HAL_STATUS_FAILED; + goto reply; + } + + if (!prim_service->incl_search_done) { + if (search_included_services(conn, prim_service)) + status = HAL_STATUS_SUCCESS; + else + status = HAL_STATUS_FAILED; + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, + status); + return; + } + + /* Try to use cache here */ + if (!cmd->continuation) { + incl_service = queue_peek_head(prim_service->included); + } else { + uint8_t inst_id = cmd->incl_srvc_id[0].inst_id; + incl_service = queue_find(prim_service->included, + match_srvc_by_higher_inst_id, + INT_TO_PTR(inst_id)); + } + + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, status); + + /* + * In case of error in handling request we need to send event with + * service id of cmd and gatt failure status. + */ + send_client_incl_service_notify(&srvc_id, incl_service, cmd->conn_id); +} + +static void send_client_char_notify(const struct characteristic *ch, + int32_t conn_id, + const struct service *service) +{ + struct hal_ev_gatt_client_get_characteristic ev; + + memset(&ev, 0, sizeof(ev)); + ev.status = ch ? GATT_SUCCESS : GATT_FAILURE; + + if (ch) { + ev.char_prop = ch->ch.properties; + element_id_to_hal_gatt_id(&ch->id, &ev.char_id); + } + + ev.conn_id = conn_id; + element_id_to_hal_srvc_id(&service->id, service->primary, &ev.srvc_id); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC, + sizeof(ev), &ev); +} + +static void cache_all_srvc_chars(struct service *srvc, GSList *characteristics) +{ + uint16_t inst_id = 0; + bt_uuid_t uuid; + + for (; characteristics; characteristics = characteristics->next) { + struct characteristic *ch; + + ch = new0(struct characteristic, 1); + if (!ch) { + error("gatt: Error while caching characteristic"); + continue; + } + + ch->descriptors = queue_new(); + if (!ch->descriptors) { + error("gatt: Error while caching characteristic"); + free(ch); + continue; + } + + memcpy(&ch->ch, characteristics->data, sizeof(ch->ch)); + + bt_string_to_uuid(&uuid, ch->ch.uuid); + bt_uuid_to_uuid128(&uuid, &ch->id.uuid); + + /* + * For now we increment inst_id and use it as characteristic + * handle + */ + ch->id.instance = ++inst_id; + + /* Store end handle to use later for descriptors discovery */ + if (characteristics->next) { + struct gatt_char *next = characteristics->next->data; + ch->end_handle = next->handle - 1; + } else { + ch->end_handle = srvc->primary ? srvc->prim.range.end : + srvc->incl.range.end; + } + + if (!queue_push_tail(srvc->chars, ch)) { + error("gatt: Error while caching characteristic"); + destroy_characteristic(ch); + } + } +} + +struct discover_char_data { + int32_t conn_id; + struct service *service; +}; + +static void discover_char_cb(uint8_t status, GSList *characteristics, + void *user_data) +{ + struct discover_char_data *data = user_data; + struct service *srvc = data->service; + + if (queue_isempty(srvc->chars)) + cache_all_srvc_chars(srvc, characteristics); + + send_client_char_notify(queue_peek_head(srvc->chars), data->conn_id, + srvc); + + free(data); +} + +static void handle_client_get_characteristic(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_get_characteristic *cmd = buf; + struct characteristic *ch; + struct element_id match_id; + struct app_connection *conn; + struct service *srvc; + uint8_t status; + + DBG(""); + + if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->char_id[0]) : 0)) { + error("Invalid get characteristic size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); + if (!find_service(cmd->conn_id, &match_id, &conn, &srvc)) { + status = HAL_STATUS_FAILED; + goto done; + } + + /* Discover all characteristics for services if not cached yet */ + if (queue_isempty(srvc->chars)) { + struct att_range range; + + struct discover_char_data *cb_data = + new0(struct discover_char_data, 1); + + if (!cb_data) { + error("gatt: Cannot allocate cb data"); + status = HAL_STATUS_FAILED; + goto done; + } + + cb_data->service = srvc; + cb_data->conn_id = conn->id; + + range = srvc->primary ? srvc->prim.range : srvc->incl.range; + + if (!gatt_discover_char(conn->device->attrib, range.start, + range.end, NULL, + discover_char_cb, cb_data)) { + free(cb_data); + + status = HAL_STATUS_FAILED; + goto done; + } + + status = HAL_STATUS_SUCCESS; + goto done; + } + + if (cmd->continuation) + ch = queue_find(srvc->chars, match_char_by_higher_inst_id, + INT_TO_PTR(cmd->char_id[0].inst_id)); + else + ch = queue_peek_head(srvc->chars); + + send_client_char_notify(ch, conn->id, srvc); + + status = HAL_STATUS_SUCCESS; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status); +} + +static void send_client_descr_notify(int32_t status, int32_t conn_id, + bool primary, + const struct element_id *srvc, + const struct element_id *ch, + const struct element_id *opt_descr) +{ + struct hal_ev_gatt_client_get_descriptor ev; + + memset(&ev, 0, sizeof(ev)); + + ev.status = status; + ev.conn_id = conn_id; + + element_id_to_hal_srvc_id(srvc, primary, &ev.srvc_id); + element_id_to_hal_gatt_id(ch, &ev.char_id); + + if (opt_descr) + element_id_to_hal_gatt_id(opt_descr, &ev.descr_id); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev); +} + +struct discover_desc_data { + struct app_connection *conn; + struct service *srvc; + struct characteristic *ch; +}; + +static void gatt_discover_desc_cb(guint8 status, GSList *descs, + gpointer user_data) +{ + struct discover_desc_data *data = user_data; + struct app_connection *conn = data->conn; + struct service *srvc = data->srvc; + struct characteristic *ch = data->ch; + struct descriptor *descr; + int i = 0; + + if (status != 0) { + error("Discover all characteristic descriptors failed [%s]: %s", + ch->ch.uuid, att_ecode2str(status)); + goto reply; + } + + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; + bt_uuid_t uuid; + + descr = new0(struct descriptor, 1); + if (!descr) + continue; + + bt_string_to_uuid(&uuid, desc->uuid); + bt_uuid_to_uuid128(&uuid, &descr->id.uuid); + + descr->id.instance = i++; + descr->handle = desc->handle; + + if (!queue_push_tail(ch->descriptors, descr)) + free(descr); + } + +reply: + descr = queue_peek_head(ch->descriptors); + + send_client_descr_notify(status, conn->id, srvc->primary, &srvc->id, + &ch->id, + descr ? &descr->id : NULL); + + free(data); +} + +static bool build_descr_cache(struct app_connection *connection, + struct service *srvc, + struct characteristic *ch) +{ + struct discover_desc_data *cb_data; + uint16_t start, end; + + /* Clip range to given characteristic */ + start = ch->ch.value_handle + 1; + end = ch->end_handle; + + /* If there are no descriptors, notify with fail status. */ + if (start > end) + return false; + + cb_data = new0(struct discover_desc_data, 1); + if (!cb_data) + return false; + + cb_data->conn = connection; + cb_data->srvc = srvc; + cb_data->ch = ch; + + if (!gatt_discover_desc(connection->device->attrib, start, end, NULL, + gatt_discover_desc_cb, cb_data)) { + free(cb_data); + return false; + } + + return true; +} + +static void handle_client_get_descriptor(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_get_descriptor *cmd = buf; + struct descriptor *descr = NULL; + struct characteristic *ch; + struct service *srvc; + struct element_id srvc_id; + struct element_id char_id; + struct app_connection *conn; + int32_t conn_id; + uint8_t primary; + uint8_t status; + + DBG(""); + + if (len != sizeof(*cmd) + + (cmd->continuation ? sizeof(cmd->descr_id[0]) : 0)) { + error("gatt: Invalid get descr command (%u bytes), terminating", + len); + + raise(SIGTERM); + return; + } + + conn_id = cmd->conn_id; + primary = cmd->srvc_id.is_primary; + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + + if (!find_service(conn_id, &srvc_id, &conn, &srvc)) { + error("gatt: Get descr. could not find service"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Get descr. could not find characteristic"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + if (queue_isempty(ch->descriptors)) { + if (build_descr_cache(conn, srvc, ch)) { + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, + HAL_STATUS_SUCCESS); + return; + } + } + + status = HAL_STATUS_SUCCESS; + + /* Send from cache */ + if (cmd->continuation) + descr = queue_find(ch->descriptors, + match_descr_by_higher_inst_id, + INT_TO_PTR(cmd->descr_id[0].inst_id)); + else + descr = queue_peek_head(ch->descriptors); + +failed: + send_client_descr_notify(descr ? GATT_SUCCESS : GATT_FAILURE, conn_id, + primary, &srvc_id, &char_id, + &descr->id); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status); +} + +struct char_op_data { + int32_t conn_id; + const struct element_id *srvc_id; + const struct element_id *char_id; + uint8_t primary; +}; + +static struct char_op_data *create_char_op_data(int32_t conn_id, + const struct element_id *s_id, + const struct element_id *ch_id, + bool primary) +{ + struct char_op_data *d; + + d = new0(struct char_op_data, 1); + if (!d) + return NULL; + + d->conn_id = conn_id; + d->srvc_id = s_id; + d->char_id = ch_id; + d->primary = primary; + + return d; +} + +static void send_client_read_char_notify(int32_t status, const uint8_t *pdu, + uint16_t len, int32_t conn_id, + const struct element_id *s_id, + const struct element_id *ch_id, + uint8_t primary) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_read_characteristic *ev = (void *) buf; + ssize_t vlen; + + memset(buf, 0, sizeof(buf)); + + ev->conn_id = conn_id; + ev->status = status; + + element_id_to_hal_srvc_id(s_id, primary, &ev->data.srvc_id); + element_id_to_hal_gatt_id(ch_id, &ev->data.char_id); + + if (pdu) { + vlen = dec_read_resp(pdu, len, ev->data.value, sizeof(buf)); + if (vlen < 0) { + error("gatt: Protocol error"); + ev->status = GATT_FAILURE; + } else { + ev->data.len = vlen; + } + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC, + sizeof(*ev) + ev->data.len, ev); +} + +static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct char_op_data *data = user_data; + + send_client_read_char_notify(status, pdu, len, data->conn_id, + data->srvc_id, data->char_id, + data->primary); + + free(data); +} + +static void handle_client_read_characteristic(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_read_characteristic *cmd = buf; + struct char_op_data *cb_data; + struct characteristic *ch; + struct app_connection *conn; + struct service *srvc; + struct element_id srvc_id; + struct element_id char_id; + uint8_t status; + + DBG(""); + + /* TODO authorization needs to be handled */ + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + + if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + /* search characteristics by element id */ + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Characteristic with inst_id: %d not found", + cmd->char_id.inst_id); + status = HAL_STATUS_FAILED; + goto failed; + } + + cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id, + cmd->srvc_id.is_primary); + if (!cb_data) { + error("gatt: Cannot allocate cb data"); + status = HAL_STATUS_NOMEM; + goto failed; + } + + if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle, + read_char_cb, cb_data)) { + error("gatt: Cannot read characteristic with inst_id: %d", + cmd->char_id.inst_id); + status = HAL_STATUS_FAILED; + free(cb_data); + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status); + + /* + * We should send notification with service, characteristic id in case + * of errors. + */ + if (status != HAL_STATUS_SUCCESS) + send_client_read_char_notify(GATT_FAILURE, NULL, 0, + cmd->conn_id, &srvc_id, &char_id, + cmd->srvc_id.is_primary); +} + +static void send_client_write_char_notify(int32_t status, int32_t conn_id, + const struct element_id *srvc_id, + const struct element_id *char_id, + uint8_t primary) +{ + struct hal_ev_gatt_client_write_characteristic ev; + + memset(&ev, 0, sizeof(ev)); + + ev.conn_id = conn_id; + ev.status = status; + + element_id_to_hal_srvc_id(srvc_id, primary, &ev.data.srvc_id); + element_id_to_hal_gatt_id(char_id, &ev.data.char_id); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC, + sizeof(ev), &ev); +} + +static void write_char_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct char_op_data *data = user_data; + + send_client_write_char_notify(status, data->conn_id, data->srvc_id, + data->char_id, data->primary); + + free(data); +} + +static void handle_client_write_characteristic(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_write_characteristic *cmd = buf; + struct char_op_data *cb_data = NULL; + struct characteristic *ch; + struct app_connection *conn; + struct service *srvc; + struct element_id srvc_id; + struct element_id char_id; + uint8_t status; + guint res; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid write char size (%u bytes), terminating", len); + raise(SIGTERM); + return; + } + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + + if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + /* search characteristics by instance id */ + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Characteristic with inst_id: %d not found", + cmd->char_id.inst_id); + status = HAL_STATUS_FAILED; + goto failed; + } + + if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE) { + cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id, + cmd->srvc_id.is_primary); + if (!cb_data) { + error("gatt: Cannot allocate call data"); + status = HAL_STATUS_NOMEM; + goto failed; + } + } + + switch (cmd->write_type) { + case GATT_WRITE_TYPE_NO_RESPONSE: + res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle, + cmd->value, cmd->len, + NULL, NULL); + break; + case GATT_WRITE_TYPE_PREPARE: + res = gatt_reliable_write_char(conn->device->attrib, + ch->ch.value_handle, + cmd->value, cmd->len, + write_char_cb, cb_data); + break; + case GATT_WRITE_TYPE_DEFAULT: + res = gatt_write_char(conn->device->attrib, ch->ch.value_handle, + cmd->value, cmd->len, + write_char_cb, cb_data); + break; + default: + error("gatt: Write type %d unsupported", cmd->write_type); + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } + + if (!res) { + error("gatt: Cannot write char. with inst_id: %d", + cmd->char_id.inst_id); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status); + + /* + * We should send notification with service, characteristic id in case + * of error and write with no response + */ + if (status != HAL_STATUS_SUCCESS || + cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) { + int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ? + GATT_SUCCESS : GATT_FAILURE; + + send_client_write_char_notify(gatt_status, cmd->conn_id, + &srvc_id, &char_id, + cmd->srvc_id.is_primary); + free(cb_data); + } +} + +static void send_client_descr_read_notify(int32_t status, const uint8_t *pdu, + guint16 len, int32_t conn_id, + const struct element_id *srvc, + const struct element_id *ch, + const struct element_id *descr, + uint8_t primary) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_read_descriptor *ev = (void *) buf; + + memset(buf, 0, sizeof(buf)); + + ev->status = status; + ev->conn_id = conn_id; + + element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id); + element_id_to_hal_gatt_id(ch, &ev->data.char_id); + element_id_to_hal_gatt_id(descr, &ev->data.descr_id); + + if (len && pdu) { + ssize_t ret; + + ret = dec_read_resp(pdu, len, ev->data.value, + GATT_MAX_ATTR_LEN); + if (ret < 0) { + error("gatt: Protocol error"); + ev->status = GATT_FAILURE; + } else { + ev->data.len = ret; + } + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_READ_DESCRIPTOR, + sizeof(*ev) + ev->data.len, ev); +} + +struct desc_data { + int32_t conn_id; + const struct element_id *srvc_id; + const struct element_id *char_id; + const struct element_id *descr_id; + uint8_t primary; +}; + +static void read_desc_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct desc_data *cb_data = user_data; + + if (status != 0) + error("gatt: Discover all char descriptors failed: %s", + att_ecode2str(status)); + + send_client_descr_read_notify(status, pdu, len, cb_data->conn_id, + cb_data->srvc_id, cb_data->char_id, + cb_data->descr_id, cb_data->primary); + + free(cb_data); +} + +static struct desc_data *create_desc_data(int32_t conn_id, + const struct element_id *s_id, + const struct element_id *ch_id, + const struct element_id *d_id, + uint8_t primary) +{ + struct desc_data *d; + + d = new0(struct desc_data, 1); + if (!d) + return NULL; + + d->conn_id = conn_id; + d->srvc_id = s_id; + d->char_id = ch_id; + d->descr_id = d_id; + d->primary = primary; + + return d; +} + +static void handle_client_read_descriptor(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_read_descriptor *cmd = buf; + struct desc_data *cb_data; + struct characteristic *ch; + struct descriptor *descr; + struct service *srvc; + struct element_id char_id; + struct element_id descr_id; + struct element_id srvc_id; + struct app_connection *conn; + int32_t conn_id = 0; + uint8_t primary; + uint8_t status; + + DBG(""); + + conn_id = cmd->conn_id; + primary = cmd->srvc_id.is_primary; + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id); + + if (!find_service(conn_id, &srvc_id, &conn, &srvc)) { + error("gatt: Read descr. could not find service"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Read descr. could not find characteristic"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + descr = queue_find(ch->descriptors, match_descr_by_element_id, + &descr_id); + if (!descr) { + error("gatt: Read descr. could not find descriptor"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, &descr->id, + primary); + if (!cb_data) { + error("gatt: Read descr. could not allocate callback data"); + + status = HAL_STATUS_NOMEM; + goto failed; + } + + if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb, + cb_data)) { + free(cb_data); + + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + if (status != HAL_STATUS_SUCCESS) + send_client_descr_read_notify(GATT_FAILURE, NULL, 0, conn_id, + &srvc_id, &char_id, &descr_id, + primary); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status); +} + +static void send_client_descr_write_notify(int32_t status, int32_t conn_id, + const struct element_id *srvc, + const struct element_id *ch, + const struct element_id *descr, + uint8_t primary) { + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_write_descriptor *ev = (void *) buf; + + memset(buf, 0, sizeof(buf)); + + ev->status = status; + ev->conn_id = conn_id; + + element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id); + element_id_to_hal_gatt_id(ch, &ev->data.char_id); + element_id_to_hal_gatt_id(descr, &ev->data.descr_id); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR, + sizeof(*ev), ev); +} + +static void write_descr_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct desc_data *cb_data = user_data; + + if (status) + error("gatt: Write descriptors failed: %s", + att_ecode2str(status)); + + send_client_descr_write_notify(status, cb_data->conn_id, + cb_data->srvc_id, cb_data->char_id, + cb_data->descr_id, cb_data->primary); + + free(cb_data); +} + +static void handle_client_write_descriptor(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_write_descriptor *cmd = buf; + struct desc_data *cb_data = NULL; + struct characteristic *ch; + struct descriptor *descr; + struct service *srvc; + struct element_id srvc_id; + struct element_id char_id; + struct element_id descr_id; + struct app_connection *conn; + int32_t conn_id; + uint8_t primary; + uint8_t status; + guint res; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid write desriptor command (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + primary = cmd->srvc_id.is_primary; + conn_id = cmd->conn_id; + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id); + + if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { + error("gatt: Write descr. could not find service"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Write descr. could not find characteristic"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + descr = queue_find(ch->descriptors, match_descr_by_element_id, + &descr_id); + if (!descr) { + error("gatt: Write descr. could not find descriptor"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE) { + cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, + &descr->id, primary); + if (!cb_data) { + error("gatt: Write descr. could not allocate cb_data"); + + status = HAL_STATUS_NOMEM; + goto failed; + } + } + + switch (cmd->write_type) { + case GATT_WRITE_TYPE_NO_RESPONSE: + res = gatt_write_cmd(conn->device->attrib, descr->handle, + cmd->value, cmd->len, NULL , NULL); + break; + case GATT_WRITE_TYPE_PREPARE: + res = gatt_reliable_write_char(conn->device->attrib, + descr->handle, cmd->value, + cmd->len, write_descr_cb, + cb_data); + break; + case GATT_WRITE_TYPE_DEFAULT: + res = gatt_write_char(conn->device->attrib, descr->handle, + cmd->value, cmd->len, + write_descr_cb, cb_data); + break; + default: + error("gatt: Write type %d unsupported", cmd->write_type); + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } + + if (!res) { + error("gatt: Write desc, could not write desc"); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + if (status != HAL_STATUS_SUCCESS || + cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) { + int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ? + GATT_SUCCESS : GATT_FAILURE; + + send_client_descr_write_notify(gatt_status, conn_id, &srvc_id, + &char_id, &descr_id, primary); + free(cb_data); + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, status); +} + +static void send_client_write_execute_notify(int32_t id, int32_t status) +{ + struct hal_ev_gatt_client_exec_write ev; + + ev.conn_id = id; + ev.status = status; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_EXEC_WRITE, + sizeof(ev), &ev); +} + +static void write_execute_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + send_client_write_execute_notify(PTR_TO_INT(user_data), status); +} + +static void handle_client_execute_write(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_execute_write *cmd = buf; + struct app_connection *conn; + uint8_t status; + uint8_t flags; + + DBG(""); + + conn = find_connection_by_id(cmd->conn_id); + if (!conn) { + status = HAL_STATUS_FAILED; + goto reply; + } + + flags = cmd->execute ? ATT_WRITE_ALL_PREP_WRITES : + ATT_CANCEL_ALL_PREP_WRITES; + + if (!gatt_execute_write(conn->device->attrib, flags, write_execute_cb, + INT_TO_PTR(cmd->conn_id))) { + error("gatt: Could not send execute write"); + status = HAL_STATUS_FAILED; + goto reply; + } + + status = HAL_STATUS_SUCCESS; +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_EXECUTE_WRITE, status); + + /* In case of early error send also notification.*/ + if (status != HAL_STATUS_SUCCESS) + send_client_write_execute_notify(cmd->conn_id, GATT_FAILURE); +} + +static void handle_notification(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_notify *ev = (void *) buf; + struct notification_data *notification = user_data; + uint8_t data_offset = sizeof(uint8_t) + sizeof(uint16_t); + + if (len < data_offset) + return; + + memcpy(&ev->char_id, ¬ification->ch, sizeof(ev->char_id)); + memcpy(&ev->srvc_id, ¬ification->service, sizeof(ev->srvc_id)); + bdaddr2android(¬ification->conn->device->bdaddr, &ev->bda); + ev->conn_id = notification->conn->id; + ev->is_notify = pdu[0] == ATT_OP_HANDLE_NOTIFY; + + /* We have to cut opcode and handle from data */ + ev->len = len - data_offset; + memcpy(ev->value, pdu + data_offset, len - data_offset); + + if (!ev->is_notify) { + uint8_t *res; + uint16_t len; + size_t plen; + + res = g_attrib_get_buffer( + notification->conn->device->attrib, + &plen); + len = enc_confirmation(res, plen); + if (len > 0) + g_attrib_send(notification->conn->device->attrib, + 0, res, len, NULL, NULL, NULL); + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY, + sizeof(*ev) + ev->len, ev); +} + +static void send_register_for_notification_ev(int32_t id, int32_t registered, + int32_t status, + const struct hal_gatt_srvc_id *srvc, + const struct hal_gatt_gatt_id *ch) +{ + struct hal_ev_gatt_client_reg_for_notif ev; + + ev.conn_id = id; + ev.status = status; + ev.registered = registered; + memcpy(&ev.srvc_id, srvc, sizeof(ev.srvc_id)); + memcpy(&ev.char_id, ch, sizeof(ev.char_id)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF, sizeof(ev), &ev); +} + +static void handle_client_register_for_notification(const void *buf, + uint16_t len) +{ + const struct hal_cmd_gatt_client_register_for_notification *cmd = buf; + struct notification_data *notification; + struct characteristic *c; + struct element_id match_id; + struct app_connection *conn; + int32_t conn_id = 0; + struct service *service; + uint8_t status; + int32_t gatt_status; + bdaddr_t addr; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &addr); + + conn = find_conn(&addr, cmd->client_if); + if (!conn) { + status = HAL_STATUS_FAILED; + goto failed; + } + + conn_id = conn->id; + + hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); + service = queue_find(conn->device->services, match_srvc_by_element_id, + &match_id); + if (!service) { + status = HAL_STATUS_FAILED; + goto failed; + } + + hal_gatt_id_to_element_id(&cmd->char_id, &match_id); + c = queue_find(service->chars, match_char_by_element_id, &match_id); + if (!c) { + status = HAL_STATUS_FAILED; + goto failed; + } + + notification = new0(struct notification_data, 1); + if (!notification) { + status = HAL_STATUS_FAILED; + goto failed; + } + + memcpy(¬ification->ch, &cmd->char_id, sizeof(notification->ch)); + memcpy(¬ification->service, &cmd->srvc_id, + sizeof(notification->service)); + notification->conn = conn; + + if (queue_find(conn->app->notifications, match_notification, + notification)) { + free(notification); + status = HAL_STATUS_SUCCESS; + goto failed; + } + + notification->notif_id = g_attrib_register(conn->device->attrib, + ATT_OP_HANDLE_NOTIFY, + c->ch.value_handle, + handle_notification, + notification, + destroy_notification); + if (!notification->notif_id) { + free(notification); + status = HAL_STATUS_FAILED; + goto failed; + } + + notification->ind_id = g_attrib_register(conn->device->attrib, + ATT_OP_HANDLE_IND, + c->ch.value_handle, + handle_notification, + notification, + destroy_notification); + if (!notification->ind_id) { + g_attrib_unregister(conn->device->attrib, + notification->notif_id); + free(notification); + status = HAL_STATUS_FAILED; + goto failed; + } + + /* + * Because same data - notification - is shared by two handlers, we + * introduce ref counter to be sure that data can be freed with no risk. + * Counter is decremented in destroy_notification. + */ + notification->ref = 2; + + if (!queue_push_tail(conn->app->notifications, notification)) { + unregister_notification(notification); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; + send_register_for_notification_ev(conn_id, 1, gatt_status, + &cmd->srvc_id, &cmd->char_id); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, status); +} + +static void handle_client_deregister_for_notification(const void *buf, + uint16_t len) +{ + const struct hal_cmd_gatt_client_deregister_for_notification *cmd = buf; + struct notification_data *notification, notif; + struct app_connection *conn; + int32_t conn_id = 0; + uint8_t status; + int32_t gatt_status; + bdaddr_t addr; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &addr); + + conn = find_conn(&addr, cmd->client_if); + if (!conn) { + status = HAL_STATUS_FAILED; + goto failed; + } + + conn_id = conn->id; + + memcpy(¬if.ch, &cmd->char_id, sizeof(notif.ch)); + memcpy(¬if.service, &cmd->srvc_id, sizeof(notif.service)); + notif.conn = conn; + + notification = queue_find(conn->app->notifications, + match_notification, ¬if); + if (!notification) { + status = HAL_STATUS_FAILED; + goto failed; + } + + unregister_notification(notification); + + status = HAL_STATUS_SUCCESS; + +failed: + gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; + send_register_for_notification_ev(conn_id, 0, gatt_status, + &cmd->srvc_id, &cmd->char_id); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, status); +} + +static void send_client_remote_rssi_notify(int32_t client_if, + const bdaddr_t *addr, + int32_t rssi, int32_t status) +{ + struct hal_ev_gatt_client_read_remote_rssi ev; + + ev.client_if = client_if; + bdaddr2android(addr, &ev.address); + ev.rssi = rssi; + ev.status = status; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI, sizeof(ev), &ev); +} + +static void read_remote_rssi_cb(uint8_t status, const bdaddr_t *addr, + int8_t rssi, void *user_data) +{ + int32_t client_if = PTR_TO_INT(user_data); + int32_t gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; + + send_client_remote_rssi_notify(client_if, addr, rssi, gatt_status); +} + +static void handle_client_read_remote_rssi(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_read_remote_rssi *cmd = buf; + uint8_t status; + bdaddr_t bdaddr; + + DBG(""); + + if (!find_app_by_id(cmd->client_if)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2bdaddr(cmd->bdaddr, &bdaddr); + if (!bt_read_device_rssi(&bdaddr, read_remote_rssi_cb, + INT_TO_PTR(cmd->client_if))) { + error("gatt: Could not read RSSI"); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, status); + + if (status != HAL_STATUS_SUCCESS) + send_client_remote_rssi_notify(cmd->client_if, &bdaddr, 0, + GATT_FAILURE); +} + +static void handle_client_get_device_type(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_get_device_type *cmd = buf; + struct hal_rsp_gatt_client_get_device_type rsp; + bdaddr_t bdaddr; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + rsp.type = bt_get_device_android_type(&bdaddr); + + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, + sizeof(rsp), &rsp, -1); +} + +static void handle_client_set_adv_data(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_set_adv_data *cmd = buf; + uint8_t status; + + if (len != sizeof(*cmd) + cmd->manufacturer_len) { + error("Invalid set adv data command (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d manufacturer=%u", + cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower, + cmd->min_interval, cmd->max_interval, cmd->appearance, + cmd->manufacturer_len); + + /* + * TODO + * Currently kernel is setting all except for vendor data. + * This should be implemented when kernel supports it. + */ + + if (cmd->manufacturer_len) { + error("gatt: Manufacturer advertising data not supported"); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_SET_ADV_DATA, status); +} + +static void handle_client_test_command(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_TEST_COMMAND, HAL_STATUS_FAILED); +} + +static void handle_server_register(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_register *cmd = buf; + struct hal_ev_gatt_server_register ev; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + ev.server_if = register_app(cmd->uuid, APP_SERVER); + + if (ev.server_if) + ev.status = GATT_SUCCESS; + else + ev.status = GATT_FAILURE; + + memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_REGISTER, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER, + HAL_STATUS_SUCCESS); +} + +static void handle_server_unregister(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_unregister *cmd = buf; + uint8_t status; + struct gatt_app *server; + + DBG(""); + + server = find_app_by_id(cmd->server_if); + if (!server) { + error("gatt: server_if=%d not found", cmd->server_if); + status = HAL_STATUS_FAILED; + goto failed; + } + + destroy_gatt_app(server); + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_UNREGISTER, status); +} + +static void handle_server_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_connect *cmd = buf; + uint8_t status; + bdaddr_t addr; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &addr); + + status = handle_connect(cmd->server_if, &addr); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT, + status); +} + +static void handle_server_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_disconnect *cmd = buf; + struct app_connection *conn; + uint8_t status; + + DBG(""); + + /* TODO: should we care to match also bdaddr when conn_id is unique? */ + conn = find_connection_by_id(cmd->conn_id); + if (conn) + trigger_disconnection(conn); + + status = HAL_STATUS_SUCCESS; + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_DISCONNECT, status); +} + +static void handle_server_add_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_add_service *cmd = buf; + struct hal_ev_gatt_server_service_added ev; + struct gatt_app *server; + uint8_t status; + bt_uuid_t uuid; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2uuid(cmd->srvc_id.uuid, &uuid); + + ev.srvc_handle = gatt_db_add_service(gatt_db, &uuid, + cmd->srvc_id.is_primary, + cmd->num_handles); + if (!ev.srvc_handle) { + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + ev.srvc_id = cmd->srvc_id; + ev.server_if = cmd->server_if; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_SERVICE_ADDED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_SERVICE, status); +} + +static void handle_server_add_included_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_add_inc_service *cmd = buf; + struct hal_ev_gatt_server_inc_srvc_added ev; + struct gatt_app *server; + uint8_t status; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + ev.incl_srvc_handle = gatt_db_add_included_service(gatt_db, + cmd->service_handle, + cmd->included_handle); + if (!ev.incl_srvc_handle) { + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; +failed: + ev.srvc_handle = cmd->service_handle; + ev.status = status; + ev.server_if = cmd->server_if; + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_INC_SRVC_ADDED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status); +} + +static bool is_service(const bt_uuid_t *type) +{ + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); + if (!bt_uuid_cmp(&uuid, type)) + return true; + + bt_uuid16_create(&uuid, GATT_SND_SVC_UUID); + if (!bt_uuid_cmp(&uuid, type)) + return true; + + return false; +} + +static void send_dev_pending_response(struct gatt_device *device, + uint8_t opcode) +{ + uint8_t rsp[ATT_DEFAULT_LE_MTU]; + struct pending_request *val; + uint16_t len = 0; + uint8_t error = 0; + + switch (opcode) { + case ATT_OP_READ_BY_TYPE_REQ: { + struct att_data_list *adl; + int iterator = 0; + int length; + struct queue *temp; + + + temp = queue_new(); + if (!temp) + goto done; + + val = queue_pop_head(device->pending_requests); + if (!val) { + queue_destroy(temp, NULL); + error = ATT_ECODE_ATTR_NOT_FOUND; + goto done; + } + + length = val->length; + + while (val && val->length == length) { + queue_push_tail(temp, val); + val = queue_pop_head(device->pending_requests); + } + + adl = att_data_list_alloc(queue_length(temp), sizeof(uint16_t) + + length); + + val = queue_pop_head(temp); + while (val) { + uint8_t *value = adl->data[iterator++]; + + put_le16(val->handle, value); + memcpy(&value[2], val->value, val->length); + + destroy_pending_request(val); + val = queue_pop_head(temp); + } + + len = enc_read_by_type_resp(adl, rsp, sizeof(rsp)); + + att_data_list_free(adl); + queue_destroy(temp, destroy_pending_request); + + break; + } + case ATT_OP_READ_BLOB_REQ: + val = queue_pop_head(device->pending_requests); + if (!val || val->length < 0) { + error = ATT_ECODE_ATTR_NOT_FOUND; + goto done; + } + + len = enc_read_blob_resp(val->value, val->length, val->offset, + rsp, sizeof(rsp)); + destroy_pending_request(val); + break; + case ATT_OP_READ_REQ: + val = queue_pop_head(device->pending_requests); + if (!val || val->length < 0) { + error = ATT_ECODE_ATTR_NOT_FOUND; + goto done; + } + + len = enc_read_resp(val->value, val->length, rsp, sizeof(rsp)); + destroy_pending_request(val); + break; + case ATT_OP_READ_BY_GROUP_REQ: { + struct att_data_list *adl; + int iterator = 0; + int length; + struct queue *temp; + + temp = queue_new(); + if (!temp) + goto done; + + val = queue_pop_head(device->pending_requests); + if (!val) { + queue_destroy(temp, NULL); + error = ATT_ECODE_ATTR_NOT_FOUND; + goto done; + } + + length = val->length; + + while (val && val->length == length) { + queue_push_tail(temp, val); + val = queue_pop_head(device->pending_requests); + } + + adl = att_data_list_alloc(queue_length(temp), + 2 * sizeof(uint16_t) + length); + + val = queue_pop_head(temp); + while (val) { + uint8_t *value = adl->data[iterator++]; + uint16_t end_handle; + + end_handle = gatt_db_get_end_handle(gatt_db, + val->handle); + + put_le16(val->handle, value); + put_le16(end_handle, &value[2]); + memcpy(&value[4], val->value, val->length); + + destroy_pending_request(val); + val = queue_pop_head(temp); + } + + len = enc_read_by_grp_resp(adl, rsp, sizeof(rsp)); + + att_data_list_free(adl); + queue_destroy(temp, destroy_pending_request); + + break; + } + case ATT_OP_FIND_BY_TYPE_REQ: { + GSList *list = NULL; + + val = queue_pop_head(device->pending_requests); + while (val) { + struct att_range *range; + const bt_uuid_t *type; + + /* Its find by type and value - filter by value here */ + if ((val->length != val->filter_vlen) || + memcmp(val->value, val->filter_value, + val->length)) { + + destroy_pending_request(val); + val = queue_pop_head(device->pending_requests); + continue; + } + + range = new0(struct att_range, 1); + if (!range) { + destroy_pending_request(val); + error = ATT_ECODE_INSUFF_RESOURCES; + break; + } + + range->start = val->handle; + range->end = range->start; + + /* Get proper end handle if its group type */ + type = gatt_db_get_attribute_type(gatt_db, val->handle); + if (is_service(type)) + range->end = gatt_db_get_end_handle(gatt_db, + val->handle); + + list = g_slist_append(list, range); + + destroy_pending_request(val); + val = queue_pop_head(device->pending_requests); + } + + if (list && !error) + len = enc_find_by_type_resp(list, rsp, sizeof(rsp)); + else + error = ATT_ECODE_ATTR_NOT_FOUND; + + g_slist_free_full(list, free); + + break; + } + case ATT_OP_EXEC_WRITE_REQ: + val = queue_pop_head(device->pending_requests); + if (!val) { + error = ATT_ECODE_ATTR_NOT_FOUND; + break; + } + + len = enc_exec_write_resp(rsp); + destroy_pending_request(val); + break; + case ATT_OP_WRITE_REQ: + val = queue_pop_head(device->pending_requests); + if (!val) { + error = ATT_ECODE_ATTR_NOT_FOUND; + break; + } + + len = enc_write_resp(rsp); + destroy_pending_request(val); + break; + case ATT_OP_PREP_WRITE_REQ: + val = queue_pop_head(device->pending_requests); + if (!val) { + error = ATT_ECODE_ATTR_NOT_FOUND; + break; + } + + len = enc_prep_write_resp(val->handle, val->offset, val->value, + val->length, rsp, sizeof(rsp)); + destroy_pending_request(val); + break; + default: + break; + } + +done: + if (!len) + len = enc_error_resp(opcode, 0x0000, error, rsp, + ATT_DEFAULT_LE_MTU); + + g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL); + + queue_remove_all(device->pending_requests, NULL, NULL, + destroy_pending_request); +} + +struct request_processing_data { + uint8_t opcode; + struct gatt_device *device; +}; + +static bool match_pending_dev_request(const void *data, const void *user_data) +{ + const struct pending_request *pending_request = data; + + return pending_request->length == READ_PENDING; +} + +static bool match_dev_request_by_handle(const void *data, const void *user_data) +{ + const struct pending_request *handle_data = data; + uint16_t handle = PTR_TO_UINT(user_data); + + return handle_data->handle == handle; +} + +static void read_requested_attributes(void *data, void *user_data) +{ + struct pending_request *resp_data = data; + struct request_processing_data *process_data = user_data; + uint8_t *value; + int value_len; + + if (!gatt_db_read(gatt_db, resp_data->handle, + resp_data->offset, + process_data->opcode, + &process_data->device->bdaddr, + &value, &value_len)) { + resp_data->length = READ_FAILED; + return; + } + + /* We have value here already if no callback will be called */ + if (value_len >= 0) { + resp_data->value = malloc0(value_len); + if (!resp_data->value) { + /* If data cannot be copied, act like when read fails */ + resp_data->length = READ_FAILED; + return; + } + + memcpy(resp_data->value, value, value_len); + resp_data->length = value_len; + } else if (resp_data->length == READ_INIT) { + resp_data->length = READ_PENDING; + } +} + +static void process_dev_pending_requests(struct gatt_device *device, + uint8_t att_opcode) +{ + struct request_processing_data process_data; + + process_data.device = device; + process_data.opcode = att_opcode; + + /* Process pending requests and prepare response */ + queue_foreach(device->pending_requests, read_requested_attributes, + &process_data); + + if (!queue_find(device->pending_requests, + match_pending_dev_request, NULL)) + send_dev_pending_response(device, att_opcode); +} + +static void send_gatt_response(uint8_t opcode, uint16_t handle, + uint16_t offset, uint8_t status, + uint16_t len, const uint8_t *data, + bdaddr_t *bdaddr) +{ + struct gatt_device *dev; + struct pending_request *entry; + + dev = find_device_by_addr(bdaddr); + if (!dev) { + error("gatt: send_gatt_response, could not find dev"); + goto done; + } + + if (status) + goto done; + + entry = queue_find(dev->pending_requests, match_dev_request_by_handle, + UINT_TO_PTR(handle)); + if (!entry) { + DBG("No pending response found! Bogus android response?"); + return; + } + + entry->handle = handle; + entry->offset = offset; + entry->length = len; + + if (!len) + goto done; + + entry->value = malloc0(len); + if (!entry->value) { + /* send_dev_pending_request on empty queue sends error resp. */ + queue_remove(dev->pending_requests, entry); + destroy_pending_request(entry); + + goto done; + } + + memcpy(entry->value, data, len); + +done: + if (!queue_find(dev->pending_requests, match_pending_dev_request, NULL)) + send_dev_pending_response(dev, opcode); +} + +static void set_trans_id(struct gatt_app *app, unsigned int id, int8_t opcode) +{ + app->trans_id.id = id; + app->trans_id.opcode = opcode; +} + +static void clear_trans_id(struct gatt_app *app) +{ + app->trans_id.id = 0; + app->trans_id.opcode = 0; +} + +static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, + bdaddr_t *bdaddr, void *user_data) +{ + struct hal_ev_gatt_server_request_read ev; + struct gatt_app *app; + struct app_connection *conn; + int32_t id = PTR_TO_INT(user_data); + static int32_t trans_id = 1; + + app = find_app_by_id(id); + if (!app) { + error("gatt: read_cb, cound not found app id"); + goto failed; + } + + conn = find_conn(bdaddr, app->id); + if (!conn) { + error("gatt: read_cb, cound not found connection"); + goto failed; + } + + memset(&ev, 0, sizeof(ev)); + + /* Store the request data, complete callback and transaction id */ + set_trans_id(app, trans_id++, att_opcode); + + bdaddr2android(bdaddr, ev.bdaddr); + ev.conn_id = conn->id; + ev.attr_handle = handle; + ev.offset = offset; + ev.is_long = att_opcode == ATT_OP_READ_BLOB_REQ; + ev.trans_id = app->trans_id.id; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_REQUEST_READ, + sizeof(ev), &ev); + + return; + +failed: + send_gatt_response(att_opcode, handle, 0, ATT_ECODE_UNLIKELY, 0, + NULL, bdaddr); +} + +static void write_cb(uint16_t handle, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t att_opcode, bdaddr_t *bdaddr, + void *user_data) +{ + struct hal_ev_gatt_server_request_write ev; + struct gatt_app *app; + int32_t id = PTR_TO_INT(user_data); + static int32_t trans_id = 1; + struct app_connection *conn; + + app = find_app_by_id(id); + if (!app) { + error("gatt: write_cb could not found app id"); + goto failed; + } + + conn = find_conn(bdaddr, app->id); + if (!conn) { + error("gatt: write_cb could not found connection"); + goto failed; + } + + /* Store the request data, complete callback and transaction id */ + set_trans_id(app, trans_id++, att_opcode); + + /* TODO figure it out */ + if (att_opcode == ATT_OP_EXEC_WRITE_REQ) + goto failed; + + memset(&ev, 0, sizeof(ev)); + + bdaddr2android(bdaddr, ev.bdaddr); + ev.attr_handle = handle; + ev.offset = offset; + + ev.conn_id = conn->id; + ev.trans_id = app->trans_id.id; + + ev.is_prep = att_opcode == ATT_OP_PREP_WRITE_REQ; + ev.need_rsp = att_opcode == ATT_OP_WRITE_REQ; + + ev.length = len; + memcpy(&ev.value, value, len); + + return; + +failed: + send_gatt_response(att_opcode, handle, 0, ATT_ECODE_UNLIKELY, 0, NULL, + bdaddr); +} + +static void handle_server_add_characteristic(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_add_characteristic *cmd = buf; + struct hal_ev_gatt_server_characteristic_added ev; + struct gatt_app *server; + bt_uuid_t uuid; + uint8_t status; + int32_t app_id = cmd->server_if; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(app_id); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2uuid(cmd->uuid, &uuid); + + /*FIXME: Handle properties. Register callback if needed. */ + ev.char_handle = gatt_db_add_characteristic(gatt_db, + cmd->service_handle, + &uuid, cmd->permissions, + cmd->properties, + read_cb, write_cb, + INT_TO_PTR(app_id)); + if (!ev.char_handle) + status = HAL_STATUS_FAILED; + else + status = HAL_STATUS_SUCCESS; + +failed: + ev.srvc_handle = cmd->service_handle; + ev.status = status; + ev.server_if = app_id; + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_CHAR_ADDED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, status); +} + +static void handle_server_add_descriptor(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_add_descriptor *cmd = buf; + struct hal_ev_gatt_server_descriptor_added ev; + struct gatt_app *server; + bt_uuid_t uuid; + uint8_t status; + int32_t app_id = cmd->server_if; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(app_id); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2uuid(cmd->uuid, &uuid); + + /*FIXME: Handle properties. Register callback if needed. */ + ev.descr_handle = gatt_db_add_char_descriptor(gatt_db, + cmd->service_handle, + &uuid, cmd->permissions, + read_cb, write_cb, + INT_TO_PTR(app_id)); + if (!ev.descr_handle) + status = HAL_STATUS_FAILED; + else + status = HAL_STATUS_SUCCESS; + +failed: + ev.server_if = app_id; + ev.srvc_handle = cmd->service_handle; + memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid)); + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status); +} + +static void handle_server_start_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_start_service *cmd = buf; + struct hal_ev_gatt_server_service_started ev; + struct gatt_app *server; + uint8_t status; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + /* TODO: support BR/EDR (cmd->transport) */ + + if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, true)) { + /* we ignore service now */ + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + ev.server_if = cmd->server_if; + ev.srvc_handle = cmd->service_handle; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_SERVICE_STARTED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_START_SERVICE, status); +} + +static void handle_server_stop_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_stop_service *cmd = buf; + struct hal_ev_gatt_server_service_stopped ev; + struct gatt_app *server; + uint8_t status; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, false)) + status = HAL_STATUS_FAILED; + else + status = HAL_STATUS_SUCCESS; + +failed: + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + ev.server_if = cmd->server_if; + ev.srvc_handle = cmd->service_handle; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_SERVICE_STOPPED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_STOP_SERVICE, status); +} + +static void handle_server_delete_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_delete_service *cmd = buf; + struct hal_ev_gatt_server_service_deleted ev; + struct gatt_app *server; + uint8_t status; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!gatt_db_remove_service(gatt_db, cmd->service_handle)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + ev.srvc_handle = cmd->service_handle; + ev.server_if = cmd->server_if; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_SERVICE_DELETED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_DELETE_SERVICE, status); +} + +static void handle_server_send_indication(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_send_indication *cmd = buf; + uint8_t pdu[ATT_DEFAULT_LE_MTU]; + struct app_connection *conn; + uint8_t status; + uint16_t length; + + DBG(""); + + conn = find_connection_by_id(cmd->conn_id); + if (!conn) { + error("gatt: Could not find connection"); + status = HAL_STATUS_FAILED; + goto reply; + } + + if (cmd->confirm) + /* TODO: Add data to track confirmation for this request */ + length = enc_indication(cmd->attribute_handle, + (uint8_t *)cmd->value, cmd->len, + pdu, sizeof(pdu)); + else + length = enc_notification(cmd->attribute_handle, + (uint8_t *)cmd->value, cmd->len, + pdu, sizeof(pdu)); + + g_attrib_send(conn->device->attrib, 0, pdu, length, NULL, NULL, NULL); + + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_SEND_INDICATION, status); +} + +static void handle_server_send_response(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_send_response *cmd = buf; + struct app_connection *conn; + struct gatt_app *app; + uint8_t status; + + DBG(""); + + conn = find_connection_by_id(cmd->conn_id); + if (!conn) { + error("gatt: could not found connection"); + status = HAL_STATUS_FAILED; + goto reply; + } + + app = conn->app; + + if ((unsigned int)cmd->trans_id != app->trans_id.id) { + error("gatt: transaction ID mismatch (%d!=%d)", + cmd->trans_id, app->trans_id.id); + + status = HAL_STATUS_FAILED; + goto reply; + } + + send_gatt_response(conn->app->trans_id.opcode, cmd->handle, cmd->offset, + cmd->status, cmd->len, cmd->data, + &conn->device->bdaddr); + + /* Clean request data */ + clear_trans_id(app); + + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_SEND_RESPONSE, status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_GATT_CLIENT_REGISTER */ + { handle_client_register, false, + sizeof(struct hal_cmd_gatt_client_register) }, + /* HAL_OP_GATT_CLIENT_UNREGISTER */ + { handle_client_unregister, false, + sizeof(struct hal_cmd_gatt_client_unregister) }, + /* HAL_OP_GATT_CLIENT_SCAN */ + { handle_client_scan, false, + sizeof(struct hal_cmd_gatt_client_scan) }, + /* HAL_OP_GATT_CLIENT_CONNECT */ + { handle_client_connect, false, + sizeof(struct hal_cmd_gatt_client_connect) }, + /* HAL_OP_GATT_CLIENT_DISCONNECT */ + { handle_client_disconnect, false, + sizeof(struct hal_cmd_gatt_client_disconnect) }, + /* HAL_OP_GATT_CLIENT_LISTEN */ + { handle_client_listen, false, + sizeof(struct hal_cmd_gatt_client_listen) }, + /* HAL_OP_GATT_CLIENT_REFRESH */ + { handle_client_refresh, false, + sizeof(struct hal_cmd_gatt_client_refresh) }, + /* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */ + { handle_client_search_service, true, + sizeof(struct hal_cmd_gatt_client_search_service) }, + /* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */ + { handle_client_get_included_service, true, + sizeof(struct hal_cmd_gatt_client_get_included_service) }, + /* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */ + { handle_client_get_characteristic, true, + sizeof(struct hal_cmd_gatt_client_get_characteristic) }, + /* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */ + { handle_client_get_descriptor, true, + sizeof(struct hal_cmd_gatt_client_get_descriptor) }, + /* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */ + { handle_client_read_characteristic, false, + sizeof(struct hal_cmd_gatt_client_read_characteristic) }, + /* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */ + { handle_client_write_characteristic, true, + sizeof(struct hal_cmd_gatt_client_write_characteristic) }, + /* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */ + { handle_client_read_descriptor, false, + sizeof(struct hal_cmd_gatt_client_read_descriptor) }, + /* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */ + { handle_client_write_descriptor, true, + sizeof(struct hal_cmd_gatt_client_write_descriptor) }, + /* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */ + { handle_client_execute_write, false, + sizeof(struct hal_cmd_gatt_client_execute_write)}, + /* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */ + { handle_client_register_for_notification, false, + sizeof(struct hal_cmd_gatt_client_register_for_notification) }, + /* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */ + { handle_client_deregister_for_notification, false, + sizeof(struct hal_cmd_gatt_client_deregister_for_notification) }, + /* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */ + { handle_client_read_remote_rssi, false, + sizeof(struct hal_cmd_gatt_client_read_remote_rssi) }, + /* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */ + { handle_client_get_device_type, false, + sizeof(struct hal_cmd_gatt_client_get_device_type) }, + /* HAL_OP_GATT_CLIENT_SET_ADV_DATA */ + { handle_client_set_adv_data, true, + sizeof(struct hal_cmd_gatt_client_set_adv_data) }, + /* HAL_OP_GATT_CLIENT_TEST_COMMAND */ + { handle_client_test_command, false, + sizeof(struct hal_cmd_gatt_client_test_command) }, + /* HAL_OP_GATT_SERVER_REGISTER */ + { handle_server_register, false, + sizeof(struct hal_cmd_gatt_server_register) }, + /* HAL_OP_GATT_SERVER_UNREGISTER */ + { handle_server_unregister, false, + sizeof(struct hal_cmd_gatt_server_unregister) }, + /* HAL_OP_GATT_SERVER_CONNECT */ + { handle_server_connect, false, + sizeof(struct hal_cmd_gatt_server_connect) }, + /* HAL_OP_GATT_SERVER_DISCONNECT */ + { handle_server_disconnect, false, + sizeof(struct hal_cmd_gatt_server_disconnect) }, + /* HAL_OP_GATT_SERVER_ADD_SERVICE */ + { handle_server_add_service, false, + sizeof(struct hal_cmd_gatt_server_add_service) }, + /* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */ + { handle_server_add_included_service, false, + sizeof(struct hal_cmd_gatt_server_add_inc_service) }, + /* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */ + { handle_server_add_characteristic, false, + sizeof(struct hal_cmd_gatt_server_add_characteristic) }, + /* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */ + { handle_server_add_descriptor, false, + sizeof(struct hal_cmd_gatt_server_add_descriptor) }, + /* HAL_OP_GATT_SERVER_START_SERVICE */ + { handle_server_start_service, false, + sizeof(struct hal_cmd_gatt_server_start_service) }, + /* HAL_OP_GATT_SERVER_STOP_SERVICE */ + { handle_server_stop_service, false, + sizeof(struct hal_cmd_gatt_server_stop_service) }, + /* HAL_OP_GATT_SERVER_DELETE_SERVICE */ + { handle_server_delete_service, false, + sizeof(struct hal_cmd_gatt_server_delete_service) }, + /* HAL_OP_GATT_SERVER_SEND_INDICATION */ + { handle_server_send_indication, true, + sizeof(struct hal_cmd_gatt_server_send_indication) }, + /* HAL_OP_GATT_SERVER_SEND_RESPONSE */ + { handle_server_send_response, true, + sizeof(struct hal_cmd_gatt_server_send_response) }, +}; + +static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len, + uint8_t *rsp, size_t rsp_size, + uint16_t *length, + struct gatt_device *device) +{ + uint16_t start, end; + int len; + bt_uuid_t uuid; + struct queue *q; + + len = dec_read_by_grp_req(cmd, cmd_len, &start, &end, &uuid); + if (!len) + return ATT_ECODE_INVALID_PDU; + + q = queue_new(); + if (!q) + return ATT_ECODE_INSUFF_RESOURCES; + + gatt_db_read_by_group_type(gatt_db, start, end, uuid, q); + + if (queue_isempty(q)) { + queue_destroy(q, NULL); + return ATT_ECODE_ATTR_NOT_FOUND; + } + + while (queue_peek_head(q)) { + uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + uint8_t *value; + int value_len; + struct pending_request *entry; + + entry = new0(struct pending_request, 1); + if (!entry) { + queue_destroy(q, destroy_pending_request); + return ATT_ECODE_UNLIKELY; + } + + entry->handle = handle; + + if (!gatt_db_read(gatt_db, handle, 0, ATT_OP_READ_BY_GROUP_REQ, + &device->bdaddr, &value, &value_len)) + break; + + entry->value = malloc0(value_len); + if (!entry->value) { + queue_destroy(q, destroy_pending_request); + return ATT_ECODE_UNLIKELY; + } + + memcpy(entry->value, value, value_len); + entry->length = value_len; + + if (!queue_push_tail(device->pending_requests, entry)) { + queue_remove_all(device->pending_requests, NULL, NULL, + destroy_pending_request); + queue_destroy(q, NULL); + return ATT_ECODE_UNLIKELY; + } + } + + queue_destroy(q, NULL); + + send_dev_pending_response(device, cmd[0]); + + return 0; +} + +static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *device) +{ + uint16_t start, end; + uint16_t len; + bt_uuid_t uuid; + struct queue *q; + + DBG(""); + + len = dec_read_by_type_req(cmd, cmd_len, &start, &end, &uuid); + if (!len) + return ATT_ECODE_INVALID_PDU; + + q = queue_new(); + if (!q) + return ATT_ECODE_INSUFF_RESOURCES; + + gatt_db_read_by_type(gatt_db, start, end, uuid, q); + + if (queue_isempty(q)) { + queue_destroy(q, NULL); + return ATT_ECODE_ATTR_NOT_FOUND; + } + + while (queue_peek_head(q)) { + struct pending_request *data; + uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + + data = new0(struct pending_request, 1); + if (!data) { + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + data->length = READ_INIT; + data->handle = handle; + queue_push_tail(device->pending_requests, data); + } + + queue_destroy(q, NULL); + + process_dev_pending_requests(device, ATT_OP_READ_BY_TYPE_REQ); + + return 0; +} + +static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint16_t handle; + uint16_t len; + uint16_t offset; + struct pending_request *data; + + DBG(""); + + switch (cmd[0]) { + case ATT_OP_READ_BLOB_REQ: + len = dec_read_blob_req(cmd, cmd_len, &handle, &offset); + if (!len) + return ATT_ECODE_INVALID_PDU; + break; + case ATT_OP_READ_REQ: + len = dec_read_req(cmd, cmd_len, &handle); + if (!len) + return ATT_ECODE_INVALID_PDU; + offset = 0; + break; + default: + error("gatt: Unexpected read type 0x%02x", cmd[0]); + return ATT_ECODE_REQ_NOT_SUPP; + } + + data = new0(struct pending_request, 1); + if (!data) + return ATT_ECODE_INSUFF_RESOURCES; + + data->offset = offset; + data->handle = handle; + data->length = READ_INIT; + if (!queue_push_tail(dev->pending_requests, data)) { + free(data); + return ATT_ECODE_INSUFF_RESOURCES; + } + + process_dev_pending_requests(dev, cmd[0]); + + return 0; +} + +static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len, + uint8_t *rsp, size_t rsp_size, + struct gatt_device *dev, + uint16_t *length) +{ + uint16_t mtu, imtu, omtu; + GIOChannel *io; + GError *gerr = NULL; + uint16_t len; + + DBG(""); + + len = dec_mtu_req(cmd, cmd_len, &mtu); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (mtu < ATT_DEFAULT_LE_MTU) + return ATT_ECODE_REQ_NOT_SUPP; + + io = g_attrib_get_channel(dev->attrib); + + bt_io_get(io, &gerr, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_INVALID); + if (gerr) { + error("bt_io_get: %s", gerr->message); + g_error_free(gerr); + return ATT_ECODE_UNLIKELY; + } + + /* Limit OMTU to received value */ + mtu = MIN(mtu, omtu); + g_attrib_set_mtu(dev->attrib, mtu); + + /* Respond with our IMTU */ + len = enc_mtu_resp(imtu, rsp, rsp_size); + if (!len) + return ATT_ECODE_UNLIKELY; + + *length = len; + + return 0; +} + +static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len, + uint8_t *rsp, size_t rsp_size, + uint16_t *length) +{ + struct queue *q; + struct att_data_list *adl; + int iterator = 0; + uint16_t start, end; + uint16_t len; + + DBG(""); + + len = dec_find_info_req(cmd, cmd_len, &start, &end); + if (!len) + return ATT_ECODE_INVALID_PDU; + + q = queue_new(); + if (!q) + return ATT_ECODE_UNLIKELY; + + gatt_db_find_information(gatt_db, start, end, q); + + if (queue_isempty(q)) { + queue_destroy(q, NULL); + return ATT_ECODE_ATTR_NOT_FOUND; + } + + len = queue_length(q); + adl = att_data_list_alloc(len, 2 * sizeof(uint16_t)); + if (!adl) { + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + while (queue_peek_head(q)) { + uint8_t *value; + const bt_uuid_t *type; + uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + + type = gatt_db_get_attribute_type(gatt_db, handle); + if (!type) + break; + + value = adl->data[iterator++]; + + put_le16(handle, value); + memcpy(&value[2], &type->value.u16, bt_uuid_len(type)); + + } + + if (!adl) { + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + len = enc_find_info_resp(ATT_FIND_INFO_RESP_FMT_16BIT, adl, rsp, + rsp_size); + if (!len) + return ATT_ECODE_UNLIKELY; + + *length = len; + att_data_list_free(adl); + queue_destroy(q, free); + + return 0; +} + +static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *device) +{ + uint8_t search_value[ATT_DEFAULT_LE_MTU]; + size_t search_vlen; + uint16_t start, end; + uint16_t handle; + struct queue *q; + bt_uuid_t uuid; + uint16_t len; + + DBG(""); + + len = dec_find_by_type_req(cmd, cmd_len, &start, &end, &uuid, + search_value, &search_vlen); + if (!len) + return ATT_ECODE_INVALID_PDU; + + q = queue_new(); + if (!q) + return ATT_ECODE_UNLIKELY; + + gatt_db_find_by_type(gatt_db, start, end, &uuid, q); + + handle = PTR_TO_UINT(queue_pop_head(q)); + while (handle) { + struct pending_request *data; + + data = new0(struct pending_request, 1); + if (!data) { + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + data->filter_value = malloc0(search_vlen); + if (!data) { + destroy_pending_request(data); + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + data->length = READ_INIT; + data->handle = handle; + data->filter_vlen = search_vlen; + memcpy(data->filter_value, search_value, search_vlen); + + queue_push_tail(device->pending_requests, data); + + handle = PTR_TO_UINT(queue_pop_head(q)); + } + + queue_destroy(q, NULL); + + process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ); + + return 0; +} + +static uint8_t write_cmd_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint8_t value[ATT_DEFAULT_LE_MTU]; + uint16_t handle; + uint16_t len; + size_t vlen; + + len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], + &dev->bdaddr)) + return ATT_ECODE_UNLIKELY; + + return 0; +} + +static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint8_t value[ATT_DEFAULT_LE_MTU]; + uint16_t handle; + uint16_t len; + size_t vlen; + + len = dec_write_req(cmd, cmd_len, &handle, value, &vlen); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], + &dev->bdaddr)) + return ATT_ECODE_UNLIKELY; + + return 0; +} + +static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint8_t value[ATT_DEFAULT_LE_MTU]; + uint16_t handle; + uint16_t offset; + uint16_t len; + size_t vlen; + + len = dec_prep_write_req(cmd, cmd_len, &handle, &offset, + value, &vlen); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (!gatt_db_write(gatt_db, handle, offset, value, vlen, cmd[0], + &dev->bdaddr)) + return ATT_ECODE_UNLIKELY; + + return 0; +} + +static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data) +{ + struct gatt_device *dev = user_data; + uint8_t opdu[ATT_DEFAULT_LE_MTU]; + uint8_t status; + uint16_t length = 0; + size_t vlen; + uint8_t *value = g_attrib_get_buffer(dev->attrib, &vlen); + + DBG("op 0x%02x", ipdu[0]); + + if (len > vlen) { + error("gatt: Too much data on ATT socket %p", value); + status = ATT_ECODE_INVALID_PDU; + goto done; + } + + switch (ipdu[0]) { + case ATT_OP_READ_BY_GROUP_REQ: + status = read_by_group_type(ipdu, len, opdu, sizeof(opdu), + &length, dev); + break; + case ATT_OP_READ_BY_TYPE_REQ: + status = read_by_type(ipdu, len, dev); + break; + case ATT_OP_READ_REQ: + case ATT_OP_READ_BLOB_REQ: + status = read_request(ipdu, len, dev); + break; + case ATT_OP_MTU_REQ: + status = mtu_att_handle(ipdu, len, opdu, sizeof(opdu), dev, + &length); + break; + case ATT_OP_FIND_INFO_REQ: + status = find_info_handle(ipdu, len, opdu, sizeof(opdu), + &length); + break; + case ATT_OP_WRITE_REQ: + status = write_req_request(ipdu, len, dev); + if (!status) + return; + break; + case ATT_OP_WRITE_CMD: + status = write_cmd_request(ipdu, len, dev); + if (!status) + return; + break; + case ATT_OP_PREP_WRITE_REQ: + status = write_prep_request(ipdu, len, dev); + if (!status) + return; + break; + case ATT_OP_FIND_BY_TYPE_REQ: + status = find_by_type_request(ipdu, len, dev); + break; + case ATT_OP_EXEC_WRITE_REQ: + /* TODO */ + case ATT_OP_HANDLE_CNF: + case ATT_OP_HANDLE_IND: + case ATT_OP_HANDLE_NOTIFY: + case ATT_OP_READ_MULTI_REQ: + default: + DBG("Unsupported request 0x%02x", ipdu[0]); + status = ATT_ECODE_REQ_NOT_SUPP; + goto done; + } + +done: + if (status) + length = enc_error_resp(ipdu[0], 0x0000, status, opdu, + ATT_DEFAULT_LE_MTU); + + if (length) + g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL); +} + +static void create_listen_connections(void *data, void *user_data) +{ + struct gatt_device *dev = user_data; + int32_t id = PTR_TO_INT(data); + struct gatt_app *app; + + app = find_app_by_id(id); + if (app) + create_connection(dev, app); +} + +static void connect_event(GIOChannel *io, GError *gerr, void *user_data) +{ + struct gatt_device *dev; + uint8_t dst_type; + bdaddr_t dst; + struct connect_data data; + + DBG(""); + + if (gerr) { + error("gatt: %s", gerr->message); + g_error_free(gerr); + return; + } + + bt_io_get(io, &gerr, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST_TYPE, &dst_type, + BT_IO_OPT_INVALID); + if (gerr) { + error("gatt: bt_io_get: %s", gerr->message); + g_error_free(gerr); + return; + } + + /* TODO Handle collision */ + dev = find_device_by_addr(&dst); + if (!dev) { + dev = create_device(&dst); + if (!dev) { + error("gatt: Could not create device"); + return; + } + + dev->bdaddr_type = dst_type; + } else { + if (dev->state != DEVICE_DISCONNECTED) { + char addr[18]; + + ba2str(&dst, addr); + info("gatt: Rejecting incoming connection from %s", + addr); + return; + } + } + + dev->attrib = g_attrib_new(io); + if (!dev->attrib) { + error("gatt: unable to create new GAttrib instance"); + destroy_device(dev); + return; + } + dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + disconnected_cb, dev); + + queue_foreach(listen_apps, create_listen_connections, dev); + + data.dev = dev; + data.status = GATT_SUCCESS; + device_set_state(dev, DEVICE_CONNECTED); + + queue_foreach(app_connections, send_app_connect_notifications, &data); + + dev->server_id = g_attrib_register(dev->attrib, GATTRIB_ALL_REQS, + GATTRIB_ALL_HANDLES, + att_handler, dev, NULL); + if (dev->server_id == 0) + error("gatt: Could not attach to server"); +} + +struct gap_srvc_handles { + uint16_t srvc; + + /* Characteristics */ + uint16_t dev_name; + uint16_t appear; + uint16_t priv; +}; + +static struct gap_srvc_handles gap_srvc_data; + +#define APPEARANCE_GENERIC_PHONE 0x0040 +#define PERIPHERAL_PRIVACY_DISABLE 0x00 + +static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, + bdaddr_t *bdaddr, void *user_data) +{ + struct pending_request *entry; + struct gatt_device *dev; + + DBG(""); + + dev = find_device_by_addr(bdaddr); + if (!dev) { + error("gatt: Could not find device ?!"); + return; + } + + entry = queue_find(dev->pending_requests, match_dev_request_by_handle, + UINT_TO_PTR(handle)); + if (!entry) + return; + + if (handle == gap_srvc_data.dev_name) { + const char *name = bt_get_adapter_name(); + + entry->value = malloc0(strlen(name)); + if (!entry->value) { + queue_remove(dev->pending_requests, entry); + free(entry); + return; + } + + entry->length = strlen(name); + memcpy(entry->value, bt_get_adapter_name(), entry->length); + } else if (handle == gap_srvc_data.appear) { + entry->value = malloc0(2); + if (!entry->value) { + queue_remove(dev->pending_requests, entry); + free(entry); + return; + } + + put_le16(APPEARANCE_GENERIC_PHONE, entry->value); + entry->length = sizeof(uint8_t) * 2; + } else if (handle == gap_srvc_data.priv) { + entry->value = malloc0(1); + if (!entry->value) { + queue_remove(dev->pending_requests, entry); + free(entry); + return; + } + + *entry->value = PERIPHERAL_PRIVACY_DISABLE; + entry->length = sizeof(uint8_t); + } + + entry->offset = offset; +} + +static void register_gap_service(void) +{ + bt_uuid_t uuid; + + /* GAP UUID */ + bt_uuid16_create(&uuid, 0x1800); + gap_srvc_data.srvc = gatt_db_add_service(gatt_db, &uuid, true, 7); + + /* Device name characteristic */ + bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); + gap_srvc_data.dev_name = + gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + &uuid, 0, + GATT_CHR_PROP_READ, + gap_read_cb, NULL, + NULL); + + /* Appearance */ + bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE); + gap_srvc_data.appear = + gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + &uuid, 0, + GATT_CHR_PROP_READ, + gap_read_cb, NULL, + NULL); + + /* Pripheral privacy flag */ + bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG); + gap_srvc_data.priv = + gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + &uuid, 0, + GATT_CHR_PROP_READ, + gap_read_cb, NULL, + NULL); + + gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true); +} + +/* TODO: Get those data from device possible via androig/bluetooth.c */ +static struct device_info { + const char *manufacturer_name; + const char *system_id; + const char *model_number; + const char *serial_number; + const char *firmware_rev; + const char *hardware_rev; + const char *software_rev; +} device_info = { + .manufacturer_name = "BlueZ", + .system_id = "BlueZ for Android", + .model_number = "model no", + .serial_number = "serial no", + .firmware_rev = "firmware rev", + .hardware_rev = "hardware rev", + .software_rev = "software rev", +}; + +static void device_info_read_cb(uint16_t handle, uint16_t offset, + uint8_t att_opcode, bdaddr_t *bdaddr, + void *user_data) +{ + struct pending_request *entry; + struct gatt_device *dev; + char *buf = user_data; + + dev = find_device_by_addr(bdaddr); + if (!dev) { + error("gatt: Could not find device ?!"); + return; + } + + entry = queue_find(dev->pending_requests, match_dev_request_by_handle, + UINT_TO_PTR(handle)); + if (!entry) + return; + + entry->value = malloc0(strlen(buf)); + if (!entry->value) { + queue_remove(dev->pending_requests, entry); + free(entry); + return; + } + + entry->length = strlen(buf); + memcpy(entry->value, buf, entry->length); + entry->offset = offset; +} + +static void register_device_info_service(void) +{ + bt_uuid_t uuid; + uint16_t srvc_handle; + + DBG(""); + + /* Device Information Service */ + bt_uuid16_create(&uuid, 0x180a); + srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 15); + + /* User data are not const hence (void *) cast is used */ + bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.system_id); + + bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.model_number); + + bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.serial_number); + + bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.firmware_rev); + + bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.hardware_rev); + + bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.software_rev); + + bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.manufacturer_name); + + gatt_db_service_set_active(gatt_db, srvc_handle, true); +} + +static void gatt_srvc_change_register_cb(uint16_t handle, uint16_t offset, + const uint8_t *val, size_t len, + uint8_t att_opcode, + bdaddr_t *bdaddr, + void *user_data) +{ + uint8_t pdu[ATT_DEFAULT_LE_MTU]; + struct gatt_device *dev; + uint16_t length; + + dev = find_device_by_addr(bdaddr); + if (!dev) { + error("gatt: Could not find device ?!"); + return; + } + + /* TODO handle CCC */ + + /* Set services changed notification flag */ + dev->notify_services_changed = !!(*val); + + length = enc_write_resp(pdu); + + g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL); +} + +static void register_gatt_service(void) +{ + bt_uuid_t uuid; + uint16_t srvc_handle; + + DBG(""); + + bt_uuid16_create(&uuid, 0x1801); + srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 4); + + bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0, + GATT_CHR_PROP_INDICATE, NULL, NULL, + NULL); + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + gatt_db_add_char_descriptor(gatt_db, srvc_handle, &uuid, 0, NULL, + gatt_srvc_change_register_cb, NULL); + + gatt_db_service_set_active(gatt_db, srvc_handle, true); +} + +static bool start_listening_io(void) +{ + GError *gerr = NULL; + + /* For now only listen on BLE */ + listening_io = bt_io_listen(connect_event, NULL, + &listening_io, NULL, &gerr, + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, + BT_IO_OPT_CID, ATT_CID, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!listening_io) { + error("gatt: Failed to start listening IO (%s)", gerr->message); + g_error_free(gerr); + return false; + } + + return true; +} + +bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr) +{ + DBG(""); + + if (!start_listening_io()) + return false; + + gatt_devices = queue_new(); + gatt_apps = queue_new(); + app_connections = queue_new(); + listen_apps = queue_new(); + gatt_db = gatt_db_new(); + + if (!gatt_devices || !gatt_apps || !listen_apps || + !app_connections || !gatt_db) { + error("gatt: Failed to allocate memory for queues"); + + queue_destroy(gatt_apps, NULL); + gatt_apps = NULL; + + queue_destroy(gatt_devices, NULL); + gatt_devices = NULL; + + queue_destroy(app_connections, NULL); + app_connections = NULL; + + queue_destroy(listen_apps, NULL); + listen_apps = NULL; + + gatt_db_destroy(gatt_db); + gatt_db = NULL; + + g_io_channel_unref(listening_io); + listening_io = NULL; + + return false; + } + + bacpy(&adapter_addr, addr); + + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + register_gap_service(); + register_device_info_service(); + register_gatt_service(); + + return true; +} + +void bt_gatt_unregister(void) +{ + DBG(""); + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT); + hal_ipc = NULL; + + queue_destroy(gatt_apps, destroy_gatt_app); + gatt_apps = NULL; + + queue_destroy(app_connections, destroy_connection); + app_connections = NULL; + + queue_destroy(gatt_devices, destroy_device); + gatt_devices = NULL; + + queue_destroy(listen_apps, NULL); + listen_apps = NULL; + + gatt_db_destroy(gatt_db); + gatt_db = NULL; + + g_io_channel_unref(listening_io); + listening_io = NULL; +} diff --git a/android/gatt.h b/android/gatt.h new file mode 100644 index 0000000..d4392d9 --- /dev/null +++ b/android/gatt.h @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 + * + */ + +bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr); +void bt_gatt_unregister(void); diff --git a/android/hal-a2dp.c b/android/hal-a2dp.c index c898995..68888b2 100644 --- a/android/hal-a2dp.c +++ b/android/hal-a2dp.c @@ -48,8 +48,10 @@ static void handle_audio_state(void *buf, uint16_t len) cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr)); } -/* handlers will be called from notification thread context, - * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ static const struct hal_ipc_handler ev_handlers[] = { { /* HAL_EV_A2DP_CONN_STATE */ .handler = handle_conn_state, @@ -109,6 +111,7 @@ static bt_status_t init(btav_callbacks_t *callbacks) sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_A2DP; + cmd.mode = HAL_MODE_DEFAULT; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); diff --git a/android/hal-audio.c b/android/hal-audio.c new file mode 100644 index 0000000..1c889cc --- /dev/null +++ b/android/hal-audio.c @@ -0,0 +1,1898 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "audio-msg.h" +#include "ipc-common.h" +#include "hal-log.h" +#include "hal-msg.h" +#include "../profiles/audio/a2dp-codecs.h" +#include "../src/shared/util.h" + +#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25 + +#define FIXED_BUFFER_SIZE (20 * 512) + +#define MAX_FRAMES_IN_PAYLOAD 15 + +#define MAX_DELAY 100000 /* 100ms */ + +#define SBC_QUALITY_MIN_BITPOOL 33 +#define SBC_QUALITY_STEP 5 + +static const uint8_t a2dp_src_uuid[] = { + 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }; + +static int listen_sk = -1; +static int audio_sk = -1; + +static pthread_t ipc_th = 0; +static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER; + +#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 + +struct media_packet { + struct rtp_header hdr; + struct rtp_payload payload; + uint8_t data[0]; +}; + +struct audio_input_config { + uint32_t rate; + uint32_t channels; + audio_format_t format; +}; + +struct sbc_data { + a2dp_sbc_t sbc; + + sbc_t enc; + + uint16_t payload_len; + + size_t in_frame_len; + size_t in_buf_size; + + size_t out_frame_len; + + unsigned frame_duration; + unsigned frames_per_packet; +}; + +static void timespec_add(struct timespec *base, uint64_t time_us, + struct timespec *res) +{ + res->tv_sec = base->tv_sec + time_us / 1000000; + res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000; + + if (res->tv_nsec >= 1000000000) { + res->tv_sec++; + res->tv_nsec -= 1000000000; + } +} + +static void timespec_diff(struct timespec *a, struct timespec *b, + struct timespec *res) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + + if (res->tv_nsec < 0) { + res->tv_sec--; + res->tv_nsec += 1000000000; /* 1sec */ + } +} + +static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b) +{ + struct timespec res; + + timespec_diff(a, b, &res); + + return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll; +} + +#if defined(ANDROID) +/* + * Bionic does not have clock_nanosleep() prototype in time.h even though + * it provides its implementation. + */ +extern int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain); +#endif + +static int sbc_get_presets(struct audio_preset *preset, size_t *len); +static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu, + void **codec_data); +static int sbc_cleanup(void *codec_data); +static int sbc_get_config(void *codec_data, struct audio_input_config *config); +static size_t sbc_get_buffer_size(void *codec_data); +static size_t sbc_get_mediapacket_duration(void *codec_data); +static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer, + size_t len, struct media_packet *mp, + size_t mp_data_len, size_t *written); +static bool sbc_update_qos(void *codec_data, uint8_t op); + +#define QOS_POLICY_DEFAULT 0x00 +#define QOS_POLICY_DECREASE 0x01 + +struct audio_codec { + uint8_t type; + + int (*get_presets) (struct audio_preset *preset, size_t *len); + + int (*init) (struct audio_preset *preset, uint16_t mtu, + void **codec_data); + int (*cleanup) (void *codec_data); + int (*get_config) (void *codec_data, + struct audio_input_config *config); + size_t (*get_buffer_size) (void *codec_data); + size_t (*get_mediapacket_duration) (void *codec_data); + ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer, + size_t len, struct media_packet *mp, + size_t mp_data_len, size_t *written); + bool (*update_qos) (void *codec_data, uint8_t op); +}; + +static const struct audio_codec audio_codecs[] = { + { + .type = A2DP_CODEC_SBC, + + .get_presets = sbc_get_presets, + + .init = sbc_codec_init, + .cleanup = sbc_cleanup, + .get_config = sbc_get_config, + .get_buffer_size = sbc_get_buffer_size, + .get_mediapacket_duration = sbc_get_mediapacket_duration, + .encode_mediapacket = sbc_encode_mediapacket, + .update_qos = sbc_update_qos, + } +}; + +#define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0])) + +#define MAX_AUDIO_ENDPOINTS NUM_CODECS + +struct audio_endpoint { + uint8_t id; + const struct audio_codec *codec; + void *codec_data; + int fd; + + struct media_packet *mp; + size_t mp_data_len; + + uint16_t seq; + uint32_t samples; + struct timespec start; + + bool resync; +}; + +static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS]; + +enum a2dp_state_t { + AUDIO_A2DP_STATE_NONE, + AUDIO_A2DP_STATE_STANDBY, + AUDIO_A2DP_STATE_SUSPENDED, + AUDIO_A2DP_STATE_STARTED +}; + +struct a2dp_stream_out { + struct audio_stream_out stream; + + struct audio_endpoint *ep; + enum a2dp_state_t audio_state; + struct audio_input_config cfg; + + uint8_t *downmix_buf; +}; + +struct a2dp_audio_dev { + struct audio_hw_device dev; + struct a2dp_stream_out *out; +}; + +static const a2dp_sbc_t sbc_presets[] = { + { + .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000, + .channel_mode = SBC_CHANNEL_MODE_MONO | + SBC_CHANNEL_MODE_DUAL_CHANNEL | + SBC_CHANNEL_MODE_STEREO | + SBC_CHANNEL_MODE_JOINT_STEREO, + .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8, + .allocation_method = SBC_ALLOCATION_SNR | + SBC_ALLOCATION_LOUDNESS, + .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | + SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16, + .min_bitpool = MIN_BITPOOL, + .max_bitpool = MAX_BITPOOL + }, + { + .frequency = SBC_SAMPLING_FREQ_44100, + .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO, + .subbands = SBC_SUBBANDS_8, + .allocation_method = SBC_ALLOCATION_LOUDNESS, + .block_length = SBC_BLOCK_LENGTH_16, + .min_bitpool = MIN_BITPOOL, + .max_bitpool = MAX_BITPOOL + }, + { + .frequency = SBC_SAMPLING_FREQ_48000, + .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO, + .subbands = SBC_SUBBANDS_8, + .allocation_method = SBC_ALLOCATION_LOUDNESS, + .block_length = SBC_BLOCK_LENGTH_16, + .min_bitpool = MIN_BITPOOL, + .max_bitpool = MAX_BITPOOL + }, +}; + +static int sbc_get_presets(struct audio_preset *preset, size_t *len) +{ + int i; + int count; + size_t new_len = 0; + uint8_t *ptr = (uint8_t *) preset; + size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t); + + count = sizeof(sbc_presets) / sizeof(sbc_presets[0]); + + for (i = 0; i < count; i++) { + preset = (struct audio_preset *) ptr; + + if (new_len + preset_size > *len) + break; + + preset->len = sizeof(a2dp_sbc_t); + memcpy(preset->data, &sbc_presets[i], preset->len); + + new_len += preset_size; + ptr += preset_size; + } + + *len = new_len; + + return i; +} + +static int sbc_freq2int(uint8_t freq) +{ + switch (freq) { + case SBC_SAMPLING_FREQ_16000: + return 16000; + case SBC_SAMPLING_FREQ_32000: + return 32000; + case SBC_SAMPLING_FREQ_44100: + return 44100; + case SBC_SAMPLING_FREQ_48000: + return 48000; + default: + return 0; + } +} + +static const char *sbc_mode2str(uint8_t mode) +{ + switch (mode) { + case SBC_CHANNEL_MODE_MONO: + return "Mono"; + case SBC_CHANNEL_MODE_DUAL_CHANNEL: + return "DualChannel"; + case SBC_CHANNEL_MODE_STEREO: + return "Stereo"; + case SBC_CHANNEL_MODE_JOINT_STEREO: + return "JointStereo"; + default: + return "(unknown)"; + } +} + +static int sbc_blocks2int(uint8_t blocks) +{ + switch (blocks) { + case SBC_BLOCK_LENGTH_4: + return 4; + case SBC_BLOCK_LENGTH_8: + return 8; + case SBC_BLOCK_LENGTH_12: + return 12; + case SBC_BLOCK_LENGTH_16: + return 16; + default: + return 0; + } +} + +static int sbc_subbands2int(uint8_t subbands) +{ + switch (subbands) { + case SBC_SUBBANDS_4: + return 4; + case SBC_SUBBANDS_8: + return 8; + default: + return 0; + } +} + +static const char *sbc_allocation2str(uint8_t allocation) +{ + switch (allocation) { + case SBC_ALLOCATION_SNR: + return "SNR"; + case SBC_ALLOCATION_LOUDNESS: + return "Loudness"; + default: + return "(unknown)"; + } +} + +static void sbc_init_encoder(struct sbc_data *sbc_data) +{ + a2dp_sbc_t *in = &sbc_data->sbc; + sbc_t *out = &sbc_data->enc; + + sbc_init_a2dp(out, 0L, in, sizeof(*in)); + + out->endian = SBC_LE; + out->bitpool = in->max_bitpool; + + DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d " + "allocation=%s bitpool=%d-%d", + sbc_freq2int(in->frequency), + sbc_mode2str(in->channel_mode), + sbc_blocks2int(in->block_length), + sbc_subbands2int(in->subbands), + sbc_allocation2str(in->allocation_method), + in->min_bitpool, in->max_bitpool); +} + +static void sbc_codec_calculate(struct sbc_data *sbc_data) +{ + size_t in_frame_len; + size_t out_frame_len; + size_t num_frames; + + in_frame_len = sbc_get_codesize(&sbc_data->enc); + out_frame_len = sbc_get_frame_length(&sbc_data->enc); + num_frames = sbc_data->payload_len / out_frame_len; + + sbc_data->in_frame_len = in_frame_len; + sbc_data->in_buf_size = num_frames * in_frame_len; + + sbc_data->out_frame_len = out_frame_len; + + sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc); + sbc_data->frames_per_packet = num_frames; + + DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu", + in_frame_len, out_frame_len, num_frames); +} + +static int sbc_codec_init(struct audio_preset *preset, uint16_t payload_len, + void **codec_data) +{ + struct sbc_data *sbc_data; + + if (preset->len != sizeof(a2dp_sbc_t)) { + error("SBC: preset size mismatch"); + return AUDIO_STATUS_FAILED; + } + + sbc_data = calloc(sizeof(struct sbc_data), 1); + if (!sbc_data) + return AUDIO_STATUS_FAILED; + + memcpy(&sbc_data->sbc, preset->data, preset->len); + + sbc_init_encoder(sbc_data); + + sbc_data->payload_len = payload_len; + + sbc_codec_calculate(sbc_data); + + *codec_data = sbc_data; + + return AUDIO_STATUS_SUCCESS; +} + +static int sbc_cleanup(void *codec_data) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + + sbc_finish(&sbc_data->enc); + free(codec_data); + + return AUDIO_STATUS_SUCCESS; +} + +static int sbc_get_config(void *codec_data, struct audio_input_config *config) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + + switch (sbc_data->sbc.frequency) { + case SBC_SAMPLING_FREQ_16000: + config->rate = 16000; + break; + case SBC_SAMPLING_FREQ_32000: + config->rate = 32000; + break; + case SBC_SAMPLING_FREQ_44100: + config->rate = 44100; + break; + case SBC_SAMPLING_FREQ_48000: + config->rate = 48000; + break; + default: + return AUDIO_STATUS_FAILED; + } + config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ? + AUDIO_CHANNEL_OUT_MONO : + AUDIO_CHANNEL_OUT_STEREO; + config->format = AUDIO_FORMAT_PCM_16_BIT; + + return AUDIO_STATUS_SUCCESS; +} + +static size_t sbc_get_buffer_size(void *codec_data) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + + return sbc_data->in_buf_size; +} + +static size_t sbc_get_mediapacket_duration(void *codec_data) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + + return sbc_data->frame_duration * sbc_data->frames_per_packet; +} + +static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer, + size_t len, struct media_packet *mp, + size_t mp_data_len, size_t *written) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + size_t consumed = 0; + size_t encoded = 0; + uint8_t frame_count = 0; + + while (len - consumed >= sbc_data->in_frame_len && + mp_data_len - encoded >= sbc_data->out_frame_len && + frame_count < MAX_FRAMES_IN_PAYLOAD) { + ssize_t read; + ssize_t written = 0; + + read = sbc_encode(&sbc_data->enc, buffer + consumed, + sbc_data->in_frame_len, mp->data + encoded, + mp_data_len - encoded, &written); + + if (read < 0) { + error("SBC: failed to encode block at frame %d (%zd)", + frame_count, read); + break; + } + + frame_count++; + consumed += read; + encoded += written; + } + + *written = encoded; + mp->payload.frame_count = frame_count; + + return consumed; +} + +static bool sbc_update_qos(void *codec_data, uint8_t op) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + uint8_t curr_bitpool = sbc_data->enc.bitpool; + uint8_t new_bitpool = curr_bitpool; + + switch (op) { + case QOS_POLICY_DEFAULT: + new_bitpool = sbc_data->sbc.max_bitpool; + break; + + case QOS_POLICY_DECREASE: + if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) { + new_bitpool = curr_bitpool - SBC_QUALITY_STEP; + if (new_bitpool < SBC_QUALITY_MIN_BITPOOL) + new_bitpool = SBC_QUALITY_MIN_BITPOOL; + } + break; + } + + if (new_bitpool == curr_bitpool) + return false; + + sbc_data->enc.bitpool = new_bitpool; + + sbc_codec_calculate(sbc_data); + + info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool); + + return true; +} + +static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, + void *param, size_t *rsp_len, void *rsp, int *fd) +{ + ssize_t ret; + struct msghdr msg; + struct iovec iv[2]; + struct ipc_hdr cmd; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct ipc_status s; + size_t s_len = sizeof(s); + + pthread_mutex_lock(&sk_mutex); + + if (audio_sk < 0) { + error("audio: Invalid cmd socket passed to audio_ipc_cmd"); + goto failed; + } + + if (!rsp || !rsp_len) { + memset(&s, 0, s_len); + rsp_len = &s_len; + rsp = &s; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.service_id = service_id; + cmd.opcode = opcode; + cmd.len = len; + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = param; + iv[1].iov_len = len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + ret = sendmsg(audio_sk, &msg, 0); + if (ret < 0) { + error("audio: Sending command failed:%s", strerror(errno)); + goto failed; + } + + /* socket was shutdown */ + if (ret == 0) { + error("audio: Command socket closed"); + goto failed; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = rsp; + iv[1].iov_len = *rsp_len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + if (fd) { + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + } + + ret = recvmsg(audio_sk, &msg, 0); + if (ret < 0) { + error("audio: Receiving command response failed:%s", + strerror(errno)); + goto failed; + } + + if (ret < (ssize_t) sizeof(cmd)) { + error("audio: Too small response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.service_id != service_id) { + error("audio: Invalid service id (%u vs %u)", cmd.service_id, + service_id); + goto failed; + } + + if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { + error("audio: Malformed response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.opcode != opcode && cmd.opcode != AUDIO_OP_STATUS) { + error("audio: Invalid opcode received (%u vs %u)", + cmd.opcode, opcode); + goto failed; + } + + if (cmd.opcode == AUDIO_OP_STATUS) { + struct ipc_status *s = rsp; + + if (sizeof(*s) != cmd.len) { + error("audio: Invalid status length"); + goto failed; + } + + if (s->code == AUDIO_STATUS_SUCCESS) { + error("audio: Invalid success status response"); + goto failed; + } + + pthread_mutex_unlock(&sk_mutex); + + return s->code; + } + + pthread_mutex_unlock(&sk_mutex); + + /* Receive auxiliary data in msg */ + if (fd) { + struct cmsghdr *cmsg; + + *fd = -1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); + break; + } + } + + if (*fd < 0) + goto failed; + } + + if (rsp_len) + *rsp_len = cmd.len; + + return AUDIO_STATUS_SUCCESS; + +failed: + /* Some serious issue happen on IPC - recover */ + shutdown(audio_sk, SHUT_RDWR); + pthread_mutex_unlock(&sk_mutex); + + return AUDIO_STATUS_FAILED; +} + +static int ipc_open_cmd(const struct audio_codec *codec) +{ + uint8_t buf[BLUEZ_AUDIO_MTU]; + struct audio_cmd_open *cmd = (struct audio_cmd_open *) buf; + struct audio_rsp_open rsp; + size_t cmd_len = sizeof(buf) - sizeof(*cmd); + size_t rsp_len = sizeof(rsp); + int result; + + DBG(""); + + memcpy(cmd->uuid, a2dp_src_uuid, sizeof(a2dp_src_uuid)); + + cmd->codec = codec->type; + cmd->presets = codec->get_presets(cmd->preset, &cmd_len); + + cmd_len += sizeof(*cmd); + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN, cmd_len, cmd, + &rsp_len, &rsp, NULL); + + if (result != AUDIO_STATUS_SUCCESS) + return 0; + + return rsp.id; +} + +static int ipc_close_cmd(uint8_t endpoint_id) +{ + struct audio_cmd_close cmd; + int result; + + DBG(""); + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + return result; +} + +static int ipc_open_stream_cmd(uint8_t endpoint_id, uint16_t *mtu, int *fd, + struct audio_preset **caps) +{ + char buf[BLUEZ_AUDIO_MTU]; + struct audio_cmd_open_stream cmd; + struct audio_rsp_open_stream *rsp = + (struct audio_rsp_open_stream *) &buf; + size_t rsp_len = sizeof(buf); + int result; + + DBG(""); + + if (!caps) + return AUDIO_STATUS_FAILED; + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, + sizeof(cmd), &cmd, &rsp_len, rsp, fd); + if (result == AUDIO_STATUS_SUCCESS) { + size_t buf_len = sizeof(struct audio_preset) + + rsp->preset[0].len; + *mtu = rsp->mtu; + *caps = malloc(buf_len); + memcpy(*caps, &rsp->preset, buf_len); + } else { + *caps = NULL; + } + + return result; +} + +static int ipc_close_stream_cmd(uint8_t endpoint_id) +{ + struct audio_cmd_close_stream cmd; + int result; + + DBG(""); + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + return result; +} + +static int ipc_resume_stream_cmd(uint8_t endpoint_id) +{ + struct audio_cmd_resume_stream cmd; + int result; + + DBG(""); + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + return result; +} + +static int ipc_suspend_stream_cmd(uint8_t endpoint_id) +{ + struct audio_cmd_suspend_stream cmd; + int result; + + DBG(""); + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + return result; +} + +static int register_endpoints(void) +{ + struct audio_endpoint *ep = &audio_endpoints[0]; + size_t i; + + for (i = 0; i < NUM_CODECS; i++, ep++) { + const struct audio_codec *codec = &audio_codecs[i]; + + ep->id = ipc_open_cmd(codec); + + if (!ep->id) + return AUDIO_STATUS_FAILED; + + ep->codec = codec; + ep->codec_data = NULL; + ep->fd = -1; + } + + return AUDIO_STATUS_SUCCESS; +} + +static void unregister_endpoints(void) +{ + size_t i; + + for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) { + struct audio_endpoint *ep = &audio_endpoints[i]; + + if (ep->id) { + ipc_close_cmd(ep->id); + memset(ep, 0, sizeof(*ep)); + } + } +} + +static bool open_endpoint(struct audio_endpoint *ep, + struct audio_input_config *cfg) +{ + struct audio_preset *preset; + const struct audio_codec *codec; + uint16_t mtu; + uint16_t payload_len; + int fd; + + if (ipc_open_stream_cmd(ep->id, &mtu, &fd, &preset) != + AUDIO_STATUS_SUCCESS) + return false; + + DBG("mtu=%u", mtu); + + payload_len = mtu - sizeof(*ep->mp); + + ep->fd = fd; + + codec = ep->codec; + codec->init(preset, payload_len, &ep->codec_data); + codec->get_config(ep->codec_data, cfg); + + ep->mp = calloc(mtu, 1); + if (!ep->mp) + goto failed; + ep->mp->hdr.v = 2; + ep->mp->hdr.pt = 1; + ep->mp->hdr.ssrc = htonl(1); + + ep->mp_data_len = payload_len; + + free(preset); + + return true; + +failed: + close(fd); + free(preset); + + return false; +} + +static void close_endpoint(struct audio_endpoint *ep) +{ + ipc_close_stream_cmd(ep->id); + if (ep->fd >= 0) { + close(ep->fd); + ep->fd = -1; + } + + free(ep->mp); + + ep->codec->cleanup(ep->codec_data); + ep->codec_data = NULL; +} + +static bool resume_endpoint(struct audio_endpoint *ep) +{ + if (ipc_resume_stream_cmd(ep->id) != AUDIO_STATUS_SUCCESS) + return false; + + ep->samples = 0; + ep->resync = false; + + ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT); + + return true; +} + +static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer, + size_t bytes) +{ + const int16_t *input = (const void *) buffer; + int16_t *output = (void *) out->downmix_buf; + size_t i; + + for (i = 0; i < bytes / 2; i++) { + int16_t l = le16_to_cpu(get_unaligned(&input[i * 2])); + int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1])); + + put_unaligned(cpu_to_le16((l + r) / 2), &output[i]); + } +} + +static bool wait_for_endpoint(struct audio_endpoint *ep, bool *writable) +{ + int ret; + + while (true) { + struct pollfd pollfd; + + pollfd.fd = ep->fd; + pollfd.events = POLLOUT; + pollfd.revents = 0; + + ret = poll(&pollfd, 1, 500); + + if (ret >= 0) { + *writable = !!(pollfd.revents & POLLOUT); + break; + } + + if (errno != EINTR) { + ret = errno; + error("poll failed (%d)", ret); + return false; + } + } + + return true; +} + +static bool write_to_endpoint(struct audio_endpoint *ep, size_t bytes) +{ + struct media_packet *mp = (struct media_packet *) ep->mp; + int ret; + + while (true) { + ret = write(ep->fd, mp, sizeof(*mp) + bytes); + + if (ret >= 0) + break; + + /* + * this should not happen so let's issue warning, but do not + * fail, we can try to write next packet + */ + if (errno == EAGAIN) { + ret = errno; + warn("write failed (%d)", ret); + break; + } + + if (errno != EINTR) { + ret = errno; + error("write failed (%d)", ret); + return false; + } + } + + return true; +} + +static bool write_data(struct a2dp_stream_out *out, const void *buffer, + size_t bytes) +{ + struct audio_endpoint *ep = out->ep; + struct media_packet *mp = (struct media_packet *) ep->mp; + size_t free_space = ep->mp_data_len; + size_t consumed = 0; + + while (consumed < bytes) { + size_t written = 0; + ssize_t read; + uint32_t samples; + int ret; + struct timespec current; + uint64_t audio_sent, audio_passed; + bool do_write = false; + + /* + * prepare media packet in advance so we don't waste time after + * wakeup + */ + mp->hdr.sequence_number = htons(ep->seq++); + mp->hdr.timestamp = htonl(ep->samples); + read = ep->codec->encode_mediapacket(ep->codec_data, + buffer + consumed, + bytes - consumed, mp, + free_space, &written); + + /* + * not much we can do here, let's just ignore remaining + * data and continue + */ + if (read <= 0) + return true; + + /* calculate where are we and where we should be */ + clock_gettime(CLOCK_MONOTONIC, ¤t); + if (!ep->samples) + memcpy(&ep->start, ¤t, sizeof(ep->start)); + audio_sent = ep->samples * 1000000ll / out->cfg.rate; + audio_passed = timespec_diff_us(¤t, &ep->start); + + /* + * if we're ahead of stream then wait for next write point, + * if we're lagging more than 100ms then stop writing and just + * skip data until we're back in sync + */ + if (audio_sent > audio_passed) { + struct timespec anchor; + + ep->resync = false; + + timespec_add(&ep->start, audio_sent, &anchor); + + while (true) { + ret = clock_nanosleep(CLOCK_MONOTONIC, + TIMER_ABSTIME, &anchor, + NULL); + + if (!ret) + break; + + if (ret != EINTR) { + error("clock_nanosleep failed (%d)", + ret); + return false; + } + } + } else if (!ep->resync) { + uint64_t diff = audio_passed - audio_sent; + + if (diff > MAX_DELAY) { + warn("lag is %jums, resyncing", diff / 1000); + + ep->codec->update_qos(ep->codec_data, + QOS_POLICY_DECREASE); + ep->resync = true; + } + } + + /* in resync mode we'll just drop mediapackets */ + if (!ep->resync) { + /* wait some time for socket to be ready for write, + * but we'll just skip writing data if timeout occurs + */ + if (!wait_for_endpoint(ep, &do_write)) + return false; + + if (do_write) + if (!write_to_endpoint(ep, written)) + return false; + } + + /* + * AudioFlinger provides 16bit PCM, so sample size is 2 bytes + * multiplied by number of channels. Number of channels is + * simply number of bits set in channels mask. + */ + samples = read / (2 * popcount(out->cfg.channels)); + ep->samples += samples; + consumed += read; + } + + return true; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, + size_t bytes) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + const void *in_buf = buffer; + size_t in_len = bytes; + + /* just return in case we're closing */ + if (out->audio_state == AUDIO_A2DP_STATE_NONE) + return -1; + + /* We can auto-start only from standby */ + if (out->audio_state == AUDIO_A2DP_STATE_STANDBY) { + DBG("stream in standby, auto-start"); + + if (!resume_endpoint(out->ep)) + return -1; + + out->audio_state = AUDIO_A2DP_STATE_STARTED; + } + + if (out->audio_state != AUDIO_A2DP_STATE_STARTED) { + error("audio: stream not started"); + return -1; + } + + if (out->ep->fd < 0) { + error("audio: no transport socket"); + return -1; + } + + /* + * currently Android audioflinger is not able to provide mono stream on + * A2DP output so down mixing needs to be done in hal-audio plugin. + * + * for reference see + * AudioFlinger::PlaybackThread::readOutputParameters() + * frameworks/av/services/audioflinger/Threads.cpp:1631 + */ + if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) { + if (!out->downmix_buf) { + error("audio: downmix buffer not initialized"); + return -1; + } + + downmix_to_mono(out, buffer, bytes); + + in_buf = out->downmix_buf; + in_len = bytes / 2; + } + + if (!write_data(out, in_buf, in_len)) + return -1; + + return bytes; +} + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + return out->cfg.rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + if (rate != out->cfg.rate) { + warn("audio: cannot set sample rate to %d", rate); + return -1; + } + + return 0; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + DBG(""); + + /* + * We should return proper buffer size calculated by codec (so each + * input buffer is encoded into single media packed) but this does not + * work well with AudioFlinger and causes problems. For this reason we + * use magic value here and out_write code takes care of splitting + * input buffer into multiple media packets. + */ + return FIXED_BUFFER_SIZE; +} + +static uint32_t out_get_channels(const struct audio_stream *stream) +{ + DBG(""); + + /* + * AudioFlinger can only provide stereo stream, so we return it here and + * later we'll downmix this to mono in case codec requires it + */ + + return AUDIO_CHANNEL_OUT_STEREO; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + return out->cfg.format; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + DBG(""); + return -ENOSYS; +} + +static int out_standby(struct audio_stream *stream) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + if (out->audio_state == AUDIO_A2DP_STATE_STARTED) { + if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS) + return -1; + out->audio_state = AUDIO_A2DP_STATE_STANDBY; + } + + return 0; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + DBG(""); + return -ENOSYS; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + char *kvpair; + char *str; + char *saveptr; + bool enter_suspend = false; + bool exit_suspend = false; + + DBG("%s", kvpairs); + + str = strdup(kvpairs); + if (!str) + return -ENOMEM; + + kvpair = strtok_r(str, ";", &saveptr); + + for (; kvpair && *kvpair; kvpair = strtok_r(NULL, ";", &saveptr)) { + char *keyval; + + keyval = strchr(kvpair, '='); + if (!keyval) + continue; + + *keyval = '\0'; + keyval++; + + if (!strcmp(kvpair, "closing")) { + if (!strcmp(keyval, "true")) + out->audio_state = AUDIO_A2DP_STATE_NONE; + } else if (!strcmp(kvpair, "A2dpSuspended")) { + if (!strcmp(keyval, "true")) + enter_suspend = true; + else + exit_suspend = true; + } + } + + free(str); + + if (enter_suspend && out->audio_state == AUDIO_A2DP_STATE_STARTED) { + if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS) + return -1; + out->audio_state = AUDIO_A2DP_STATE_SUSPENDED; + } + + if (exit_suspend && out->audio_state == AUDIO_A2DP_STATE_SUSPENDED) + out->audio_state = AUDIO_A2DP_STATE_STANDBY; + + return 0; +} + +static char *out_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + DBG(""); + return strdup(""); +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + struct audio_endpoint *ep = out->ep; + size_t pkt_duration; + + DBG(""); + + pkt_duration = ep->codec->get_mediapacket_duration(ep->codec_data); + + return FIXED_A2DP_PLAYBACK_LATENCY_MS + pkt_duration / 1000; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + DBG(""); + /* volume controlled in audioflinger mixer (digital) */ + return -ENOSYS; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + DBG(""); + return -ENOSYS; +} + +static int out_add_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + return -ENOSYS; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + return -ENOSYS; +} + +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + DBG(""); + return -ENOSYS; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static uint32_t in_get_channels(const struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + DBG(""); + return -ENOSYS; +} + +static int in_standby(struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + DBG(""); + return -ENOSYS; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + DBG(""); + return -ENOSYS; +} + +static char *in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + DBG(""); + return strdup(""); +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + DBG(""); + return -ENOSYS; +} + +static ssize_t in_read(struct audio_stream_in *stream, void *buffer, + size_t bytes) +{ + DBG(""); + return -ENOSYS; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + DBG(""); + return -ENOSYS; +} + +static int in_add_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + return -ENOSYS; +} + +static int in_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_open_output_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out) + +{ + struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; + struct a2dp_stream_out *out; + + out = calloc(1, sizeof(struct a2dp_stream_out)); + if (!out) + return -ENOMEM; + + DBG(""); + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + + /* TODO: for now we always use endpoint 0 */ + out->ep = &audio_endpoints[0]; + + if (!open_endpoint(out->ep, &out->cfg)) + goto fail; + + DBG("rate=%d channels=%d format=%d", out->cfg.rate, + out->cfg.channels, out->cfg.format); + + if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) { + out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2); + if (!out->downmix_buf) + goto fail; + } + + *stream_out = &out->stream; + a2dp_dev->out = out; + + out->audio_state = AUDIO_A2DP_STATE_STANDBY; + + return 0; + +fail: + error("audio: cannot open output stream"); + free(out); + *stream_out = NULL; + return -EIO; +} + +static void audio_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream) +{ + struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + close_endpoint(a2dp_dev->out->ep); + + free(out->downmix_buf); + + free(stream); + a2dp_dev->out = NULL; +} + +static int audio_set_parameters(struct audio_hw_device *dev, + const char *kvpairs) +{ + struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; + struct a2dp_stream_out *out = a2dp_dev->out; + + DBG(""); + + if (!out) + return 0; + + return out->stream.common.set_parameters((struct audio_stream *) out, + kvpairs); +} + +static char *audio_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + DBG(""); + return strdup(""); +} + +static int audio_init_check(const struct audio_hw_device *dev) +{ + DBG(""); + return 0; +} + +static int audio_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_set_master_volume(struct audio_hw_device *dev, float volume) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_set_mode(struct audio_hw_device *dev, int mode) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + DBG(""); + return -ENOSYS; +} + +static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in) +{ + struct audio_stream_in *in; + + DBG(""); + + in = calloc(1, sizeof(struct audio_stream_in)); + if (!in) + return -ENOMEM; + + in->common.get_sample_rate = in_get_sample_rate; + in->common.set_sample_rate = in_set_sample_rate; + in->common.get_buffer_size = in_get_buffer_size; + in->common.get_channels = in_get_channels; + in->common.get_format = in_get_format; + in->common.set_format = in_set_format; + in->common.standby = in_standby; + in->common.dump = in_dump; + in->common.set_parameters = in_set_parameters; + in->common.get_parameters = in_get_parameters; + in->common.add_audio_effect = in_add_audio_effect; + in->common.remove_audio_effect = in_remove_audio_effect; + in->set_gain = in_set_gain; + in->read = in_read; + in->get_input_frames_lost = in_get_input_frames_lost; + + *stream_in = in; + + return 0; +} + +static void audio_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream_in) +{ + DBG(""); + free(stream_in); +} + +static int audio_dump(const audio_hw_device_t *device, int fd) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_close(hw_device_t *device) +{ + struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device; + + DBG(""); + + unregister_endpoints(); + + shutdown(listen_sk, SHUT_RDWR); + shutdown(audio_sk, SHUT_RDWR); + + pthread_join(ipc_th, NULL); + + close(listen_sk); + listen_sk = -1; + + free(a2dp_dev); + return 0; +} + +static void *ipc_handler(void *data) +{ + bool done = false; + struct pollfd pfd; + int sk; + + DBG(""); + + while (!done) { + DBG("Waiting for connection ..."); + + sk = accept(listen_sk, NULL, NULL); + if (sk < 0) { + int err = errno; + + if (err == EINTR) + continue; + + if (err != ECONNABORTED && err != EINVAL) + error("audio: Failed to accept socket: %d (%s)", + err, strerror(err)); + + break; + } + + pthread_mutex_lock(&sk_mutex); + audio_sk = sk; + pthread_mutex_unlock(&sk_mutex); + + DBG("Audio IPC: Connected"); + + if (register_endpoints() != AUDIO_STATUS_SUCCESS) { + error("audio: Failed to register endpoints"); + + unregister_endpoints(); + + pthread_mutex_lock(&sk_mutex); + shutdown(audio_sk, SHUT_RDWR); + close(audio_sk); + audio_sk = -1; + pthread_mutex_unlock(&sk_mutex); + + continue; + } + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = audio_sk; + pfd.events = POLLHUP | POLLERR | POLLNVAL; + + /* Check if socket is still alive. Empty while loop.*/ + while (poll(&pfd, 1, -1) < 0 && errno == EINTR); + + if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) { + info("Audio HAL: Socket closed"); + + pthread_mutex_lock(&sk_mutex); + close(audio_sk); + audio_sk = -1; + pthread_mutex_unlock(&sk_mutex); + } + } + + /* audio_sk is closed at this point, just cleanup endpoints states */ + memset(audio_endpoints, 0, sizeof(audio_endpoints)); + + info("Closing Audio IPC thread"); + return NULL; +} + +static int audio_ipc_init(void) +{ + struct sockaddr_un addr; + int err; + int sk; + + DBG(""); + + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + err = -errno; + error("audio: Failed to create socket: %d (%s)", -err, + strerror(-err)); + return err; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_AUDIO_SK_PATH, + sizeof(BLUEZ_AUDIO_SK_PATH)); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = -errno; + error("audio: Failed to bind socket: %d (%s)", -err, + strerror(-err)); + goto failed; + } + + if (listen(sk, 1) < 0) { + err = -errno; + error("audio: Failed to listen on the socket: %d (%s)", -err, + strerror(-err)); + goto failed; + } + + listen_sk = sk; + + err = pthread_create(&ipc_th, NULL, ipc_handler, NULL); + if (err) { + err = -err; + ipc_th = 0; + error("audio: Failed to start Audio IPC thread: %d (%s)", + -err, strerror(-err)); + goto failed; + } + + return 0; + +failed: + close(sk); + return err; +} + +static int audio_open(const hw_module_t *module, const char *name, + hw_device_t **device) +{ + struct a2dp_audio_dev *a2dp_dev; + int err; + + DBG(""); + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) { + error("audio: interface %s not matching [%s]", name, + AUDIO_HARDWARE_INTERFACE); + return -EINVAL; + } + + err = audio_ipc_init(); + if (err < 0) + return err; + + a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev)); + if (!a2dp_dev) + return -ENOMEM; + + a2dp_dev->dev.common.tag = HARDWARE_DEVICE_TAG; + a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT; + a2dp_dev->dev.common.module = (struct hw_module_t *) module; + a2dp_dev->dev.common.close = audio_close; + + a2dp_dev->dev.init_check = audio_init_check; + a2dp_dev->dev.set_voice_volume = audio_set_voice_volume; + a2dp_dev->dev.set_master_volume = audio_set_master_volume; + a2dp_dev->dev.set_mode = audio_set_mode; + a2dp_dev->dev.set_mic_mute = audio_set_mic_mute; + a2dp_dev->dev.get_mic_mute = audio_get_mic_mute; + a2dp_dev->dev.set_parameters = audio_set_parameters; + a2dp_dev->dev.get_parameters = audio_get_parameters; + a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size; + a2dp_dev->dev.open_output_stream = audio_open_output_stream; + a2dp_dev->dev.close_output_stream = audio_close_output_stream; + a2dp_dev->dev.open_input_stream = audio_open_input_stream; + a2dp_dev->dev.close_input_stream = audio_close_input_stream; + a2dp_dev->dev.dump = audio_dump; + + /* + * Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev. + * This results from the structure of following structs:a2dp_audio_dev, + * audio_hw_device. We will rely on this later in the code. + */ + *device = &a2dp_dev->dev.common; + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = audio_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "A2DP Bluez HW HAL", + .author = "Intel Corporation", + .methods = &hal_module_methods, + }, +}; diff --git a/android/hal-avrcp.c b/android/hal-avrcp.c new file mode 100644 index 0000000..5e07366 --- /dev/null +++ b/android/hal-avrcp.c @@ -0,0 +1,686 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" + +static const btrc_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void handle_remote_features(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_remote_features *ev = buf; + + if (cbs->remote_features_cb) + cbs->remote_features_cb((bt_bdaddr_t *) (ev->bdaddr), + ev->features); +} + +static void handle_get_play_status(void *buf, uint16_t len) +{ + if (cbs->get_play_status_cb) + cbs->get_play_status_cb(); +} + +static void handle_list_player_attrs(void *buf, uint16_t len) +{ + if (cbs->list_player_app_attr_cb) + cbs->list_player_app_attr_cb(); +} + +static void handle_list_player_values(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_list_player_values *ev = buf; + + if (cbs->list_player_app_values_cb) + cbs->list_player_app_values_cb(ev->attr); +} + +static void handle_get_player_values(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_get_player_values *ev = buf; + btrc_player_attr_t attrs[4]; + int i; + + if (!cbs->get_player_app_value_cb) + return; + + /* Convert uint8_t array to btrc_player_attr_t array */ + for (i = 0; i < ev->number; i++) + attrs[i] = ev->attrs[i]; + + cbs->get_player_app_value_cb(ev->number, attrs); +} + +static void handle_get_player_attrs_text(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_get_player_attrs_text *ev = buf; + btrc_player_attr_t attrs[4]; + int i; + + if (!cbs->get_player_app_attrs_text_cb) + return; + + /* Convert uint8_t array to btrc_player_attr_t array */ + for (i = 0; i < ev->number; i++) + attrs[i] = ev->attrs[i]; + + cbs->get_player_app_attrs_text_cb(ev->number, attrs); +} + +static void handle_get_player_values_text(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_get_player_values_text *ev = buf; + + if (cbs->get_player_app_values_text_cb) + cbs->get_player_app_values_text_cb(ev->attr, ev->number, + ev->values); +} + +static void handle_set_player_value(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_set_player_values *ev = buf; + struct hal_avrcp_player_attr_value *attrs; + btrc_player_settings_t values; + int i; + + if (!cbs->set_player_app_value_cb) + return; + + attrs = (struct hal_avrcp_player_attr_value *) ev->attrs; + + /* Convert to btrc_player_settings_t */ + values.num_attr = ev->number; + for (i = 0; i < ev->number; i++) { + values.attr_ids[i] = attrs[i].attr; + values.attr_values[i] = attrs[i].value; + } + + cbs->set_player_app_value_cb(&values); +} + +static void handle_get_element_attrs(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_get_element_attrs *ev = buf; + btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS]; + int i; + + if (!cbs->get_element_attr_cb) + return; + + /* Convert uint8_t array to btrc_media_attr_t array */ + for (i = 0; i < ev->number; i++) + attrs[i] = ev->attrs[i]; + + cbs->get_element_attr_cb(ev->number, attrs); +} + +static void handle_register_notification(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_register_notification *ev = buf; + + if (cbs->register_notification_cb) + cbs->register_notification_cb(ev->event, ev->param); +} + +static void handle_volume_changed(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_volume_changed *ev = buf; + + if (cbs->volume_change_cb) + cbs->volume_change_cb(ev->volume, ev->type); +} + +static void handle_passthrough_cmd(void *buf, uint16_t len) +{ + struct hal_ev_avrcp_passthrough_cmd *ev = buf; + + if (cbs->passthrough_cmd_cb) + cbs->passthrough_cmd_cb(ev->id, ev->state); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_AVRCP_REMOTE_FEATURES */ + { handle_remote_features, false, + sizeof(struct hal_ev_avrcp_remote_features) }, + /* HAL_EV_AVRCP_GET_PLAY_STATUS */ + { handle_get_play_status, false, 0 }, + /* HAL_EV_AVRCP_LIST_PLAYER_ATTRS */ + { handle_list_player_attrs, false, 0 }, + /* HAL_EV_AVRCP_LIST_PLAYER_VALUES */ + { handle_list_player_values, false, + sizeof(struct hal_ev_avrcp_list_player_values) }, + /* HAL_EV_AVRCP_GET_PLAYER_VALUES */ + { handle_get_player_values, true, + sizeof(struct hal_ev_avrcp_get_player_values) }, + /* HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT */ + { handle_get_player_attrs_text, true, + sizeof(struct hal_ev_avrcp_get_player_attrs_text) }, + /* HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT */ + { handle_get_player_values_text, true, + sizeof(struct hal_ev_avrcp_get_player_values_text) }, + /* HAL_EV_AVRCP_SET_PLAYER_VALUES */ + { handle_set_player_value, true, + sizeof(struct hal_ev_avrcp_set_player_values) }, + /* HAL_EV_AVRCP_GET_ELEMENT_ATTRS */ + { handle_get_element_attrs, true, + sizeof(struct hal_ev_avrcp_get_element_attrs) }, + /* HAL_EV_AVRCP_REGISTER_NOTIFICATION */ + { handle_register_notification, false, + sizeof(struct hal_ev_avrcp_register_notification) }, + /* HAL_EV_AVRCP_VOLUME_CHANGED */ + { handle_volume_changed, false, + sizeof(struct hal_ev_avrcp_volume_changed) }, + /* HAL_EV_AVRCP_PASSTHROUGH_CMD */ + { handle_passthrough_cmd, false, + sizeof(struct hal_ev_avrcp_passthrough_cmd) }, +}; + +static bt_status_t init(btrc_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + cbs = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_AVRCP, ev_handlers, + sizeof(ev_handlers) / sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_AVRCP; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_AVRCP); + } + + return ret; +} + +static bt_status_t get_play_status_rsp(btrc_play_status_t status, + uint32_t song_len, uint32_t song_pos) +{ + struct hal_cmd_avrcp_get_play_status cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.status = status; + cmd.duration = song_len; + cmd.position = song_pos; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t list_player_app_attr_rsp(int num_attr, + btrc_player_attr_t *p_attrs) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_list_player_attrs *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (num_attr < 0) + return BT_STATUS_PARM_INVALID; + + len = sizeof(*cmd) + num_attr; + if (len > IPC_MTU) + return BT_STATUS_PARM_INVALID; + + cmd->number = num_attr; + memcpy(cmd->attrs, p_attrs, num_attr); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_LIST_PLAYER_ATTRS, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t list_player_app_value_rsp(int num_val, uint8_t *p_vals) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_list_player_values *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (num_val < 0) + return BT_STATUS_PARM_INVALID; + + len = sizeof(*cmd) + num_val; + + if (len > IPC_MTU) + return BT_STATUS_PARM_INVALID; + + cmd->number = num_val; + memcpy(cmd->values, p_vals, num_val); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_LIST_PLAYER_VALUES, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_get_player_attrs *cmd = (void *) buf; + size_t len, attrs_len; + int i; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!p_vals) + return BT_STATUS_PARM_INVALID; + + attrs_len = p_vals->num_attr * + sizeof(struct hal_avrcp_player_attr_value); + len = sizeof(*cmd) + attrs_len; + + if (len > IPC_MTU) + return BT_STATUS_PARM_INVALID; + + cmd->number = p_vals->num_attr; + + for (i = 0; i < p_vals->num_attr; i++) { + cmd->attrs[i].attr = p_vals->attr_ids[i]; + cmd->attrs[i].value = p_vals->attr_values[i]; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_ATTRS, + len, cmd, 0, NULL, NULL); +} + +static int write_text(uint8_t *ptr, uint8_t id, uint8_t *text, size_t *len) +{ + struct hal_avrcp_player_setting_text *value = (void *) ptr; + size_t attr_len = sizeof(*value); + + if (attr_len + *len > IPC_MTU) + return 0; + + value->id = id; + value->len = strnlen((const char *) text, BTRC_MAX_ATTR_STR_LEN); + + *len += attr_len; + + if (value->len + *len > IPC_MTU) + value->len = IPC_MTU - *len; + + memcpy(value->text, text, value->len); + + *len += value->len; + + return attr_len + value->len; +} + +static uint8_t write_player_setting_text(uint8_t *ptr, uint8_t num_attr, + btrc_player_setting_text_t *p_attrs, + size_t *len) +{ + int i; + + for (i = 0; i < num_attr && *len < IPC_MTU; i++) { + int ret; + + ret = write_text(ptr, p_attrs[i].id, p_attrs[i].text, len); + if (ret == 0) + break; + + ptr += ret; + } + + return i; +} + +static bt_status_t get_player_app_attr_text_rsp(int num_attr, + btrc_player_setting_text_t *p_attrs) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_get_player_attrs_text *cmd = (void *) buf; + uint8_t *ptr; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (num_attr < 0 || num_attr > BTRC_MAX_APP_SETTINGS) + return BT_STATUS_PARM_INVALID; + + len = sizeof(*cmd); + ptr = (uint8_t *) &cmd->attrs[0]; + cmd->number = write_player_setting_text(ptr, num_attr, p_attrs, &len); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t get_player_app_value_text_rsp(int num_val, + btrc_player_setting_text_t *p_vals) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_get_player_values_text *cmd = (void *) buf; + uint8_t *ptr; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (num_val < 0) + return BT_STATUS_PARM_INVALID; + + len = sizeof(*cmd); + ptr = (uint8_t *) &cmd->values[0]; + cmd->number = write_player_setting_text(ptr, num_val, p_vals, &len); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, + len, cmd, 0, NULL, NULL); +} + +static uint8_t write_element_attr_text(uint8_t *ptr, uint8_t num_attr, + btrc_element_attr_val_t *p_attrs, + size_t *len) +{ + int i; + + for (i = 0; i < num_attr && *len < IPC_MTU; i++) { + int ret; + + ret = write_text(ptr, p_attrs[i].attr_id, p_attrs[i].text, len); + if (ret == 0) + break; + + ptr += ret; + } + + return i; +} + +static bt_status_t get_element_attr_rsp(uint8_t num_attr, + btrc_element_attr_val_t *p_attrs) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf; + size_t len; + uint8_t *ptr; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + len = sizeof(*cmd); + ptr = (uint8_t *) &cmd->values[0]; + cmd->number = write_element_attr_text(ptr, num_attr, p_attrs, &len); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t set_player_app_value_rsp(btrc_status_t rsp_status) +{ + struct hal_cmd_avrcp_set_player_attrs_value cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.status = rsp_status; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t play_status_changed_rsp(btrc_notification_type_t type, + btrc_play_status_t *play_status) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + size_t len; + + cmd->event = BTRC_EVT_PLAY_STATUS_CHANGED; + cmd->type = type; + cmd->len = 1; + memcpy(cmd->data, play_status, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t track_change_rsp(btrc_notification_type_t type, + btrc_uid_t *track) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + size_t len; + + cmd->event = BTRC_EVT_TRACK_CHANGE; + cmd->type = type; + cmd->len = sizeof(*track); + memcpy(cmd->data, track, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t track_reached_end_rsp(btrc_notification_type_t type) +{ + struct hal_cmd_avrcp_register_notification cmd; + + cmd.event = BTRC_EVT_TRACK_REACHED_END; + cmd.type = type; + cmd.len = 0; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t track_reached_start_rsp(btrc_notification_type_t type) +{ + struct hal_cmd_avrcp_register_notification cmd; + + cmd.event = BTRC_EVT_TRACK_REACHED_START; + cmd.type = type; + cmd.len = 0; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t play_pos_changed_rsp(btrc_notification_type_t type, + uint32_t *song_pos) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + size_t len; + + cmd->event = BTRC_EVT_PLAY_POS_CHANGED; + cmd->type = type; + cmd->len = sizeof(*song_pos); + memcpy(cmd->data, song_pos, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t settings_changed_rsp(btrc_notification_type_t type, + btrc_player_settings_t *player_setting) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + struct hal_avrcp_player_attr_value *attrs; + size_t len, param_len; + int i; + + param_len = player_setting->num_attr * sizeof(*attrs); + len = sizeof(*cmd) + param_len; + + if (len > IPC_MTU) + return BT_STATUS_PARM_INVALID; + + cmd->event = BTRC_EVT_APP_SETTINGS_CHANGED; + cmd->type = type; + cmd->len = param_len; + + attrs = (struct hal_avrcp_player_attr_value *) &cmd->data[0]; + for (i = 0; i < player_setting->num_attr; i++) { + attrs[i].attr = player_setting->attr_ids[i]; + attrs[i].value = player_setting->attr_values[i]; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t register_notification_rsp(btrc_event_id_t event_id, + btrc_notification_type_t type, + btrc_register_notification_t *p_param) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + switch (event_id) { + case BTRC_EVT_PLAY_STATUS_CHANGED: + return play_status_changed_rsp(type, &p_param->play_status); + case BTRC_EVT_TRACK_CHANGE: + return track_change_rsp(type, &p_param->track); + case BTRC_EVT_TRACK_REACHED_END: + return track_reached_end_rsp(type); + case BTRC_EVT_TRACK_REACHED_START: + return track_reached_start_rsp(type); + case BTRC_EVT_PLAY_POS_CHANGED: + return play_pos_changed_rsp(type, &p_param->song_pos); + case BTRC_EVT_APP_SETTINGS_CHANGED: + return settings_changed_rsp(type, &p_param->player_setting); + default: + return BT_STATUS_PARM_INVALID; + } +} + +static bt_status_t set_volume(uint8_t volume) +{ + struct hal_cmd_avrcp_set_volume cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.value = volume; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static void cleanup() +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_AVRCP; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_AVRCP); +} + +static btrc_interface_t iface = { + .size = sizeof(iface), + .init = init, + .get_play_status_rsp = get_play_status_rsp, + .list_player_app_attr_rsp = list_player_app_attr_rsp, + .list_player_app_value_rsp = list_player_app_value_rsp, + .get_player_app_value_rsp = get_player_app_value_rsp, + .get_player_app_attr_text_rsp = get_player_app_attr_text_rsp, + .get_player_app_value_text_rsp = get_player_app_value_text_rsp, + .get_element_attr_rsp = get_element_attr_rsp, + .set_player_app_value_rsp = set_player_app_value_rsp, + .register_notification_rsp = register_notification_rsp, + .set_volume = set_volume, + .cleanup = cleanup +}; + +btrc_interface_t *bt_get_avrcp_interface() +{ + return &iface; +} diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c index 7cac15c..2155978 100644 --- a/android/hal-bluetooth.c +++ b/android/hal-bluetooth.c @@ -20,12 +20,17 @@ #include #include +#include + #include "hal-log.h" #include "hal.h" #include "hal-msg.h" +#include "ipc-common.h" #include "hal-ipc.h" #include "hal-utils.h" +#define MODE_PROPERTY_NAME "persist.sys.bluetooth.mode" + static const bt_callbacks_t *bt_hal_cbacks = NULL; #define enum_prop_to_hal(prop, hal_prop, type) do { \ @@ -109,7 +114,7 @@ static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type, /* type match IPC type */ *type = property->type; - switch(property->type) { + switch (property->type) { case HAL_PROP_ADAPTER_SCAN_MODE: enum_prop_from_hal(property, len, val, bt_scan_mode_t); break; @@ -141,8 +146,37 @@ static void device_props_to_hal(bt_property_t *send_props, enum_prop_to_hal(send_props[i], prop, bt_device_type_t); break; - case HAL_PROP_DEVICE_SERVICE_REC: case HAL_PROP_DEVICE_VERSION_INFO: + { + static bt_remote_version_t e; + const struct hal_prop_device_info *p; + + send_props[i].val = &e; + send_props[i].len = sizeof(e); + + p = (struct hal_prop_device_info *) prop->val; + + e.manufacturer = p->manufacturer; + e.sub_ver = p->sub_version; + e.version = p->version; + } + break; + case HAL_PROP_DEVICE_SERVICE_REC: + { + static bt_service_record_t e; + const struct hal_prop_device_service_rec *p; + + send_props[i].val = &e; + send_props[i].len = sizeof(e); + + p = (struct hal_prop_device_service_rec *) prop->val; + + memset(&e, 0, sizeof(e)); + memcpy(&e.channel, &p->channel, sizeof(e.channel)); + memcpy(e.uuid.uu, p->uuid, sizeof(e.uuid.uu)); + memcpy(e.name, p->name, p->name_len); + } + break; default: send_props[i].len = prop->len; send_props[i].val = prop->val; @@ -307,8 +341,20 @@ static void handle_dut_mode_receive(void *buf, uint16_t len) bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len); } -/* handlers will be called from notification thread context, - * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ +static void handle_le_test_mode(void *buf, uint16_t len) +{ + struct hal_ev_le_test_mode *ev = buf; + + DBG(""); + + if (bt_hal_cbacks->le_test_mode_cb) + bt_hal_cbacks->le_test_mode_cb(ev->status, ev->num_packets); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ static const struct hal_ipc_handler ev_handlers[] = { { /* HAL_EV_ADAPTER_STATE_CHANGED */ .handler = handle_adapter_state_changed, @@ -363,8 +409,28 @@ static const struct hal_ipc_handler ev_handlers[] = { .var_len = true, .data_len = sizeof(struct hal_ev_dut_mode_receive), }, + { /* HAL_EV_LE_TEST_MODE */ + .handler = handle_le_test_mode, + .var_len = false, + .data_len = sizeof(struct hal_ev_le_test_mode), + } }; +static uint8_t get_mode(void) +{ + char value[PROPERTY_VALUE_MAX]; + + if (property_get(MODE_PROPERTY_NAME, value, "") > 0 && + (!strcasecmp(value, "bredr"))) + return HAL_MODE_BREDR; + + if (property_get(MODE_PROPERTY_NAME, value, "") > 0 && + (!strcasecmp(value, "le"))) + return HAL_MODE_LE; + + return HAL_MODE_DEFAULT; +} + static int init(bt_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; @@ -386,6 +452,7 @@ static int init(bt_callbacks_t *callbacks) } cmd.service_id = HAL_SERVICE_ID_BLUETOOTH; + cmd.mode = get_mode(); status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); @@ -394,7 +461,8 @@ static int init(bt_callbacks_t *callbacks) goto fail; } - cmd.service_id = HAL_SERVICE_ID_SOCK; + cmd.service_id = HAL_SERVICE_ID_SOCKET; + cmd.mode = HAL_MODE_DEFAULT; status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, NULL, NULL, NULL); @@ -479,8 +547,9 @@ static int get_adapter_property(bt_property_type_t type) static int set_adapter_property(const bt_property_t *property) { - char buf[sizeof(struct hal_cmd_set_adapter_prop) + property->len]; + char buf[IPC_MTU]; struct hal_cmd_set_adapter_prop *cmd = (void *) buf; + size_t len; DBG("prop: %s", btproperty2str(property)); @@ -489,8 +558,10 @@ static int set_adapter_property(const bt_property_t *property) adapter_prop_from_hal(property, &cmd->type, &cmd->len, cmd->val); + len = sizeof(*cmd) + cmd->len; + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, - sizeof(*cmd) + cmd->len, cmd, 0, NULL, NULL); + len, cmd, 0, NULL, NULL); } static int get_remote_device_properties(bt_bdaddr_t *remote_addr) @@ -533,8 +604,9 @@ static int get_remote_device_property(bt_bdaddr_t *remote_addr, static int set_remote_device_property(bt_bdaddr_t *remote_addr, const bt_property_t *property) { - struct hal_cmd_set_remote_device_prop *cmd; - uint8_t buf[sizeof(*cmd) + property->len]; + char buf[IPC_MTU]; + struct hal_cmd_set_remote_device_prop *cmd = (void *) buf; + size_t len; DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr), bt_property_type_t2str(property->type)); @@ -542,8 +614,6 @@ static int set_remote_device_property(bt_bdaddr_t *remote_addr, if (!interface_ready()) return BT_STATUS_NOT_READY; - cmd = (void *) buf; - memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr)); /* type match IPC type */ @@ -551,9 +621,11 @@ static int set_remote_device_property(bt_bdaddr_t *remote_addr, cmd->len = property->len; memcpy(cmd->val, property->val, property->len); + len = sizeof(*cmd) + cmd->len; + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP, - sizeof(buf), cmd, 0, NULL, NULL); + len, cmd, 0, NULL, NULL); } static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid) @@ -705,7 +777,7 @@ static const void *get_profile_interface(const char *profile_id) return NULL; if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID)) - return bt_get_sock_interface(); + return bt_get_socket_interface(); if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID)) return bt_get_hidhost_interface(); @@ -716,6 +788,18 @@ static const void *get_profile_interface(const char *profile_id) if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID)) return bt_get_a2dp_interface(); + if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID)) + return bt_get_avrcp_interface(); + + if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID)) + return bt_get_handsfree_interface(); + + if (!strcmp(profile_id, BT_PROFILE_GATT_ID)) + return bt_get_gatt_interface(); + + if (!strcmp(profile_id, BT_PROFILE_HEALTH_ID)) + return bt_get_health_interface(); + return NULL; } @@ -734,22 +818,62 @@ static int dut_mode_configure(uint8_t enable) sizeof(cmd), &cmd, 0, NULL, NULL); } -static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t len) +static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t buf_len) { - uint8_t cmd_buf[sizeof(struct hal_cmd_dut_mode_send) + len]; + char cmd_buf[IPC_MTU]; struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf; + size_t len; - DBG("opcode %u len %u", opcode, len); + DBG("opcode %u len %u", opcode, buf_len); if (!interface_ready()) return BT_STATUS_NOT_READY; cmd->opcode = opcode; - cmd->len = len; + cmd->len = buf_len; memcpy(cmd->data, buf, cmd->len); + len = sizeof(*cmd) + cmd->len; + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, - sizeof(cmd_buf), cmd, 0, NULL, NULL); + len, cmd, 0, NULL, NULL); +} + +static int le_test_mode(uint16_t opcode, uint8_t *buf, uint8_t buf_len) +{ + char cmd_buf[IPC_MTU]; + struct hal_cmd_le_test_mode *cmd = (void *) cmd_buf; + size_t len; + + DBG("opcode %u len %u", opcode, buf_len); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->opcode = opcode; + cmd->len = buf_len; + memcpy(cmd->data, buf, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE, + len, cmd, 0, NULL, NULL); +} + +static int config_hci_snoop_log(uint8_t enable) +{ + const char *property; + + DBG("enable %u", enable); + + property = enable ? "bluetooth.start" : "bluetooth.stop"; + + if (property_set(property, "snoop") < 0) { + error("Failed to set %s=snoop", property); + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; } static const bt_interface_t bluetooth_if = { @@ -775,7 +899,9 @@ static const bt_interface_t bluetooth_if = { .ssp_reply = ssp_reply, .get_profile_interface = get_profile_interface, .dut_mode_configure = dut_mode_configure, - .dut_mode_send = dut_mode_send + .dut_mode_send = dut_mode_send, + .le_test_mode = le_test_mode, + .config_hci_snoop_log = config_hci_snoop_log, }; static const bt_interface_t *get_bluetooth_interface(void) @@ -791,6 +917,8 @@ static int close_bluetooth(struct hw_device_t *device) cleanup(); + free(device); + return 0; } diff --git a/android/hal-gatt.c b/android/hal-gatt.c new file mode 100644 index 0000000..93dc066 --- /dev/null +++ b/android/hal-gatt.c @@ -0,0 +1,1354 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" +#include "hal-utils.h" + +static const btgatt_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void gatt_id_from_hal(btgatt_gatt_id_t *to, + struct hal_gatt_gatt_id *from) +{ + memcpy(&to->uuid, from->uuid, sizeof(to->uuid)); + to->inst_id = from->inst_id; +} + +static void gatt_id_to_hal(struct hal_gatt_gatt_id *to, btgatt_gatt_id_t *from) +{ + memcpy(to->uuid, &from->uuid, sizeof(from->uuid)); + to->inst_id = from->inst_id; +} + +static void srvc_id_from_hal(btgatt_srvc_id_t *to, + struct hal_gatt_srvc_id *from) +{ + memcpy(&to->id.uuid, from->uuid, sizeof(to->id.uuid)); + to->id.inst_id = from->inst_id; + to->is_primary = from->is_primary; +} + +static void srvc_id_to_hal(struct hal_gatt_srvc_id *to, btgatt_srvc_id_t *from) +{ + memcpy(to->uuid, &from->id.uuid, sizeof(from->id.uuid)); + to->inst_id = from->id.inst_id; + to->is_primary = from->is_primary; +} + +/* Client Event Handlers */ + +static void handle_register_client(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_register_client *ev = buf; + + if (cbs->client->register_client_cb) + cbs->client->register_client_cb(ev->status, ev->client_if, + (bt_uuid_t *) ev->app_uuid); +} + +static void handle_scan_result(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_scan_result *ev = buf; + uint8_t ad[62]; + + if (len != sizeof(*ev) + ev->len ) { + error("gatt: invalid scan result event, aborting"); + exit(EXIT_FAILURE); + } + + /* Java assumes that passed data has 62 bytes */ + memset(ad, 0, sizeof(ad)); + memcpy(ad, ev->adv_data, ev->len > sizeof(ad) ? sizeof(ad) : ev->len); + + if (cbs->client->scan_result_cb) + cbs->client->scan_result_cb((bt_bdaddr_t *) ev->bda, ev->rssi, + ad); +} + +static void handle_connect(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_connect *ev = buf; + + if (cbs->client->open_cb) + cbs->client->open_cb(ev->conn_id, ev->status, ev->client_if, + (bt_bdaddr_t *) ev->bda); +} + +static void handle_disconnect(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_disconnect *ev = buf; + + if (cbs->client->close_cb) + cbs->client->close_cb(ev->conn_id, ev->status, ev->client_if, + (bt_bdaddr_t *) ev->bda); +} + +static void handle_search_complete(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_search_complete *ev = buf; + + if (cbs->client->search_complete_cb) + cbs->client->search_complete_cb(ev->conn_id, ev->status); +} + +static void handle_search_result(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_search_result *ev = buf; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + + if (cbs->client->search_result_cb) + cbs->client->search_result_cb(ev->conn_id, &srvc_id); +} + +static void handle_get_characteristic(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_get_characteristic *ev = buf; + btgatt_gatt_id_t char_id; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + gatt_id_from_hal(&char_id, &ev->char_id); + + if (cbs->client->get_characteristic_cb) + cbs->client->get_characteristic_cb(ev->conn_id, ev->status, + &srvc_id, &char_id, + ev->char_prop); +} + +static void handle_get_descriptor(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_get_descriptor *ev = buf; + btgatt_gatt_id_t descr_id; + btgatt_gatt_id_t char_id; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + gatt_id_from_hal(&char_id, &ev->char_id); + gatt_id_from_hal(&descr_id, &ev->descr_id); + + if (cbs->client->get_descriptor_cb) + cbs->client->get_descriptor_cb(ev->conn_id, ev->status, + &srvc_id, &char_id, &descr_id); +} + +static void handle_get_included_service(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_get_inc_service *ev = buf; + btgatt_srvc_id_t srvc_id; + btgatt_srvc_id_t incl_srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + srvc_id_from_hal(&incl_srvc_id, &ev->incl_srvc_id); + + if (cbs->client->get_included_service_cb) + cbs->client->get_included_service_cb(ev->conn_id, ev->status, + &srvc_id, + &incl_srvc_id); +} + +static void handle_register_for_notification(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_reg_for_notif *ev = buf; + btgatt_gatt_id_t char_id; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + gatt_id_from_hal(&char_id, &ev->char_id); + + if (cbs->client->register_for_notification_cb) + cbs->client->register_for_notification_cb(ev->conn_id, + ev->registered, + ev->status, + &srvc_id, + &char_id); +} + +static void handle_notify(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_notify *ev = buf; + btgatt_notify_params_t params; + + if (len != sizeof(*ev) + ev->len ) { + error("gatt: invalid notify event, aborting"); + exit(EXIT_FAILURE); + } + + memset(¶ms, 0, sizeof(params)); + memcpy(params.value, ev->value, ev->len); + memcpy(¶ms.bda, ev->bda, sizeof(params.bda)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->char_id); + + params.len = ev->len; + params.is_notify = ev->is_notify; + + if (cbs->client->notify_cb) + cbs->client->notify_cb(ev->conn_id, ¶ms); +} + +static void handle_read_characteristic(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_read_characteristic *ev = buf; + btgatt_read_params_t params; + + if (len != sizeof(*ev) + ev->data.len ) { + error("gatt: invalid read characteristic event, aborting"); + exit(EXIT_FAILURE); + } + + memset(¶ms, 0, sizeof(params)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); + gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); + + memcpy(¶ms.value.value, ev->data.value, ev->data.len); + + params.value_type = ev->data.value_type; + params.value.len = ev->data.len; + params.status = ev->data.status; + + if (cbs->client->read_characteristic_cb) + cbs->client->read_characteristic_cb(ev->conn_id, ev->status, + ¶ms); +} + +static void handle_write_characteristic(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_write_characteristic *ev = buf; + btgatt_write_params_t params; + + memset(¶ms, 0, sizeof(params)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); + gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); + + params.status = ev->data.status; + + if (cbs->client->write_characteristic_cb) + cbs->client->write_characteristic_cb(ev->conn_id, ev->status, + ¶ms); +} + +static void handle_read_descriptor(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_read_descriptor *ev = buf; + btgatt_read_params_t params; + + if (len != sizeof(*ev) + ev->data.len ) { + error("gatt: invalid read descriptor event, aborting"); + exit(EXIT_FAILURE); + } + + memset(¶ms, 0, sizeof(params)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); + gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); + + memcpy(¶ms.value.value, ev->data.value, ev->data.len); + + params.value_type = ev->data.value_type; + params.value.len = ev->data.len; + params.status = ev->data.status; + + if (cbs->client->read_descriptor_cb) + cbs->client->read_descriptor_cb(ev->conn_id, ev->status, + ¶ms); +} + +static void handle_write_descriptor(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_write_descriptor *ev = buf; + btgatt_write_params_t params; + + memset(¶ms, 0, sizeof(params)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); + gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); + + params.status = ev->data.status; + + if (cbs->client->write_descriptor_cb) + cbs->client->write_descriptor_cb(ev->conn_id, ev->status, + ¶ms); +} + +static void handle_execute_write(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_exec_write *ev = buf; + + if (cbs->client->execute_write_cb) + cbs->client->execute_write_cb(ev->conn_id, ev->status); +} + +static void handle_read_remote_rssi(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_read_remote_rssi *ev = buf; + + if (cbs->client->read_remote_rssi_cb) + cbs->client->read_remote_rssi_cb(ev->client_if, + (bt_bdaddr_t *) ev->address, + ev->rssi, ev->status); +} + +static void handle_listen(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_listen *ev = buf; + + if (cbs->client->listen_cb) + cbs->client->listen_cb(ev->status, ev->server_if); +} + +/* Server Event Handlers */ + +static void handle_register_server(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_register *ev = buf; + + if (cbs->server->register_server_cb) + cbs->server->register_server_cb(ev->status, ev->server_if, + (bt_uuid_t *) &ev->uuid); +} + +static void handle_connection(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_connection *ev = buf; + + if (cbs->server->connection_cb) + cbs->server->connection_cb(ev->conn_id, ev->server_if, + ev->connected, + (bt_bdaddr_t *) &ev->bdaddr); +} + +static void handle_service_added(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_service_added *ev = buf; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + + if (cbs->server->service_added_cb) + cbs->server->service_added_cb(ev->status, ev->server_if, + &srvc_id, ev->srvc_handle); +} + +static void handle_included_service_added(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_inc_srvc_added *ev = buf; + + if (cbs->server->included_service_added_cb) + cbs->server->included_service_added_cb(ev->status, + ev->server_if, + ev->srvc_handle, + ev->incl_srvc_handle); +} + +static void handle_characteristic_added(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_characteristic_added *ev = buf; + + if (cbs->server->characteristic_added_cb) + cbs->server->characteristic_added_cb(ev->status, ev->server_if, + (bt_uuid_t *) &ev->uuid, + ev->srvc_handle, + ev->char_handle); +} + +static void handle_descriptor_added(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_descriptor_added *ev = buf; + + if (cbs->server->descriptor_added_cb) + cbs->server->descriptor_added_cb(ev->status, ev->server_if, + (bt_uuid_t *) &ev->uuid, + ev->srvc_handle, + ev->descr_handle); +} + +static void handle_service_started(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_service_started *ev = buf; + + if (cbs->server->service_started_cb) + cbs->server->service_started_cb(ev->status, ev->server_if, + ev->srvc_handle); +} + +static void handle_service_stopped(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_service_stopped *ev = buf; + + if (cbs->server->service_stopped_cb) + cbs->server->service_stopped_cb(ev->status, ev->server_if, + ev->srvc_handle); +} + +static void handle_service_deleted(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_service_deleted *ev = buf; + + if (cbs->server->service_deleted_cb) + cbs->server->service_deleted_cb(ev->status, ev->server_if, + ev->srvc_handle); +} + +static void handle_request_read(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_request_read *ev = buf; + + if (cbs->server->request_read_cb) + cbs->server->request_read_cb(ev->conn_id, ev->trans_id, + (bt_bdaddr_t *) &ev->bdaddr, + ev->attr_handle, ev->offset, + ev->is_long); +} + +static void handle_request_write(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_request_write *ev = buf; + + if (len != sizeof(*ev) + ev->length ) { + error("gatt: invalid request write event, aborting"); + exit(EXIT_FAILURE); + } + + if (cbs->server->request_write_cb) + cbs->server->request_write_cb(ev->conn_id, ev->trans_id, + (bt_bdaddr_t *) ev->bdaddr, + ev->attr_handle, ev->offset, + ev->length, ev->need_rsp, + ev->is_prep, ev->value); +} + +static void handle_request_exec_write(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_request_exec_write *ev = buf; + + if (cbs->server->request_exec_write_cb) + cbs->server->request_exec_write_cb(ev->conn_id, ev->trans_id, + (bt_bdaddr_t *) ev->bdaddr, + ev->exec_write); +} + +static void handle_response_confirmation(void *buf, uint16_t len) +{ + struct hal_ev_gatt_server_rsp_confirmation *ev = buf; + + if (cbs->server->response_confirmation_cb) + cbs->server->response_confirmation_cb(ev->status, ev->handle); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_GATT_CLIENT_REGISTER_CLIENT */ + { handle_register_client, false, + sizeof(struct hal_ev_gatt_client_register_client) }, + /* HAL_EV_GATT_CLIENT_SCAN_RESULT */ + { handle_scan_result, true, + sizeof(struct hal_ev_gatt_client_scan_result) }, + /* HAL_EV_GATT_CLIENT_CONNECT */ + { handle_connect, false, sizeof(struct hal_ev_gatt_client_connect) }, + /* HAL_EV_GATT_CLIENT_DISCONNECT */ + { handle_disconnect, false, + sizeof(struct hal_ev_gatt_client_disconnect) }, + /* HAL_EV_GATT_CLIENT_SEARCH_COMPLETE */ + { handle_search_complete, false, + sizeof(struct hal_ev_gatt_client_search_complete) }, + /* HAL_EV_GATT_CLIENT_SEARCH_RESULT */ + { handle_search_result, false, + sizeof(struct hal_ev_gatt_client_search_result) }, + /* HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC */ + { handle_get_characteristic, false, + sizeof(struct hal_ev_gatt_client_get_characteristic) }, + /* HAL_EV_GATT_CLIENT_GET_DESCRIPTOR */ + { handle_get_descriptor, false, + sizeof(struct hal_ev_gatt_client_get_descriptor) }, + /* HAL_EV_GATT_CLIENT_GET_INC_SERVICE */ + { handle_get_included_service, false, + sizeof(struct hal_ev_gatt_client_get_inc_service) }, + /* HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF */ + { handle_register_for_notification, false, + sizeof(struct hal_ev_gatt_client_reg_for_notif) }, + /* HAL_EV_GATT_CLIENT_NOTIFY */ + { handle_notify, true, sizeof(struct hal_ev_gatt_client_notify) }, + /* HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC */ + { handle_read_characteristic, true, + sizeof(struct hal_ev_gatt_client_read_characteristic) }, + /* HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC */ + { handle_write_characteristic, false, + sizeof(struct hal_ev_gatt_client_write_characteristic) }, + /* HAL_EV_GATT_CLIENT_READ_DESCRIPTOR */ + { handle_read_descriptor, true, + sizeof(struct hal_ev_gatt_client_read_descriptor) }, + /* HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR */ + { handle_write_descriptor, false, + sizeof(struct hal_ev_gatt_client_write_descriptor) }, + /* HAL_EV_GATT_CLIENT_EXEC_WRITE */ + { handle_execute_write, false, + sizeof(struct hal_ev_gatt_client_exec_write) }, + /* HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI */ + { handle_read_remote_rssi, false, + sizeof(struct hal_ev_gatt_client_read_remote_rssi) }, + /* HAL_EV_GATT_CLIENT_LISTEN */ + { handle_listen, false, sizeof(struct hal_ev_gatt_client_listen) }, + /* HAL_EV_GATT_SERVER_REGISTER */ + { handle_register_server, false, + sizeof(struct hal_ev_gatt_server_register) }, + /* HAL_EV_GATT_SERVER_CONNECTION */ + { handle_connection, false, + sizeof(struct hal_ev_gatt_server_connection) }, + /* HAL_EV_GATT_SERVER_SERVICE_ADDED */ + { handle_service_added, false, + sizeof(struct hal_ev_gatt_server_service_added) }, + /* HAL_EV_GATT_SERVER_INC_SRVC_ADDED */ + { handle_included_service_added, false, + sizeof(struct hal_ev_gatt_server_inc_srvc_added) }, + /* HAL_EV_GATT_SERVER_CHAR_ADDED */ + { handle_characteristic_added, false, + sizeof(struct hal_ev_gatt_server_characteristic_added) }, + /* HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED */ + { handle_descriptor_added, false, + sizeof(struct hal_ev_gatt_server_descriptor_added) }, + /* HAL_EV_GATT_SERVER_SERVICE_STARTED */ + { handle_service_started, false, + sizeof(struct hal_ev_gatt_server_service_started) }, + /* HAL_EV_GATT_SERVER_SERVICE_STOPPED */ + { handle_service_stopped, false, + sizeof(struct hal_ev_gatt_server_service_stopped) }, + /* HAL_EV_GATT_SERVER_SERVICE_DELETED */ + { handle_service_deleted, false, + sizeof(struct hal_ev_gatt_server_service_deleted) }, + /* HAL_EV_GATT_SERVER_REQUEST_READ */ + { handle_request_read, false, + sizeof(struct hal_ev_gatt_server_request_read) }, + /* HAL_EV_GATT_SERVER_REQUEST_WRITE */ + { handle_request_write, true, + sizeof(struct hal_ev_gatt_server_request_write) }, + /* HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE */ + { handle_request_exec_write, false, + sizeof(struct hal_ev_gatt_server_request_exec_write) }, + /* HAL_EV_GATT_SERVER_RSP_CONFIRMATION */ + { handle_response_confirmation, false, + sizeof(struct hal_ev_gatt_server_rsp_confirmation) }, +}; + +/* Client API */ + +static bt_status_t register_client(bt_uuid_t *uuid) +{ + struct hal_cmd_gatt_client_register cmd; + + memcpy(cmd.uuid, uuid, sizeof(*uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t unregister_client(int client_if) +{ + struct hal_cmd_gatt_client_unregister cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t scan(int client_if, bool start) +{ + struct hal_cmd_gatt_client_scan cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + cmd.start = start; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr, + bool is_direct) +{ + struct hal_cmd_gatt_client_connect cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + cmd.is_direct = is_direct; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr, + int conn_id) +{ + struct hal_cmd_gatt_client_disconnect cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + cmd.conn_id = conn_id; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISCONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t listen(int client_if, bool start) +{ + struct hal_cmd_gatt_client_listen cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + cmd.start = start; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_gatt_client_refresh cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t search_service(int conn_id, bt_uuid_t *filter_uuid) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_search_service *cmd = (void *) buf; + size_t len = sizeof(*cmd); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->conn_id = conn_id; + + if (filter_uuid) { + memcpy(cmd->filter_uuid, filter_uuid, sizeof(*filter_uuid)); + len += sizeof(*filter_uuid); + cmd->filtered = 1; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_SEARCH_SERVICE, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t get_included_service(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_srvc_id_t *start_incl_srvc_id) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_get_included_service *cmd = (void *) buf; + size_t len = sizeof(*cmd); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + cmd->continuation = 0; + + if (start_incl_srvc_id) { + srvc_id_to_hal(&cmd->incl_srvc_id[0], start_incl_srvc_id); + len += sizeof(cmd->incl_srvc_id[0]); + cmd->continuation = 1; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t get_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *start_char_id) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_get_characteristic *cmd = (void *) buf; + size_t len = sizeof(*cmd); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + cmd->continuation = 0; + + if (start_char_id) { + gatt_id_to_hal(&cmd->char_id[0], start_char_id); + len += sizeof(cmd->char_id[0]); + cmd->continuation = 1; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t get_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *start_descr_id) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_get_descriptor *cmd = (void *) buf; + size_t len = sizeof(*cmd); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + gatt_id_to_hal(&cmd->char_id, char_id); + cmd->continuation = 0; + + if (start_descr_id) { + gatt_id_to_hal(&cmd->descr_id[0], start_descr_id); + len += sizeof(cmd->descr_id[0]); + cmd->continuation = 1; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, + len, cmd, 0 , NULL, NULL); +} + +static bt_status_t read_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + int auth_req) +{ + struct hal_cmd_gatt_client_read_characteristic cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.conn_id = conn_id; + cmd.auth_req = auth_req; + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + gatt_id_to_hal(&cmd.char_id, char_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t write_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + int write_type, int len, int auth_req, + char *p_value) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_write_characteristic *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + len; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + cmd->write_type = write_type; + cmd->len = len; + cmd->auth_req = auth_req; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + gatt_id_to_hal(&cmd->char_id, char_id); + + memcpy(cmd->value, p_value, len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, + cmd_len, cmd, 0, NULL, NULL); +} + +static bt_status_t read_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, + int auth_req) +{ + struct hal_cmd_gatt_client_read_descriptor cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.conn_id = conn_id; + cmd.auth_req = auth_req; + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + gatt_id_to_hal(&cmd.char_id, char_id); + gatt_id_to_hal(&cmd.descr_id, descr_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t write_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, + int write_type, int len, int auth_req, + char *p_value) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_write_descriptor *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + len; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + cmd->write_type = write_type; + cmd->len = len; + cmd->auth_req = auth_req; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + gatt_id_to_hal(&cmd->char_id, char_id); + gatt_id_to_hal(&cmd->descr_id, descr_id); + + memcpy(cmd->value, p_value, len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, + cmd_len, cmd, 0, NULL, NULL); +} + +static bt_status_t execute_write(int conn_id, int execute) +{ + struct hal_cmd_gatt_client_execute_write cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.conn_id = conn_id; + cmd.execute = execute; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_EXECUTE_WRITE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t register_for_notification(int client_if, + const bt_bdaddr_t *bd_addr, + btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id) +{ + struct hal_cmd_gatt_client_register_for_notification cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + gatt_id_to_hal(&cmd.char_id, char_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t deregister_for_notification(int client_if, + const bt_bdaddr_t *bd_addr, + btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id) +{ + struct hal_cmd_gatt_client_deregister_for_notification cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + gatt_id_to_hal(&cmd.char_id, char_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t read_remote_rssi(int client_if, const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_gatt_client_read_remote_rssi cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static int get_device_type(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_gatt_client_get_device_type cmd; + uint8_t dev_type; + size_t resp_len = sizeof(dev_type); + bt_status_t status; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + status = hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, + sizeof(cmd), &cmd, &resp_len, &dev_type, NULL); + + if (status != BT_STATUS_SUCCESS || resp_len != sizeof(dev_type)) + return 0; + + return dev_type; +} + +static bt_status_t set_adv_data(int server_if, bool set_scan_rsp, + bool include_name, bool include_txpower, + int min_interval, int max_interval, + int appearance, uint16_t manufacturer_len, + char *manufacturer_data) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + manufacturer_len; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->server_if = server_if; + cmd->set_scan_rsp = set_scan_rsp; + cmd->include_name = include_name; + cmd->include_txpower = include_txpower; + cmd->min_interval = min_interval; + cmd->max_interval = max_interval; + cmd->appearance = appearance; + cmd->manufacturer_len = manufacturer_len; + + memcpy(cmd->manufacturer_data, manufacturer_data, manufacturer_len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA, + cmd_len, cmd, 0, NULL, NULL); +} + +static bt_status_t test_command(int command, btgatt_test_params_t *params) +{ + struct hal_cmd_gatt_client_test_command cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.command = command; + + memcpy(cmd.bda1, params->bda1, sizeof(*params->bda1)); + memcpy(cmd.uuid1, params->uuid1, sizeof(*params->uuid1)); + + cmd.u1 = params->u1; + cmd.u2 = params->u2; + cmd.u3 = params->u3; + cmd.u4 = params->u4; + cmd.u5 = params->u5; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_TEST_COMMAND, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +/* Server API */ + +static bt_status_t register_server(bt_uuid_t *uuid) +{ + struct hal_cmd_gatt_server_register cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.uuid, uuid, sizeof(*uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t unregister_server(int server_if) +{ + struct hal_cmd_gatt_server_unregister cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr, + bool is_direct) +{ + struct hal_cmd_gatt_server_connect cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.is_direct = is_direct; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t server_disconnect(int server_if, const bt_bdaddr_t *bd_addr, + int conn_id) +{ + struct hal_cmd_gatt_server_disconnect cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.conn_id = conn_id; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DISCONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t add_service(int server_if, btgatt_srvc_id_t *srvc_id, + int num_handles) +{ + struct hal_cmd_gatt_server_add_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.num_handles = num_handles; + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_SERVICE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t add_included_service(int server_if, int service_handle, + int included_handle) +{ + struct hal_cmd_gatt_server_add_inc_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + cmd.included_handle = included_handle; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_INC_SERVICE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t add_characteristic(int server_if, int service_handle, + bt_uuid_t *uuid, int properties, + int permissions) +{ + struct hal_cmd_gatt_server_add_characteristic cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + cmd.properties = properties; + cmd.permissions = permissions; + + memcpy(cmd.uuid, uuid, sizeof(*uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t add_descriptor(int server_if, int service_handle, + bt_uuid_t *uuid, int permissions) +{ + struct hal_cmd_gatt_server_add_descriptor cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + cmd.permissions = permissions; + + memcpy(cmd.uuid, uuid, sizeof(*uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t start_service(int server_if, int service_handle, + int transport) +{ + struct hal_cmd_gatt_server_start_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + cmd.transport = transport; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_START_SERVICE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t stop_service(int server_if, int service_handle) +{ + struct hal_cmd_gatt_server_stop_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_STOP_SERVICE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t delete_service(int server_if, int service_handle) +{ + struct hal_cmd_gatt_server_delete_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_DELETE_SERVICE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t send_indication(int server_if, int attribute_handle, + int conn_id, int len, int confirm, + char *p_value) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_server_send_indication *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + len; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->server_if = server_if; + cmd->attribute_handle = attribute_handle; + cmd->conn_id = conn_id; + cmd->len = len; + cmd->confirm = confirm; + + memcpy(cmd->value, p_value, len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_SEND_INDICATION, + cmd_len, cmd, 0, NULL, NULL); +} + +static bt_status_t send_response(int conn_id, int trans_id, int status, + btgatt_response_t *response) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_server_send_response *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + sizeof(*response); + + memset(buf, 0 , IPC_MTU); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + cmd->trans_id = trans_id; + cmd->status = status; + cmd->handle = response->attr_value.handle; + cmd->offset = response->attr_value.offset; + cmd->auth_req = response->attr_value.auth_req; + cmd->len = response->attr_value.len; + + memcpy(cmd->data, response->attr_value.value, cmd->len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_SEND_RESPONSE, + cmd_len, cmd, 0, NULL, NULL); +} + +static bt_status_t init(const btgatt_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + cbs = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_GATT, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_GATT; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_GATT); + } + + return ret; +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_GATT; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_GATT); +} + +static btgatt_client_interface_t client_iface = { + .register_client = register_client, + .unregister_client = unregister_client, + .scan = scan, + .connect = connect, + .disconnect = disconnect, + .listen = listen, + .refresh = refresh, + .search_service = search_service, + .get_included_service = get_included_service, + .get_characteristic = get_characteristic, + .get_descriptor = get_descriptor, + .read_characteristic = read_characteristic, + .write_characteristic = write_characteristic, + .read_descriptor = read_descriptor, + .write_descriptor = write_descriptor, + .execute_write = execute_write, + .register_for_notification = register_for_notification, + .deregister_for_notification = deregister_for_notification, + .read_remote_rssi = read_remote_rssi, + .get_device_type = get_device_type, + .set_adv_data = set_adv_data, + .test_command = test_command, +}; + +static btgatt_server_interface_t server_iface = { + .register_server = register_server, + .unregister_server = unregister_server, + .connect = server_connect, + .disconnect = server_disconnect, + .add_service = add_service, + .add_included_service = add_included_service, + .add_characteristic = add_characteristic, + .add_descriptor = add_descriptor, + .start_service = start_service, + .stop_service = stop_service, + .delete_service = delete_service, + .send_indication = send_indication, + .send_response = send_response, +}; + +static btgatt_interface_t iface = { + .size = sizeof(iface), + .init = init, + .cleanup = cleanup, + .client = &client_iface, + .server = &server_iface, +}; + +btgatt_interface_t *bt_get_gatt_interface(void) +{ + return &iface; +} diff --git a/android/hal-handsfree.c b/android/hal-handsfree.c new file mode 100644 index 0000000..d992506 --- /dev/null +++ b/android/hal-handsfree.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" + +#define MODE_PROPERTY_NAME "persist.sys.bluetooth.handsfree" + +static const bthf_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void handle_conn_state(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_conn_state *ev = buf; + + if (cbs->connection_state_cb) + cbs->connection_state_cb(ev->state, + (bt_bdaddr_t *) (ev->bdaddr)); +} + +static void handle_audio_state(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_audio_state *ev = buf; + + if (cbs->audio_state_cb) + cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); +} + +static void handle_vr_state(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_vr_state *ev = buf; + + if (cbs->vr_cmd_cb) + cbs->vr_cmd_cb(ev->state); +} + +static void handle_answer(void *buf, uint16_t len) +{ + if (cbs->answer_call_cmd_cb) + cbs->answer_call_cmd_cb(); +} + +static void handle_hangup(void *buf, uint16_t len) +{ + if (cbs->hangup_call_cmd_cb) + cbs->hangup_call_cmd_cb(); +} + +static void handle_volume(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_volume *ev = buf; + + if (cbs->volume_cmd_cb) + cbs->volume_cmd_cb(ev->type, ev->volume); +} + +static void handle_dial(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_dial *ev = buf; + uint16_t num_len = ev->number_len; + + if (len != sizeof(*ev) + num_len || + (num_len != 0 && ev->number[num_len - 1] != '\0')) { + error("invalid dial event, aborting"); + exit(EXIT_FAILURE); + } + + if (!cbs->dial_call_cmd_cb) + return; + + if (ev->number_len) + cbs->dial_call_cmd_cb((char *) ev->number); + else + cbs->dial_call_cmd_cb(NULL); +} + +static void handle_dtmf(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_dtmf *ev = buf; + + if (cbs->dtmf_cmd_cb) + cbs->dtmf_cmd_cb(ev->tone); +} + +static void handle_nrec(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_nrec *ev = buf; + + if (cbs->nrec_cmd_cb) + cbs->nrec_cmd_cb(ev->nrec); +} + +static void handle_chld(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_chld *ev = buf; + + if (cbs->chld_cmd_cb) + cbs->chld_cmd_cb(ev->chld); +} + +static void handle_cnum(void *buf, uint16_t len) +{ + if (cbs->cnum_cmd_cb) + cbs->cnum_cmd_cb(); +} + +static void handle_cind(void *buf, uint16_t len) +{ + if (cbs->cind_cmd_cb) + cbs->cind_cmd_cb(); +} + +static void handle_cops(void *buf, uint16_t len) +{ + if (cbs->cops_cmd_cb) + cbs->cops_cmd_cb(); +} + +static void handle_clcc(void *buf, uint16_t len) +{ + if (cbs->clcc_cmd_cb) + cbs->clcc_cmd_cb(); +} + +static void handle_unknown_at(void *buf, uint16_t len) +{ + struct hal_ev_handsfree_unknown_at *ev = buf; + + if (len != sizeof(*ev) + ev->len || + (ev->len != 0 && ev->buf[ev->len - 1] != '\0')) { + error("invalid unknown command event, aborting"); + exit(EXIT_FAILURE); + } + + if (cbs->unknown_at_cmd_cb) + cbs->unknown_at_cmd_cb((char *) ev->buf); +} + +static void handle_hsp_key_press(void *buf, uint16_t len) +{ + if (cbs->key_pressed_cmd_cb) + cbs->key_pressed_cmd_cb(); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_HANDSFREE_CONN_STATE */ + { handle_conn_state, false, + sizeof(struct hal_ev_handsfree_conn_state) }, + /* HAL_EV_HANDSFREE_AUDIO_STATE */ + { handle_audio_state, false, + sizeof(struct hal_ev_handsfree_audio_state) }, + /* HAL_EV_HANDSFREE_VR */ + { handle_vr_state, false, sizeof(struct hal_ev_handsfree_vr_state) }, + /*HAL_EV_HANDSFREE_ANSWER */ + { handle_answer, false, 0 }, + /*HAL_EV_HANDSFREE_HANGUP */ + { handle_hangup, false, 0 }, + /* HAL_EV_HANDSFREE_VOLUME */ + { handle_volume, false, sizeof(struct hal_ev_handsfree_volume) }, + /* HAL_EV_HANDSFREE_DIAL */ + { handle_dial, true, sizeof(struct hal_ev_handsfree_dial) }, + /* HAL_EV_HANDSFREE_DTMF */ + { handle_dtmf, false, sizeof(struct hal_ev_handsfree_dtmf) }, + /* HAL_EV_HANDSFREE_NREC */ + { handle_nrec, false, sizeof(struct hal_ev_handsfree_nrec) }, + /* HAL_EV_HANDSFREE_CHLD */ + { handle_chld, false, sizeof(struct hal_ev_handsfree_chld) }, + /* HAL_EV_HANDSFREE_CNUM */ + { handle_cnum, false, 0 }, + /* HAL_EV_HANDSFREE_CIND */ + { handle_cind, false, 0 }, + /* HAL_EV_HANDSFREE_COPS */ + { handle_cops, false, 0 }, + /* HAL_EV_HANDSFREE_CLCC */ + { handle_clcc, false, 0 }, + /* HAL_EV_HANDSFREE_UNKNOWN_AT */ + { handle_unknown_at, true, sizeof(struct hal_ev_handsfree_unknown_at) }, + /* HAL_EV_HANDSFREE_HSP_KEY_PRESS */ + { handle_hsp_key_press, false, 0 }, +}; + +static uint8_t get_mode(void) +{ + char value[PROPERTY_VALUE_MAX]; + + if (property_get(MODE_PROPERTY_NAME, value, "") > 0 && + (!strcasecmp(value, "hfp"))) + return HAL_MODE_HANDSFREE_HFP; + + if (property_get(MODE_PROPERTY_NAME, value, "") > 0 && + (!strcasecmp(value, "hfp_wbs"))) + return HAL_MODE_HANDSFREE_HFP_WBS; + + return HAL_MODE_HANDSFREE_HSP_ONLY; +} + +static bt_status_t init(bthf_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + cbs = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_HANDSFREE, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_HANDSFREE; + cmd.mode = get_mode(); + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE); + } + + return ret; +} + +static bt_status_t handsfree_connect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_handsfree_connect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t disconnect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_handsfree_disconnect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DISCONNECT, sizeof(cmd), &cmd, + 0, NULL, NULL); +} + +static bt_status_t connect_audio(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_handsfree_connect_audio cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CONNECT_AUDIO, sizeof(cmd), + &cmd, 0, NULL, NULL); +} + +static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_handsfree_disconnect_audio cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DISCONNECT_AUDIO, sizeof(cmd), + &cmd, 0, NULL, NULL); +} + +static bt_status_t start_voice_recognition(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_START_VR, + 0, NULL, 0, NULL, NULL); +} + +static bt_status_t stop_voice_recognition(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_STOP_VR, + 0, NULL, 0, NULL, NULL); +} + +static bt_status_t volume_control(bthf_volume_type_t type, int volume) +{ + struct hal_cmd_handsfree_volume_control cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.type = type; + cmd.volume = volume; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_VOLUME_CONTROL, sizeof(cmd), + &cmd, 0, NULL, NULL); +} + +static bt_status_t device_status_notification(bthf_network_state_t state, + bthf_service_type_t type, + int signal, int battery) +{ + struct hal_cmd_handsfree_device_status_notif cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.state = state; + cmd.type = type; + cmd.signal = signal; + cmd.battery = battery; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t cops_response(const char *cops) +{ + char buf[IPC_MTU]; + struct hal_cmd_handsfree_cops_response *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!cops) + return BT_STATUS_PARM_INVALID; + + cmd->len = strlen(cops) + 1; + memcpy(cmd->buf, cops, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_COPS_RESPONSE, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t cind_response(int svc, int num_active, int num_held, + bthf_call_state_t state, int signal, + int roam, int batt_chg) +{ + struct hal_cmd_handsfree_cind_response cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.svc = svc; + cmd.num_active = num_active; + cmd.num_held = num_held; + cmd.state = state; + cmd.signal = signal; + cmd.roam = roam; + cmd.batt_chg = batt_chg; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CIND_RESPONSE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t formatted_at_response(const char *rsp) +{ + char buf[IPC_MTU]; + struct hal_cmd_handsfree_formatted_at_response *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!rsp) + return BT_STATUS_PARM_INVALID; + + cmd->len = strlen(rsp) + 1; + memcpy(cmd->buf, rsp, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t at_response(bthf_at_response_t response, int error) +{ + struct hal_cmd_handsfree_at_response cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.response = response; + cmd.error = error; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_AT_RESPONSE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t clcc_response(int index, bthf_call_direction_t dir, + bthf_call_state_t state, + bthf_call_mode_t mode, + bthf_call_mpty_type_t mpty, + const char *number, + bthf_call_addrtype_t type) +{ + char buf[IPC_MTU]; + struct hal_cmd_handsfree_clcc_response *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->index = index; + cmd->dir = dir; + cmd->state = state; + cmd->mode = mode; + cmd->mpty = mpty; + cmd->type = type; + + if (number) { + cmd->number_len = strlen(number) + 1; + memcpy(cmd->number, number, cmd->number_len); + } else { + cmd->number_len = 0; + } + + len = sizeof(*cmd) + cmd->number_len; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CLCC_RESPONSE, + len, cmd, 0, NULL, NULL); +} + +static bt_status_t phone_state_change(int num_active, int num_held, + bthf_call_state_t state, + const char *number, + bthf_call_addrtype_t type) +{ + char buf[IPC_MTU]; + struct hal_cmd_handsfree_phone_state_change *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->num_active = num_active; + cmd->num_held = num_held; + cmd->state = state; + cmd->type = type; + + if (number) { + cmd->number_len = strlen(number) + 1; + memcpy(cmd->number, number, cmd->number_len); + } else { + cmd->number_len = 0; + } + + len = sizeof(*cmd) + cmd->number_len; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, + len, cmd, 0, NULL, NULL); +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_HANDSFREE; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE); +} + +static bthf_interface_t iface = { + .size = sizeof(iface), + .init = init, + .connect = handsfree_connect, + .disconnect = disconnect, + .connect_audio = connect_audio, + .disconnect_audio = disconnect_audio, + .start_voice_recognition = start_voice_recognition, + .stop_voice_recognition = stop_voice_recognition, + .volume_control = volume_control, + .device_status_notification = device_status_notification, + .cops_response = cops_response, + .cind_response = cind_response, + .formatted_at_response = formatted_at_response, + .at_response = at_response, + .clcc_response = clcc_response, + .phone_state_change = phone_state_change, + .cleanup = cleanup +}; + +bthf_interface_t *bt_get_handsfree_interface(void) +{ + return &iface; +} diff --git a/android/hal-health.c b/android/hal-health.c new file mode 100644 index 0000000..427d4c9 --- /dev/null +++ b/android/hal-health.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" + +static const bthl_callbacks_t *cbacks = NULL; + +static bool interface_ready(void) +{ + return cbacks != NULL; +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { +}; + +static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id) +{ + uint8_t buf[IPC_MTU]; + struct hal_cmd_health_reg_app *cmd = (void *) buf; + struct hal_cmd_health_mdep *mdep = (void *) buf; + struct hal_rsp_health_reg_app rsp; + size_t rsp_len = sizeof(rsp); + bt_status_t status; + uint16_t off, len; + int i; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!reg || !app_id || !reg->application_name) + return BT_STATUS_PARM_INVALID; + + memset(buf, 0, IPC_MTU); + + cmd->num_of_mdep = reg->number_of_mdeps; + + off = 0; + cmd->app_name_off = off; + len = strlen(reg->application_name) + 1; + memcpy(cmd->data, reg->application_name, len); + off += len; + + if (reg->provider_name) { + len = strlen(reg->provider_name) + 1; + cmd->provider_name_off = off; + memcpy(cmd->data + off, reg->provider_name, len); + off += len; + } + + if (reg->srv_name) { + len = strlen(reg->srv_name) + 1; + cmd->service_name_off = off; + memcpy(cmd->data + off, reg->srv_name, len); + off += len; + } + + if (reg->srv_desp) { + len = strlen(reg->srv_desp) + 1; + cmd->service_descr_off = off; + memcpy(cmd->data + off, reg->srv_desp, len); + off += len; + } + + cmd->len = off; + status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP, + sizeof(*cmd) + cmd->len, &cmd, + &rsp_len, &rsp, NULL); + + if (status != BT_STATUS_SUCCESS) + return status; + + for (i = 0; i < reg->number_of_mdeps; i++) { + memset(buf, 0, IPC_MTU); + mdep->role = reg->mdep_cfg[i].mdep_role; + mdep->data_type = reg->mdep_cfg[i].data_type; + mdep->channel_type = reg->mdep_cfg[i].channel_type; + + if (reg->mdep_cfg[i].mdep_description) { + mdep->descr_len = + strlen(reg->mdep_cfg[i].mdep_description) + 1; + memcpy(mdep->descr, reg->mdep_cfg[i].mdep_description, + mdep->descr_len); + } + + status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, + sizeof(*mdep) + mdep->descr_len, + buf, 0, NULL, NULL); + + if (status != BT_STATUS_SUCCESS) + return status; + + } + + *app_id = rsp.app_id; + + return status; +} + +static bt_status_t unregister_application(int app_id) +{ + struct hal_cmd_health_unreg_app cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.app_id = app_id; + + return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t connect_channel(int app_id, bt_bdaddr_t *bd_addr, + int mdep_cfg_index, int *channel_id) +{ + struct hal_cmd_health_connect_channel cmd; + struct hal_rsp_health_connect_channel rsp; + size_t len = sizeof(rsp); + bt_status_t status; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr || !channel_id) + return BT_STATUS_PARM_INVALID; + + cmd.app_id = app_id; + cmd.mdep_index = mdep_cfg_index; + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_CONNECT_CHANNEL, + sizeof(cmd), &cmd, &len, &rsp, NULL); + + if (status == BT_STATUS_SUCCESS) + *channel_id = rsp.channel_id; + + return status; +} + +static bt_status_t destroy_channel(int channel_id) +{ + struct hal_cmd_health_destroy_channel cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.channel_id = channel_id; + + return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t init(bthl_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + /* store reference to user callbacks */ + cbacks = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_HEALTH, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_HEALTH; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbacks = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_HEALTH); + } + + return ret; +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbacks = NULL; + + cmd.service_id = HAL_SERVICE_ID_HEALTH; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_HEALTH); +} + +static bthl_interface_t health_if = { + .size = sizeof(health_if), + .init = init, + .register_application = register_application, + .unregister_application = unregister_application, + .connect_channel = connect_channel, + .destroy_channel = destroy_channel, + .cleanup = cleanup +}; + +bthl_interface_t *bt_get_health_interface(void) +{ + return &health_if; +} diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c index 6a6b682..5787b5e 100644 --- a/android/hal-hidhost.c +++ b/android/hal-hidhost.c @@ -23,6 +23,7 @@ #include "hal-log.h" #include "hal.h" #include "hal-msg.h" +#include "ipc-common.h" #include "hal-ipc.h" static const bthh_callbacks_t *cbacks; @@ -69,6 +70,15 @@ static void handle_proto_mode(void *buf, uint16_t len) ev->status, ev->mode); } +static void handle_idle_time(void *buf, uint16_t len) +{ + struct hal_ev_hidhost_idle_time *ev = buf; + + if (cbacks->idle_time_cb) + cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status, + ev->idle_rate); +} + static void handle_get_report(void *buf, uint16_t len) { struct hal_ev_hidhost_get_report *ev = buf; @@ -92,8 +102,10 @@ static void handle_virtual_unplug(void *buf, uint16_t len) ev->status); } -/* handlers will be called from notification thread context, - * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ static const struct hal_ipc_handler ev_handlers[] = { { /* HAL_EV_HIDHOST_CONN_STATE */ .handler = handle_conn_state, @@ -110,6 +122,11 @@ static const struct hal_ipc_handler ev_handlers[] = { .var_len = false, .data_len = sizeof(struct hal_ev_hidhost_proto_mode), }, + { /* HAL_EV_HIDHOST_IDLE_TIME */ + .handler = handle_idle_time, + .var_len = false, + .data_len = sizeof(struct hal_ev_hidhost_idle_time), + }, { /* HAL_EV_HIDHOST_GET_REPORT */ .handler = handle_get_report, .var_len = true, @@ -218,16 +235,8 @@ static bt_status_t get_protocol(bt_bdaddr_t *bd_addr, memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); - switch (protocol_mode) { - case BTHH_REPORT_MODE: - cmd.mode = HAL_HIDHOST_REPORT_PROTOCOL; - break; - case BTHH_BOOT_MODE: - cmd.mode = HAL_HIDHOST_BOOT_PROTOCOL; - break; - default: - return BT_STATUS_PARM_INVALID; - } + /* type match IPC type */ + cmd.mode = protocol_mode; return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL, @@ -249,16 +258,8 @@ static bt_status_t set_protocol(bt_bdaddr_t *bd_addr, memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); - switch (protocol_mode) { - case BTHH_REPORT_MODE: - cmd.mode = HAL_HIDHOST_REPORT_PROTOCOL; - break; - case BTHH_BOOT_MODE: - cmd.mode = HAL_HIDHOST_BOOT_PROTOCOL; - break; - default: - return BT_STATUS_PARM_INVALID; - } + /* type match IPC type */ + cmd.mode = protocol_mode; return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL, @@ -284,19 +285,8 @@ static bt_status_t get_report(bt_bdaddr_t *bd_addr, cmd.id = report_id; cmd.buf_size = buffer_size; - switch (report_type) { - case BTHH_INPUT_REPORT: - cmd.type = HAL_HIDHOST_INPUT_REPORT; - break; - case BTHH_OUTPUT_REPORT: - cmd.type = HAL_HIDHOST_OUTPUT_REPORT; - break; - case BTHH_FEATURE_REPORT: - cmd.type = HAL_HIDHOST_FEATURE_REPORT; - break; - default: - return BT_STATUS_PARM_INVALID; - } + /* type match IPC type */ + cmd.type = report_type; return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, sizeof(cmd), &cmd, 0, NULL, NULL); @@ -306,7 +296,7 @@ static bt_status_t set_report(bt_bdaddr_t *bd_addr, bthh_report_type_t report_type, char *report) { - uint8_t buf[BLUEZ_HAL_MTU]; + uint8_t buf[IPC_MTU]; struct hal_cmd_hidhost_set_report *cmd = (void *) buf; DBG(""); @@ -321,19 +311,8 @@ static bt_status_t set_report(bt_bdaddr_t *bd_addr, cmd->len = strlen(report); memcpy(cmd->data, report, cmd->len); - switch (report_type) { - case BTHH_INPUT_REPORT: - cmd->type = HAL_HIDHOST_INPUT_REPORT; - break; - case BTHH_OUTPUT_REPORT: - cmd->type = HAL_HIDHOST_OUTPUT_REPORT; - break; - case BTHH_FEATURE_REPORT: - cmd->type = HAL_HIDHOST_FEATURE_REPORT; - break; - default: - return BT_STATUS_PARM_INVALID; - } + /* type match IPC type */ + cmd->type = report_type; return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, sizeof(*cmd) + cmd->len, buf, 0, NULL, NULL); @@ -341,7 +320,7 @@ static bt_status_t set_report(bt_bdaddr_t *bd_addr, static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data) { - uint8_t buf[BLUEZ_HAL_MTU]; + uint8_t buf[IPC_MTU]; struct hal_cmd_hidhost_send_data *cmd = (void *) buf; DBG(""); @@ -377,6 +356,7 @@ static bt_status_t init(bthh_callbacks_t *callbacks) sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_HIDHOST; + cmd.mode = HAL_MODE_DEFAULT; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); diff --git a/android/hal-ipc-api.txt b/android/hal-ipc-api.txt index 57f4c13..43e2709 100644 --- a/android/hal-ipc-api.txt +++ b/android/hal-ipc-api.txt @@ -124,12 +124,16 @@ Core Service (ID 0) Opcode 0x01 - Register module command/response Command parameters: Service id (1 octet) + Mode (1 octet) Response parameters: In case a command is sent for an undeclared service ID, it will be rejected. Also there will be no notifications for undeclared service ID. + Valid Mode values: 0x00 = Default Mode + 0xXX = as defined by service + In case of an error, the error response will be returned. Opcode 0x02 - Unregister module command/response @@ -145,6 +149,10 @@ Bluetooth Core HAL (ID 1) Android HAL name: "bluetooth" (BT_HARDWARE_MODULE_ID) + Service modes: 0x00 = Enable BR/EDR/LE if supported (default) + 0x01 = Enable BR/EDR only + 0x02 = Enable LE only + Commands and responses: Opcode 0x00 - Error response @@ -317,38 +325,41 @@ Notifications: Opcode 0x81 - Adapter State Changed notification - Notifications parameters: State (1 octect) + Notifications parameters: State (1 octet) Valid state values: 0x00 = Off 0x01 = On Opcode 0x82 - Adapter Properties Changed notification - Notification parameters: Status (1 octect) + Notification parameters: Status (1 octet) Num properties (1 octet) - Type[i] (1 octect) - Length[i] (2 octets) - Value[i] (variable) + Type # (1 octet) + Length # (2 octets) + Value # (variable) + ... Opcode 0x83 - Remote Device Properties notification - Notification parameters: Status (1 octect) + Notification parameters: Status (1 octet) Remote address (6 octets) Num properties (1 octet) - Type[i] (1 octect) - Length[i] (2 octets) - Value[i] (variable) + Type # (1 octet) + Length # (2 octets) + Value # (variable) + ... Opcode 0x84 - Device Found notification Notification parameters: Num properties (1 octet) - Type[i] (1 octect) - Length[i] (2 octets) - Value[i] (variable) + Type # (1 octet) + Length # (2 octets) + Value # (variable) + ... Opcode 0x85 - Discovery State Changed notification - Notifications parameters: State (1 octect) + Notifications parameters: State (1 octet) Opcode 0x86 - PIN Request notification @@ -366,7 +377,7 @@ Notifications: Opcode 0x88 - Bond State Changed notification - Notification parameters: Status (1 octect) + Notification parameters: Status (1 octet) Remote address (6 octets) Bond state (1 octet) @@ -376,19 +387,19 @@ Notifications: Opcode 0x89 - ACL State Changed notification - Notification parameters: Status (1 octect) + Notification parameters: Status (1 octet) Remote address (6 octets) ACL state (1 octet) Opcode 0x8a - DUT Mode Receive notification - Notification parameters: Opcode (2 octects) + Notification parameters: Opcode (2 octets) Length (1 octet) Data (variable) Opcode 0x8b - LE Test Mode notification - Notification parameters: Status (1 octect) + Notification parameters: Status (1 octet) Num packets (2 octets) @@ -614,14 +625,20 @@ Notifications: 0x01 = Boot 0xff = Unsupported - Opcode 0x84 - Get Report notification + Opcode 0x84 - Idle Time notification + + Notification parameters: Remote address (6 octets) + Status (1 octet) + Idle time (2 octets) + + Opcode 0x85 - Get Report notification Notification parameters: Remote address (6 octets) Status (1 octet) Report length (2 octets) Report data (variable) - Opcode 0x85 - Virtual Unplug notification + Opcode 0x86 - Virtual Unplug notification Notification parameters: Remote address (6 octets) Status (1 octet) @@ -709,7 +726,7 @@ Notifications: Opcode 0x81 - Control State notification - Notification parameters: Control state (1 octect) + Notification parameters: Control state (1 octet) Status (1 octet) Local role (1 octet) Interface name (17 octet) @@ -723,7 +740,7 @@ Notifications: Opcode 0x82 - Connection State notification - Notification parameters: Connection state (1 octect) + Notification parameters: Connection state (1 octet) Status (1 octet) Remote address (6 octets) Local role (1 octet) @@ -743,6 +760,12 @@ Bluetooth Handsfree HAL (ID 5) Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID) + Service modes: 0x00 = Headset Profile only mode (default) + 0x01 = Handsfree Profile (narrowband speech) + 0x02 = Handsfree Profile (narrowband and wideband speech) + +Commands and responses: + Opcode 0x00 - Error response Response parameters: Status (1 octet) @@ -881,7 +904,7 @@ Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID) Call mode (1 octet) Call multiparty type (1 octet) Call number type (1 octet) - Call number (variable) + Call number (string) Response parameters: Valid call directions: 0x00 = Outgoing @@ -913,7 +936,7 @@ Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID) Number of held calls (1 octet) Call setup state (1 octet) Call number type (1 octet) - Call number (variable) + Call number (string) Response parameters: Valid call setup states: 0x00 = Active @@ -933,7 +956,7 @@ Notifications: Opcode 0x81 - Connection State notification - Notification parameters: Connection state (1 octect) + Notification parameters: Connection state (1 octet) Remote address (6 octets) Valid connection states: 0x00 = Disconnected @@ -944,7 +967,7 @@ Notifications: Opcode 0x82 - Audio State notification - Notification parameters: Audio state (1 octect) + Notification parameters: Audio state (1 octet) Remote address (6 octets) Valid audio states: 0x00 = Disconnected @@ -970,6 +993,7 @@ Notifications: Opcode 0x86 - Volume Command notification Notification parameters: Volume type (1 octet) + Volume (1 octet) Valid volume types: 0x00 = Speaker 0x01 = Microphone @@ -1063,7 +1087,7 @@ Notifications: Opcode 0x81 - Connection State notification - Notification parameters: Connection state (1 octect) + Notification parameters: Connection state (1 octet) Remote address (6 octets) Valid connection states: 0x00 = Disconnected @@ -1073,7 +1097,7 @@ Notifications: Opcode 0x82 - Audio State notification - Notification parameters: Audio state (1 octect) + Notification parameters: Audio state (1 octet) Remote address (6 octets) Valid connection states: 0x00 = Remote suspend @@ -1086,6 +1110,8 @@ Bluetooth Health HAL (ID 7) Android HAL name: "health" (BT_PROFILE_HEALTH_ID) +Commands and responses: + Opcode 0x00 - Error response Response parameters: Status (1 octet) @@ -1103,27 +1129,37 @@ Android HAL name: "health" (BT_PROFILE_HEALTH_ID) Opcode 0x01 - Register Application command/response - Command parameters: Application name (string) - Provider name (string) - Service name (string) - Service description (string) - Number of MDEP (1 octet) - MDEP Role[i] (1 octet) - Data type[i] (1 octet) - Channel type[i] (1 octet) - MDEP description (string) + Command parameters: Number of MDEP (1 octet) + Application name offset (2 octets) + Provider name offset (2 octets) + Service name offset (2 octets) + Service description offset (2 octets) + Data length (2 octets) + Data (data length) Response parameters: Application ID (2 octets) + Strings are null terminated. In case of an error, the error response will be returned. - Opcode 0x02 - Unregister Application command/response + Opcode 0x02 - Register Application MDEP data command/response + + Command parameters: MDEP Role (1 octet) + Data type (1 octet) + Channel type (1 octet) + MDEP description length (2 octets) + MDEP description (MDEP desciption length) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x03 - Unregister Application command/response Command parameters: Application ID (2 octets) Response parameters: In case of an error, the error response will be returned. - Opcode 0x03 - Connect Channel command/response + Opcode 0x04 - Connect Channel command/response Command parameters: Application ID (2 octets) Remote address (6 octets) @@ -1132,7 +1168,7 @@ Android HAL name: "health" (BT_PROFILE_HEALTH_ID) In case of an error, the error response will be returned. - Opcode 0x04 - Destroy Channel command/response + Opcode 0x05 - Destroy Channel command/response Command parameters: Channel ID (2 octets) Response parameters: @@ -1143,7 +1179,7 @@ Notifications: Opcode 0x81 - Application Registration State notification - Notification parameters: Application ID (2 octects) + Notification parameters: Application ID (2 octets) Application state (1 octet) Valid application states: 0x00 = Registration success @@ -1153,7 +1189,7 @@ Notifications: Opcode 0x82 - Channel State notification - Notification parameters: Application ID (2 octects) + Notification parameters: Application ID (2 octets) Remote address (6 octets) MDEP index (1 octet) Channel ID (2 octets) @@ -1172,20 +1208,229 @@ Bluetooth Remote Control HAL (ID 8) Android HAL name: "avrcp" (BT_PROFILE_AV_RC_ID) +Commands and responses: + Opcode 0x00 - Error response - Opcode 0x01 - Get Play Status command/response - Opcode 0x02 - List Player Application Attributes command/response - Opcode 0x03 - List Player Application Values command/response - Opcode 0x04 - Get Player Application Values command/response - Opcode 0x05 - Get Player Application Attributes Text command/response - Opcode 0x06 - Get Player Application Values Text command/response - Opcode 0x07 - Get Element Attributes Text command/response - Opcode 0x08 - Set Player Attributes Value command/response - Opcode 0x09 - Register Notification command/response - Opcode 0x81 - Get Play Status notification - Opcode 0x82 - List Player Application Attributes notification - ... + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Get Play Status Response command/response + + Command parameters: Status (1 octet) + Duration (4 octets) + Position (4 octets) + + In case of an error, the error response will be returned. + + Valid status values: 0x00 = Stopped + 0x01 = Playing + 0x02 = Paused + 0x03 = Fwd seek + 0x04 = Rev seek + 0xff = Error + + Opcode 0x02 - List Player Attributes Response command/response + + Command parameters: Number of attributes (1 octet) + Attribute # (1 octet) + ... + + In case of an error, the error response will be returned. + + Valid attributes: 0x01 = Equalizer + 0x02 = Repead + 0x03 = Shuffle + 0x04 = Scan + + Opcode 0x03 - List Player Values Response command/response + + Command parameters: Number of values (1 octet) + Value # (1 octet) + ... + + In case of an error, the error response will be returned. + + Opcode 0x04 - Get Player Values Response command/response + + Command parameters: Number of attributes (1 octet) + Attribute # (1 octet) + Value # (1 octet) + ... + + In case of an error, the error response will be returned. + + Valid attributes: Same as in List Player Attributes + + Opcode 0x05 - Get Player Attributes Text Response command/response + + Command parameters: Number of attributes (1 octet) + Attribute # (1 octet) + Attribute # text length (1 octet) + Attribute # text (variable) + ... + + In case of an error, the error response will be returned. + + Valid attributes: Same as in List Player Attributes + + Opcode 0x06 - Get Player Values Text Response command/response + + Command parameters: Number of values (1 octet) + Value # (1 octet) + Value # text length (1 octet) + Value # text (variable) + ... + + In case of an error, the error response will be returned. + + Opcode 0x07 - Get Element Attributes Text Response command/response + + Command parameters: Number of elements (1 octet) + Element # (1 octet) + Element # text length (1 octet) + Element # text (variable) + ... + + In case of an error, the error response will be returned. + + Valid elements: 0x01 = Title + 0x02 = Artist + 0x03 = Album + 0x04 = Track Number + 0x05 = Number of Tracks + 0x06 = Genre + 0x06 = Duration + + Opcode 0x08 - Set Player Attributes Value Response command/response + + Command parameters: Status (1 octet) + + In case of an error, the error response will be returned. + + Valid status values: Same as in Get Play Status Response + + Opcode 0x09 - Register Notification Response command/response + + Command parameters: Event (1 octet) + Type (1 octet) + Data length (1 octet) + Data (variable) + + In case of an error, the error response will be returned. + + Valid event values: 0x01 = Status Changed + 0x02 = Track Changed + 0x03 = Track Reached End + 0x04 = Track Reached Start + 0x05 = Position Changed + 0x08 = Setting Changed + + Valid type values : 0x00 = Interim + 0x01 = Changed + + Opcode 0x0a - Set Volume command/response + + Command parameters: Value (1 octet) + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Remote Features notification + + Notification parameters: Remote address (6 octets) + Features (1 octet) + + Valid features values : 0x00 = None + 0x01 = Metadata + 0x02 = Absolute Volume + 0x03 = Browse + + Opcode 0x82 - Get Play Status notification + + Notification parameters: + + Opcode 0x83 - List Player Attributes notification + + Notification parameters: + + Opcode 0x84 - List Player Values notification + + Notification parameters: Attribute (1 octet) + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x85 - Get Player Values notification + + Notification parameters: Number of attributes (1 octet) + Attribute # (1 octet) + ... + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x86 - Get Player Attributes Text notification + + Notification parameters: Number of attributes (1 octet) + Attribute # (1 octet) + ... + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x87 - Get Player Values Text notification + + Notification parameters: Attribute (1 octet) + Number of values (1 octet) + Value # (1 octet) + ... + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x88 - Set Player Values notification + + Notification parameters: Number of attributes (1 octet) + Attribute # (1 octet) + Value # (1 octet) + ... + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x89 - Get Element Attributes notification + + Notification parameters: Number of attributes (1 octet) + Attribute # (1 octet) + ... + + Valid attribute values: Same as in Get Element Attribute + + Opcode 0x8a - Register Notification notification + + Notification parameters: Event (1 octet) + Parameter (4 octets) + + Valid event values: Same as in Register Notification + + Opcode 0x8b - Volume Changed notification + + Notification parameters: Volume (1 octet) + Type (1 octet) + + Valid type values: Same as in Register Notification + + Opcode 0x8c - Passthrough Command notification + + Notification parameters: ID (1 octet) + State (1 octet) Bluetooth GATT HAL (ID 9) @@ -1193,68 +1438,642 @@ Bluetooth GATT HAL (ID 9) Android HAL name: "gatt" (BT_PROFILE_GATT_ID) +Structures: + + GATT Service ID: UUID (16 octets) + Instance ID (1 octet) + Is Primary (1 octet) + + GATT Included Service ID: UUID (16 octets) + Instance ID (1 octet) + Is Primary (1 octet) + + GATT Characteristic ID: UUID (16 octets) + Instance ID (1 octet) + + GATT Descriptor ID: UUID (16 octets) + Instance ID (1 octet) + +Commands and responses: + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + Opcode 0x01 - Register Client command/response + + Command parameters: Service UUID (16 octets) + Response parameters: + + In case of an error, the error response will be returned. + Opcode 0x02 - Unregister Client command/response + + Command parameters: Client Interface (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + Opcode 0x03 - Scan command/response + + Command parameters: Client Interface (4 octets) + Start (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + Opcode 0x04 - Connect Device command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + Is Direct (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + Opcode 0x05 - Disconnect Device command/response - Opcode 0x06 - Refresh command/response - Opcode 0x07 - Search Service command/response - Opcode 0x08 - Get Included Service command/response - Opcode 0x09 - Get Characteristic command/response - Opcode 0x0a - Get Descriptor command/response - Opcode 0x0b - Read Characteristic command/response - Opcode 0x0c - Write Characteristic command/response - Opcode 0x0d - Read Descriptor command/response - Opcode 0x0e - Write Descriptor command/response - Opcode 0x0f - Execute Write command/response - Opcode 0x10 - Register For Notification command/response - Opcode 0x11 - Deregister For Notification command/response - Opcode 0x12 - Read Remote RSSI command/response - Opcode 0x13 - Get Device Type command/response - Opcode 0x14 - Test Command command/response - Opcode 0x15 - Register Server command/response - Opcode 0x16 - Unregister Server command/response - Opcode 0x17 - Connect Peripheral command/response - Opcode 0x18 - Disconnect Peripheral command/response - Opcode 0x19 - Add Service command/response - Opcode 0x1a - Add Included Service command/response - Opcode 0x1b - Add Characteristic command/response - Opcode 0x1c - Add Descriptor command/response - Opcode 0x1d - Start Service command/response - Opcode 0x1e - Stop Service command/response - Opcode 0x1f - Delete Service command/response - Opcode 0x20 - Send Indication command/response - Opcode 0x21 - Send Response command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + Connection ID (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x06 - Listen command/response + + Command parameters: Client Interface (4 octets) + Start (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x07 - Refresh command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x08 - Search Service command/response + + Command parameters: Connection ID (4 octets) + Filtered (1 octet) + Filter UUID (16 octets) + Response parameters: + + Filter UUID shall only be present when Filtered is non-zero. + + In case of an error, the error response will be returned. + + Opcode 0x09 - Get Included Service command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + Continuation (1 octet) + GATT Included Service ID (18 octets) + ... + Response parameters: + + GATT Included Service ID shall only be present when Continuation is non-zero. + + In case of an error, the error response will be returned. + + Opcode 0x0a - Get Characteristic command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + Continuation (1 octet) + GATT Characteristic ID (17 octets) + ... + Response parameters: + + GATT Characteristic ID shall only be present when Continuation is non-zero. + + In case of an error, the error response will be returned. + + Opcode 0x0b - Get Descriptor command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Continuation (1 octet) + GATT Descriptor ID (17 octets) + ... + Response parameters: + + GATT Descriptor ID shall only be present when Continuation is non-zero. + + In case of an error, the error response will be returned. + + Opcode 0x0c - Read Characteristic command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Authorization (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0d - Write Characteristic command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Write Type (4 octets) + Length (4 octets) + Authorization Req. (4 octets) + Value (variable) + Response parameters: + + Valid Write Type: 0x01 = No response + 0x02 = Default + 0x03 = Prepare + 0x04 = Signed + + In case of an error, the error response will be returned. + + Opcode 0x0e - Read Descriptor command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Descriptor ID (17 octets) + Authorization Req. (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0f - Write Descriptor command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Descriptor ID (17 octets) + Write Type (4 octets) + Length (4 octets) + Authorization Req. (4 octets) + Value (variable) + Response parameters: + + Valid Write Type: 0x01 = No response + 0x02 = Default + 0x03 = Prepare + 0x04 = Signed + + In case of an error, the error response will be returned. + + Opcode 0x10 - Execute Write command/response + + Command parameters: Connection ID (4 octets) + Execute (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x11 - Register For Notification command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x12 - Deregister For Notification command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x13 - Read Remote RSSI command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x14 - Get Device Type command/response + + Command parameters: Remote address (6 octets) + Response parameters: Device Type + + Valid Device Type: 0x01 = BREDR + 0x02 = BLE + 0x03 = DUAL + + In case of an error, the error response will be returned. + + Opcode 0x15 - Set Advertising data command/response + + Command parameters: Server Interface (4 octets) + Set Scan Resp. (1 octet) + Include Name (1 octet) + Include TX Power (1 octet) + Min. Interval (4 octets) + Max. Interval (4 octets) + Appearance (4 octets) + Manufacturer Len. (2 octets) + Manufacturer Data (variable) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x16 - Test Command command/response + + Command parameters: Command (4 octets) + Address (6 octets) + UUID (16 octets) + U1 (2 octets) + U2 (2 octets) + U3 (2 octets) + U4 (2 octets) + U5 (2 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x17 - Register Server command/response + + Command parameters: UUID (16 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x18 - Unregister Server command/response + + Command parameters: Server (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x19 - Connect Peripheral command/response + + Command parameters: Server (4 octets) + Remote address (6 octes) + Is Direct (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1a - Disconnect Peripheral command/response + + Command parameters: Server (4 octets) + Remote address (6 octes) + Connection ID (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1b - Add Service command/response + + Command parameters: Server (4 octets) + GATT Service ID (18 octets) + Number of Handles (4 octet) + Response parameters: + + Valid GATT Service ID: UUID (16 octets) + Instance ID (1 octet) + Is Primary (1 octet) + + In case of an error, the error response will be returned. + + Opcode 0x1c - Add Included Service command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + Included handle (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1d - Add Characteristic command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + UUID (16 octets) + Properties (4 octets) + Permissions (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1e - Add Descriptor command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + UUID (16 octets) + Permissions (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1f - Start Service command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + Transport (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x20 - Stop Service command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x21 - Delete Service command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x22 - Send Indication command/response + + Command parameters: Server (4 octets) + Attribute handle (4 octets) + Connection ID (4 octets) + Length (4 octets) + Confirmation (4 octets) + Value (variable) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x23 - Send Response command/response + + Command parameters: Connection ID (4 octets) + Transaction ID (4 octets) + Handle (2 octets) + Offset (2 octets) + Auth Request (1 octect) + Status (4 octets) + GATT Response (4 octets) + Response parameters: + + Valid GATT Response: GATT Value (607 octets) + Handle (2 octets) + + Valid GATT Value: Value (600 octets) + Handle (2 octets) + Offset (2 octets) + Length (2 octets) + Authentication Request (1 octet) + + In case of an error, the error response will be returned. + +Notifications: Opcode 0x81 - Register Client notification + + Notification parameters: Status (4 octets) + Client Interface (4 octets) + UUID (16 octets) + Opcode 0x82 - Scan Result notification + + Notification parameters: Address (6 octets) + RSSI (4 octets) + Length (2 octets) + Data (variable) + Opcode 0x83 - Connect Device notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + Client Interface (4 octets) + Address (6 octets) + Opcode 0x84 - Disconnect Device notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + Client Interface (4 octets) + Address (6 octets) + Opcode 0x85 - Search Complete notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + Opcode 0x86 - Search Result notification + + Notification parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + Opcode 0x87 - Get Characteristic notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Char Prop. (4 octets) + Opcode 0x88 - Get Descriptor notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Descriptor ID (17 octets) + Opcode 0x89 - Get Included Service notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Service ID (18 octets) + GATT Included Service ID (18 octets) + Opcode 0x8a - Register For Notification notification + + Notification parameters: Connection ID (4 octets) + Registered (4 octets) + Status (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Opcode 0x8b - Notify notification + + Notification parameters: Connection ID (4 octets) + Address (6 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Is Notify (1 octet) + Length (2 octets) + Value (variable) + Opcode 0x8c - Read Characteristic notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Read Parameters (variable) + + Valid GATT Read Parameters: GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Descriptor ID (17 octets) + Value Type (4 octets) + Status (1 octet) + Length (2 octets) + Value (variable) + Opcode 0x8d - Write Characteristic notification - Opcode 0x8e - Execute Write notification - Opcode 0x8f - Read Descriptor notification - Opcode 0x90 - Write Descriptor notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Write Parameters (53 octets) + + Valid GATT Write Parameters: GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Description ID (17 octets) + Status (1 octet) + + Opcode 0x8e - Read Descriptor notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Read Parameters (variable) + + Valid GATT Read Parameters: As described in Read Characteristic + + Opcode 0x8f - Write Descriptor notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Write Parameters (53 octets) + + Valid GATT Write Parameters: As described in Write Characteristic + + Opcode 0x90 - Execute Write notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + Opcode 0x91 - Read Remote RSSI notification - Opcode 0x92 - Register Server notification - Opcode 0x93 - Connection notification - Opcode 0x94 - Service Added notification - Opcode 0x95 - Included Service Added notification - Opcode 0x96 - Characteristic Added notification - Opcode 0x97 - Descriptor Added notification - Opcode 0x98 - Service Started notification - Opcode 0x99 - Service Stopped notification - Opcode 0x9a - Service Deleted notification - Opcode 0x9b - Request Read notification - Opcode 0x9c - Request Write notification - Opcode 0x9d - Request Execute Write notification - Opcode 0x9e - Response Confirmation notification + + Notification parameters: Client (4 octets) + Address (6 octets) + RSSI (4 octets) + Status (4 octets) + + Opcode 0x92 - Listen notification + + Notification parameters: Status (4 octets) + Server Interface (4 octets) + + Opcode 0x93 - Register Server notification + + Notification parameters: Status (4 octets) + Server (4 octets) + UUID (16 octets) + + Opcode 0x94 - Connection notification + + Notification parameters: Connection ID (4 octets) + Server (4 octets) + Connected (4 octets) + Address (6 octets) + + Opcode 0x95 - Service Added notification + + Notification parameters: Status (4 octets) + Server (4 octets) + GATT Service ID (18 octets) + Service Handle (4 octets) + + Opcode 0x96 - Included Service Added notification + + Notification patemeters: Status (4 octets) + Server (4 octets) + Service Handle (4 octets) + Included Service Handle (4 octets) + + Opcode 0x97 - Characteristic Added notification + + Notification parameters: Status (4 octets) + Server (4 octets) + UUID (16 octets) + Service Handle (4 octets) + Characteristic Handle (4 octets) + + Opcode 0x98 - Descriptor Added notification + + Notification parameters: Status (4 octets) + Server (4 octets) + UUID (6 octets) + Service Handle (4 octets) + Descriptor Handle (4 octets) + + Opcode 0x99 - Service Started notification + + Notification parameters: Status (4 octets) + Server (4 octets) + Service Handle (4 octets) + + Opcode 0x9a - Service Stopped notification + + Notification parameters: Status (4 octets) + Server (4 octets) + Service Handle (4 octets) + + Opcode 0x9b - Service Deleted notification + + Notification parameters: Status (4 octets) + Server (4 octets) + Service Handle (4 octets) + + Opcode 0x9c - Request Read notification + + Notification parameters: Connection ID (4 octets) + Trans ID (4 octets) + Address (6 octets) + Attribute Handle (4 octets) + Offset (4 octets) + Is Long (1 octet) + + Opcode 0x9d - Request Write notification + + Notification parameters: Connection ID (4 octets) + Trans ID (4 octets) + Address (6 octets) + Attribute Handle (4 octets) + Offset (4 octets) + Length (4 octets) + Need Response (4 octets) + Is Prepare (1 octet) + Value (variable) + + Opcode 0x9e - Request Execute Write notification + + Notification parameters: Connection ID (4 octets) + Trans ID (4 octets) + Address (6 octets) + Execute Write (4 octets) + + Opcode 0x9f - Response Confirmation notification + + Notification parameters: Status (4 octets) + Handle (4 octets) diff --git a/android/hal-ipc.c b/android/hal-ipc.c index b19704a..8f5babe 100644 --- a/android/hal-ipc.c +++ b/android/hal-ipc.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -31,10 +30,10 @@ #include "hal.h" #include "hal-msg.h" #include "hal-log.h" +#include "ipc-common.h" #include "hal-ipc.h" -#define CONNECT_TIMEOUT (5 * 1000) -#define SERVICE_NAME "bluetoothd" +#define CONNECT_TIMEOUT (10 * 1000) static int cmd_sk = -1; static int notif_sk = -1; @@ -65,7 +64,7 @@ void hal_ipc_unregister(uint8_t service) static void handle_msg(void *buf, ssize_t len) { - struct hal_hdr *msg = buf; + struct ipc_hdr *msg = buf; const struct hal_ipc_handler *handler; uint8_t opcode; @@ -100,8 +99,10 @@ static void handle_msg(void *buf, ssize_t len) exit(EXIT_FAILURE); } - /* opcode is used as table offset and must be adjusted as events start - * with HAL_MINIMUM_EVENT offset */ + /* + * opcode is used as table offset and must be adjusted as events start + * with HAL_MINIMUM_EVENT offset + */ opcode = msg->opcode - HAL_MINIMUM_EVENT; /* if opcode is valid */ @@ -131,7 +132,7 @@ static void *notification_handler(void *data) struct iovec iv; struct cmsghdr *cmsg; char cmsgbuf[CMSG_SPACE(sizeof(int))]; - char buf[BLUEZ_HAL_MTU]; + char buf[IPC_MTU]; ssize_t ret; int fd; @@ -160,8 +161,12 @@ static void *notification_handler(void *data) /* socket was shutdown */ if (ret == 0) { - if (cmd_sk == -1) + pthread_mutex_lock(&cmd_sk_mutex); + if (cmd_sk == -1) { + pthread_mutex_unlock(&cmd_sk_mutex); break; + } + pthread_mutex_unlock(&cmd_sk_mutex); error("Notification socket closed, aborting"); exit(EXIT_FAILURE); @@ -259,8 +264,8 @@ bool hal_ipc_init(void) } /* Start Android Bluetooth daemon service */ - if (property_set("ctl.start", SERVICE_NAME) < 0) { - error("Failed to start service %s", SERVICE_NAME); + if (property_set("bluetooth.start", "daemon") < 0) { + error("Failed to set bluetooth.start=daemon"); close(sk); return false; } @@ -284,10 +289,10 @@ bool hal_ipc_init(void) close(sk); err = pthread_create(¬if_th, NULL, notification_handler, NULL); - if (err < 0) { + if (err) { notif_th = 0; - error("Failed to start notification thread: %d (%s)", -err, - strerror(-err)); + error("Failed to start notification thread: %d (%s)", err, + strerror(err)); close(cmd_sk); cmd_sk = -1; close(notif_sk); @@ -300,8 +305,10 @@ bool hal_ipc_init(void) void hal_ipc_cleanup(void) { + pthread_mutex_lock(&cmd_sk_mutex); close(cmd_sk); cmd_sk = -1; + pthread_mutex_unlock(&cmd_sk_mutex); shutdown(notif_sk, SHUT_RD); @@ -315,9 +322,9 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, ssize_t ret; struct msghdr msg; struct iovec iv[2]; - struct hal_hdr cmd; + struct ipc_hdr cmd; char cmsgbuf[CMSG_SPACE(sizeof(int))]; - struct hal_status s; + struct ipc_status s; size_t s_len = sizeof(s); if (cmd_sk < 0) { @@ -396,7 +403,7 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, } if (cmd.service_id != service_id) { - error("Invalid service id (%u vs %u), aborting", + error("Invalid service id (0x%x vs 0x%x), aborting", cmd.service_id, service_id); exit(EXIT_FAILURE); } @@ -407,13 +414,13 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, } if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) { - error("Invalid opcode received (%u vs %u), aborting", + error("Invalid opcode received (0x%x vs 0x%x), aborting", cmd.opcode, opcode); exit(EXIT_FAILURE); } if (cmd.opcode == HAL_OP_STATUS) { - struct hal_status *s = rsp; + struct ipc_status *s = rsp; if (sizeof(*s) != cmd.len) { error("Invalid status length, aborting"); diff --git a/android/hal-log.h b/android/hal-log.h index 9bd024d..63ff61b 100644 --- a/android/hal-log.h +++ b/android/hal-log.h @@ -25,7 +25,7 @@ #define LOG_WARN " W" #define LOG_ERROR " E" #define LOG_DEBUG " D" -#define ALOG(pri, tag, fmt, arg...) printf(tag pri": " fmt"\n", ##arg) +#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg) #endif #define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg) diff --git a/android/hal-msg.h b/android/hal-msg.h index 80c4a25..09bd9a0 100644 --- a/android/hal-msg.h +++ b/android/hal-msg.h @@ -21,22 +21,13 @@ * */ -#define BLUEZ_HAL_MTU 1024 - static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket"; -struct hal_hdr { - uint8_t service_id; - uint8_t opcode; - uint16_t len; - uint8_t payload[0]; -} __attribute__((packed)); - #define HAL_MINIMUM_EVENT 0x81 #define HAL_SERVICE_ID_CORE 0 #define HAL_SERVICE_ID_BLUETOOTH 1 -#define HAL_SERVICE_ID_SOCK 2 +#define HAL_SERVICE_ID_SOCKET 2 #define HAL_SERVICE_ID_HIDHOST 3 #define HAL_SERVICE_ID_PAN 4 #define HAL_SERVICE_ID_HANDSFREE 5 @@ -49,7 +40,7 @@ struct hal_hdr { /* Core Service */ -#define HAL_STATUS_SUCCESS 0x00 +#define HAL_STATUS_SUCCESS IPC_STATUS_SUCCESS #define HAL_STATUS_FAILED 0x01 #define HAL_STATUS_NOT_READY 0x02 #define HAL_STATUS_NOMEM 0x03 @@ -61,14 +52,16 @@ struct hal_hdr { #define HAL_STATUS_AUTH_FAILURE 0x09 #define HAL_STATUS_REMOTE_DEVICE_DOWN 0x0a -#define HAL_OP_STATUS 0x00 -struct hal_status { - uint8_t code; -} __attribute__((packed)); +#define HAL_OP_STATUS IPC_OP_STATUS + +#define HAL_MODE_DEFAULT 0x00 +#define HAL_MODE_BREDR 0x01 +#define HAL_MODE_LE 0x02 #define HAL_OP_REGISTER_MODULE 0x01 struct hal_cmd_register_module { uint8_t service_id; + uint8_t mode; } __attribute__((packed)); #define HAL_OP_UNREGISTER_MODULE 0x02 @@ -107,15 +100,32 @@ struct hal_cmd_get_adapter_prop { #define HAL_PROP_DEVICE_CLASS 0x04 #define HAL_PROP_DEVICE_TYPE 0x05 #define HAL_PROP_DEVICE_SERVICE_REC 0x06 +struct hal_prop_device_service_rec { + uint8_t uuid[16]; + uint16_t channel; + uint8_t name_len; + uint8_t name[]; +} __attribute__((packed)); + #define HAL_PROP_DEVICE_FRIENDLY_NAME 0x0a #define HAL_PROP_DEVICE_RSSI 0x0b #define HAL_PROP_DEVICE_VERSION_INFO 0x0c +struct hal_prop_device_info { + uint8_t version; + uint16_t sub_version; + uint16_t manufacturer; +} __attribute__((packed)); + #define HAL_PROP_DEVICE_TIMESTAMP 0xFF #define HAL_ADAPTER_SCAN_MODE_NONE 0x00 #define HAL_ADAPTER_SCAN_MODE_CONN 0x01 #define HAL_ADAPTER_SCAN_MODE_CONN_DISC 0x02 +#define HAL_TYPE_BREDR 0x01 +#define HAL_TYPE_LE 0x02 +#define HAL_TYPE_DUAL 0x03 + #define HAL_OP_SET_ADAPTER_PROP 0x05 struct hal_cmd_set_adapter_prop { uint8_t type; @@ -214,22 +224,29 @@ struct hal_cmd_le_test_mode { /* Bluetooth Socket HAL api */ -#define HAL_OP_SOCK_LISTEN 0x01 -struct hal_cmd_sock_listen { - uint8_t type; - uint8_t name[256]; - uint8_t uuid[16]; - uint16_t channel; - uint8_t flags; +#define HAL_SOCK_RFCOMM 0x01 +#define HAL_SOCK_SCO 0x02 +#define HAL_SOCK_L2CAP 0x03 + +#define HAL_SOCK_FLAG_ENCRYPT 0x01 +#define HAL_SOCK_FLAG_AUTH 0x02 + +#define HAL_OP_SOCKET_LISTEN 0x01 +struct hal_cmd_socket_listen { + uint8_t type; + uint8_t name[256]; + uint8_t uuid[16]; + int32_t channel; + uint8_t flags; } __attribute__((packed)); -#define HAL_OP_SOCK_CONNECT 0x02 -struct hal_cmd_sock_connect { - uint8_t bdaddr[6]; - uint8_t type; - uint8_t uuid[16]; - uint16_t channel; - uint8_t flags; +#define HAL_OP_SOCKET_CONNECT 0x02 +struct hal_cmd_socket_connect { + uint8_t bdaddr[6]; + uint8_t type; + uint8_t uuid[16]; + int32_t channel; + uint8_t flags; } __attribute__((packed)); /* Bluetooth HID Host HAL API */ @@ -368,8 +385,477 @@ struct hal_cmd_pan_disconnect { uint8_t bdaddr[6]; } __attribute__((packed)); -/* Notifications and confirmations */ +#define HAL_HEALTH_MDEP_ROLE_SOURCE 0x00 +#define HAL_HEALTH_MDEP_ROLE_SINK 0x01 + +#define HAL_HEALTH_CHANNEL_TYPE_RELIABLE 0x00 +#define HAL_HEALTH_CHANNEL_TYPE_STREAMING 0x01 +#define HAL_HEALTH_CHANNEL_TYPE_ANY 0x02 + +#define HAL_OP_HEALTH_REG_APP 0x01 +struct hal_cmd_health_reg_app { + uint8_t num_of_mdep; + uint16_t app_name_off; + uint16_t provider_name_off; + uint16_t service_name_off; + uint16_t service_descr_off; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_OP_HEALTH_MDEP 0x02 +struct hal_cmd_health_mdep { + uint8_t role; + uint8_t data_type; + uint8_t channel_type; + uint16_t descr_len; + uint8_t descr[0]; +} __attribute__((packed)); + +struct hal_rsp_health_reg_app { + uint16_t app_id; +} __attribute__((packed)); + +#define HAL_OP_HEALTH_UNREG_APP 0x03 +struct hal_cmd_health_unreg_app { + uint16_t app_id; +} __attribute__((packed)); + +#define HAL_OP_HEALTH_CONNECT_CHANNEL 0x04 +struct hal_cmd_health_connect_channel { + uint16_t app_id; + uint8_t bdaddr[6]; + uint8_t mdep_index; +} __attribute__((packed)); + +struct hal_rsp_health_connect_channel { + uint16_t channel_id; +} __attribute__((packed)); + +#define HAL_OP_HEALTH_DESTROY_CHANNEL 0x05 +struct hal_cmd_health_destroy_channel { + uint16_t channel_id; +} __attribute__((packed)); + +/* Handsfree HAL API */ + +#define HAL_MODE_HANDSFREE_HSP_ONLY HAL_MODE_DEFAULT +#define HAL_MODE_HANDSFREE_HFP 0x01 +#define HAL_MODE_HANDSFREE_HFP_WBS 0x02 + +#define HAL_OP_HANDSFREE_CONNECT 0x01 +struct hal_cmd_handsfree_connect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_DISCONNECT 0x02 +struct hal_cmd_handsfree_disconnect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_CONNECT_AUDIO 0x03 +struct hal_cmd_handsfree_connect_audio { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_DISCONNECT_AUDIO 0x04 +struct hal_cmd_handsfree_disconnect_audio { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_START_VR 0x05 + +#define HAL_OP_HANDSFREE_STOP_VR 0x06 + +#define HAL_HANDSFREE_VOLUME_TYPE_SPEAKER 0x00 +#define HAL_HANDSFREE_VOLUME_TYPE_MIC 0x01 + +#define HAL_OP_HANDSFREE_VOLUME_CONTROL 0x07 +struct hal_cmd_handsfree_volume_control { + uint8_t type; + uint8_t volume; +} __attribute__((packed)); + +#define HAL_HANDSFREE_NETWORK_STATE_NOT_AVAILABLE 0x00 +#define HAL_HANDSFREE_NETWORK_STATE_AVAILABLE 0x01 + +#define HAL_HANDSFREE_SERVICE_TYPE_HOME 0x00 +#define HAL_HANDSFREE_SERVICE_TYPE_ROAMING 0x01 + +#define HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF 0x08 +struct hal_cmd_handsfree_device_status_notif { + uint8_t state; + uint8_t type; + uint8_t signal; + uint8_t battery; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_COPS_RESPONSE 0x09 +struct hal_cmd_handsfree_cops_response { + uint16_t len; + uint8_t buf[0]; +} __attribute__((packed)); + +#define HAL_HANDSFREE_CALL_STATE_ACTIVE 0x00 +#define HAL_HANDSFREE_CALL_STATE_HELD 0x01 +#define HAL_HANDSFREE_CALL_STATE_DIALING 0x02 +#define HAL_HANDSFREE_CALL_STATE_ALERTING 0x03 +#define HAL_HANDSFREE_CALL_STATE_INCOMING 0x04 +#define HAL_HANDSFREE_CALL_STATE_WAITING 0x05 +#define HAL_HANDSFREE_CALL_STATE_IDLE 0x06 + +#define HAL_OP_HANDSFREE_CIND_RESPONSE 0x0A +struct hal_cmd_handsfree_cind_response { + uint8_t svc; + uint8_t num_active; + uint8_t num_held; + uint8_t state; + uint8_t signal; + uint8_t roam; + uint8_t batt_chg; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE 0x0B +struct hal_cmd_handsfree_formatted_at_response { + uint16_t len; + uint8_t buf[0]; +} __attribute__((packed)); + +#define HAL_HANDSFREE_AT_RESPONSE_ERROR 0x00 +#define HAL_HANDSFREE_AT_RESPONSE_OK 0x01 + +#define HAL_OP_HANDSFREE_AT_RESPONSE 0x0C +struct hal_cmd_handsfree_at_response { + uint8_t response; + uint8_t error; +} __attribute__((packed)); + +#define HAL_HANDSFREE_CALL_DIRECTION_OUTGOING 0x00 +#define HAL_HANDSFREE_CALL_DIRECTION_INCOMING 0x01 + +#define HAL_HANDSFREE_CALL_TYPE_VOICE 0x00 +#define HAL_HANDSFREE_CALL_TYPE_DATA 0x01 +#define HAL_HANDSFREE_CALL_TYPE_FAX 0x02 + +#define HAL_HANDSFREE_CALL_MPTY_TYPE_SINGLE 0x00 +#define HAL_HANDSFREE_CALL_MPTY_TYPE_MULTI 0x01 + +#define HAL_HANDSFREE_CALL_ADDRTYPE_UNKNOWN 0x81 +#define HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL 0x91 + +#define HAL_OP_HANDSFREE_CLCC_RESPONSE 0x0D +struct hal_cmd_handsfree_clcc_response { + uint8_t index; + uint8_t dir; + uint8_t state; + uint8_t mode; + uint8_t mpty; + uint8_t type; + uint16_t number_len; + uint8_t number[0]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_PHONE_STATE_CHANGE 0x0E +struct hal_cmd_handsfree_phone_state_change { + uint8_t num_active; + uint8_t num_held; + uint8_t state; + uint8_t type; + uint16_t number_len; + uint8_t number[0]; +} __attribute__((packed)); + +/* GATT HAL API */ + +#define HAL_OP_GATT_CLIENT_REGISTER 0x01 +struct hal_cmd_gatt_client_register { + uint8_t uuid[16]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_UNREGISTER 0x02 +struct hal_cmd_gatt_client_unregister { + int32_t client_if; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_SCAN 0x03 +struct hal_cmd_gatt_client_scan { + int32_t client_if; + uint8_t start; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_CONNECT 0x04 +struct hal_cmd_gatt_client_connect { + int32_t client_if; + uint8_t bdaddr[6]; + uint8_t is_direct; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_DISCONNECT 0x05 +struct hal_cmd_gatt_client_disconnect { + int32_t client_if; + uint8_t bdaddr[6]; + int32_t conn_id; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_LISTEN 0x06 +struct hal_cmd_gatt_client_listen { + int32_t client_if; + uint8_t start; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_REFRESH 0x07 +struct hal_cmd_gatt_client_refresh { + int32_t client_if; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_SEARCH_SERVICE 0x08 +struct hal_cmd_gatt_client_search_service { + int32_t conn_id; + uint8_t filtered; + uint8_t filter_uuid[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE 0x09 +struct hal_gatt_srvc_id { + uint8_t uuid[16]; + uint8_t inst_id; + uint8_t is_primary; +} __attribute__((packed)); + +struct hal_cmd_gatt_client_get_included_service { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + uint8_t continuation; + struct hal_gatt_srvc_id incl_srvc_id[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC 0x0a +struct hal_gatt_gatt_id { + uint8_t uuid[16]; + uint8_t inst_id; +} __attribute__((packed)); + +struct hal_cmd_gatt_client_get_characteristic { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + uint8_t continuation; + struct hal_gatt_gatt_id char_id[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_GET_DESCRIPTOR 0x0b +struct hal_cmd_gatt_client_get_descriptor { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + uint8_t continuation; + struct hal_gatt_gatt_id descr_id[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC 0x0c +struct hal_cmd_gatt_client_read_characteristic { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + int32_t auth_req; +} __attribute__((packed)); + +#define GATT_WRITE_TYPE_NO_RESPONSE 0x01 +#define GATT_WRITE_TYPE_DEFAULT 0x02 +#define GATT_WRITE_TYPE_PREPARE 0x03 +#define GATT_WRITE_TYPE_SIGNED 0x04 + +#define HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC 0x0d +struct hal_cmd_gatt_client_write_characteristic { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + int32_t write_type; + int32_t len; + int32_t auth_req; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_READ_DESCRIPTOR 0x0e +struct hal_cmd_gatt_client_read_descriptor { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; + int32_t auth_req; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR 0x0f +struct hal_cmd_gatt_client_write_descriptor { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; + int32_t write_type; + int32_t len; + int32_t auth_req; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_EXECUTE_WRITE 0x10 +struct hal_cmd_gatt_client_execute_write { + int32_t conn_id; + int32_t execute; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION 0x11 +struct hal_cmd_gatt_client_register_for_notification { + int32_t client_if; + uint8_t bdaddr[6]; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION 0x12 +struct hal_cmd_gatt_client_deregister_for_notification { + int32_t client_if; + uint8_t bdaddr[6]; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI 0x13 +struct hal_cmd_gatt_client_read_remote_rssi { + int32_t client_if; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE 0x14 +struct hal_cmd_gatt_client_get_device_type { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +struct hal_rsp_gatt_client_get_device_type { + uint8_t type; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_SET_ADV_DATA 0x015 +struct hal_cmd_gatt_client_set_adv_data { + int32_t server_if; + uint8_t set_scan_rsp; + uint8_t include_name; + uint8_t include_txpower; + int32_t min_interval; + int32_t max_interval; + int32_t appearance; + uint16_t manufacturer_len; + uint8_t manufacturer_data[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_TEST_COMMAND 0x16 +struct hal_cmd_gatt_client_test_command { + int32_t command; + uint8_t bda1[6]; + uint8_t uuid1[16]; + uint16_t u1; + uint16_t u2; + uint16_t u3; + uint16_t u4; + uint16_t u5; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_REGISTER 0x17 +struct hal_cmd_gatt_server_register { + uint8_t uuid[16]; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_UNREGISTER 0x18 +struct hal_cmd_gatt_server_unregister { + int32_t server_if; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_CONNECT 0x19 +struct hal_cmd_gatt_server_connect { + int32_t server_if; + uint8_t bdaddr[6]; + uint8_t is_direct; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_DISCONNECT 0x1a +struct hal_cmd_gatt_server_disconnect { + int32_t server_if; + uint8_t bdaddr[6]; + int32_t conn_id; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_ADD_SERVICE 0x1b +struct hal_cmd_gatt_server_add_service { + int32_t server_if; + struct hal_gatt_srvc_id srvc_id; + int32_t num_handles; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_ADD_INC_SERVICE 0x1c +struct hal_cmd_gatt_server_add_inc_service { + int32_t server_if; + int32_t service_handle; + int32_t included_handle; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC 0x1d +struct hal_cmd_gatt_server_add_characteristic { + int32_t server_if; + int32_t service_handle; + uint8_t uuid[16]; + int32_t properties; + int32_t permissions; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_ADD_DESCRIPTOR 0x1e +struct hal_cmd_gatt_server_add_descriptor { + int32_t server_if; + int32_t service_handle; + uint8_t uuid[16]; + int32_t permissions; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_START_SERVICE 0x1f +struct hal_cmd_gatt_server_start_service { + int32_t server_if; + int32_t service_handle; + int32_t transport; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_STOP_SERVICE 0x20 +struct hal_cmd_gatt_server_stop_service { + int32_t server_if; + int32_t service_handle; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_DELETE_SERVICE 0x21 +struct hal_cmd_gatt_server_delete_service { + int32_t server_if; + int32_t service_handle; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_SEND_INDICATION 0x22 +struct hal_cmd_gatt_server_send_indication { + int32_t server_if; + int32_t attribute_handle; + int32_t conn_id; + int32_t len; + int32_t confirm; + uint8_t value[0]; +} __attribute__((packed)); +#define HAL_OP_GATT_SERVER_SEND_RESPONSE 0x23 +struct hal_cmd_gatt_server_send_response { + int32_t conn_id; + int32_t trans_id; + uint16_t handle; + uint16_t offset; + uint8_t auth_req; + int32_t status; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +/* Notifications and confirmations */ #define HAL_POWER_OFF 0x00 #define HAL_POWER_ON 0x01 @@ -501,7 +987,14 @@ struct hal_ev_hidhost_proto_mode { uint8_t mode; } __attribute__((packed)); -#define HAL_EV_HIDHOST_GET_REPORT 0x84 +#define HAL_EV_HIDHOST_IDLE_TIME 0x84 +struct hal_ev_hidhost_idle_time { + uint8_t bdaddr[6]; + uint8_t status; + uint32_t idle_rate; +} __attribute__((packed)); + +#define HAL_EV_HIDHOST_GET_REPORT 0x85 struct hal_ev_hidhost_get_report { uint8_t bdaddr[6]; uint8_t status; @@ -509,7 +1002,7 @@ struct hal_ev_hidhost_get_report { uint8_t data[0]; } __attribute__((packed)); -#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG 0x85 +#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG 0x86 struct hal_ev_hidhost_virtual_unplug { uint8_t bdaddr[6]; uint8_t status; @@ -532,6 +1025,32 @@ struct hal_ev_pan_conn_state { uint8_t remote_role; } __attribute__((packed)); +#define HAL_HEALTH_APP_REG_SUCCESS 0x00 +#define HAL_HEALTH_APP_REG_FAILED 0x01 +#define HAL_HEALTH_APP_DEREG_SUCCESS 0x02 +#define HAL_HEALTH_APP_DEREG_FAILED 0x03 + +#define HAL_HEALTH_CHANNEL_CONNECTING 0x00 +#define HAL_HEALTH_CHANNEL_CONNECTED 0x01 +#define HAL_HEALTH_CHANNEL_DISCONNECTING 0x02 +#define HAL_HEALTH_CHANNEL_DISCONNECTED 0x03 +#define HAL_HEALTH_CHANNEL_DESTROYED 0x04 + +#define HAL_EV_HEALTH_APP_REG_STATE 0x81 +struct hal_ev_health_app_reg_state { + uint16_t id; + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_HEALTH_CHANNEL_STATE 0x82 +struct hal_ev_health_channel_state { + uint16_t app_id; + uint8_t bdaddr[6]; + uint8_t mdep_index; + uint16_t channel_id; + uint8_t channel_state; +} __attribute__((packed)); + #define HAL_A2DP_STATE_DISCONNECTED 0x00 #define HAL_A2DP_STATE_CONNECTING 0x01 #define HAL_A2DP_STATE_CONNECTED 0x02 @@ -543,8 +1062,531 @@ struct hal_ev_a2dp_conn_state { uint8_t bdaddr[6]; } __attribute__((packed)); +#define HAL_AUDIO_SUSPEND 0x00 +#define HAL_AUDIO_STOPPED 0x01 +#define HAL_AUDIO_STARTED 0x02 + #define HAL_EV_A2DP_AUDIO_STATE 0x82 struct hal_ev_a2dp_audio_state { uint8_t state; uint8_t bdaddr[6]; } __attribute__((packed)); + +#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED 0x00 +#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTING 0x01 +#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTED 0x02 +#define HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED 0x03 +#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING 0x04 + +#define HAL_EV_HANDSFREE_CONN_STATE 0x81 +struct hal_ev_handsfree_conn_state { + uint8_t state; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED 0x00 +#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING 0x01 +#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED 0x02 +#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING 0x03 + +#define HAL_EV_HANDSFREE_AUDIO_STATE 0x82 +struct hal_ev_handsfree_audio_state { + uint8_t state; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_HANDSFREE_VR_STOPPED 0x00 +#define HAL_HANDSFREE_VR_STARTED 0x01 + +#define HAL_EV_HANDSFREE_VR 0x83 +struct hal_ev_handsfree_vr_state { + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_ANSWER 0x84 + +#define HAL_EV_HANDSFREE_HANGUP 0x85 + +#define HAL_EV_HANDSFREE_VOLUME 0x86 +struct hal_ev_handsfree_volume { + uint8_t type; + uint8_t volume; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_DIAL 0x87 +struct hal_ev_handsfree_dial { + uint16_t number_len; + uint8_t number[0]; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_DTMF 0x88 +struct hal_ev_handsfree_dtmf { + uint8_t tone; +} __attribute__((packed)); + +#define HAL_HANDSFREE_NREC_STOP 0x00 +#define HAL_HANDSFREE_NREC_START 0x01 + +#define HAL_EV_HANDSFREE_NREC 0x89 +struct hal_ev_handsfree_nrec { + uint8_t nrec; +} __attribute__((packed)); + +#define HAL_HANDSFREE_CHLD_TYPE_RELEASEHELD 0x00 +#define HAL_HANDSFREE_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD 0x01 +#define HAL_HANDSFREE_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD 0x02 +#define HAL_HANDSFREE_CHLD_TYPE_ADDHELDTOCONF 0x03 + +#define HAL_EV_HANDSFREE_CHLD 0x8A +struct hal_ev_handsfree_chld { + uint8_t chld; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_CNUM 0x8B + +#define HAL_EV_HANDSFREE_CIND 0x8C + +#define HAL_EV_HANDSFREE_COPS 0x8D + +#define HAL_EV_HANDSFREE_CLCC 0x8E + +#define HAL_EV_HANDSFREE_UNKNOWN_AT 0x8F +struct hal_ev_handsfree_unknown_at { + uint16_t len; + uint8_t buf[0]; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_HSP_KEY_PRESS 0x90 + +/* AVRCP HAL API */ + +#define HAL_AVRCP_PLAY_STATUS_STOPPED 0x00 +#define HAL_AVRCP_PLAY_STATUS_PLAYING 0x01 +#define HAL_AVRCP_PLAY_STATUS_PAUSED 0x02 +#define HAL_AVRCP_PLAY_STATUS_FWD_SEEK 0x03 +#define HAL_AVRCP_PLAY_STATUS_REV_SEEK 0x04 +#define HAL_AVRCP_PLAY_STATUS_ERROR 0xff + +#define HAL_OP_AVRCP_GET_PLAY_STATUS 0x01 +struct hal_cmd_avrcp_get_play_status { + uint8_t status; + uint32_t duration; + uint32_t position; +} __attribute__((packed)); + +#define HAL_AVRCP_PLAYER_ATTR_EQUALIZER 0x01 +#define HAL_AVRCP_PLAYER_ATTR_REPEAT 0x02 +#define HAL_AVRCP_PLAYER_ATTR_SHUFFLE 0x03 +#define HAL_AVRCP_PLAYER_ATTR_SCAN 0x04 + +#define HAL_OP_AVRCP_LIST_PLAYER_ATTRS 0x02 +struct hal_cmd_avrcp_list_player_attrs { + uint8_t number; + uint8_t attrs[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_LIST_PLAYER_VALUES 0x03 +struct hal_cmd_avrcp_list_player_values { + uint8_t number; + uint8_t values[0]; +} __attribute__((packed)); + +struct hal_avrcp_player_attr_value { + uint8_t attr; + uint8_t value; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_GET_PLAYER_ATTRS 0x04 +struct hal_cmd_avrcp_get_player_attrs { + uint8_t number; + struct hal_avrcp_player_attr_value attrs[0]; +} __attribute__((packed)); + +struct hal_avrcp_player_setting_text { + uint8_t id; + uint8_t len; + uint8_t text[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT 0x05 +struct hal_cmd_avrcp_get_player_attrs_text { + uint8_t number; + struct hal_avrcp_player_setting_text attrs[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT 0x06 +struct hal_cmd_avrcp_get_player_values_text { + uint8_t number; + struct hal_avrcp_player_setting_text values[0]; +} __attribute__((packed)); + +#define HAL_AVRCP_MEDIA_ATTR_TITLE 0x01 +#define HAL_AVRCP_MEDIA_ATTR_ARTIST 0x02 +#define HAL_AVRCP_MEDIA_ATTR_ALBUM 0x03 +#define HAL_AVRCP_MEDIA_ATTR_TRACK_NUM 0x04 +#define HAL_AVRCP_MEDIA_ATTR_NUM_TRACKS 0x05 +#define HAL_AVRCP_MEDIA_ATTR_GENRE 0x06 +#define HAL_AVRCP_MEDIA_ATTR_DURATION 0x07 + +#define HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT 0x07 +struct hal_cmd_avrcp_get_element_attrs_text { + uint8_t number; + struct hal_avrcp_player_setting_text values[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE 0x08 +struct hal_cmd_avrcp_set_player_attrs_value { + uint8_t status; +} __attribute__((packed)); + +#define HAL_AVRCP_EVENT_STATUS_CHANGED 0x01 +#define HAL_AVRCP_EVENT_TRACK_CHANGED 0x02 +#define HAL_AVRCP_EVENT_TRACK_REACHED_END 0x03 +#define HAL_AVRCP_EVENT_TRACK_REACHED_START 0x04 +#define HAL_AVRCP_EVENT_POSITION_CHANGED 0x05 +#define HAL_AVRCP_EVENT_SETTING_CHANGED 0x08 + +#define HAL_AVRCP_EVENT_TYPE_INTERIM 0x00 +#define HAL_AVRCP_EVENT_TYPE_CHANGED 0x01 + +#define HAL_OP_AVRCP_REGISTER_NOTIFICATION 0x09 +struct hal_cmd_avrcp_register_notification { + uint8_t event; + uint8_t type; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_SET_VOLUME 0x0a +struct hal_cmd_avrcp_set_volume { + uint8_t value; +} __attribute__((packed)); + +#define HAL_AVRCP_FEATURE_NONE 0x00 +#define HAL_AVRCP_FEATURE_METADATA 0x01 +#define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME 0x02 +#define HAL_AVRCP_FEATURE_BROWSE 0x04 + +#define HAL_EV_AVRCP_REMOTE_FEATURES 0x81 +struct hal_ev_avrcp_remote_features { + uint8_t bdaddr[6]; + uint8_t features; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_PLAY_STATUS 0x82 +#define HAL_EV_AVRCP_LIST_PLAYER_ATTRS 0x83 + +#define HAL_EV_AVRCP_LIST_PLAYER_VALUES 0x84 +struct hal_ev_avrcp_list_player_values { + uint8_t attr; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_PLAYER_VALUES 0x85 +struct hal_ev_avrcp_get_player_values { + uint8_t number; + uint8_t attrs[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT 0x86 +struct hal_ev_avrcp_get_player_attrs_text { + uint8_t number; + uint8_t attrs[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT 0x87 +struct hal_ev_avrcp_get_player_values_text { + uint8_t attr; + uint8_t number; + uint8_t values[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_SET_PLAYER_VALUES 0x88 +struct hal_ev_avrcp_set_player_values { + uint8_t number; + struct hal_avrcp_player_attr_value attrs[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_ELEMENT_ATTRS 0x89 +struct hal_ev_avrcp_get_element_attrs { + uint8_t number; + uint8_t attrs[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_REGISTER_NOTIFICATION 0x8a +struct hal_ev_avrcp_register_notification { + uint8_t event; + uint32_t param; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_VOLUME_CHANGED 0x8b +struct hal_ev_avrcp_volume_changed { + uint8_t volume; + uint8_t type; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_PASSTHROUGH_CMD 0x8c +struct hal_ev_avrcp_passthrough_cmd { + uint8_t id; + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_REGISTER_CLIENT 0x81 +struct hal_ev_gatt_client_register_client { + int32_t status; + int32_t client_if; + uint8_t app_uuid[16]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_SCAN_RESULT 0x82 +struct hal_ev_gatt_client_scan_result { + uint8_t bda[6]; + int32_t rssi; + uint16_t len; + uint8_t adv_data[0]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_CONNECT 0x83 +struct hal_ev_gatt_client_connect { + int32_t conn_id; + int32_t status; + int32_t client_if; + uint8_t bda[6]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_DISCONNECT 0x84 +struct hal_ev_gatt_client_disconnect { + int32_t conn_id; + int32_t status; + int32_t client_if; + uint8_t bda[6]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_SEARCH_COMPLETE 0x85 +struct hal_ev_gatt_client_search_complete { + int32_t conn_id; + int32_t status; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_SEARCH_RESULT 0x86 +struct hal_ev_gatt_client_search_result { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC 0x87 +struct hal_ev_gatt_client_get_characteristic { + int32_t conn_id; + int32_t status; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + int32_t char_prop; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_GET_DESCRIPTOR 0x88 +struct hal_ev_gatt_client_get_descriptor { + int32_t conn_id; + int32_t status; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_GET_INC_SERVICE 0X89 +struct hal_ev_gatt_client_get_inc_service { + int32_t conn_id; + int32_t status; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_srvc_id incl_srvc_id; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF 0x8a +struct hal_ev_gatt_client_reg_for_notif { + int32_t conn_id; + int32_t registered; + int32_t status; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_NOTIFY 0x8b +struct hal_ev_gatt_client_notify { + int32_t conn_id; + uint8_t bda[6]; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + uint8_t is_notify; + uint16_t len; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC 0x8c +struct hal_gatt_read_params { + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; + uint8_t status; + uint16_t value_type; + uint16_t len; + uint8_t value[0]; +} __attribute__((packed)); + +struct hal_ev_gatt_client_read_characteristic { + int32_t conn_id; + int32_t status; + struct hal_gatt_read_params data; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC 0x8d +struct hal_gatt_write_params { + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; + uint8_t status; +} __attribute__((packed)); + +struct hal_ev_gatt_client_write_characteristic { + int32_t conn_id; + int32_t status; + struct hal_gatt_write_params data; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_READ_DESCRIPTOR 0x8e +struct hal_ev_gatt_client_read_descriptor { + int32_t conn_id; + int32_t status; + struct hal_gatt_read_params data; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR 0x8f +struct hal_ev_gatt_client_write_descriptor { + int32_t conn_id; + int32_t status; + struct hal_gatt_write_params data; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_EXEC_WRITE 0x90 +struct hal_ev_gatt_client_exec_write { + int32_t conn_id; + int32_t status; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI 0x91 +struct hal_ev_gatt_client_read_remote_rssi { + int32_t client_if; + uint8_t address[6]; + int32_t rssi; + int32_t status; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_LISTEN 0x92 +struct hal_ev_gatt_client_listen { + int32_t status; + int32_t server_if; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_REGISTER 0x93 +struct hal_ev_gatt_server_register { + int32_t status; + int32_t server_if; + uint8_t uuid[16]; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_CONNECTION 0x94 +struct hal_ev_gatt_server_connection { + int32_t conn_id; + int32_t server_if; + int32_t connected; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_SERVICE_ADDED 0x95 +struct hal_ev_gatt_server_service_added { + int32_t status; + int32_t server_if; + struct hal_gatt_srvc_id srvc_id; + int32_t srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_INC_SRVC_ADDED 0x96 +struct hal_ev_gatt_server_inc_srvc_added { + int32_t status; + int32_t server_if; + int32_t srvc_handle; + int32_t incl_srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_CHAR_ADDED 0x97 +struct hal_ev_gatt_server_characteristic_added { + int32_t status; + int32_t server_if; + uint8_t uuid[16]; + int32_t srvc_handle; + int32_t char_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED 0x98 +struct hal_ev_gatt_server_descriptor_added { + int32_t status; + int32_t server_if; + uint8_t uuid[16]; + int32_t srvc_handle; + int32_t descr_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_SERVICE_STARTED 0x99 +struct hal_ev_gatt_server_service_started { + int32_t status; + int32_t server_if; + int32_t srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_SERVICE_STOPPED 0x9a +struct hal_ev_gatt_server_service_stopped { + int32_t status; + int32_t server_if; + int32_t srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_SERVICE_DELETED 0x9b +struct hal_ev_gatt_server_service_deleted { + int32_t status; + int32_t server_if; + int32_t srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_REQUEST_READ 0x9c +struct hal_ev_gatt_server_request_read { + int32_t conn_id; + int32_t trans_id; + uint8_t bdaddr[6]; + int32_t attr_handle; + int32_t offset; + uint8_t is_long; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_REQUEST_WRITE 0x9d +struct hal_ev_gatt_server_request_write { + int32_t conn_id; + int32_t trans_id; + uint8_t bdaddr[6]; + int32_t attr_handle; + int32_t offset; + int32_t length; + uint8_t need_rsp; + uint8_t is_prep; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE 0x9e +struct hal_ev_gatt_server_request_exec_write { + int32_t conn_id; + int32_t trans_id; + uint8_t bdaddr[6]; + int32_t exec_write; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_RSP_CONFIRMATION 0x9f +struct hal_ev_gatt_server_rsp_confirmation { + int32_t status; + int32_t handle; +} __attribute__((packed)); diff --git a/android/hal-pan.c b/android/hal-pan.c index 8c0f8d8..a9ac4ff 100644 --- a/android/hal-pan.c +++ b/android/hal-pan.c @@ -45,13 +45,25 @@ static void handle_ctrl_state(void *buf, uint16_t len) { struct hal_ev_pan_ctrl_state *ev = buf; + /* + * FIXME: Callback declared in bt_pan.h is 'typedef void + * (*btpan_control_state_callback)(btpan_control_state_t state, + * bt_status_t error, int local_role, const char* ifname); + * But PanService.Java defined it wrong way. + * private void onControlStateChanged(int local_role, int state, + * int error, String ifname). + * First and third parameters are misplaced, so sending data according + * to PanService.Java, fix this if issue fixed in PanService.Java. + */ if (cbs->control_state_cb) - cbs->control_state_cb(ev->state, ev->status, - ev->local_role, (char *)ev->name); + cbs->control_state_cb(ev->local_role, ev->state, ev->status, + (char *)ev->name); } -/* handlers will be called from notification thread context, - * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ static const struct hal_ipc_handler ev_handlers[] = { { /* HAL_EV_PAN_CTRL_STATE */ .handler = handle_ctrl_state, @@ -148,6 +160,7 @@ static bt_status_t pan_init(const btpan_callbacks_t *callbacks) sizeof(ev_handlers)/sizeof(ev_handlers[0])); cmd.service_id = HAL_SERVICE_ID_PAN; + cmd.mode = HAL_MODE_DEFAULT; ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); @@ -160,9 +173,9 @@ static bt_status_t pan_init(const btpan_callbacks_t *callbacks) return ret; } -static void pan_cleanup() +static void pan_cleanup(void) { - struct hal_cmd_register_module cmd; + struct hal_cmd_unregister_module cmd; DBG(""); diff --git a/android/hal-sco.c b/android/hal-sco.c new file mode 100644 index 0000000..ea9857e --- /dev/null +++ b/android/hal-sco.c @@ -0,0 +1,913 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../src/shared/util.h" +#include "sco-msg.h" +#include "ipc-common.h" +#include "hal-log.h" + +#define AUDIO_STREAM_DEFAULT_RATE 44100 +#define AUDIO_STREAM_SCO_RATE 8000 +#define AUDIO_STREAM_DEFAULT_FORMAT AUDIO_FORMAT_PCM_16_BIT + +#define OUT_BUFFER_SIZE 2560 +#define OUT_STREAM_FRAMES 2560 + +#define SOCKET_POLL_TIMEOUT_MS 500 + +static int listen_sk = -1; +static int ipc_sk = -1; + +static pthread_t ipc_th = 0; +static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER; + +struct sco_audio_config { + uint32_t rate; + uint32_t channels; + uint32_t frame_num; + uint16_t mtu; + audio_format_t format; +}; + +struct sco_stream_out { + struct audio_stream_out stream; + + struct sco_audio_config cfg; + int fd; + + uint8_t *downmix_buf; + + struct resampler_itfe *resampler; + int16_t *resample_buf; + uint32_t resample_frame_num; +}; + +struct sco_dev { + struct audio_hw_device dev; + struct sco_stream_out *out; +}; + +/* + * return the minimum frame numbers from resampling between BT stack's rate + * and audio flinger's. For output stream, 'output' shall be true, otherwise + * false for input streams at audio flinger side. + */ +static size_t get_resample_frame_num(uint32_t sco_rate, uint32_t rate, + size_t frame_num, bool output) +{ + size_t resample_frames_num = frame_num * sco_rate / rate + output; + + DBG("resampler: sco_rate %d frame_num %zd rate %d resample frames %zd", + sco_rate, frame_num, rate, resample_frames_num); + + return resample_frames_num; +} + +/* SCO IPC functions */ + +static int sco_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, + void *param, size_t *rsp_len, void *rsp, int *fd) +{ + ssize_t ret; + struct msghdr msg; + struct iovec iv[2]; + struct ipc_hdr cmd; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct ipc_status s; + size_t s_len = sizeof(s); + + pthread_mutex_lock(&sk_mutex); + + if (ipc_sk < 0) { + error("sco: Invalid cmd socket passed to sco_ipc_cmd"); + goto failed; + } + + if (!rsp || !rsp_len) { + memset(&s, 0, s_len); + rsp_len = &s_len; + rsp = &s; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.service_id = service_id; + cmd.opcode = opcode; + cmd.len = len; + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = param; + iv[1].iov_len = len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + ret = sendmsg(ipc_sk, &msg, 0); + if (ret < 0) { + error("sco: Sending command failed:%s", strerror(errno)); + goto failed; + } + + /* socket was shutdown */ + if (ret == 0) { + error("sco: Command socket closed"); + goto failed; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = rsp; + iv[1].iov_len = *rsp_len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + if (fd) { + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + } + + ret = recvmsg(ipc_sk, &msg, 0); + if (ret < 0) { + error("sco: Receiving command response failed:%s", + strerror(errno)); + goto failed; + } + + if (ret < (ssize_t) sizeof(cmd)) { + error("sco: Too small response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.service_id != service_id) { + error("sco: Invalid service id (%u vs %u)", cmd.service_id, + service_id); + goto failed; + } + + if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { + error("sco: Malformed response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.opcode != opcode && cmd.opcode != SCO_OP_STATUS) { + error("sco: Invalid opcode received (%u vs %u)", + cmd.opcode, opcode); + goto failed; + } + + if (cmd.opcode == SCO_OP_STATUS) { + struct ipc_status *s = rsp; + + if (sizeof(*s) != cmd.len) { + error("sco: Invalid status length"); + goto failed; + } + + if (s->code == SCO_STATUS_SUCCESS) { + error("sco: Invalid success status response"); + goto failed; + } + + pthread_mutex_unlock(&sk_mutex); + + return s->code; + } + + pthread_mutex_unlock(&sk_mutex); + + /* Receive auxiliary data in msg */ + if (fd) { + struct cmsghdr *cmsg; + + *fd = -1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); + break; + } + } + + if (*fd < 0) + goto failed; + } + + if (rsp_len) + *rsp_len = cmd.len; + + return SCO_STATUS_SUCCESS; + +failed: + /* Some serious issue happen on IPC - recover */ + shutdown(ipc_sk, SHUT_RDWR); + pthread_mutex_unlock(&sk_mutex); + + return SCO_STATUS_FAILED; +} + +static int ipc_connect_sco(int *fd, uint16_t *mtu) +{ + struct sco_rsp_connect rsp; + size_t rsp_len = sizeof(rsp); + int ret; + + DBG(""); + + ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_CONNECT, 0, NULL, &rsp_len, + &rsp, fd); + + *mtu = rsp.mtu; + + return ret; +} + +/* Audio stream functions */ + +static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer, + size_t frame_num) +{ + const int16_t *input = (const void *) buffer; + int16_t *output = (void *) out->downmix_buf; + size_t i; + + for (i = 0; i < frame_num; i++) { + int16_t l = le16_to_cpu(get_unaligned(&input[i * 2])); + int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1])); + + put_unaligned(cpu_to_le16((l + r) >> 1), &output[i]); + } +} + +static bool write_data(struct sco_stream_out *out, const uint8_t *buffer, + size_t bytes) +{ + struct pollfd pfd; + size_t len, written = 0; + int ret; + uint16_t mtu = /* out->cfg.mtu */ 48; + uint8_t read_buf[mtu]; + bool do_write = false; + + pfd.fd = out->fd; + pfd.events = POLLOUT | POLLIN | POLLHUP | POLLNVAL; + + while (bytes > written) { + + /* poll for sending */ + if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) { + DBG("timeout fd %d", out->fd); + return false; + } + + if (pfd.revents & (POLLHUP | POLLNVAL)) { + error("error fd %d, events 0x%x", out->fd, pfd.revents); + return false; + } + + /* FIXME synchronize by time instead of read() */ + if (pfd.revents & POLLIN) { + ret = read(out->fd, read_buf, mtu); + if (ret < 0) { + error("Error reading fd %d (%s)", out->fd, + strerror(errno)); + return false; + } + + do_write = true; + } + + if (!do_write) + continue; + + len = bytes - written > mtu ? mtu : bytes - written; + + ret = write(out->fd, buffer + written, len); + if (ret > 0) { + written += ret; + do_write = false; + continue; + } + + if (errno == EAGAIN) { + ret = errno; + warn("write failed (%d)", ret); + continue; + } + + if (errno != EINTR) { + ret = errno; + error("write failed (%d) fd %d bytes %zd", ret, out->fd, + bytes); + return false; + } + } + + DBG("written %zd bytes", bytes); + + return true; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, + size_t bytes) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + size_t frame_num = bytes / audio_stream_frame_size(&out->stream.common); + size_t output_frame_num = frame_num; + void *send_buf = out->downmix_buf; + size_t total; + + DBG("write to fd %d bytes %zu", out->fd, bytes); + + if (!out->downmix_buf) { + error("sco: downmix buffer not initialized"); + return -1; + } + + downmix_to_mono(out, buffer, frame_num); + + if (out->resampler) { + int ret; + + /* limit resampler's output within what resample buf can hold */ + output_frame_num = out->resample_frame_num; + + ret = out->resampler->resample_from_input(out->resampler, + send_buf, + &frame_num, + out->resample_buf, + &output_frame_num); + if (ret) { + error("Failed to resample frames: %zd input %zd (%s)", + frame_num, output_frame_num, strerror(ret)); + return -1; + } + + send_buf = out->resample_buf; + + DBG("Resampled: frame_num %zd, output_frame_num %zd", + frame_num, output_frame_num); + } + + total = output_frame_num * sizeof(int16_t) * 1; + + DBG("total %zd", total); + + if (!write_data(out, send_buf, total)) + return -1; + + return bytes; +} + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + + DBG("rate %u", out->cfg.rate); + + return out->cfg.rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + DBG("rate %u", rate); + + return 0; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + size_t size = audio_stream_frame_size(&out->stream.common) * + out->cfg.frame_num; + + DBG("buf size %zd", size); + + return size; +} + +static uint32_t out_get_channels(const struct audio_stream *stream) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + + DBG("channels num: %u", popcount(out->cfg.channels)); + + return out->cfg.channels; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + + DBG("format: %u", out->cfg.format); + + return out->cfg.format; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_standby(struct audio_stream *stream) +{ + DBG(""); + + return 0; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + DBG("%s", kvpairs); + + return 0; +} + +static char *out_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + DBG(""); + + return strdup(""); +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + DBG(""); + + return 0; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_add_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + + return -ENOSYS; +} + +static int sco_open_output_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out) +{ + struct sco_dev *adev = (struct sco_dev *) dev; + struct sco_stream_out *out; + int fd = -1; + int chan_num, ret; + size_t resample_size; + uint16_t mtu; + + DBG(""); + + if (ipc_connect_sco(&fd, &mtu) != SCO_STATUS_SUCCESS) { + error("sco: cannot get fd"); + return -EIO; + } + + DBG("got sco fd %d mtu %u", fd, mtu); + + out = calloc(1, sizeof(struct sco_stream_out)); + if (!out) + return -ENOMEM; + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + + /* Configuration for Android */ + out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT; + out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO; + out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE; + out->cfg.frame_num = OUT_STREAM_FRAMES; + out->cfg.mtu = mtu; + + out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common)); + if (!out->downmix_buf) { + free(out); + return -ENOMEM; + } + + DBG("size %zd", out_get_buffer_size(&out->stream.common)); + + /* Channel numbers for resampler */ + chan_num = 1; + + ret = create_resampler(out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num, + RESAMPLER_QUALITY_DEFAULT, NULL, + &out->resampler); + if (ret) { + error("Failed to create resampler (%s)", strerror(ret)); + goto failed; + } + + DBG("Created resampler: input rate [%d] output rate [%d] channels [%d]", + out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num); + + out->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE, + out->cfg.rate, + out->cfg.frame_num, 1); + + if (!out->resample_frame_num) { + error("frame num is too small to resample, discard it"); + goto failed; + } + + resample_size = sizeof(int16_t) * chan_num * out->resample_frame_num; + + out->resample_buf = malloc(resample_size); + if (!out->resample_buf) { + error("failed to allocate resample buffer for %u frames", + out->resample_frame_num); + goto failed; + } + + DBG("resampler: frame num %u buf size %zd bytes", + out->resample_frame_num, resample_size); + + *stream_out = &out->stream; + adev->out = out; + out->fd = fd; + + return 0; +failed: + free(out->downmix_buf); + free(out); + stream_out = NULL; + adev->out = NULL; + + return ret; +} + +static void sco_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream_out) +{ + struct sco_dev *sco_dev = (struct sco_dev *) dev; + struct sco_stream_out *out = (struct sco_stream_out *) stream_out; + + DBG("dev %p stream %p fd %d", dev, stream_out, sco_dev->out->fd); + + if (sco_dev->out && sco_dev->out->fd) { + close(sco_dev->out->fd); + sco_dev->out->fd = -1; + } + + free(out->downmix_buf); + free(out); + sco_dev->out = NULL; +} + +static int sco_set_parameters(struct audio_hw_device *dev, + const char *kvpairs) +{ + DBG("%s", kvpairs); + + return 0; +} + +static char *sco_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + DBG(""); + + return strdup(""); +} + +static int sco_init_check(const struct audio_hw_device *dev) +{ + DBG(""); + + return 0; +} + +static int sco_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + DBG("%f", volume); + + return 0; +} + +static int sco_set_master_volume(struct audio_hw_device *dev, float volume) +{ + DBG("%f", volume); + + return 0; +} + +static int sco_set_mode(struct audio_hw_device *dev, int mode) +{ + DBG(""); + + return -ENOSYS; +} + +static int sco_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + DBG(""); + + return -ENOSYS; +} + +static int sco_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + DBG(""); + + return -ENOSYS; +} + +static size_t sco_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + DBG(""); + + return -ENOSYS; +} + +static int sco_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in) +{ + DBG(""); + + return 0; +} + +static void sco_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream_in) +{ + DBG(""); + + free(stream_in); +} + +static int sco_dump(const audio_hw_device_t *device, int fd) +{ + DBG(""); + + return 0; +} + +static int sco_close(hw_device_t *device) +{ + DBG(""); + + free(device); + + return 0; +} + +static void *ipc_handler(void *data) +{ + bool done = false; + struct pollfd pfd; + int sk; + + DBG(""); + + while (!done) { + DBG("Waiting for connection ..."); + + sk = accept(listen_sk, NULL, NULL); + if (sk < 0) { + int err = errno; + + if (err == EINTR) + continue; + + if (err != ECONNABORTED && err != EINVAL) + error("sco: Failed to accept socket: %d (%s)", + err, strerror(err)); + + break; + } + + pthread_mutex_lock(&sk_mutex); + ipc_sk = sk; + pthread_mutex_unlock(&sk_mutex); + + DBG("SCO IPC: Connected"); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = ipc_sk; + pfd.events = POLLHUP | POLLERR | POLLNVAL; + + /* Check if socket is still alive. Empty while loop.*/ + while (poll(&pfd, 1, -1) < 0 && errno == EINTR); + + if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) { + info("SCO HAL: Socket closed"); + + pthread_mutex_lock(&sk_mutex); + close(ipc_sk); + ipc_sk = -1; + pthread_mutex_unlock(&sk_mutex); + } + } + + info("Closing SCO IPC thread"); + return NULL; +} + +static int sco_ipc_init(void) +{ + struct sockaddr_un addr; + int err; + int sk; + + DBG(""); + + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + err = -errno; + error("sco: Failed to create socket: %d (%s)", -err, + strerror(-err)); + return err; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH)); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = -errno; + error("sco: Failed to bind socket: %d (%s)", -err, + strerror(-err)); + goto failed; + } + + if (listen(sk, 1) < 0) { + err = -errno; + error("sco: Failed to listen on the socket: %d (%s)", -err, + strerror(-err)); + goto failed; + } + + listen_sk = sk; + + err = pthread_create(&ipc_th, NULL, ipc_handler, NULL); + if (err) { + err = -err; + ipc_th = 0; + error("sco: Failed to start IPC thread: %d (%s)", + -err, strerror(-err)); + goto failed; + } + + return 0; + +failed: + close(sk); + return err; +} + +static int sco_open(const hw_module_t *module, const char *name, + hw_device_t **device) +{ + struct sco_dev *dev; + int err; + + DBG(""); + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) { + error("SCO: interface %s not matching [%s]", name, + AUDIO_HARDWARE_INTERFACE); + return -EINVAL; + } + + err = sco_ipc_init(); + if (err < 0) + return err; + + dev = calloc(1, sizeof(struct sco_dev)); + if (!dev) + return -ENOMEM; + + dev->dev.common.tag = HARDWARE_DEVICE_TAG; + dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT; + dev->dev.common.module = (struct hw_module_t *) module; + dev->dev.common.close = sco_close; + + dev->dev.init_check = sco_init_check; + dev->dev.set_voice_volume = sco_set_voice_volume; + dev->dev.set_master_volume = sco_set_master_volume; + dev->dev.set_mode = sco_set_mode; + dev->dev.set_mic_mute = sco_set_mic_mute; + dev->dev.get_mic_mute = sco_get_mic_mute; + dev->dev.set_parameters = sco_set_parameters; + dev->dev.get_parameters = sco_get_parameters; + dev->dev.get_input_buffer_size = sco_get_input_buffer_size; + dev->dev.open_output_stream = sco_open_output_stream; + dev->dev.close_output_stream = sco_close_output_stream; + dev->dev.open_input_stream = sco_open_input_stream; + dev->dev.close_input_stream = sco_close_input_stream; + dev->dev.dump = sco_dump; + + *device = &dev->dev.common; + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = sco_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "SCO Audio HW HAL", + .author = "Intel Corporation", + .methods = &hal_module_methods, + }, +}; diff --git a/android/hal-sock.c b/android/hal-socket.c similarity index 55% rename from android/hal-sock.c rename to android/hal-socket.c index f45be30..cfd50d1 100644 --- a/android/hal-sock.c +++ b/android/hal-socket.c @@ -26,18 +26,23 @@ #include "hal-utils.h" #include "hal.h" -static bt_status_t sock_listen_rfcomm(const char *service_name, +static bt_status_t socket_listen(btsock_type_t type, const char *service_name, const uint8_t *uuid, int chan, int *sock, int flags) { - struct hal_cmd_sock_listen cmd; + struct hal_cmd_socket_listen cmd; - DBG(""); + if (!sock) + return BT_STATUS_PARM_INVALID; + + DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x", + btuuid2str(uuid), chan, sock, type, service_name, flags); memset(&cmd, 0, sizeof(cmd)); + /* type match IPC type */ + cmd.type = type; cmd.flags = flags; - cmd.type = BTSOCK_RFCOMM; cmd.channel = chan; if (uuid) @@ -46,77 +51,46 @@ static bt_status_t sock_listen_rfcomm(const char *service_name, if (service_name) memcpy(cmd.name, service_name, strlen(service_name)); - return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, + return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, sizeof(cmd), &cmd, NULL, NULL, sock); } -static bt_status_t sock_listen(btsock_type_t type, const char *service_name, +static bt_status_t socket_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type, const uint8_t *uuid, int chan, int *sock, int flags) { - if ((!uuid && chan <= 0) || !sock) { - error("Invalid params: uuid %s, chan %d, sock %p", - btuuid2str(uuid), chan, sock); - return BT_STATUS_PARM_INVALID; - } - - DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x", - btuuid2str(uuid), chan, sock, type, service_name, flags); - - switch (type) { - case BTSOCK_RFCOMM: - return sock_listen_rfcomm(service_name, uuid, chan, sock, - flags); - default: - error("%s: Socket type %d not supported", __func__, type); - break; - } + struct hal_cmd_socket_connect cmd; - return BT_STATUS_UNSUPPORTED; -} - -static bt_status_t sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type, - const uint8_t *uuid, int chan, - int *sock, int flags) -{ - struct hal_cmd_sock_connect cmd; - - if ((!uuid && chan <= 0) || !bdaddr || !sock) { - error("Invalid params: bd_addr %s, uuid %s, chan %d, sock %p", - bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock); + if (!sock) return BT_STATUS_PARM_INVALID; - } DBG("bdaddr %s uuid %s chan %d sock %p type %d flags 0x%02x", bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock, type, flags); - if (type != BTSOCK_RFCOMM) { - error("Socket type %u not supported", type); - return BT_STATUS_UNSUPPORTED; - } - memset(&cmd, 0, sizeof(cmd)); - cmd.flags = flags; + /* type match IPC type */ cmd.type = type; + cmd.flags = flags; cmd.channel = chan; if (uuid) memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); - memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr)); + if (bdaddr) + memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr)); - return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, + return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, sizeof(cmd), &cmd, NULL, NULL, sock); } -static btsock_interface_t sock_if = { - sizeof(sock_if), - sock_listen, - sock_connect +static btsock_interface_t socket_if = { + sizeof(socket_if), + socket_listen, + socket_connect }; -btsock_interface_t *bt_get_sock_interface(void) +btsock_interface_t *bt_get_socket_interface(void) { - return &sock_if; + return &socket_if; } diff --git a/android/hal-utils.c b/android/hal-utils.c index e3c0c60..ceefefc 100644 --- a/android/hal-utils.c +++ b/android/hal-utils.c @@ -125,9 +125,7 @@ INTMAP(bt_property_type_t, -1, "(unknown)") DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT), DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME), DELEMENT(BT_PROPERTY_REMOTE_RSSI), -#if PLATFORM_SDK_VERSION > 17 DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO), -#endif DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP), ENDMAP diff --git a/android/hal-utils.h b/android/hal-utils.h index 75de7e9..8a1d015 100644 --- a/android/hal-utils.h +++ b/android/hal-utils.h @@ -34,7 +34,7 @@ void str2bt_uuid_t(const char *str, bt_uuid_t *uuid); const char *btproperty2str(const bt_property_t *property); const char *bdaddr2str(const bt_bdaddr_t *bd_addr); -/** +/* * Begin mapping section * * There are some mappings between integer values (enums) and strings diff --git a/android/hal.h b/android/hal.h index b475411..6998e9a 100644 --- a/android/hal.h +++ b/android/hal.h @@ -20,11 +20,21 @@ #include #include #include +#include +#include +#include +#include +#include +#include -btsock_interface_t *bt_get_sock_interface(void); +btsock_interface_t *bt_get_socket_interface(void); bthh_interface_t *bt_get_hidhost_interface(void); btpan_interface_t *bt_get_pan_interface(void); btav_interface_t *bt_get_a2dp_interface(void); +btrc_interface_t *bt_get_avrcp_interface(void); +bthf_interface_t *bt_get_handsfree_interface(void); +btgatt_interface_t *bt_get_gatt_interface(void); +bthl_interface_t *bt_get_health_interface(void); void bt_thread_associate(void); void bt_thread_disassociate(void); diff --git a/android/handsfree.c b/android/handsfree.c new file mode 100644 index 0000000..7ca0ba8 --- /dev/null +++ b/android/handsfree.c @@ -0,0 +1,2681 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/sdp-client.h" +#include "src/uuid-helper.h" +#include "src/shared/hfp.h" +#include "btio/btio.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "handsfree.h" +#include "bluetooth.h" +#include "src/log.h" +#include "utils.h" +#include "sco-msg.h" + +#define HSP_AG_CHANNEL 12 +#define HFP_AG_CHANNEL 13 + +#define HFP_AG_FEAT_3WAY 0x00000001 +#define HFP_AG_FEAT_ECNR 0x00000002 +#define HFP_AG_FEAT_VR 0x00000004 +#define HFP_AG_FEAT_INBAND 0x00000008 +#define HFP_AG_FEAT_VTAG 0x00000010 +#define HFP_AG_FEAT_REJ_CALL 0x00000020 +#define HFP_AG_FEAT_ECS 0x00000040 +#define HFP_AG_FEAT_ECC 0x00000080 +#define HFP_AG_FEAT_EXT_ERR 0x00000100 +#define HFP_AG_FEAT_CODEC 0x00000200 + +#define HFP_HF_FEAT_ECNR 0x00000001 +#define HFP_HF_FEAT_3WAY 0x00000002 +#define HFP_HF_FEAT_CLI 0x00000004 +#define HFP_HF_FEAT_VR 0x00000008 +#define HFP_HF_FEAT_RVC 0x00000010 +#define HFP_HF_FEAT_ECS 0x00000020 +#define HFP_HF_FEAT_ECC 0x00000040 +#define HFP_HF_FEAT_CODEC 0x00000080 + +#define HFP_AG_FEATURES (HFP_AG_FEAT_3WAY | HFP_AG_FEAT_ECNR |\ + HFP_AG_FEAT_VR | HFP_AG_FEAT_REJ_CALL |\ + HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR) + +#define HFP_AG_CHLD "0,1,2,3" + +/* offsets in indicators table, should be incremented when sending CIEV */ +#define IND_SERVICE 0 +#define IND_CALL 1 +#define IND_CALLSETUP 2 +#define IND_CALLHELD 3 +#define IND_SIGNAL 4 +#define IND_ROAM 5 +#define IND_BATTCHG 6 +#define IND_COUNT (IND_BATTCHG + 1) + +#define RING_TIMEOUT 2 + +#define CVSD_OFFSET 0 +#define MSBC_OFFSET 1 +#define CODECS_COUNT (MSBC_OFFSET + 1) + +#define CODEC_ID_CVSD 0x01 +#define CODEC_ID_MSBC 0x02 + +struct indicator { + const char *name; + int min; + int max; + int val; + bool always_active; + bool active; +}; + +struct hfp_codec { + uint8_t type; + bool local_supported; + bool remote_supported; +}; + +static const struct indicator inds_defaults[] = { + { "service", 0, 1, 0, false, true }, + { "call", 0, 1, 0, true, true }, + { "callsetup", 0, 3, 0, true, true }, + { "callheld", 0, 2, 0, true, true }, + { "signal", 0, 5, 0, false, true }, + { "roam", 0, 1, 0, false, true }, + { "battchg", 0, 5, 0, false, true }, +}; + +static const struct hfp_codec codecs_defaults[] = { + { CODEC_ID_CVSD, true, false}, + { CODEC_ID_MSBC, false, false}, +}; + +static struct { + bdaddr_t bdaddr; + uint8_t state; + uint8_t audio_state; + uint32_t features; + + bool clip_enabled; + bool cmee_enabled; + bool ccwa_enabled; + bool indicators_enabled; + struct indicator inds[IND_COUNT]; + int num_active; + int num_held; + int setup_state; + bool call_hanging_up; + + uint8_t negotiated_codec; + uint8_t proposed_codec; + struct hfp_codec codecs[CODECS_COUNT]; + + guint ring; + bool hsp; + + struct hfp_gw *gw; + + GIOChannel *sco; + guint sco_watch; +} device; + +static uint32_t hfp_ag_features = 0; + +static bdaddr_t adapter_addr; + +static struct ipc *hal_ipc = NULL; +static struct ipc *sco_ipc = NULL; + +static uint32_t hfp_record_id = 0; +static GIOChannel *hfp_server = NULL; + +static uint32_t hsp_record_id = 0; +static GIOChannel *hsp_server = NULL; + +static GIOChannel *sco_server = NULL; + +static void device_set_state(uint8_t state) +{ + struct hal_ev_handsfree_conn_state ev; + char address[18]; + + if (device.state == state) + return; + + device.state = state; + + ba2str(&device.bdaddr, address); + DBG("device %s state %u", address, state); + + bdaddr2android(&device.bdaddr, ev.bdaddr); + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CONN_STATE, sizeof(ev), &ev); +} + +static void device_set_audio_state(uint8_t state) +{ + struct hal_ev_handsfree_audio_state ev; + char address[18]; + + if (device.audio_state == state) + return; + + device.audio_state = state; + + ba2str(&device.bdaddr, address); + DBG("device %s audio state %u", address, state); + + bdaddr2android(&device.bdaddr, ev.bdaddr); + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_AUDIO_STATE, sizeof(ev), &ev); +} + +static void init_codecs(void) +{ + memcpy(device.codecs, codecs_defaults, sizeof(device.codecs)); + + if (hfp_ag_features & HFP_AG_FEAT_CODEC) + device.codecs[MSBC_OFFSET].local_supported = true; +} + +static void device_init(const bdaddr_t *bdaddr) +{ + bacpy(&device.bdaddr, bdaddr); + + device.setup_state = HAL_HANDSFREE_CALL_STATE_IDLE; + + memcpy(device.inds, inds_defaults, sizeof(device.inds)); + + init_codecs(); + + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTING); +} + +static void device_cleanup(void) +{ + if (device.gw) { + hfp_gw_unref(device.gw); + device.gw = NULL; + } + + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED); + + if (device.sco_watch) { + g_source_remove(device.sco_watch); + device.sco_watch = 0; + } + + if (device.sco) { + g_io_channel_shutdown(device.sco, TRUE, NULL); + g_io_channel_unref(device.sco); + device.sco = NULL; + } + + if (device.ring) { + g_source_remove(device.ring); + device.ring = 0; + } + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); + + memset(&device, 0, sizeof(device)); +} + +static void disconnect_watch(void *user_data) +{ + DBG(""); + + device_cleanup(); +} + +static void at_cmd_unknown(const char *command, void *user_data) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_handsfree_unknown_at *ev = (void *) buf; + + if (device.state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) { + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); + hfp_gw_disconnect(device.gw); + return; + } + + /* copy while string including terminating NULL */ + ev->len = strlen(command) + 1; + memcpy(ev->buf, command, ev->len); + + if (ev->len > IPC_MTU - sizeof(*ev)) { + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); + return; + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_UNKNOWN_AT, sizeof(*ev) + ev->len, ev); +} + +static void at_cmd_vgm(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_volume ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 15) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ev.type = HAL_HANDSFREE_VOLUME_TYPE_MIC; + ev.volume = val; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev); + + /* Framework is not replying with result for AT+VGM */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_vgs(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_volume ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 15) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ev.type = HAL_HANDSFREE_VOLUME_TYPE_SPEAKER; + ev.volume = val; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev); + + /* Framework is not replying with result for AT+VGS */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_cops(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val != 3) + break; + + if (!hfp_gw_result_get_number(result, &val) || val != 0) + break; + + if (hfp_gw_result_has_next(result)) + break; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_COPS, 0, NULL); + return; + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_bia(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val, i, def; + bool tmp[IND_COUNT]; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + for (i = 0; i < IND_COUNT; i++) + tmp[i] = device.inds[i].active; + + i = 0; + + do { + def = (i < IND_COUNT) ? device.inds[i].active : 0; + + if (!hfp_gw_result_get_number_default(result, &val, def)) + goto failed; + + if (val > 1) + goto failed; + + if (i < IND_COUNT) { + tmp[i] = val || device.inds[i].always_active; + i++; + } + } while (hfp_gw_result_has_next(result)); + + for (i = 0; i < IND_COUNT; i++) + device.inds[i].active = tmp[i]; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + +failed: + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_a(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_ANSWER, 0, NULL); + + /* Framework is not replying with result for ATA */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_SET: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_d(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + char buf[IPC_MTU]; + struct hal_ev_handsfree_dial *ev = (void *) buf; + int cnt; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_unquoted_string(result, + (char *) ev->number, 255)) + break; + + ev->number_len = strlen((char *) ev->number); + + if (ev->number[ev->number_len - 1] != ';') + break; + + if (ev->number[0] == '>') + cnt = strspn((char *) ev->number + 1, "0123456789") + 1; + else + cnt = strspn((char *) ev->number, "0123456789ABC*#+"); + + if (cnt != ev->number_len - 1) + break; + + ev->number_len++; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_DIAL, + sizeof(*ev) + ev->number_len, ev); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_ccwa(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + device.ccwa_enabled = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_chup(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_HANGUP, 0, NULL); + + /* Framework is not replying with result for AT+CHUP */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_SET: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_clcc(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CLCC, 0, NULL); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_SET: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_cmee(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + device.cmee_enabled = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_clip(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + device.clip_enabled = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_vts(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_dtmf ev; + char str[2]; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_unquoted_string(result, str, 2)) + break; + + if (!((str[0] >= '0' && str[0] <= '9') || + (str[0] >= 'A' && str[0] <= 'D') || + str[0] == '*' || str[0] == '#')) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ev.tone = str[0]; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_DTMF, sizeof(ev), &ev); + + /* Framework is not replying with result for AT+VTS */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_cnum(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CNUM, 0, NULL); + return; + case HFP_GW_CMD_TYPE_SET: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_binp(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + /* TODO */ + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_bldn(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_dial ev; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ev.number_len = 0; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_DIAL, sizeof(ev), &ev); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_SET: + break; + } +} + +static void at_cmd_bvra(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_vr_state ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + if (val) + ev.state = HAL_HANDSFREE_VR_STARTED; + else + ev.state = HAL_HANDSFREE_VR_STOPPED; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_VR, sizeof(ev), &ev); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_nrec(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_nrec ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + /* + * Android HAL defines start and stop parameter for NREC + * callback, but spec allows HF to only disable AG's NREC + * feature for SLC duration. Follow spec here. + */ + if (!hfp_gw_result_get_number(result, &val) || val != 0) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ev.nrec = HAL_HANDSFREE_NREC_STOP; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_NREC, sizeof(ev), &ev); + + /* Framework is not replying with result for AT+NREC */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_bsir(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + /* TODO */ + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_btrh(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + /* TODO */ + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static gboolean sco_watch_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + g_io_channel_shutdown(device.sco, TRUE, NULL); + g_io_channel_unref(device.sco); + device.sco = NULL; + + DBG(""); + + device.sco_watch = 0; + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); + + return FALSE; +} + +static void select_codec(uint8_t codec_type) +{ + uint8_t type = CODEC_ID_CVSD; + int i; + + if (codec_type > 0) { + type = codec_type; + goto done; + } + + for (i = CODECS_COUNT - 1; i >= CVSD_OFFSET; i--) { + if (!device.codecs[i].local_supported) + continue; + + if (!device.codecs[i].remote_supported) + continue; + + type = device.codecs[i].type; + break; + } + +done: + device.proposed_codec = type; + + hfp_gw_send_info(device.gw, "+BCS: %u", type); +} + +static void connect_sco_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + if (err) { + uint8_t status; + + error("handsfree: audio connect failed (%s)", err->message); + + status = HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED; + device_set_audio_state(status); + + if (!(device.features & HFP_HF_FEAT_CODEC)) + return; + + /* If other failed, try connecting with CVSD */ + if (device.negotiated_codec != CODEC_ID_CVSD) { + info("handsfree: trying fallback with CVSD"); + select_codec(CODEC_ID_CVSD); + } + + return; + } + + g_io_channel_set_close_on_unref(chan, TRUE); + + device.sco = g_io_channel_ref(chan); + device.sco_watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + sco_watch_cb, NULL); + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED); +} + +static bool connect_sco(void) +{ + GIOChannel *io; + GError *gerr = NULL; + uint16_t voice_settings; + + if (device.sco) + return false; + + if (!(device.features & HFP_HF_FEAT_CODEC)) + voice_settings = 0; + else if (device.negotiated_codec != CODEC_ID_CVSD) + voice_settings = BT_VOICE_TRANSPARENT; + else + voice_settings = BT_VOICE_CVSD_16BIT; + + io = bt_io_connect(connect_sco_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &device.bdaddr, + BT_IO_OPT_VOICE, voice_settings, + BT_IO_OPT_INVALID); + + if (!io) { + error("handsfree: unable to connect audio: %s", gerr->message); + g_error_free(gerr); + return false; + } + + g_io_channel_unref(io); + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING); + + return true; +} + +static void at_cmd_bcc(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (!(device.features & HFP_HF_FEAT_CODEC)) + break; + + if (hfp_gw_result_has_next(result)) + break; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + /* we haven't negotiated codec, start selection */ + if (!device.negotiated_codec) { + select_codec(0); + return; + } + /* + * we try connect to negotiated codec. If it fails, and it isn't + * CVSD codec, try connect CVSD + */ + if (!connect_sco() && device.negotiated_codec != CODEC_ID_CVSD) + select_codec(CODEC_ID_CVSD); + + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_SET: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_bcs(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val)) + break; + + if (hfp_gw_result_has_next(result)) + break; + + /* Remote replied with other codec. Reply with error */ + if (device.proposed_codec != val) { + device.proposed_codec = 0; + break; + } + + device.proposed_codec = 0; + device.negotiated_codec = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + /* Connect sco with negotiated parameters */ + connect_sco(); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_ckpd(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val != 200) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_HSP_KEY_PRESS, 0, NULL); + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void register_post_slc_at(void) +{ + if (device.hsp) { + hfp_gw_register(device.gw, at_cmd_ckpd, "+CKPD", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vgs, "+VGS", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vgm, "+VGM", NULL, NULL); + return; + } + + hfp_gw_register(device.gw, at_cmd_a, "A", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_d, "D", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_ccwa, "+CCWA", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_chup, "+CHUP", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_clcc, "+CLCC", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cops, "+COPS", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cmee, "+CMEE", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_clip, "+CLIP", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vts, "+VTS", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cnum, "+CNUM", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bia, "+BIA", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_binp, "+BINP", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bldn, "+BLDN", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bvra, "+BVRA", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_nrec, "+NREC", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vgs, "+VGS", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vgm, "+VGM", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bsir, "+BSIR", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_btrh, "+BTRH", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bcc, "+BCC", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bcs, "+BCS", NULL, NULL); +} + +static void at_cmd_cmer(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + /* mode must be =3 */ + if (!hfp_gw_result_get_number(result, &val) || val != 3) + break; + + /* keyp is don't care */ + if (!hfp_gw_result_get_number(result, &val)) + break; + + /* disp is don't care */ + if (!hfp_gw_result_get_number(result, &val)) + break; + + /* ind must be 0 or 1 */ + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + device.indicators_enabled = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + if (device.features & HFP_HF_FEAT_3WAY) + return; + + register_post_slc_at(); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); + return; + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_cind(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + char *buf, *ptr; + int len; + unsigned int i; + + switch (type) { + case HFP_GW_CMD_TYPE_TEST: + + /* + * If device supports Codec Negotiation, AT+BAC should be + * received first + */ + if ((device.features & HFP_HF_FEAT_CODEC) && + !device.codecs[CVSD_OFFSET].remote_supported) + break; + + len = strlen("+CIND:") + 1; + + for (i = 0; i < IND_COUNT; i++) { + len += strlen("(\"\",(X,X)),"); + len += strlen(device.inds[i].name); + } + + buf = g_malloc(len); + + ptr = buf + sprintf(buf, "+CIND:"); + + for (i = 0; i < IND_COUNT; i++) { + ptr += sprintf(ptr, "(\"%s\",(%d%c%d)),", + device.inds[i].name, + device.inds[i].min, + device.inds[i].max == 1 ? ',' : '-', + device.inds[i].max); + } + + ptr--; + *ptr = '\0'; + + hfp_gw_send_info(device.gw, "%s", buf); + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + g_free(buf); + return; + case HFP_GW_CMD_TYPE_READ: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CIND, 0, NULL); + return; + case HFP_GW_CMD_TYPE_SET: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_brsf(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int feat; + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &feat)) + break; + + if (hfp_gw_result_has_next(result)) + break; + + /* TODO verify features */ + device.features = feat; + + hfp_gw_send_info(device.gw, "+BRSF: %u", hfp_ag_features); + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_chld(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_chld ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 3) + break; + + /* No ECC support */ + if (hfp_gw_result_has_next(result)) + break; + + /* value match HAL type */ + ev.chld = val; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CHLD, sizeof(ev), &ev); + return; + case HFP_GW_CMD_TYPE_TEST: + hfp_gw_send_info(device.gw, "+CHLD: (%s)", HFP_AG_CHLD); + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + register_post_slc_at(); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static struct hfp_codec *find_codec_by_type(uint8_t type) +{ + int i; + + for (i = 0; i < CODECS_COUNT; i++) + if (type == device.codecs[i].type) + return &device.codecs[i]; + + return NULL; +} + +static void at_cmd_bac(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!(device.features & HFP_HF_FEAT_CODEC)) + goto failed; + + /* set codecs to defaults */ + init_codecs(); + device.negotiated_codec = 0; + + /* + * At least CVSD mandatory codec must exist + * HFP V1.6 4.34.1 + */ + if (!hfp_gw_result_get_number(result, &val) || + val != CODEC_ID_CVSD) + goto failed; + + device.codecs[CVSD_OFFSET].remote_supported = true; + + if (hfp_gw_result_get_number(result, &val)) { + if (val != CODEC_ID_MSBC) + goto failed; + + device.codecs[MSBC_OFFSET].remote_supported = true; + } + + while (hfp_gw_result_has_next(result)) { + struct hfp_codec *codec; + + if (!hfp_gw_result_get_number(result, &val)) + goto failed; + + codec = find_codec_by_type(val); + if (!codec) + continue; + + codec->remote_supported = true; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + if (device.proposed_codec) + select_codec(0); + return; + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + +failed: + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void register_slc_at(void) +{ + hfp_gw_register(device.gw, at_cmd_brsf, "+BRSF", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cind, "+CIND", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cmer, "+CMER", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_chld, "+CHLD", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bac, "+BAC", NULL, NULL); +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + DBG(""); + + if (err) { + error("handsfree: connect failed (%s)", err->message); + goto failed; + } + + device.gw = hfp_gw_new(g_io_channel_unix_get_fd(chan)); + if (!device.gw) + goto failed; + + g_io_channel_set_close_on_unref(chan, FALSE); + + hfp_gw_set_close_on_unref(device.gw, true); + hfp_gw_set_command_handler(device.gw, at_cmd_unknown, NULL, NULL); + hfp_gw_set_disconnect_handler(device.gw, disconnect_watch, NULL, NULL); + + if (device.hsp) { + register_post_slc_at(); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTED); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); + return; + } + + register_slc_at(); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTED); + return; + +failed: + g_io_channel_shutdown(chan, TRUE, NULL); + device_cleanup(); +} + +static void confirm_cb(GIOChannel *chan, gpointer data) +{ + char address[18]; + bdaddr_t bdaddr; + GError *err = NULL; + + bt_io_get(chan, &err, + BT_IO_OPT_DEST, address, + BT_IO_OPT_DEST_BDADDR, &bdaddr, + BT_IO_OPT_INVALID); + if (err) { + error("handsfree: confirm failed (%s)", err->message); + g_error_free(err); + goto drop; + } + + DBG("incoming connect from %s", address); + + if (device.state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) { + info("handsfree: refusing connection from %s", address); + goto drop; + } + + device_init(&bdaddr); + + if (!bt_io_accept(chan, connect_cb, NULL, NULL, NULL)) { + error("handsfree: failed to accept connection"); + device_cleanup(); + goto drop; + } + + device.hsp = GPOINTER_TO_INT(data); + return; + +drop: + g_io_channel_shutdown(chan, TRUE, NULL); +} + +static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + sdp_list_t *protos, *classes; + GError *gerr = NULL; + GIOChannel *io; + uuid_t uuid; + int channel; + + DBG(""); + + if (err < 0) { + error("handsfree: unable to get SDP record: %s", + strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + info("handsfree: no HSP SDP records found"); + goto fail; + } + + if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) { + error("handsfree: unable to get service classes from record"); + goto fail; + } + + if (sdp_get_access_protos(recs->data, &protos) < 0) { + error("handsfree: unable to get access protocols from record"); + sdp_list_free(classes, free); + goto fail; + } + + /* TODO read remote version? */ + /* TODO read volume control support */ + + memcpy(&uuid, classes->data, sizeof(uuid)); + sdp_list_free(classes, free); + + if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 || + uuid.value.uuid16 != HEADSET_SVCLASS_ID) { + sdp_list_free(protos, NULL); + error("handsfree: invalid service record or not HSP"); + goto fail; + } + + 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); + if (channel <= 0) { + error("handsfree: unable to get RFCOMM channel from record"); + goto fail; + } + + io = bt_io_connect(connect_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &device.bdaddr, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_CHANNEL, channel, + BT_IO_OPT_INVALID); + if (!io) { + error("handsfree: unable to connect: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + + device.hsp = true; + + g_io_channel_unref(io); + return; + +fail: + device_cleanup(); +} + +static int sdp_search_hsp(void) +{ + uuid_t uuid; + + sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID); + + return bt_search_service(&adapter_addr, &device.bdaddr, &uuid, + sdp_hsp_search_cb, NULL, NULL, 0); +} + +static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + sdp_list_t *protos, *classes; + GError *gerr = NULL; + GIOChannel *io; + uuid_t uuid; + int channel; + + DBG(""); + + if (err < 0) { + error("handsfree: unable to get SDP record: %s", + strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + info("handsfree: no HFP SDP records found, trying HSP"); + + if (sdp_search_hsp() < 0) { + error("handsfree: HSP SDP search failed"); + goto fail; + } + + return; + } + + if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) { + error("handsfree: unable to get service classes from record"); + goto fail; + } + + if (sdp_get_access_protos(recs->data, &protos) < 0) { + error("handsfree: unable to get access protocols from record"); + sdp_list_free(classes, free); + goto fail; + } + + /* TODO read remote version? */ + + 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_SVCLASS_ID) { + sdp_list_free(protos, NULL); + error("handsfree: invalid service record or not HFP"); + goto fail; + } + + 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); + if (channel <= 0) { + error("handsfree: unable to get RFCOMM channel from record"); + goto fail; + } + + io = bt_io_connect(connect_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &device.bdaddr, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_CHANNEL, channel, + BT_IO_OPT_INVALID); + if (!io) { + error("handsfree: unable to connect: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + + g_io_channel_unref(io); + return; + +fail: + device_cleanup(); +} + +static int sdp_search_hfp(void) +{ + uuid_t uuid; + + sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID); + + return bt_search_service(&adapter_addr, &device.bdaddr, &uuid, + sdp_hfp_search_cb, NULL, NULL, 0); +} + +static void handle_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_connect *cmd = buf; + char addr[18]; + uint8_t status; + bdaddr_t bdaddr; + int ret; + + DBG(""); + + if (device.state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2bdaddr(&cmd->bdaddr, &bdaddr); + + ba2str(&bdaddr, addr); + DBG("connecting to %s", addr); + + device_init(&bdaddr); + + /* prefer HFP over HSP */ + ret = hfp_server ? sdp_search_hfp() : sdp_search_hsp(); + if (ret < 0) { + error("handsfree: SDP search failed"); + device_cleanup(); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CONNECT, status); +} + +static void handle_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_disconnect *cmd = buf; + bdaddr_t bdaddr; + uint8_t status; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + if (device.state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED || + bacmp(&device.bdaddr, &bdaddr)) { + status = HAL_STATUS_FAILED; + goto failed; + + } + + if (device.state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING) { + status = HAL_STATUS_SUCCESS; + goto failed; + } + + if (device.state == HAL_EV_HANDSFREE_CONN_STATE_CONNECTING) { + device_cleanup(); + } else { + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING); + hfp_gw_disconnect(device.gw); + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DISCONNECT, status); +} + +static bool disconnect_sco(void) +{ + if (!device.sco) + return false; + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING); + + if (device.sco_watch) { + g_source_remove(device.sco_watch); + device.sco_watch = 0; + } + + g_io_channel_shutdown(device.sco, TRUE, NULL); + g_io_channel_unref(device.sco); + device.sco = NULL; + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); + return true; +} + +static bool connect_audio(void) +{ + if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) + return false; + + /* we haven't negotiated codec, start selection */ + if ((device.features & HFP_HF_FEAT_CODEC) && !device.negotiated_codec) { + select_codec(0); + return true; + } + + return connect_sco(); +} + +static void handle_connect_audio(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_connect_audio *cmd = buf; + bdaddr_t bdaddr; + uint8_t status; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED || + bacmp(&device.bdaddr, &bdaddr)) { + status = HAL_STATUS_FAILED; + goto done; + } + + status = connect_audio() ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CONNECT_AUDIO, status); +} + +static void handle_disconnect_audio(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_disconnect_audio *cmd = buf; + bdaddr_t bdaddr; + uint8_t status; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED || + bacmp(&device.bdaddr, &bdaddr)) { + status = HAL_STATUS_FAILED; + goto done; + } + + status = disconnect_sco() ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DISCONNECT_AUDIO, status); +} + +static void handle_start_vr(const void *buf, uint16_t len) +{ + uint8_t status; + + DBG(""); + + if (device.features & HFP_HF_FEAT_VR) { + hfp_gw_send_info(device.gw, "+BVRA: 1"); + status = HAL_STATUS_SUCCESS; + } else { + status = HAL_STATUS_FAILED; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_START_VR, status); +} + +static void handle_stop_vr(const void *buf, uint16_t len) +{ + uint8_t status; + + DBG(""); + + if (device.features & HFP_HF_FEAT_VR) { + hfp_gw_send_info(device.gw, "+BVRA: 0"); + status = HAL_STATUS_SUCCESS; + } else { + status = HAL_STATUS_FAILED; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_STOP_VR, status); +} + +static void handle_volume_control(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_volume_control *cmd = buf; + uint8_t status, volume; + + DBG("type=%u volume=%u", cmd->type, cmd->volume); + + volume = cmd->volume > 15 ? 15 : cmd->volume; + + switch (cmd->type) { + case HAL_HANDSFREE_VOLUME_TYPE_MIC: + hfp_gw_send_info(device.gw, "+VGM: %u", volume); + + status = HAL_STATUS_SUCCESS; + break; + case HAL_HANDSFREE_VOLUME_TYPE_SPEAKER: + hfp_gw_send_info(device.gw, "+VGS: %u", volume); + + status = HAL_STATUS_SUCCESS; + break; + default: + status = HAL_STATUS_FAILED; + break; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_VOLUME_CONTROL, status); +} + +static void update_indicator(int ind, uint8_t val) +{ + DBG("ind=%u new=%u old=%u", ind, val, device.inds[ind].val); + + if (device.inds[ind].val == val) + return; + + device.inds[ind].val = val; + + if (!device.indicators_enabled) + return; + + if (!device.inds[ind].active) + return; + + /* indicator numbers in CIEV start from 1 */ + hfp_gw_send_info(device.gw, "+CIEV: %u,%u", ind + 1, val); +} + +static void handle_device_status_notif(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_device_status_notif *cmd = buf; + + DBG(""); + + update_indicator(IND_SERVICE, cmd->state); + update_indicator(IND_ROAM, cmd->type); + update_indicator(IND_SIGNAL, cmd->signal); + update_indicator(IND_BATTCHG, cmd->battery); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, + HAL_STATUS_SUCCESS); +} + +static void handle_cops(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_cops_response *cmd = buf; + + if (len != sizeof(*cmd) + cmd->len || + (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) { + error("Invalid cops response command, terminating"); + raise(SIGTERM); + return; + } + + DBG(""); + + hfp_gw_send_info(device.gw, "+COPS: 0,0,\"%.16s\"", + cmd->len ? (char *) cmd->buf : ""); + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_COPS_RESPONSE, HAL_STATUS_SUCCESS); +} + +static unsigned int get_callsetup(uint8_t state) +{ + switch (state) { + case HAL_HANDSFREE_CALL_STATE_INCOMING: + return 1; + case HAL_HANDSFREE_CALL_STATE_DIALING: + return 2; + case HAL_HANDSFREE_CALL_STATE_ALERTING: + return 3; + default: + return 0; + } +} + +static void handle_cind(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_cind_response *cmd = buf; + + DBG(""); + + /* HAL doesn't provide indicators values so need to convert here */ + device.inds[IND_SERVICE].val = cmd->svc; + device.inds[IND_CALL].val = !!(cmd->num_active + cmd->num_held); + device.inds[IND_CALLSETUP].val = get_callsetup(cmd->state); + device.inds[IND_CALLHELD].val = cmd->num_held ? + (cmd->num_active ? 1 : 2) : 0; + device.inds[IND_SIGNAL].val = cmd->signal; + device.inds[IND_ROAM].val = cmd->roam; + device.inds[IND_BATTCHG].val = cmd->batt_chg; + + /* Order must match indicators_defaults table */ + hfp_gw_send_info(device.gw, "+CIND: %u,%u,%u,%u,%u,%u,%u", + device.inds[IND_SERVICE].val, + device.inds[IND_CALL].val, + device.inds[IND_CALLSETUP].val, + device.inds[IND_CALLHELD].val, + device.inds[IND_SIGNAL].val, + device.inds[IND_ROAM].val, + device.inds[IND_BATTCHG].val); + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CIND_RESPONSE, HAL_STATUS_SUCCESS); +} + +static void handle_formatted_at_resp(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_formatted_at_response *cmd = buf; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len || + (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) { + error("Invalid formatted AT response command, terminating"); + raise(SIGTERM); + return; + } + + DBG(""); + + hfp_gw_send_info(device.gw, "%s", cmd->len ? (char *) cmd->buf : ""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, + HAL_STATUS_SUCCESS); +} + +static void handle_at_resp(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_at_response *cmd = buf; + + DBG(""); + + if (cmd->response == HAL_HANDSFREE_AT_RESPONSE_OK) + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + else if (device.cmee_enabled) + hfp_gw_send_error(device.gw, cmd->error); + else + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_AT_RESPONSE, HAL_STATUS_SUCCESS); +} + +static void handle_clcc_resp(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_clcc_response *cmd = buf; + uint8_t status; + char *number; + + if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 && + cmd->number[cmd->number_len - 1] != '\0')) { + error("Invalid CLCC response command, terminating"); + raise(SIGTERM); + return; + } + + DBG(""); + + if (!cmd->index) { + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + status = HAL_STATUS_SUCCESS; + goto done; + } + + number = cmd->number_len ? (char *) cmd->number : ""; + + switch (cmd->state) { + case HAL_HANDSFREE_CALL_STATE_INCOMING: + case HAL_HANDSFREE_CALL_STATE_WAITING: + case HAL_HANDSFREE_CALL_STATE_ACTIVE: + case HAL_HANDSFREE_CALL_STATE_HELD: + case HAL_HANDSFREE_CALL_STATE_DIALING: + case HAL_HANDSFREE_CALL_STATE_ALERTING: + if (cmd->type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && + number[0] != '+') + hfp_gw_send_info(device.gw, + "+CLCC: %u,%u,%u,%u,%u,\"+%s\",%u", + cmd->index, cmd->dir, cmd->state, + cmd->mode, cmd->mpty, number, + cmd->type); + else + hfp_gw_send_info(device.gw, + "+CLCC: %u,%u,%u,%u,%u,\"%s\",%u", + cmd->index, cmd->dir, cmd->state, + cmd->mode, cmd->mpty, number, + cmd->type); + + status = HAL_STATUS_SUCCESS; + break; + case HAL_HANDSFREE_CALL_STATE_IDLE: + default: + status = HAL_STATUS_FAILED; + break; + } + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CLCC_RESPONSE, status); +} + +static gboolean ring_cb(gpointer user_data) +{ + char *clip = user_data; + + hfp_gw_send_info(device.gw, "RING"); + + if (device.clip_enabled && clip) + hfp_gw_send_info(device.gw, "%s", clip); + + return TRUE; +} + +static void phone_state_dialing(int num_active, int num_held) +{ + update_indicator(IND_CALLSETUP, 2); + + if (num_active == 0 && num_held > 0) + update_indicator(IND_CALLHELD, 2); + + if (device.num_active == 0 && device.num_held == 0) + connect_audio(); +} + +static void phone_state_alerting(int num_active, int num_held) +{ + update_indicator(IND_CALLSETUP, 3); +} + +static void phone_state_waiting(int num_active, int num_held, uint8_t type, + const uint8_t *number, int number_len) +{ + char *num; + + if (!device.ccwa_enabled) + return; + + num = number_len ? (char *) number : ""; + + if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+') + hfp_gw_send_info(device.gw, "+CCWA: \"+%s\",%u", num, type); + else + hfp_gw_send_info(device.gw, "+CCWA: \"%s\",%u", num, type); + + update_indicator(IND_CALLSETUP, 1); +} + +static void phone_state_incoming(int num_active, int num_held, uint8_t type, + const uint8_t *number, int number_len) +{ + char *clip, *num; + + if (device.setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) { + if (device.num_active != num_active || + device.num_held != num_held) { + /* + * calls changed while waiting call ie. due to + * termination of active call + */ + update_indicator(IND_CALLHELD, + num_held ? (num_active ? 1 : 2) : 0); + update_indicator(IND_CALL, !!(num_active + num_held)); + } + + return; + } + + if (device.call_hanging_up) + return; + + if (num_active > 0 || num_held > 0) { + phone_state_waiting(num_active, num_held, type, number, + number_len); + return; + } + + update_indicator(IND_CALLSETUP, 1); + + num = number_len ? (char *) number : ""; + + if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+') + clip = g_strdup_printf("+CLIP: \"+%s\",%u", num, type); + else + clip = g_strdup_printf("+CLIP: \"%s\",%u", num, type); + + /* send first RING */ + ring_cb(clip); + + device.ring = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, + RING_TIMEOUT, ring_cb, + clip, g_free); + + if (!device.ring) + g_free(clip); +} + +static void phone_state_idle(int num_active, int num_held) +{ + if (device.ring) { + g_source_remove(device.ring); + device.ring = 0; + } + + switch (device.setup_state) { + case HAL_HANDSFREE_CALL_STATE_INCOMING: + if (num_active > device.num_active) { + update_indicator(IND_CALL, 1); + + if (device.num_active == 0 && device.num_held == 0) + connect_audio(); + } + + if (num_held > device.num_held) + update_indicator(IND_CALLHELD, 1); + + update_indicator(IND_CALLSETUP, 0); + + if (num_active == device.num_active && + num_held == device.num_held) + device.call_hanging_up = true; + + break; + case HAL_HANDSFREE_CALL_STATE_DIALING: + case HAL_HANDSFREE_CALL_STATE_ALERTING: + if (num_active > device.num_active) + update_indicator(IND_CALL, 1); + + update_indicator(IND_CALLHELD, + num_held ? (num_active ? 1 : 2) : 0); + + update_indicator(IND_CALLSETUP, 0); + break; + case HAL_HANDSFREE_CALL_STATE_IDLE: + + if (device.call_hanging_up) { + device.call_hanging_up = false; + return; + } + + /* check if calls swapped */ + if (num_held != 0 && num_active != 0 && + device.num_active == num_held && + device.num_held == num_active) { + /* TODO better way for forcing indicator */ + device.inds[IND_CALLHELD].val = 0; + } else if ((num_active > 0 || num_held > 0) && + device.num_active == 0 && + device.num_held == 0) { + /* + * If number of active or held calls change but there + * was no call setup change this means that there were + * calls present when headset was connected. + */ + connect_audio(); + } else if (num_active == 0 && num_held == 0) { + disconnect_sco(); + } + + update_indicator(IND_CALLHELD, + num_held ? (num_active ? 1 : 2) : 0); + update_indicator(IND_CALL, !!(num_active + num_held)); + update_indicator(IND_CALLSETUP, 0); + + /* If call was terminated due to carrier lost send NO CARRIER */ + if (num_active == 0 && num_held == 0 && + device.inds[IND_SERVICE].val == 0 && + (device.num_active > 0 || device.num_held > 0)) + hfp_gw_send_info(device.gw, "NO CARRIER"); + + break; + default: + DBG("unhandled state %u", device.setup_state); + break; + } +} + +static void handle_phone_state_change(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_phone_state_change *cmd = buf; + uint8_t status; + + if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 && + cmd->number[cmd->number_len - 1] != '\0')) { + error("Invalid phone state change command, terminating"); + raise(SIGTERM); + return; + } + + DBG("active=%u hold=%u state=%u", cmd->num_active, cmd->num_held, + cmd->state); + + switch (cmd->state) { + case HAL_HANDSFREE_CALL_STATE_DIALING: + phone_state_dialing(cmd->num_active, cmd->num_held); + break; + case HAL_HANDSFREE_CALL_STATE_ALERTING: + phone_state_alerting(cmd->num_active, cmd->num_held); + break; + case HAL_HANDSFREE_CALL_STATE_INCOMING: + phone_state_incoming(cmd->num_active, cmd->num_held, cmd->type, + cmd->number, cmd->number_len); + break; + case HAL_HANDSFREE_CALL_STATE_IDLE: + phone_state_idle(cmd->num_active, cmd->num_held); + break; + default: + DBG("unhandled new state %u (current state %u)", cmd->state, + device.setup_state); + + status = HAL_STATUS_FAILED; + goto failed; + } + + device.num_active = cmd->num_active; + device.num_held = cmd->num_held; + device.setup_state = cmd->state; + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_HANDSFREE_CONNECT */ + { handle_connect, false, + sizeof(struct hal_cmd_handsfree_connect) }, + /* HAL_OP_HANDSFREE_DISCONNECT */ + { handle_disconnect, false, + sizeof(struct hal_cmd_handsfree_disconnect) }, + /* HAL_OP_HANDSFREE_CONNECT_AUDIO */ + { handle_connect_audio, false, + sizeof(struct hal_cmd_handsfree_connect_audio) }, + /* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */ + { handle_disconnect_audio, false, + sizeof(struct hal_cmd_handsfree_disconnect_audio) }, + /* define HAL_OP_HANDSFREE_START_VR */ + { handle_start_vr, false, 0 }, + /* define HAL_OP_HANDSFREE_STOP_VR */ + { handle_stop_vr, false, 0 }, + /* HAL_OP_HANDSFREE_VOLUME_CONTROL */ + { handle_volume_control, false, + sizeof(struct hal_cmd_handsfree_volume_control) }, + /* HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF */ + { handle_device_status_notif, false, + sizeof(struct hal_cmd_handsfree_device_status_notif) }, + /* HAL_OP_HANDSFREE_COPS_RESPONSE */ + { handle_cops, true, + sizeof(struct hal_cmd_handsfree_cops_response) }, + /* HAL_OP_HANDSFREE_CIND_RESPONSE */ + { handle_cind, false, + sizeof(struct hal_cmd_handsfree_cind_response) }, + /* HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE */ + { handle_formatted_at_resp, true, + sizeof(struct hal_cmd_handsfree_formatted_at_response) }, + /* HAL_OP_HANDSFREE_AT_RESPONSE */ + { handle_at_resp, false, + sizeof(struct hal_cmd_handsfree_at_response) }, + /* HAL_OP_HANDSFREE_CLCC_RESPONSE */ + { handle_clcc_resp, true, + sizeof(struct hal_cmd_handsfree_clcc_response) }, + /* HAL_OP_HANDSFREE_PHONE_STATE_CHANGE */ + { handle_phone_state_change, true, + sizeof(struct hal_cmd_handsfree_phone_state_change) }, +}; + +static sdp_record_t *headset_ag_record(void) +{ + 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; + uint8_t netid = 0x01; + sdp_data_t *network; + uint8_t ch = HSP_AG_CHANNEL; + + 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, 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, "Voice Gateway", 0, 0); + + sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network); + + sdp_data_free(channel); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static void confirm_sco_cb(GIOChannel *chan, gpointer user_data) +{ + char address[18]; + bdaddr_t bdaddr; + GError *err = NULL; + + if (device.sco) + goto drop; + + bt_io_get(chan, &err, + BT_IO_OPT_DEST, address, + BT_IO_OPT_DEST_BDADDR, &bdaddr, + BT_IO_OPT_INVALID); + if (err) { + error("handsfree: audio confirm failed (%s)", err->message); + g_error_free(err); + goto drop; + } + + DBG("incoming SCO connection from %s", address); + + if (device.state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED || + bacmp(&device.bdaddr, &bdaddr)) { + error("handsfree: audio connection from %s rejected", address); + goto drop; + } + + if (!bt_io_accept(chan, connect_sco_cb, NULL, NULL, NULL)) { + error("handsfree: failed to accept audio connection"); + goto drop; + } + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING); + return; + +drop: + g_io_channel_shutdown(chan, TRUE, NULL); +} + +static bool enable_hsp_ag(void) +{ + sdp_record_t *rec; + GError *err = NULL; + + DBG(""); + + hsp_server = bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(true), + NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_CHANNEL, HSP_AG_CHANNEL, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (!hsp_server) { + error("Failed to listen on Headset rfcomm: %s", err->message); + g_error_free(err); + return false; + } + + rec = headset_ag_record(); + if (!rec) { + error("Failed to allocate Headset record"); + goto failed; + } + + if (bt_adapter_add_record(rec, 0) < 0) { + error("Failed to register Headset record"); + sdp_record_free(rec); + goto failed; + } + + hsp_record_id = rec->handle; + return true; + +failed: + g_io_channel_shutdown(hsp_server, TRUE, NULL); + g_io_channel_unref(hsp_server); + hsp_server = NULL; + + return false; +} + +static void cleanup_hsp_ag(void) +{ + if (hsp_server) { + g_io_channel_shutdown(hsp_server, TRUE, NULL); + g_io_channel_unref(hsp_server); + hsp_server = NULL; + } + + if (hsp_record_id > 0) { + bt_adapter_remove_record(hsp_record_id); + hsp_record_id = 0; + } +} + +static sdp_record_t *hfp_ag_record(void) +{ + 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; + uint8_t ch = HFP_AG_CHANNEL; + + 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(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &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 = 0x0106; + pfseq = sdp_list_append(NULL, &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(NULL, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(NULL, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + /* Codec Negotiation bit in SDP feature is different then in BRSF */ + sdpfeat = hfp_ag_features & 0x0000003F; + if (hfp_ag_features & HFP_AG_FEAT_CODEC) + sdpfeat |= 0x00000020; + else + sdpfeat &= ~0x00000020; + + features = sdp_data_alloc(SDP_UINT16, &sdpfeat); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + sdp_set_info_attr(record, "Hands-Free Audio Gateway", NULL, NULL); + + sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network); + + sdp_data_free(channel); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static bool enable_hfp_ag(void) +{ + sdp_record_t *rec; + GError *err = NULL; + + DBG(""); + + if (hfp_server) + return false; + + hfp_server = bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(false), + NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_CHANNEL, HFP_AG_CHANNEL, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (!hfp_server) { + error("Failed to listen on Handsfree rfcomm: %s", err->message); + g_error_free(err); + return false; + } + + rec = hfp_ag_record(); + if (!rec) { + error("Failed to allocate Handsfree record"); + goto failed; + } + + if (bt_adapter_add_record(rec, 0) < 0) { + error("Failed to register Handsfree record"); + sdp_record_free(rec); + goto failed; + } + + hfp_record_id = rec->handle; + return true; + +failed: + g_io_channel_shutdown(hfp_server, TRUE, NULL); + g_io_channel_unref(hfp_server); + hfp_server = NULL; + + return false; +} + +static void cleanup_hfp_ag(void) +{ + if (hfp_server) { + g_io_channel_shutdown(hfp_server, TRUE, NULL); + g_io_channel_unref(hfp_server); + hfp_server = NULL; + } + + if (hfp_record_id > 0) { + bt_adapter_remove_record(hfp_record_id); + hfp_record_id = 0; + } +} + +static bool enable_sco_server(void) +{ + GError *err = NULL; + + sco_server = bt_io_listen(NULL, confirm_sco_cb, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_INVALID); + if (!sco_server) { + error("handsfree: Failed to listen on SCO: %s", err->message); + g_error_free(err); + cleanup_hsp_ag(); + cleanup_hfp_ag(); + return false; + } + + return true; +} + +static void disable_sco_server(void) +{ + if (sco_server) { + g_io_channel_shutdown(sco_server, TRUE, NULL); + g_io_channel_unref(sco_server); + sco_server = NULL; + } +} + +static void bt_sco_connect(const void *buf, uint16_t len) +{ + int fd; + GError *err; + struct sco_rsp_connect rsp; + + DBG(""); + + if (!device.sco) + goto failed; + + err = NULL; + if (!bt_io_get(device.sco, &err, BT_IO_OPT_MTU, &rsp.mtu, + BT_IO_OPT_INVALID)) { + error("Unable to get MTU: %s\n", err->message); + g_clear_error(&err); + goto failed; + } + + fd = g_io_channel_unix_get_fd(device.sco); + + DBG("fd %d mtu %u", fd, rsp.mtu); + + ipc_send_rsp_full(sco_ipc, SCO_SERVICE_ID, SCO_OP_CONNECT, + sizeof(rsp), &rsp, fd); + + return; + +failed: + ipc_send_rsp(sco_ipc, SCO_SERVICE_ID, SCO_OP_STATUS, SCO_STATUS_FAILED); +} + +static const struct ipc_handler sco_handlers[] = { + /* SCO_OP_CONNECT */ + { bt_sco_connect, false, 0 } +}; + +static void bt_sco_unregister(void) +{ + DBG(""); + + ipc_cleanup(sco_ipc); + sco_ipc = NULL; +} + +static bool bt_sco_register(ipc_disconnect_cb disconnect) +{ + DBG(""); + + sco_ipc = ipc_init(BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH), + SCO_SERVICE_ID, false, disconnect, NULL); + if (!sco_ipc) + return false; + + ipc_register(sco_ipc, SCO_SERVICE_ID, sco_handlers, + G_N_ELEMENTS(sco_handlers)); + + return true; +} + +bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + DBG("mode 0x%x", mode); + + bacpy(&adapter_addr, addr); + + if (!enable_hsp_ag()) + return false; + + if (!enable_sco_server()) { + cleanup_hsp_ag(); + return false; + } + + if (mode == HAL_MODE_HANDSFREE_HSP_ONLY) + goto done; + + hfp_ag_features = HFP_AG_FEATURES; + + if (mode == HAL_MODE_HANDSFREE_HFP_WBS) + hfp_ag_features |= HFP_AG_FEAT_CODEC; + + if (enable_hfp_ag()) + goto done; + + cleanup_hsp_ag(); + disable_sco_server(); + hfp_ag_features = 0; + return false; + +done: + hal_ipc = ipc; + ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + bt_sco_register(NULL); + + return true; +} + +void bt_handsfree_unregister(void) +{ + DBG(""); + + bt_sco_unregister(); + ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE); + hal_ipc = NULL; + + cleanup_hfp_ag(); + cleanup_hsp_ag(); + disable_sco_server(); + + hfp_ag_features = 0; +} diff --git a/android/handsfree.h b/android/handsfree.h new file mode 100644 index 0000000..e5eff47 --- /dev/null +++ b/android/handsfree.h @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * 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 + * + */ + +bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_handsfree_unregister(void); diff --git a/android/hardware/audio.h b/android/hardware/audio.h new file mode 100644 index 0000000..61d92db --- /dev/null +++ b/android/hardware/audio.h @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_AUDIO_HAL_INTERFACE_H +#define ANDROID_AUDIO_HAL_INTERFACE_H + +#include +#include +#include +#include + +#include +#include +#include + +__BEGIN_DECLS + +/** + * The id of this module + */ +#define AUDIO_HARDWARE_MODULE_ID "audio" + +/** + * Name of the audio devices to open + */ +#define AUDIO_HARDWARE_INTERFACE "audio_hw_if" + + +/* Use version 0.1 to be compatible with first generation of audio hw module with version_major + * hardcoded to 1. No audio module API change. + */ +#define AUDIO_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1) +#define AUDIO_MODULE_API_VERSION_CURRENT AUDIO_MODULE_API_VERSION_0_1 + +/* First generation of audio devices had version hardcoded to 0. all devices with versions < 1.0 + * will be considered of first generation API. + */ +#define AUDIO_DEVICE_API_VERSION_0_0 HARDWARE_DEVICE_API_VERSION(0, 0) +#define AUDIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0) +#define AUDIO_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0) +#define AUDIO_DEVICE_API_VERSION_CURRENT AUDIO_DEVICE_API_VERSION_2_0 + +/** + * List of known audio HAL modules. This is the base name of the audio HAL + * library composed of the "audio." prefix, one of the base names below and + * a suffix specific to the device. + * e.g: audio.primary.goldfish.so or audio.a2dp.default.so + */ + +#define AUDIO_HARDWARE_MODULE_ID_PRIMARY "primary" +#define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp" +#define AUDIO_HARDWARE_MODULE_ID_USB "usb" +#define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix" +#define AUDIO_HARDWARE_MODULE_ID_CODEC_OFFLOAD "codec_offload" + +/**************************************/ + +/** + * standard audio parameters that the HAL may need to handle + */ + +/** + * audio device parameters + */ + +/* BT SCO Noise Reduction + Echo Cancellation parameters */ +#define AUDIO_PARAMETER_KEY_BT_NREC "bt_headset_nrec" +#define AUDIO_PARAMETER_VALUE_ON "on" +#define AUDIO_PARAMETER_VALUE_OFF "off" + +/* TTY mode selection */ +#define AUDIO_PARAMETER_KEY_TTY_MODE "tty_mode" +#define AUDIO_PARAMETER_VALUE_TTY_OFF "tty_off" +#define AUDIO_PARAMETER_VALUE_TTY_VCO "tty_vco" +#define AUDIO_PARAMETER_VALUE_TTY_HCO "tty_hco" +#define AUDIO_PARAMETER_VALUE_TTY_FULL "tty_full" + +/* A2DP sink address set by framework */ +#define AUDIO_PARAMETER_A2DP_SINK_ADDRESS "a2dp_sink_address" + +/* Screen state */ +#define AUDIO_PARAMETER_KEY_SCREEN_STATE "screen_state" + +/** + * audio stream parameters + */ + +#define AUDIO_PARAMETER_STREAM_ROUTING "routing" // audio_devices_t +#define AUDIO_PARAMETER_STREAM_FORMAT "format" // audio_format_t +#define AUDIO_PARAMETER_STREAM_CHANNELS "channels" // audio_channel_mask_t +#define AUDIO_PARAMETER_STREAM_FRAME_COUNT "frame_count" // size_t +#define AUDIO_PARAMETER_STREAM_INPUT_SOURCE "input_source" // audio_source_t +#define AUDIO_PARAMETER_STREAM_SAMPLING_RATE "sampling_rate" // uint32_t + +/* Query supported formats. The response is a '|' separated list of strings from + * audio_format_t enum e.g: "sup_formats=AUDIO_FORMAT_PCM_16_BIT" */ +#define AUDIO_PARAMETER_STREAM_SUP_FORMATS "sup_formats" +/* Query supported channel masks. The response is a '|' separated list of strings from + * audio_channel_mask_t enum e.g: "sup_channels=AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_MONO" */ +#define AUDIO_PARAMETER_STREAM_SUP_CHANNELS "sup_channels" +/* Query supported sampling rates. The response is a '|' separated list of integer values e.g: + * "sup_sampling_rates=44100|48000" */ +#define AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES "sup_sampling_rates" + +/** + * audio codec parameters + */ + +#define AUDIO_OFFLOAD_CODEC_PARAMS "music_offload_codec_param" +#define AUDIO_OFFLOAD_CODEC_BIT_PER_SAMPLE "music_offload_bit_per_sample" +#define AUDIO_OFFLOAD_CODEC_BIT_RATE "music_offload_bit_rate" +#define AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE "music_offload_avg_bit_rate" +#define AUDIO_OFFLOAD_CODEC_ID "music_offload_codec_id" +#define AUDIO_OFFLOAD_CODEC_BLOCK_ALIGN "music_offload_block_align" +#define AUDIO_OFFLOAD_CODEC_SAMPLE_RATE "music_offload_sample_rate" +#define AUDIO_OFFLOAD_CODEC_ENCODE_OPTION "music_offload_encode_option" +#define AUDIO_OFFLOAD_CODEC_NUM_CHANNEL "music_offload_num_channels" +#define AUDIO_OFFLOAD_CODEC_DOWN_SAMPLING "music_offload_down_sampling" +#define AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES "delay_samples" +#define AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES "padding_samples" + +/**************************************/ + +/* common audio stream configuration parameters + * You should memset() the entire structure to zero before use to + * ensure forward compatibility + */ +struct audio_config { + uint32_t sample_rate; + audio_channel_mask_t channel_mask; + audio_format_t format; + audio_offload_info_t offload_info; +}; +typedef struct audio_config audio_config_t; + +/* common audio stream parameters and operations */ +struct audio_stream { + + /** + * Return the sampling rate in Hz - eg. 44100. + */ + uint32_t (*get_sample_rate)(const struct audio_stream *stream); + + /* currently unused - use set_parameters with key + * AUDIO_PARAMETER_STREAM_SAMPLING_RATE + */ + int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate); + + /** + * Return size of input/output buffer in bytes for this stream - eg. 4800. + * It should be a multiple of the frame size. See also get_input_buffer_size. + */ + size_t (*get_buffer_size)(const struct audio_stream *stream); + + /** + * Return the channel mask - + * e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO + */ + audio_channel_mask_t (*get_channels)(const struct audio_stream *stream); + + /** + * Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT + */ + audio_format_t (*get_format)(const struct audio_stream *stream); + + /* currently unused - use set_parameters with key + * AUDIO_PARAMETER_STREAM_FORMAT + */ + int (*set_format)(struct audio_stream *stream, audio_format_t format); + + /** + * Put the audio hardware input/output into standby mode. + * Driver should exit from standby mode at the next I/O operation. + * Returns 0 on success and <0 on failure. + */ + int (*standby)(struct audio_stream *stream); + + /** dump the state of the audio input/output device */ + int (*dump)(const struct audio_stream *stream, int fd); + + /** Return the set of device(s) which this stream is connected to */ + audio_devices_t (*get_device)(const struct audio_stream *stream); + + /** + * Currently unused - set_device() corresponds to set_parameters() with key + * AUDIO_PARAMETER_STREAM_ROUTING for both input and output. + * AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by + * input streams only. + */ + int (*set_device)(struct audio_stream *stream, audio_devices_t device); + + /** + * set/get audio stream parameters. The function accepts a list of + * parameter key value pairs in the form: key1=value1;key2=value2;... + * + * Some keys are reserved for standard parameters (See AudioParameter class) + * + * If the implementation does not accept a parameter change while + * the output is active but the parameter is acceptable otherwise, it must + * return -ENOSYS. + * + * The audio flinger will put the stream in standby and then change the + * parameter value. + */ + int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs); + + /* + * Returns a pointer to a heap allocated string. The caller is responsible + * for freeing the memory for it using free(). + */ + char * (*get_parameters)(const struct audio_stream *stream, + const char *keys); + int (*add_audio_effect)(const struct audio_stream *stream, + effect_handle_t effect); + int (*remove_audio_effect)(const struct audio_stream *stream, + effect_handle_t effect); +}; +typedef struct audio_stream audio_stream_t; + +/* type of asynchronous write callback events. Mutually exclusive */ +typedef enum { + STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */ + STREAM_CBK_EVENT_DRAIN_READY /* drain completed */ +} stream_callback_event_t; + +typedef int (*stream_callback_t)(stream_callback_event_t event, void *param, void *cookie); + +/* type of drain requested to audio_stream_out->drain(). Mutually exclusive */ +typedef enum { + AUDIO_DRAIN_ALL, /* drain() returns when all data has been played */ + AUDIO_DRAIN_EARLY_NOTIFY /* drain() returns a short time before all data + from the current track has been played to + give time for gapless track switch */ +} audio_drain_type_t; + +/** + * audio_stream_out is the abstraction interface for the audio output hardware. + * + * It provides information about various properties of the audio output + * hardware driver. + */ + +struct audio_stream_out { + struct audio_stream common; + + /** + * Return the audio hardware driver estimated latency in milliseconds. + */ + uint32_t (*get_latency)(const struct audio_stream_out *stream); + + /** + * Use this method in situations where audio mixing is done in the + * hardware. This method serves as a direct interface with hardware, + * allowing you to directly set the volume as apposed to via the framework. + * This method might produce multiple PCM outputs or hardware accelerated + * codecs, such as MP3 or AAC. + */ + int (*set_volume)(struct audio_stream_out *stream, float left, float right); + + /** + * Write audio buffer to driver. Returns number of bytes written, or a + * negative status_t. If at least one frame was written successfully prior to the error, + * it is suggested that the driver return that successful (short) byte count + * and then return an error in the subsequent call. + * + * If set_callback() has previously been called to enable non-blocking mode + * the write() is not allowed to block. It must write only the number of + * bytes that currently fit in the driver/hardware buffer and then return + * this byte count. If this is less than the requested write size the + * callback function must be called when more space is available in the + * driver/hardware buffer. + */ + ssize_t (*write)(struct audio_stream_out *stream, const void* buffer, + size_t bytes); + + /* return the number of audio frames written by the audio dsp to DAC since + * the output has exited standby + */ + int (*get_render_position)(const struct audio_stream_out *stream, + uint32_t *dsp_frames); + + /** + * get the local time at which the next write to the audio driver will be presented. + * The units are microseconds, where the epoch is decided by the local audio HAL. + */ + int (*get_next_write_timestamp)(const struct audio_stream_out *stream, + int64_t *timestamp); + + /** + * set the callback function for notifying completion of non-blocking + * write and drain. + * Calling this function implies that all future write() and drain() + * must be non-blocking and use the callback to signal completion. + */ + int (*set_callback)(struct audio_stream_out *stream, + stream_callback_t callback, void *cookie); + + /** + * Notifies to the audio driver to stop playback however the queued buffers are + * retained by the hardware. Useful for implementing pause/resume. Empty implementation + * if not supported however should be implemented for hardware with non-trivial + * latency. In the pause state audio hardware could still be using power. User may + * consider calling suspend after a timeout. + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*pause)(struct audio_stream_out* stream); + + /** + * Notifies to the audio driver to resume playback following a pause. + * Returns error if called without matching pause. + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*resume)(struct audio_stream_out* stream); + + /** + * Requests notification when data buffered by the driver/hardware has + * been played. If set_callback() has previously been called to enable + * non-blocking mode, the drain() must not block, instead it should return + * quickly and completion of the drain is notified through the callback. + * If set_callback() has not been called, the drain() must block until + * completion. + * If type==AUDIO_DRAIN_ALL, the drain completes when all previously written + * data has been played. + * If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all + * data for the current track has played to allow time for the framework + * to perform a gapless track switch. + * + * Drain must return immediately on stop() and flush() call + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*drain)(struct audio_stream_out* stream, audio_drain_type_t type ); + + /** + * Notifies to the audio driver to flush the queued data. Stream must already + * be paused before calling flush(). + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*flush)(struct audio_stream_out* stream); + + /** + * Return a recent count of the number of audio frames presented to an external observer. + * This excludes frames which have been written but are still in the pipeline. + * The count is not reset to zero when output enters standby. + * Also returns the value of CLOCK_MONOTONIC as of this presentation count. + * The returned count is expected to be 'recent', + * but does not need to be the most recent possible value. + * However, the associated time should correspond to whatever count is returned. + * Example: assume that N+M frames have been presented, where M is a 'small' number. + * Then it is permissible to return N instead of N+M, + * and the timestamp should correspond to N rather than N+M. + * The terms 'recent' and 'small' are not defined. + * They reflect the quality of the implementation. + * + * 3.0 and higher only. + */ + int (*get_presentation_position)(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp); + +}; +typedef struct audio_stream_out audio_stream_out_t; + +struct audio_stream_in { + struct audio_stream common; + + /** set the input gain for the audio driver. This method is for + * for future use */ + int (*set_gain)(struct audio_stream_in *stream, float gain); + + /** Read audio buffer in from audio driver. Returns number of bytes read, or a + * negative status_t. If at least one frame was read prior to the error, + * read should return that byte count and then return an error in the subsequent call. + */ + ssize_t (*read)(struct audio_stream_in *stream, void* buffer, + size_t bytes); + + /** + * Return the amount of input frames lost in the audio driver since the + * last call of this function. + * Audio driver is expected to reset the value to 0 and restart counting + * upon returning the current value by this function call. + * Such loss typically occurs when the user space process is blocked + * longer than the capacity of audio driver buffers. + * + * Unit: the number of input audio frames + */ + uint32_t (*get_input_frames_lost)(struct audio_stream_in *stream); +}; +typedef struct audio_stream_in audio_stream_in_t; + +/** + * return the frame size (number of bytes per sample). + */ +static inline size_t audio_stream_frame_size(const struct audio_stream *s) +{ + size_t chan_samp_sz; + audio_format_t format = s->get_format(s); + + if (audio_is_linear_pcm(format)) { + chan_samp_sz = audio_bytes_per_sample(format); + return popcount(s->get_channels(s)) * chan_samp_sz; + } + + return sizeof(int8_t); +} + + +/**********************************************************************/ + +/** + * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM + * and the fields of this data structure must begin with hw_module_t + * followed by module specific information. + */ +struct audio_module { + struct hw_module_t common; +}; + +struct audio_hw_device { + struct hw_device_t common; + + /** + * used by audio flinger to enumerate what devices are supported by + * each audio_hw_device implementation. + * + * Return value is a bitmask of 1 or more values of audio_devices_t + * + * NOTE: audio HAL implementations starting with + * AUDIO_DEVICE_API_VERSION_2_0 do not implement this function. + * All supported devices should be listed in audio_policy.conf + * file and the audio policy manager must choose the appropriate + * audio module based on information in this file. + */ + uint32_t (*get_supported_devices)(const struct audio_hw_device *dev); + + /** + * check to see if the audio hardware interface has been initialized. + * returns 0 on success, -ENODEV on failure. + */ + int (*init_check)(const struct audio_hw_device *dev); + + /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */ + int (*set_voice_volume)(struct audio_hw_device *dev, float volume); + + /** + * set the audio volume for all audio activities other than voice call. + * Range between 0.0 and 1.0. If any value other than 0 is returned, + * the software mixer will emulate this capability. + */ + int (*set_master_volume)(struct audio_hw_device *dev, float volume); + + /** + * Get the current master volume value for the HAL, if the HAL supports + * master volume control. AudioFlinger will query this value from the + * primary audio HAL when the service starts and use the value for setting + * the initial master volume across all HALs. HALs which do not support + * this method may leave it set to NULL. + */ + int (*get_master_volume)(struct audio_hw_device *dev, float *volume); + + /** + * set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode + * is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is + * playing, and AUDIO_MODE_IN_CALL when a call is in progress. + */ + int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode); + + /* mic mute */ + int (*set_mic_mute)(struct audio_hw_device *dev, bool state); + int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state); + + /* set/get global audio parameters */ + int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs); + + /* + * Returns a pointer to a heap allocated string. The caller is responsible + * for freeing the memory for it using free(). + */ + char * (*get_parameters)(const struct audio_hw_device *dev, + const char *keys); + + /* Returns audio input buffer size according to parameters passed or + * 0 if one of the parameters is not supported. + * See also get_buffer_size which is for a particular stream. + */ + size_t (*get_input_buffer_size)(const struct audio_hw_device *dev, + const struct audio_config *config); + + /** This method creates and opens the audio hardware output stream */ + int (*open_output_stream)(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out); + + void (*close_output_stream)(struct audio_hw_device *dev, + struct audio_stream_out* stream_out); + + /** This method creates and opens the audio hardware input stream */ + int (*open_input_stream)(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in); + + void (*close_input_stream)(struct audio_hw_device *dev, + struct audio_stream_in *stream_in); + + /** This method dumps the state of the audio hardware */ + int (*dump)(const struct audio_hw_device *dev, int fd); + + /** + * set the audio mute status for all audio activities. If any value other + * than 0 is returned, the software mixer will emulate this capability. + */ + int (*set_master_mute)(struct audio_hw_device *dev, bool mute); + + /** + * Get the current master mute status for the HAL, if the HAL supports + * master mute control. AudioFlinger will query this value from the primary + * audio HAL when the service starts and use the value for setting the + * initial master mute across all HALs. HALs which do not support this + * method may leave it set to NULL. + */ + int (*get_master_mute)(struct audio_hw_device *dev, bool *mute); +}; +typedef struct audio_hw_device audio_hw_device_t; + +/** convenience API for opening and closing a supported device */ + +static inline int audio_hw_device_open(const struct hw_module_t* module, + struct audio_hw_device** device) +{ + return module->methods->open(module, AUDIO_HARDWARE_INTERFACE, + (struct hw_device_t**)device); +} + +static inline int audio_hw_device_close(struct audio_hw_device* device) +{ + return device->common.close(&device->common); +} + + +__END_DECLS + +#endif // ANDROID_AUDIO_INTERFACE_H diff --git a/android/hardware/audio_effect.h b/android/hardware/audio_effect.h new file mode 100644 index 0000000..857a300 --- /dev/null +++ b/android/hardware/audio_effect.h @@ -0,0 +1,1009 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_AUDIO_EFFECT_H +#define ANDROID_AUDIO_EFFECT_H + +#include +#include +#include +#include +#include + +#include + +__BEGIN_DECLS + + +///////////////////////////////////////////////// +// Common Definitions +///////////////////////////////////////////////// + +// +//--- Effect descriptor structure effect_descriptor_t +// + +// Unique effect ID (can be generated from the following site: +// http://www.itu.int/ITU-T/asn1/uuid.html) +// This format is used for both "type" and "uuid" fields of the effect descriptor structure. +// - When used for effect type and the engine is implementing and effect corresponding to a standard +// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface. +// - When used as uuid, it should be a unique UUID for this particular implementation. +typedef struct effect_uuid_s { + uint32_t timeLow; + uint16_t timeMid; + uint16_t timeHiAndVersion; + uint16_t clockSeq; + uint8_t node[6]; +} effect_uuid_t; + +// Maximum length of character strings in structures defines by this API. +#define EFFECT_STRING_LEN_MAX 64 + +// NULL UUID definition (matches SL_IID_NULL_) +#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \ + { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } } +static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER; +static const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_; +static const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210"; + + +// The effect descriptor contains necessary information to facilitate the enumeration of the effect +// engines present in a library. +typedef struct effect_descriptor_s { + effect_uuid_t type; // UUID of to the OpenSL ES interface implemented by this effect + effect_uuid_t uuid; // UUID for this particular implementation + uint32_t apiVersion; // Version of the effect control API implemented + uint32_t flags; // effect engine capabilities/requirements flags (see below) + uint16_t cpuLoad; // CPU load indication (see below) + uint16_t memoryUsage; // Data Memory usage (see below) + char name[EFFECT_STRING_LEN_MAX]; // human readable effect name + char implementor[EFFECT_STRING_LEN_MAX]; // human readable effect implementor name +} effect_descriptor_t; + +// CPU load and memory usage indication: each effect implementation must provide an indication of +// its CPU and memory usage for the audio effect framework to limit the number of effects +// instantiated at a given time on a given platform. +// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS. +// The memory usage is expressed in KB and includes only dynamically allocated memory + +// Definitions for flags field of effect descriptor. +// +---------------------------+-----------+----------------------------------- +// | description | bits | values +// +---------------------------+-----------+----------------------------------- +// | connection mode | 0..2 | 0 insert: after track process +// | | | 1 auxiliary: connect to track auxiliary +// | | | output and use send level +// | | | 2 replace: replaces track process function; +// | | | must implement SRC, volume and mono to stereo. +// | | | 3 pre processing: applied below audio HAL on input +// | | | 4 post processing: applied below audio HAL on output +// | | | 5 - 7 reserved +// +---------------------------+-----------+----------------------------------- +// | insertion preference | 3..5 | 0 none +// | | | 1 first of the chain +// | | | 2 last of the chain +// | | | 3 exclusive (only effect in the insert chain) +// | | | 4..7 reserved +// +---------------------------+-----------+----------------------------------- +// | Volume management | 6..8 | 0 none +// | | | 1 implements volume control +// | | | 2 requires volume indication +// | | | 4 reserved +// +---------------------------+-----------+----------------------------------- +// | Device indication | 9..11 | 0 none +// | | | 1 requires device updates +// | | | 2, 4 reserved +// +---------------------------+-----------+----------------------------------- +// | Sample input mode | 12..13 | 1 direct: process() function or EFFECT_CMD_SET_CONFIG +// | | | command must specify a buffer descriptor +// | | | 2 provider: process() function uses the +// | | | bufferProvider indicated by the +// | | | EFFECT_CMD_SET_CONFIG command to request input. +// | | | buffers. +// | | | 3 both: both input modes are supported +// +---------------------------+-----------+----------------------------------- +// | Sample output mode | 14..15 | 1 direct: process() function or EFFECT_CMD_SET_CONFIG +// | | | command must specify a buffer descriptor +// | | | 2 provider: process() function uses the +// | | | bufferProvider indicated by the +// | | | EFFECT_CMD_SET_CONFIG command to request output +// | | | buffers. +// | | | 3 both: both output modes are supported +// +---------------------------+-----------+----------------------------------- +// | Hardware acceleration | 16..17 | 0 No hardware acceleration +// | | | 1 non tunneled hw acceleration: the process() function +// | | | reads the samples, send them to HW accelerated +// | | | effect processor, reads back the processed samples +// | | | and returns them to the output buffer. +// | | | 2 tunneled hw acceleration: the process() function is +// | | | transparent. The effect interface is only used to +// | | | control the effect engine. This mode is relevant for +// | | | global effects actually applied by the audio +// | | | hardware on the output stream. +// +---------------------------+-----------+----------------------------------- +// | Audio Mode indication | 18..19 | 0 none +// | | | 1 requires audio mode updates +// | | | 2..3 reserved +// +---------------------------+-----------+----------------------------------- +// | Audio source indication | 20..21 | 0 none +// | | | 1 requires audio source updates +// | | | 2..3 reserved +// +---------------------------+-----------+----------------------------------- +// | Effect offload supported | 22 | 0 The effect cannot be offloaded to an audio DSP +// | | | 1 The effect can be offloaded to an audio DSP +// +---------------------------+-----------+----------------------------------- + +// Insert mode +#define EFFECT_FLAG_TYPE_SHIFT 0 +#define EFFECT_FLAG_TYPE_SIZE 3 +#define EFFECT_FLAG_TYPE_MASK (((1 << EFFECT_FLAG_TYPE_SIZE) -1) \ + << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_INSERT (0 << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_AUXILIARY (1 << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_REPLACE (2 << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_PRE_PROC (3 << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_POST_PROC (4 << EFFECT_FLAG_TYPE_SHIFT) + +// Insert preference +#define EFFECT_FLAG_INSERT_SHIFT (EFFECT_FLAG_TYPE_SHIFT + EFFECT_FLAG_TYPE_SIZE) +#define EFFECT_FLAG_INSERT_SIZE 3 +#define EFFECT_FLAG_INSERT_MASK (((1 << EFFECT_FLAG_INSERT_SIZE) -1) \ + << EFFECT_FLAG_INSERT_SHIFT) +#define EFFECT_FLAG_INSERT_ANY (0 << EFFECT_FLAG_INSERT_SHIFT) +#define EFFECT_FLAG_INSERT_FIRST (1 << EFFECT_FLAG_INSERT_SHIFT) +#define EFFECT_FLAG_INSERT_LAST (2 << EFFECT_FLAG_INSERT_SHIFT) +#define EFFECT_FLAG_INSERT_EXCLUSIVE (3 << EFFECT_FLAG_INSERT_SHIFT) + + +// Volume control +#define EFFECT_FLAG_VOLUME_SHIFT (EFFECT_FLAG_INSERT_SHIFT + EFFECT_FLAG_INSERT_SIZE) +#define EFFECT_FLAG_VOLUME_SIZE 3 +#define EFFECT_FLAG_VOLUME_MASK (((1 << EFFECT_FLAG_VOLUME_SIZE) -1) \ + << EFFECT_FLAG_VOLUME_SHIFT) +#define EFFECT_FLAG_VOLUME_CTRL (1 << EFFECT_FLAG_VOLUME_SHIFT) +#define EFFECT_FLAG_VOLUME_IND (2 << EFFECT_FLAG_VOLUME_SHIFT) +#define EFFECT_FLAG_VOLUME_NONE (0 << EFFECT_FLAG_VOLUME_SHIFT) + +// Device indication +#define EFFECT_FLAG_DEVICE_SHIFT (EFFECT_FLAG_VOLUME_SHIFT + EFFECT_FLAG_VOLUME_SIZE) +#define EFFECT_FLAG_DEVICE_SIZE 3 +#define EFFECT_FLAG_DEVICE_MASK (((1 << EFFECT_FLAG_DEVICE_SIZE) -1) \ + << EFFECT_FLAG_DEVICE_SHIFT) +#define EFFECT_FLAG_DEVICE_IND (1 << EFFECT_FLAG_DEVICE_SHIFT) +#define EFFECT_FLAG_DEVICE_NONE (0 << EFFECT_FLAG_DEVICE_SHIFT) + +// Sample input modes +#define EFFECT_FLAG_INPUT_SHIFT (EFFECT_FLAG_DEVICE_SHIFT + EFFECT_FLAG_DEVICE_SIZE) +#define EFFECT_FLAG_INPUT_SIZE 2 +#define EFFECT_FLAG_INPUT_MASK (((1 << EFFECT_FLAG_INPUT_SIZE) -1) \ + << EFFECT_FLAG_INPUT_SHIFT) +#define EFFECT_FLAG_INPUT_DIRECT (1 << EFFECT_FLAG_INPUT_SHIFT) +#define EFFECT_FLAG_INPUT_PROVIDER (2 << EFFECT_FLAG_INPUT_SHIFT) +#define EFFECT_FLAG_INPUT_BOTH (3 << EFFECT_FLAG_INPUT_SHIFT) + +// Sample output modes +#define EFFECT_FLAG_OUTPUT_SHIFT (EFFECT_FLAG_INPUT_SHIFT + EFFECT_FLAG_INPUT_SIZE) +#define EFFECT_FLAG_OUTPUT_SIZE 2 +#define EFFECT_FLAG_OUTPUT_MASK (((1 << EFFECT_FLAG_OUTPUT_SIZE) -1) \ + << EFFECT_FLAG_OUTPUT_SHIFT) +#define EFFECT_FLAG_OUTPUT_DIRECT (1 << EFFECT_FLAG_OUTPUT_SHIFT) +#define EFFECT_FLAG_OUTPUT_PROVIDER (2 << EFFECT_FLAG_OUTPUT_SHIFT) +#define EFFECT_FLAG_OUTPUT_BOTH (3 << EFFECT_FLAG_OUTPUT_SHIFT) + +// Hardware acceleration mode +#define EFFECT_FLAG_HW_ACC_SHIFT (EFFECT_FLAG_OUTPUT_SHIFT + EFFECT_FLAG_OUTPUT_SIZE) +#define EFFECT_FLAG_HW_ACC_SIZE 2 +#define EFFECT_FLAG_HW_ACC_MASK (((1 << EFFECT_FLAG_HW_ACC_SIZE) -1) \ + << EFFECT_FLAG_HW_ACC_SHIFT) +#define EFFECT_FLAG_HW_ACC_SIMPLE (1 << EFFECT_FLAG_HW_ACC_SHIFT) +#define EFFECT_FLAG_HW_ACC_TUNNEL (2 << EFFECT_FLAG_HW_ACC_SHIFT) + +// Audio mode indication +#define EFFECT_FLAG_AUDIO_MODE_SHIFT (EFFECT_FLAG_HW_ACC_SHIFT + EFFECT_FLAG_HW_ACC_SIZE) +#define EFFECT_FLAG_AUDIO_MODE_SIZE 2 +#define EFFECT_FLAG_AUDIO_MODE_MASK (((1 << EFFECT_FLAG_AUDIO_MODE_SIZE) -1) \ + << EFFECT_FLAG_AUDIO_MODE_SHIFT) +#define EFFECT_FLAG_AUDIO_MODE_IND (1 << EFFECT_FLAG_AUDIO_MODE_SHIFT) +#define EFFECT_FLAG_AUDIO_MODE_NONE (0 << EFFECT_FLAG_AUDIO_MODE_SHIFT) + +// Audio source indication +#define EFFECT_FLAG_AUDIO_SOURCE_SHIFT (EFFECT_FLAG_AUDIO_MODE_SHIFT + EFFECT_FLAG_AUDIO_MODE_SIZE) +#define EFFECT_FLAG_AUDIO_SOURCE_SIZE 2 +#define EFFECT_FLAG_AUDIO_SOURCE_MASK (((1 << EFFECT_FLAG_AUDIO_SOURCE_SIZE) -1) \ + << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) +#define EFFECT_FLAG_AUDIO_SOURCE_IND (1 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) +#define EFFECT_FLAG_AUDIO_SOURCE_NONE (0 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) + +// Effect offload indication +#define EFFECT_FLAG_OFFLOAD_SHIFT (EFFECT_FLAG_AUDIO_SOURCE_SHIFT + \ + EFFECT_FLAG_AUDIO_SOURCE_SIZE) +#define EFFECT_FLAG_OFFLOAD_SIZE 1 +#define EFFECT_FLAG_OFFLOAD_MASK (((1 << EFFECT_FLAG_OFFLOAD_SIZE) -1) \ + << EFFECT_FLAG_OFFLOAD_SHIFT) +#define EFFECT_FLAG_OFFLOAD_SUPPORTED (1 << EFFECT_FLAG_OFFLOAD_SHIFT) + +#define EFFECT_MAKE_API_VERSION(M, m) (((M)<<16) | ((m) & 0xFFFF)) +#define EFFECT_API_VERSION_MAJOR(v) ((v)>>16) +#define EFFECT_API_VERSION_MINOR(v) ((m) & 0xFFFF) + + + +///////////////////////////////////////////////// +// Effect control interface +///////////////////////////////////////////////// + +// Effect control interface version 2.0 +#define EFFECT_CONTROL_API_VERSION EFFECT_MAKE_API_VERSION(2,0) + +// Effect control interface structure: effect_interface_s +// The effect control interface is exposed by each effect engine implementation. It consists of +// a set of functions controlling the configuration, activation and process of the engine. +// The functions are grouped in a structure of type effect_interface_s. +// +// Effect control interface handle: effect_handle_t +// The effect_handle_t serves two purposes regarding the implementation of the effect engine: +// - 1 it is the address of a pointer to an effect_interface_s structure where the functions +// of the effect control API for a particular effect are located. +// - 2 it is the address of the context of a particular effect instance. +// A typical implementation in the effect library would define a structure as follows: +// struct effect_module_s { +// const struct effect_interface_s *itfe; +// effect_config_t config; +// effect_context_t context; +// } +// The implementation of EffectCreate() function would then allocate a structure of this +// type and return its address as effect_handle_t +typedef struct effect_interface_s **effect_handle_t; + + +// Forward definition of type audio_buffer_t +typedef struct audio_buffer_s audio_buffer_t; + + + + + + +// Effect control interface definition +struct effect_interface_s { + //////////////////////////////////////////////////////////////////////////////// + // + // Function: process + // + // Description: Effect process function. Takes input samples as specified + // (count and location) in input buffer descriptor and output processed + // samples as specified in output buffer descriptor. If the buffer descriptor + // is not specified the function must use either the buffer or the + // buffer provider function installed by the EFFECT_CMD_SET_CONFIG command. + // The effect framework will call the process() function after the EFFECT_CMD_ENABLE + // command is received and until the EFFECT_CMD_DISABLE is received. When the engine + // receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully + // and when done indicate that it is OK to stop calling the process() function by + // returning the -ENODATA status. + // + // NOTE: the process() function implementation should be "real-time safe" that is + // it should not perform blocking calls: malloc/free, sleep, read/write/open/close, + // pthread_cond_wait/pthread_mutex_lock... + // + // Input: + // self: handle to the effect interface this function + // is called on. + // inBuffer: buffer descriptor indicating where to read samples to process. + // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command. + // + // outBuffer: buffer descriptor indicating where to write processed samples. + // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command. + // + // Output: + // returned value: 0 successful operation + // -ENODATA the engine has finished the disable phase and the framework + // can stop calling process() + // -EINVAL invalid interface handle or + // invalid input/output buffer description + //////////////////////////////////////////////////////////////////////////////// + int32_t (*process)(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer); + //////////////////////////////////////////////////////////////////////////////// + // + // Function: command + // + // Description: Send a command and receive a response to/from effect engine. + // + // Input: + // self: handle to the effect interface this function + // is called on. + // cmdCode: command code: the command can be a standardized command defined in + // effect_command_e (see below) or a proprietary command. + // cmdSize: size of command in bytes + // pCmdData: pointer to command data + // pReplyData: pointer to reply data + // + // Input/Output: + // replySize: maximum size of reply data as input + // actual size of reply data as output + // + // Output: + // returned value: 0 successful operation + // -EINVAL invalid interface handle or + // invalid command/reply size or format according to command code + // The return code should be restricted to indicate problems related to the this + // API specification. Status related to the execution of a particular command should be + // indicated as part of the reply field. + // + // *pReplyData updated with command response + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*command)(effect_handle_t self, + uint32_t cmdCode, + uint32_t cmdSize, + void *pCmdData, + uint32_t *replySize, + void *pReplyData); + //////////////////////////////////////////////////////////////////////////////// + // + // Function: get_descriptor + // + // Description: Returns the effect descriptor + // + // Input: + // self: handle to the effect interface this function + // is called on. + // + // Input/Output: + // pDescriptor: address where to return the effect descriptor. + // + // Output: + // returned value: 0 successful operation. + // -EINVAL invalid interface handle or invalid pDescriptor + // *pDescriptor: updated with the effect descriptor. + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*get_descriptor)(effect_handle_t self, + effect_descriptor_t *pDescriptor); + //////////////////////////////////////////////////////////////////////////////// + // + // Function: process_reverse + // + // Description: Process reverse stream function. This function is used to pass + // a reference stream to the effect engine. If the engine does not need a reference + // stream, this function pointer can be set to NULL. + // This function would typically implemented by an Echo Canceler. + // + // Input: + // self: handle to the effect interface this function + // is called on. + // inBuffer: buffer descriptor indicating where to read samples to process. + // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command. + // + // outBuffer: buffer descriptor indicating where to write processed samples. + // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command. + // If the buffer and buffer provider in the configuration received by + // EFFECT_CMD_SET_CONFIG_REVERSE are also NULL, do not return modified reverse + // stream data + // + // Output: + // returned value: 0 successful operation + // -ENODATA the engine has finished the disable phase and the framework + // can stop calling process_reverse() + // -EINVAL invalid interface handle or + // invalid input/output buffer description + //////////////////////////////////////////////////////////////////////////////// + int32_t (*process_reverse)(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer); +}; + + +// +//--- Standardized command codes for command() function +// +enum effect_command_e { + EFFECT_CMD_INIT, // initialize effect engine + EFFECT_CMD_SET_CONFIG, // configure effect engine (see effect_config_t) + EFFECT_CMD_RESET, // reset effect engine + EFFECT_CMD_ENABLE, // enable effect process + EFFECT_CMD_DISABLE, // disable effect process + EFFECT_CMD_SET_PARAM, // set parameter immediately (see effect_param_t) + EFFECT_CMD_SET_PARAM_DEFERRED, // set parameter deferred + EFFECT_CMD_SET_PARAM_COMMIT, // commit previous set parameter deferred + EFFECT_CMD_GET_PARAM, // get parameter + EFFECT_CMD_SET_DEVICE, // set audio device (see audio.h, audio_devices_t) + EFFECT_CMD_SET_VOLUME, // set volume + EFFECT_CMD_SET_AUDIO_MODE, // set the audio mode (normal, ring, ...) + EFFECT_CMD_SET_CONFIG_REVERSE, // configure effect engine reverse stream(see effect_config_t) + EFFECT_CMD_SET_INPUT_DEVICE, // set capture device (see audio.h, audio_devices_t) + EFFECT_CMD_GET_CONFIG, // read effect engine configuration + EFFECT_CMD_GET_CONFIG_REVERSE, // read configure effect engine reverse stream configuration + EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature. + EFFECT_CMD_GET_FEATURE_CONFIG, // get current feature configuration + EFFECT_CMD_SET_FEATURE_CONFIG, // set current feature configuration + EFFECT_CMD_SET_AUDIO_SOURCE, // set the audio source (see audio.h, audio_source_t) + EFFECT_CMD_OFFLOAD, // set if effect thread is an offload one, + // send the ioHandle of the effect thread + EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code +}; + +//================================================================================================== +// command: EFFECT_CMD_INIT +//-------------------------------------------------------------------------------------------------- +// description: +// Initialize effect engine: All configurations return to default +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_CONFIG +//-------------------------------------------------------------------------------------------------- +// description: +// Apply new audio parameters configurations for input and output buffers +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_config_t) +// data: effect_config_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_RESET +//-------------------------------------------------------------------------------------------------- +// description: +// Reset the effect engine. Keep configuration but resets state and buffer content +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_ENABLE +//-------------------------------------------------------------------------------------------------- +// description: +// Enable the process. Called by the framework before the first call to process() +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_DISABLE +//-------------------------------------------------------------------------------------------------- +// description: +// Disable the process. Called by the framework after the last call to process() +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_PARAM +//-------------------------------------------------------------------------------------------------- +// description: +// Set a parameter and apply it immediately +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_param_t) + size of param and value +// data: effect_param_t + param + value. See effect_param_t definition below for value offset +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_PARAM_DEFERRED +//-------------------------------------------------------------------------------------------------- +// description: +// Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_param_t) + size of param and value +// data: effect_param_t + param + value. See effect_param_t definition below for value offset +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_SET_PARAM_COMMIT +//-------------------------------------------------------------------------------------------------- +// description: +// Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_GET_PARAM +//-------------------------------------------------------------------------------------------------- +// description: +// Get a parameter value +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_param_t) + size of param +// data: effect_param_t + param +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(effect_param_t) + size of param and value +// data: effect_param_t + param + value. See effect_param_t definition below for value offset +//================================================================================================== +// command: EFFECT_CMD_SET_DEVICE +//-------------------------------------------------------------------------------------------------- +// description: +// Set the rendering device the audio output path is connected to. See audio.h, audio_devices_t +// for device values. +// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this +// command when the device changes +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_SET_VOLUME +//-------------------------------------------------------------------------------------------------- +// description: +// Set and get volume. Used by audio framework to delegate volume control to effect engine. +// The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in +// its descriptor to receive this command before every call to process() function +// If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return +// the volume that should be applied before the effect is processed. The overall volume (the volume +// actually applied by the effect engine multiplied by the returned value) should match the value +// indicated in the command. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: n * sizeof(uint32_t) +// data: volume for each channel defined in effect_config_t for output buffer expressed in +// 8.24 fixed point format +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: n * sizeof(uint32_t) / 0 +// data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor: +// volume for each channel defined in effect_config_t for output buffer expressed in +// 8.24 fixed point format +// - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor: +// N/A +// It is legal to receive a null pointer as pReplyData in which case the effect framework has +// delegated volume control to another effect +//================================================================================================== +// command: EFFECT_CMD_SET_AUDIO_MODE +//-------------------------------------------------------------------------------------------------- +// description: +// Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its +// descriptor to receive this command when the audio mode changes. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: audio_mode_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_SET_CONFIG_REVERSE +//-------------------------------------------------------------------------------------------------- +// description: +// Apply new audio parameters configurations for input and output buffers of reverse stream. +// An example of reverse stream is the echo reference supplied to an Acoustic Echo Canceler. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_config_t) +// data: effect_config_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_INPUT_DEVICE +//-------------------------------------------------------------------------------------------------- +// description: +// Set the capture device the audio input path is connected to. See audio.h, audio_devices_t +// for device values. +// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this +// command when the device changes +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_GET_CONFIG +//-------------------------------------------------------------------------------------------------- +// description: +// Read audio parameters configurations for input and output buffers +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(effect_config_t) +// data: effect_config_t +//================================================================================================== +// command: EFFECT_CMD_GET_CONFIG_REVERSE +//-------------------------------------------------------------------------------------------------- +// description: +// Read audio parameters configurations for input and output buffers of reverse stream +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(effect_config_t) +// data: effect_config_t +//================================================================================================== +// command: EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS +//-------------------------------------------------------------------------------------------------- +// description: +// Queries for supported configurations for a particular feature (e.g. get the supported +// combinations of main and auxiliary channels for a noise suppressor). +// The command parameter is the feature identifier (See effect_feature_e for a list of defined +// features) followed by the maximum number of configuration descriptor to return. +// The reply is composed of: +// - status (uint32_t): +// - 0 if feature is supported +// - -ENOSYS if the feature is not supported, +// - -ENOMEM if the feature is supported but the total number of supported configurations +// exceeds the maximum number indicated by the caller. +// - total number of supported configurations (uint32_t) +// - an array of configuration descriptors. +// The actual number of descriptors returned must not exceed the maximum number indicated by +// the caller. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 2 x sizeof(uint32_t) +// data: effect_feature_e + maximum number of configurations to return +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 2 x sizeof(uint32_t) + n x sizeof () +// data: status + total number of configurations supported + array of n config descriptors +//================================================================================================== +// command: EFFECT_CMD_GET_FEATURE_CONFIG +//-------------------------------------------------------------------------------------------------- +// description: +// Retrieves current configuration for a given feature. +// The reply status is: +// - 0 if feature is supported +// - -ENOSYS if the feature is not supported, +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: effect_feature_e +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(uint32_t) + sizeof () +// data: status + config descriptor +//================================================================================================== +// command: EFFECT_CMD_SET_FEATURE_CONFIG +//-------------------------------------------------------------------------------------------------- +// description: +// Sets current configuration for a given feature. +// The reply status is: +// - 0 if feature is supported +// - -ENOSYS if the feature is not supported, +// - -EINVAL if the configuration is invalid +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) + sizeof () +// data: effect_feature_e + config descriptor +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(uint32_t) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_AUDIO_SOURCE +//-------------------------------------------------------------------------------------------------- +// description: +// Set the audio source the capture path is configured for (Camcorder, voice recognition...). +// See audio.h, audio_source_t for values. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_OFFLOAD +//-------------------------------------------------------------------------------------------------- +// description: +// 1.indicate if the playback thread the effect is attached to is offloaded or not +// 2.update the io handle of the playback thread the effect is attached to +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_offload_param_t) +// data: effect_offload_param_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- +// command: EFFECT_CMD_FIRST_PROPRIETARY +//-------------------------------------------------------------------------------------------------- +// description: +// All proprietary effect commands must use command codes above this value. The size and format of +// command and response fields is free in this case +//================================================================================================== + + +// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t +// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with +// regard to the channel mask definition in audio.h, audio_channel_mask_t e.g : +// Stereo: left, right +// 5 point 1: front left, front right, front center, low frequency, back left, back right +// The buffer size is expressed in frame count, a frame being composed of samples for all +// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by +// definition +struct audio_buffer_s { + size_t frameCount; // number of frames in buffer + union { + void* raw; // raw pointer to start of buffer + int32_t* s32; // pointer to signed 32 bit data at start of buffer + int16_t* s16; // pointer to signed 16 bit data at start of buffer + uint8_t* u8; // pointer to unsigned 8 bit data at start of buffer + }; +}; + +// The buffer_provider_s structure contains functions that can be used +// by the effect engine process() function to query and release input +// or output audio buffer. +// The getBuffer() function is called to retrieve a buffer where data +// should read from or written to by process() function. +// The releaseBuffer() function MUST be called when the buffer retrieved +// with getBuffer() is not needed anymore. +// The process function should use the buffer provider mechanism to retrieve +// input or output buffer if the inBuffer or outBuffer passed as argument is NULL +// and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_SET_CONFIG +// command did not specify an audio buffer. + +typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer); + +typedef struct buffer_provider_s { + buffer_function_t getBuffer; // retrieve next buffer + buffer_function_t releaseBuffer; // release used buffer + void *cookie; // for use by client of buffer provider functions +} buffer_provider_t; + + +// The buffer_config_s structure specifies the input or output audio format +// to be used by the effect engine. It is part of the effect_config_t +// structure that defines both input and output buffer configurations and is +// passed by the EFFECT_CMD_SET_CONFIG or EFFECT_CMD_SET_CONFIG_REVERSE command. +typedef struct buffer_config_s { + audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly + uint32_t samplingRate; // sampling rate + uint32_t channels; // channel mask (see audio_channel_mask_t in audio.h) + buffer_provider_t bufferProvider; // buffer provider + uint8_t format; // Audio format (see see audio_format_t in audio.h) + uint8_t accessMode; // read/write or accumulate in buffer (effect_buffer_access_e) + uint16_t mask; // indicates which of the above fields is valid +} buffer_config_t; + +// Values for "accessMode" field of buffer_config_t: +// overwrite, read only, accumulate (read/modify/write) +enum effect_buffer_access_e { + EFFECT_BUFFER_ACCESS_WRITE, + EFFECT_BUFFER_ACCESS_READ, + EFFECT_BUFFER_ACCESS_ACCUMULATE + +}; + +// feature identifiers for EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS command +enum effect_feature_e { + EFFECT_FEATURE_AUX_CHANNELS, // supports auxiliary channels (e.g. dual mic noise suppressor) + EFFECT_FEATURE_CNT +}; + +// EFFECT_FEATURE_AUX_CHANNELS feature configuration descriptor. Describe a combination +// of main and auxiliary channels supported +typedef struct channel_config_s { + audio_channel_mask_t main_channels; // channel mask for main channels + audio_channel_mask_t aux_channels; // channel mask for auxiliary channels +} channel_config_t; + + +// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field +// in buffer_config_t must be taken into account when executing the EFFECT_CMD_SET_CONFIG command +#define EFFECT_CONFIG_BUFFER 0x0001 // buffer field must be taken into account +#define EFFECT_CONFIG_SMP_RATE 0x0002 // samplingRate field must be taken into account +#define EFFECT_CONFIG_CHANNELS 0x0004 // channels field must be taken into account +#define EFFECT_CONFIG_FORMAT 0x0008 // format field must be taken into account +#define EFFECT_CONFIG_ACC_MODE 0x0010 // accessMode field must be taken into account +#define EFFECT_CONFIG_PROVIDER 0x0020 // bufferProvider field must be taken into account +#define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \ + EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \ + EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER) + + +// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_CONFIG +// command to configure audio parameters and buffers for effect engine input and output. +typedef struct effect_config_s { + buffer_config_t inputCfg; + buffer_config_t outputCfg; +} effect_config_t; + + +// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM +// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command. +// psize and vsize represent the actual size of parameter and value. +// +// NOTE: the start of value field inside the data field is always on a 32 bit boundary: +// +// +-----------+ +// | status | sizeof(int) +// +-----------+ +// | psize | sizeof(int) +// +-----------+ +// | vsize | sizeof(int) +// +-----------+ +// | | | | +// ~ parameter ~ > psize | +// | | | > ((psize - 1)/sizeof(int) + 1) * sizeof(int) +// +-----------+ | +// | padding | | +// +-----------+ +// | | | +// ~ value ~ > vsize +// | | | +// +-----------+ + +typedef struct effect_param_s { + int32_t status; // Transaction status (unused for command, used for reply) + uint32_t psize; // Parameter size + uint32_t vsize; // Value size + char data[]; // Start of Parameter + Value data +} effect_param_t; + +// structure used by EFFECT_CMD_OFFLOAD command +typedef struct effect_offload_param_s { + bool isOffload; // true if the playback thread the effect is attached to is offloaded + int ioHandle; // io handle of the playback thread the effect is attached to +} effect_offload_param_t; + + +///////////////////////////////////////////////// +// Effect library interface +///////////////////////////////////////////////// + +// Effect library interface version 3.0 +// Note that EffectsFactory.c only checks the major version component, so changes to the minor +// number can only be used for fully backwards compatible changes +#define EFFECT_LIBRARY_API_VERSION EFFECT_MAKE_API_VERSION(3,0) + +#define AUDIO_EFFECT_LIBRARY_TAG ((('A') << 24) | (('E') << 16) | (('L') << 8) | ('T')) + +// Every effect library must have a data structure named AUDIO_EFFECT_LIBRARY_INFO_SYM +// and the fields of this data structure must begin with audio_effect_library_t + +typedef struct audio_effect_library_s { + // tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG + uint32_t tag; + // Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor + uint32_t version; + // Name of this library + const char *name; + // Author/owner/implementor of the library + const char *implementor; + + //////////////////////////////////////////////////////////////////////////////// + // + // Function: create_effect + // + // Description: Creates an effect engine of the specified implementation uuid and + // returns an effect control interface on this engine. The function will allocate the + // resources for an instance of the requested effect engine and return + // a handle on the effect control interface. + // + // Input: + // uuid: pointer to the effect uuid. + // sessionId: audio session to which this effect instance will be attached. All effects + // created with the same session ID are connected in series and process the same signal + // stream. Knowing that two effects are part of the same effect chain can help the + // library implement some kind of optimizations. + // ioId: identifies the output or input stream this effect is directed to at audio HAL. + // For future use especially with tunneled HW accelerated effects + // + // Input/Output: + // pHandle: address where to return the effect interface handle. + // + // Output: + // returned value: 0 successful operation. + // -ENODEV library failed to initialize + // -EINVAL invalid pEffectUuid or pHandle + // -ENOENT no effect with this uuid found + // *pHandle: updated with the effect interface handle. + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*create_effect)(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle); + + //////////////////////////////////////////////////////////////////////////////// + // + // Function: release_effect + // + // Description: Releases the effect engine whose handle is given as argument. + // All resources allocated to this particular instance of the effect are + // released. + // + // Input: + // handle: handle on the effect interface to be released. + // + // Output: + // returned value: 0 successful operation. + // -ENODEV library failed to initialize + // -EINVAL invalid interface handle + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*release_effect)(effect_handle_t handle); + + //////////////////////////////////////////////////////////////////////////////// + // + // Function: get_descriptor + // + // Description: Returns the descriptor of the effect engine which implementation UUID is + // given as argument. + // + // Input/Output: + // uuid: pointer to the effect uuid. + // pDescriptor: address where to return the effect descriptor. + // + // Output: + // returned value: 0 successful operation. + // -ENODEV library failed to initialize + // -EINVAL invalid pDescriptor or uuid + // *pDescriptor: updated with the effect descriptor. + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*get_descriptor)(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor); +} audio_effect_library_t; + +// Name of the hal_module_info +#define AUDIO_EFFECT_LIBRARY_INFO_SYM AELI + +// Name of the hal_module_info as a string +#define AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR "AELI" + +__END_DECLS + +#endif // ANDROID_AUDIO_EFFECT_H diff --git a/android/hardware/hardware.c b/android/hardware/hardware.c new file mode 100644 index 0000000..4bd5eba --- /dev/null +++ b/android/hardware/hardware.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "HAL" + +#define LOG_INFO " I" +#define LOG_WARN " W" +#define LOG_ERROR " E" +#define LOG_DEBUG " D" +#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg) + +#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg) +#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg) +#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg) + +/** + * Load the file defined by the variant and if successful + * return the dlopen handle and the hmi. + * @return 0 = success, !0 = failure. + */ +static int load(const char *id, + const char *path, + const struct hw_module_t **pHmi) +{ + int status; + void *handle; + struct hw_module_t *hmi; + const char *sym = HAL_MODULE_INFO_SYM_AS_STR; + + /* + * load the symbols resolving undefined symbols before + * dlopen returns. Since RTLD_GLOBAL is not or'd in with + * RTLD_NOW the external symbols will not be global + */ + handle = dlopen(path, RTLD_NOW); + if (handle == NULL) { + char const *err_str = dlerror(); + error("load: module=%s\n%s", path, err_str?err_str:"unknown"); + status = -EINVAL; + goto done; + } + + /* Get the address of the struct hal_module_info. */ + hmi = (struct hw_module_t *)dlsym(handle, sym); + if (hmi == NULL) { + error("load: couldn't find symbol %s", sym); + status = -EINVAL; + goto done; + } + + /* Check that the id matches */ + if (strcmp(id, hmi->id) != 0) { + error("load: id=%s != hmi->id=%s", id, hmi->id); + status = -EINVAL; + goto done; + } + + hmi->dso = handle; + + *pHmi = hmi; + + info("loaded HAL id=%s path=%s hmi=%p handle=%p", + id, path, *pHmi, handle); + + return 0; + +done: + hmi = NULL; + if (handle != NULL) { + dlclose(handle); + handle = NULL; + } + + return status; +} + +int hw_get_module_by_class(const char *class_id, const char *inst, + const struct hw_module_t **module) +{ + char path[PATH_MAX]; + char name[PATH_MAX]; + + if (inst) + snprintf(name, PATH_MAX, "%s.%s", class_id, inst); + else + snprintf(name, PATH_MAX, "%s", class_id); + + /* + * Here we rely on the fact that calling dlopen multiple times on + * the same .so will simply increment a refcount (and not load + * a new copy of the library). + * We also assume that dlopen() is thread-safe. + */ + snprintf(path, sizeof(path), "%s/%s.default.so", PLUGINDIR, name); + + return load(class_id, path, module); +} + +int hw_get_module(const char *id, const struct hw_module_t **module) +{ + return hw_get_module_by_class(id, NULL, module); +} diff --git a/android/health.c b/android/health.c new file mode 100644 index 0000000..655d9f9 --- /dev/null +++ b/android/health.c @@ -0,0 +1,126 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/log.h" + +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "utils.h" +#include "bluetooth.h" +#include "health.h" + +static bdaddr_t adapter_addr; +static struct ipc *hal_ipc = NULL; + +static void bt_health_register_app(const void *buf, uint16_t len) +{ + DBG("Not implemented"); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP, + HAL_STATUS_UNSUPPORTED); +} + +static void bt_health_mdep_cfg_data(const void *buf, uint16_t len) +{ + DBG("Not implemented"); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, + HAL_STATUS_UNSUPPORTED); +} + +static void bt_health_unregister_app(const void *buf, uint16_t len) +{ + DBG("Not implemented"); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP, + HAL_STATUS_UNSUPPORTED); +} + +static void bt_health_connect_channel(const void *buf, uint16_t len) +{ + DBG("Not implemented"); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_UNSUPPORTED); +} + +static void bt_health_destroy_channel(const void *buf, uint16_t len) +{ + DBG("Not implemented"); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_UNSUPPORTED); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_HEALTH_REG_APP */ + { bt_health_register_app, true, + sizeof(struct hal_cmd_health_reg_app) }, + /* HAL_OP_HEALTH_MDEP */ + { bt_health_mdep_cfg_data, true, + sizeof(struct hal_cmd_health_mdep) }, + /* HAL_OP_HEALTH_UNREG_APP */ + { bt_health_unregister_app, false, + sizeof(struct hal_cmd_health_unreg_app) }, + /* HAL_OP_HEALTH_CONNECT_CHANNEL */ + { bt_health_connect_channel, false, + sizeof(struct hal_cmd_health_connect_channel) }, + /* HAL_OP_HEALTH_DESTROY_CHANNEL */ + { bt_health_destroy_channel, false, + sizeof(struct hal_cmd_health_destroy_channel) }, +}; + +bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + DBG(""); + + bacpy(&adapter_addr, addr); + + hal_ipc = ipc; + ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + return true; +} + +void bt_health_unregister(void) +{ + DBG(""); + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH); + hal_ipc = NULL; +} diff --git a/android/health.h b/android/health.h new file mode 100644 index 0000000..0b32fd3 --- /dev/null +++ b/android/health.h @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 + * + */ + +bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_health_unregister(void); diff --git a/android/hidhost.c b/android/hidhost.c index 8bfdfed..1758020 100644 --- a/android/hidhost.c +++ b/android/hidhost.c @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -37,14 +38,15 @@ #include "lib/bluetooth.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" -#include "lib/uuid.h" #include "src/shared/mgmt.h" +#include "src/shared/util.h" #include "src/sdp-client.h" -#include "src/glib-helper.h" +#include "src/uuid-helper.h" #include "profiles/input/uhid_copy.h" +#include "src/log.h" -#include "log.h" #include "hal-msg.h" +#include "ipc-common.h" #include "ipc.h" #include "hidhost.h" #include "utils.h" @@ -82,6 +84,8 @@ static GIOChannel *ctrl_io = NULL; static GIOChannel *intr_io = NULL; static GSList *devices = NULL; +static struct ipc *hal_ipc = NULL; + struct hid_device { bdaddr_t dst; uint8_t state; @@ -119,14 +123,16 @@ static void uhid_destroy(int fd) ev.type = UHID_DESTROY; if (write(fd, &ev, sizeof(ev)) < 0) - error("Failed to destroy uHID device: %s (%d)", + error("hidhost: Failed to destroy uHID device: %s (%d)", strerror(errno), errno); close(fd); } -static void hid_device_free(struct hid_device *dev) +static void hid_device_free(void *data) { + struct hid_device *dev = data; + if (dev->ctrl_watch > 0) g_source_remove(dev->ctrl_watch); @@ -148,37 +154,72 @@ static void hid_device_free(struct hid_device *dev) uhid_destroy(dev->uhid_fd); g_free(dev->rd_data); + g_free(dev); +} +static void hid_device_remove(struct hid_device *dev) +{ devices = g_slist_remove(devices, dev); - g_free(dev); + hid_device_free(dev); } -static void handle_uhid_event(struct hid_device *dev, struct uhid_event *ev) +static bool hex2buf(const uint8_t *hex, uint8_t *buf, int buf_size) { - int fd, i; - uint8_t *req = NULL; - uint8_t req_size = 0; + int i, j; + char c; + uint8_t b; + + for (i = 0, j = 0; i < buf_size; i++, j++) { + c = toupper(hex[j]); + + if (c >= '0' && c <= '9') + b = c - '0'; + else if (c >= 'A' && c <= 'F') + b = 10 + c - 'A'; + else + return false; + + j++; + + c = toupper(hex[j]); + + if (c >= '0' && c <= '9') + b = b * 16 + c - '0'; + else if (c >= 'A' && c <= 'F') + b = b * 16 + 10 + c - 'A'; + else + return false; + + buf[i] = b; + } + + return true; +} - if (!(dev->ctrl_io)) +static void handle_uhid_output(struct hid_device *dev, + struct uhid_output_req *output) +{ + int fd, req_size; + uint8_t *req; + + if (!dev->ctrl_io) return; - req_size = 1 + (ev->u.output.size / 2); - req = g_try_malloc0(req_size); + req_size = 1 + output->size; + req = malloc0(req_size); if (!req) return; - req[0] = HID_MSG_SET_REPORT | ev->u.output.rtype; - for (i = 0; i < (req_size - 1); i++) - sscanf((char *) &(ev->u.output.data)[i * 2], - "%hhx", &req[1 + i]); + req[0] = HID_MSG_SET_REPORT | output->rtype; + memcpy(req + 1, output->data, req_size - 1); fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, req, req_size) < 0) - error("error writing set_report: %s (%d)", - strerror(errno), errno); + error("hidhost: error writing set_report: %s (%d)", + strerror(errno), errno); - g_free(req); + free(req); } static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond, @@ -208,31 +249,38 @@ static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond, switch (ev.type) { case UHID_START: case UHID_STOP: - /* These are called to start and stop the underlying hardware. + /* + * These are called to start and stop the underlying hardware. * We open the channels before creating the device so the * hardware is always ready. No need to handle these. * The kernel never destroys a device itself! Only an explicit - * UHID_DESTROY request can remove a device. */ - + * UHID_DESTROY request can remove a device. + */ break; case UHID_OPEN: case UHID_CLOSE: - /* OPEN/CLOSE are sent whenever user-space opens any interface + /* + * OPEN/CLOSE are sent whenever user-space opens any interface * provided by the kernel HID device. Whenever the open-count * is non-zero we must be ready for I/O. As long as it is zero, * we can decide to drop all I/O and put the device - * asleep This is optional, though. */ + * asleep This is optional, though. + */ break; case UHID_OUTPUT: + handle_uhid_output(dev, &ev.u.output); + break; case UHID_FEATURE: - handle_uhid_event(dev, &ev); + /* TODO */ break; case UHID_OUTPUT_EV: - /* This is only sent by kernels prior to linux-3.11. It + /* + * This is only sent by kernels prior to linux-3.11. It * requires us to parse HID-descriptors in user-space to * properly handle it. This is redundant as the kernel * does it already. That's why newer kernels assemble - * the output-reports and send it to us via UHID_OUTPUT. */ + * the output-reports and send it to us via UHID_OUTPUT. + */ DBG("UHID_OUTPUT_EV unsupported"); break; default: @@ -260,7 +308,8 @@ static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data) fd = g_io_channel_unix_get_fd(chan); bread = read(fd, buf, sizeof(buf)); if (bread < 0) { - error("read: %s(%d)", strerror(errno), -errno); + error("hidhost: read from interrupt failed: %s(%d)", + strerror(errno), -errno); return TRUE; } @@ -296,8 +345,8 @@ static void bt_hid_notify_state(struct hid_device *dev, uint8_t state) bdaddr2android(&dev->dst, ev.bdaddr); ev.state = state; - ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_CONN_STATE, - sizeof(ev), &ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev); } static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, @@ -314,9 +363,11 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, error: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); - /* Checking for ctrl_watch avoids a double g_io_channel_shutdown since + /* + * 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 */ + * this mainloop iteration + */ if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->ctrl_watch) g_io_channel_shutdown(chan, TRUE, NULL); @@ -324,7 +375,7 @@ error: if (dev->ctrl_io && !(cond & G_IO_NVAL)) g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); - hid_device_free(dev); + hid_device_remove(dev); return FALSE; } @@ -355,8 +406,8 @@ static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf, ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL; } - ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_PROTO_MODE, - sizeof(ev), &ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev); } static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf, @@ -369,26 +420,30 @@ static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf, ba2str(&dev->dst, address); DBG("device %s", address); - ev_len = sizeof(*ev) + sizeof(struct hal_ev_hidhost_get_report) + 1; + ev_len = sizeof(*ev); if (!((buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) || (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_OUTPUT)) || - (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) { + (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) { ev = g_malloc0(ev_len); ev->status = buf[0]; bdaddr2android(&dev->dst, ev->bdaddr); goto send; } - /* Report porotocol mode reply contains id after hdr, in boot - * protocol mode id doesn't exist */ + /* + * Report porotocol mode reply contains id after hdr, in boot + * protocol mode id doesn't exist + */ ev_len += (dev->boot_dev) ? (len - 1) : (len - 2); ev = g_malloc0(ev_len); ev->status = HAL_HIDHOST_STATUS_OK; bdaddr2android(&dev->dst, ev->bdaddr); - /* Report porotocol mode reply contains id after hdr, in boot - * protocol mode id doesn't exist */ + /* + * Report porotocol mode reply contains id after hdr, in boot + * protocol mode id doesn't exist + */ if (dev->boot_dev) { ev->len = len - 1; memcpy(ev->data, buf + 1, ev->len); @@ -398,8 +453,8 @@ static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf, } send: - ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_GET_REPORT, - ev_len, ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_GET_REPORT, ev_len, ev); g_free(ev); } @@ -423,9 +478,8 @@ static void bt_hid_notify_virtual_unplug(struct hid_device *dev, ev.status = HAL_HIDHOST_STATUS_OK; } - ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_VIRTUAL_UNPLUG, - sizeof(ev), &ev); - + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev); } static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data) @@ -439,7 +493,8 @@ static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data) fd = g_io_channel_unix_get_fd(chan); bread = read(fd, buf, sizeof(buf)); if (bread < 0) { - error("read: %s(%d)", strerror(errno), -errno); + error("hidhost: read from control failed: %s(%d)", + strerror(errno), -errno); return TRUE; } @@ -476,16 +531,18 @@ static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, error: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); - /* Checking for intr_watch avoids a double g_io_channel_shutdown since + /* + * 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 */ + * this mainloop iteration + */ if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch) g_io_channel_shutdown(chan, TRUE, NULL); if (dev->intr_io && !(cond & G_IO_NVAL)) g_io_channel_shutdown(dev->intr_io, TRUE, NULL); - hid_device_free(dev); + hid_device_remove(dev); return FALSE; } @@ -508,8 +565,8 @@ static void bt_hid_set_info(struct hid_device *dev) memset(ev.descr, 0, sizeof(ev.descr)); memcpy(ev.descr, dev->rd_data, ev.descr_len); - ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO, sizeof(ev), - &ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO, + sizeof(ev), &ev); } static int uhid_create(struct hid_device *dev) @@ -522,7 +579,8 @@ static int uhid_create(struct hid_device *dev) dev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC); if (dev->uhid_fd < 0) { err = -errno; - error("Failed to open uHID device: %s", strerror(errno)); + error("hidhost: Failed to open uHID device: %s", + strerror(errno)); return err; } @@ -539,7 +597,8 @@ static int uhid_create(struct hid_device *dev) if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) { err = -errno; - error("Failed to create uHID device: %s", strerror(errno)); + error("hidhost: Failed to create uHID device: %s", + strerror(errno)); close(dev->uhid_fd); dev->uhid_fd = -1; return err; @@ -564,7 +623,8 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, DBG(""); if (conn_err) { - error("%s", conn_err->message); + error("hidhost: Failed to connect interrupt channel (%s)", + conn_err->message); state = HAL_HIDHOST_STATE_FAILED; goto failed; } @@ -584,7 +644,7 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, failed: bt_hid_notify_state(dev, state); - hid_device_free(dev); + hid_device_remove(dev); } static void control_connect_cb(GIOChannel *chan, GError *conn_err, @@ -597,7 +657,8 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err, if (conn_err) { bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); - error("%s", conn_err->message); + error("hidhost: Failed to connect control channel (%s)", + conn_err->message); goto failed; } @@ -609,7 +670,8 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!dev->intr_io) { - error("%s", err->message); + error("hidhost: Failed to connect interrupt channel (%s)", + err->message); g_error_free(err); goto failed; } @@ -621,7 +683,7 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err, return; failed: - hid_device_free(dev); + hid_device_remove(dev); } static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) @@ -633,12 +695,12 @@ static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) DBG(""); if (err < 0) { - error("Unable to get SDP record: %s", strerror(-err)); + error("hidhost: Unable to get SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { - error("No SDP records found"); + error("hidhost: No SDP records found"); goto fail; } @@ -646,18 +708,6 @@ static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) sdp_record_t *rec = list->data; sdp_data_t *data; - data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); - if (data) - dev->vendor = data->val.uint16; - - data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); - if (data) - dev->product = data->val.uint16; - - data = sdp_data_get(rec, SDP_ATTR_VERSION); - if (data) - dev->version = data->val.uint16; - data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); if (data) dev->country = data->val.uint8; @@ -708,7 +758,8 @@ static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (gerr) { - error("%s", gerr->message); + error("hidhost: Failed to connect control channel (%s)", + gerr->message); g_error_free(gerr); goto fail; } @@ -717,7 +768,57 @@ static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) fail: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); - hid_device_free(dev); + hid_device_remove(dev); +} + +static void hid_sdp_did_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct hid_device *dev = data; + sdp_list_t *list; + uuid_t uuid; + + DBG(""); + + if (err < 0) { + error("hidhost: Unable to get Device ID SDP record: %s", + strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + error("hidhost: No Device ID SDP records found"); + goto fail; + } + + for (list = recs; list; list = list->next) { + sdp_record_t *rec = list->data; + sdp_data_t *data; + + data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); + if (data) + dev->vendor = data->val.uint16; + + data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); + if (data) + dev->product = data->val.uint16; + + data = sdp_data_get(rec, SDP_ATTR_VERSION); + if (data) + dev->version = data->val.uint16; + } + + sdp_uuid16_create(&uuid, HID_SVCLASS_ID); + if (bt_search_service(&adapter_addr, &dev->dst, &uuid, + hid_sdp_search_cb, dev, NULL, 0) < 0) { + error("hidhost: Failed to search SDP details"); + goto fail; + } + + return; + +fail: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + hid_device_remove(dev); } static void bt_hid_connect(const void *buf, uint16_t len) @@ -747,11 +848,11 @@ static void bt_hid_connect(const void *buf, uint16_t len) ba2str(&dev->dst, addr); DBG("connecting to %s", addr); - bt_string2uuid(&uuid, HID_UUID); + sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID); if (bt_search_service(&adapter_addr, &dev->dst, &uuid, - hid_sdp_search_cb, dev, NULL) < 0) { - error("Failed to search sdp details"); - hid_device_free(dev); + hid_sdp_did_search_cb, dev, NULL, 0) < 0) { + error("hidhost: Failed to search DeviceID SDP details"); + hid_device_remove(dev); status = HAL_STATUS_FAILED; goto failed; } @@ -762,7 +863,8 @@ static void bt_hid_connect(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, + status); } static void bt_hid_disconnect(const void *buf, uint16_t len) @@ -797,7 +899,8 @@ static void bt_hid_disconnect(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, + status); } static void bt_hid_virtual_unplug(const void *buf, uint16_t len) @@ -832,7 +935,7 @@ static void bt_hid_virtual_unplug(const void *buf, uint16_t len) fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, &hdr, sizeof(hdr)) < 0) { - error("error writing virtual unplug command: %s (%d)", + error("hidhost: Error writing virtual unplug command: %s (%d)", strerror(errno), errno); status = HAL_STATUS_FAILED; goto failed; @@ -850,19 +953,29 @@ static void bt_hid_virtual_unplug(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_VIRTUAL_UNPLUG, - status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_VIRTUAL_UNPLUG, status); } static void bt_hid_info(const void *buf, uint16_t len) { - /* Data from hal_cmd_hidhost_set_info is usefull only when we create + const struct hal_cmd_hidhost_set_info *cmd = buf; + + if (len != sizeof(*cmd) + cmd->descr_len) { + error("Invalid hid set info size (%u bytes), terminating", len); + raise(SIGTERM); + return; + } + + /* + * Data from hal_cmd_hidhost_set_info is usefull only when we create * UHID device. Once device is created all the transactions will be * done through the fd. There is no way to use this information - * once device is created with HID internals. */ + * once device is created with HID internals. + */ DBG("Not supported"); - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, HAL_STATUS_UNSUPPORTED); } @@ -878,6 +991,15 @@ static void bt_hid_get_protocol(const void *buf, uint16_t len) DBG(""); + switch (cmd->mode) { + case HAL_HIDHOST_REPORT_PROTOCOL: + case HAL_HIDHOST_BOOT_PROTOCOL: + break; + default: + status = HAL_STATUS_INVALID; + goto failed; + } + android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); @@ -888,16 +1010,11 @@ static void bt_hid_get_protocol(const void *buf, uint16_t len) dev = l->data; - if (dev->boot_dev) { - status = HAL_STATUS_UNSUPPORTED; - goto failed; - } - hdr = HID_MSG_GET_PROTOCOL | cmd->mode; fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, &hdr, sizeof(hdr)) < 0) { - error("error writing device_get_protocol: %s (%d)", + error("hidhost: Error writing device_get_protocol: %s (%d)", strerror(errno), errno); status = HAL_STATUS_FAILED; goto failed; @@ -908,8 +1025,8 @@ static void bt_hid_get_protocol(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL, - status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_PROTOCOL, status); } static void bt_hid_set_protocol(const void *buf, uint16_t len) @@ -924,6 +1041,15 @@ static void bt_hid_set_protocol(const void *buf, uint16_t len) DBG(""); + switch (cmd->mode) { + case HAL_HIDHOST_REPORT_PROTOCOL: + case HAL_HIDHOST_BOOT_PROTOCOL: + break; + default: + status = HAL_STATUS_INVALID; + goto failed; + } + android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); @@ -934,16 +1060,11 @@ static void bt_hid_set_protocol(const void *buf, uint16_t len) dev = l->data; - if (dev->boot_dev) { - status = HAL_STATUS_UNSUPPORTED; - goto failed; - } - hdr = HID_MSG_SET_PROTOCOL | cmd->mode; fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, &hdr, sizeof(hdr)) < 0) { - error("error writing device_set_protocol: %s (%d)", + error("hidhost: error writing device_set_protocol: %s (%d)", strerror(errno), errno); status = HAL_STATUS_FAILED; goto failed; @@ -954,8 +1075,8 @@ static void bt_hid_set_protocol(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL, - status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_PROTOCOL, status); } static void bt_hid_get_report(const void *buf, uint16_t len) @@ -971,6 +1092,16 @@ static void bt_hid_get_report(const void *buf, uint16_t len) DBG(""); + switch (cmd->type) { + case HAL_HIDHOST_INPUT_REPORT: + case HAL_HIDHOST_OUTPUT_REPORT: + case HAL_HIDHOST_FEATURE_REPORT: + break; + default: + status = HAL_STATUS_INVALID; + goto failed; + } + android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); @@ -992,13 +1123,13 @@ static void bt_hid_get_report(const void *buf, uint16_t len) if (cmd->buf_size > 0) { req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD; - bt_put_le16(cmd->buf_size, &req[2]); + put_le16(cmd->buf_size, &req[2]); } fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, req, req_size) < 0) { - error("error writing hid_get_report: %s (%d)", + error("hidhost: error writing hid_get_report: %s (%d)", strerror(errno), errno); g_free(req); status = HAL_STATUS_FAILED; @@ -1011,7 +1142,8 @@ static void bt_hid_get_report(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, + status); } static void bt_hid_set_report(const void *buf, uint16_t len) @@ -1020,8 +1152,8 @@ static void bt_hid_set_report(const void *buf, uint16_t len) struct hid_device *dev; GSList *l; bdaddr_t dst; - int i, fd; - uint8_t *req; + int fd; + uint8_t *req = NULL; uint8_t req_size; uint8_t status; @@ -1034,6 +1166,16 @@ static void bt_hid_set_report(const void *buf, uint16_t len) return; } + switch (cmd->type) { + case HAL_HIDHOST_INPUT_REPORT: + case HAL_HIDHOST_OUTPUT_REPORT: + case HAL_HIDHOST_FEATURE_REPORT: + break; + default: + status = HAL_STATUS_INVALID; + goto failed; + } + android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); @@ -1057,28 +1199,33 @@ static void bt_hid_set_report(const void *buf, uint16_t len) } req[0] = HID_MSG_SET_REPORT | cmd->type; - /* Report data coming to HAL is in ascii format, HAL sends - * data in hex to daemon, so convert to binary. */ - for (i = 0; i < (req_size - 1); i++) - sscanf((char *) &(cmd->data)[i * 2], "%hhx", &(req + 1)[i]); + /* + * Report data coming to HAL is in ascii format, HAL sends + * data in hex to daemon, so convert to binary. + */ + if (!hex2buf(cmd->data, req + 1, req_size - 1)) { + status = HAL_STATUS_INVALID; + goto failed; + } fd = g_io_channel_unix_get_fd(dev->ctrl_io); if (write(fd, req, req_size) < 0) { - error("error writing hid_set_report: %s (%d)", + error("hidhost: error writing hid_set_report: %s (%d)", strerror(errno), errno); - g_free(req); status = HAL_STATUS_FAILED; goto failed; } dev->last_hid_msg = HID_MSG_SET_REPORT; - g_free(req); status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, status); + g_free(req); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, + status); } static void bt_hid_send_data(const void *buf, uint16_t len) @@ -1087,8 +1234,8 @@ static void bt_hid_send_data(const void *buf, uint16_t len) struct hid_device *dev; GSList *l; bdaddr_t dst; - int i, fd; - uint8_t *req; + int fd; + uint8_t *req = NULL; uint8_t req_size; uint8_t status; @@ -1124,27 +1271,31 @@ static void bt_hid_send_data(const void *buf, uint16_t len) } req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT; - /* Report data coming to HAL is in ascii format, HAL sends - * data in hex to daemon, so convert to binary. */ - for (i = 0; i < (req_size - 1); i++) - sscanf((char *) &(cmd->data)[i * 2], "%hhx", &(req + 1)[i]); + /* + * Report data coming to HAL is in ascii format, HAL sends + * data in hex to daemon, so convert to binary. + */ + if (!hex2buf(cmd->data, req + 1, req_size - 1)) { + status = HAL_STATUS_INVALID; + goto failed; + } fd = g_io_channel_unix_get_fd(dev->intr_io); if (write(fd, req, req_size) < 0) { - error("error writing data to HID device: %s (%d)", + error("hidhost: error writing data to HID device: %s (%d)", strerror(errno), errno); - g_free(req); status = HAL_STATUS_FAILED; goto failed; } - g_free(req); - status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, status); + g_free(req); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, + status); } static const struct ipc_handler cmd_handlers[] = { @@ -1174,7 +1325,7 @@ static const struct ipc_handler cmd_handlers[] = { static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct hid_device *dev; - bdaddr_t src, dst; + bdaddr_t dst; char address[18]; uint16_t psm; GError *gerr = NULL; @@ -1182,17 +1333,17 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) uuid_t uuid; if (err) { - error("%s", err->message); + error("hidhost: Connect failed (%s)", err->message); return; } bt_io_get(chan, &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); + error("hidhost: Failed to read remote address (%s)", + gerr->message); g_io_channel_shutdown(chan, TRUE, NULL); g_error_free(gerr); return; @@ -1212,11 +1363,11 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) dev->ctrl_io = g_io_channel_ref(chan); dev->uhid_fd = -1; - bt_string2uuid(&uuid, HID_UUID); - if (bt_search_service(&src, &dev->dst, &uuid, - hid_sdp_search_cb, dev, NULL) < 0) { - error("failed to search sdp details"); - hid_device_free(dev); + sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID); + if (bt_search_service(&adapter_addr, &dev->dst, &uuid, + hid_sdp_did_search_cb, dev, NULL, 0) < 0) { + error("hidhost: Failed to search DID SDP details"); + hid_device_remove(dev); return; } @@ -1243,7 +1394,7 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) } } -bool bt_hid_register(const bdaddr_t *addr) +bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { GError *err = NULL; @@ -1257,7 +1408,8 @@ bool bt_hid_register(const bdaddr_t *addr) BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!ctrl_io) { - error("Failed to listen on ctrl channel: %s", err->message); + error("hidhost: Failed to listen on control channel: %s", + err->message); g_error_free(err); return false; } @@ -1268,7 +1420,8 @@ bool bt_hid_register(const bdaddr_t *addr) BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!intr_io) { - error("Failed to listen on intr channel: %s", err->message); + error("hidhost: Failed to listen on interrupt channel: %s", + err->message); g_error_free(err); g_io_channel_shutdown(ctrl_io, TRUE, NULL); @@ -1278,25 +1431,19 @@ bool bt_hid_register(const bdaddr_t *addr) return false; } - ipc_register(HAL_SERVICE_ID_HIDHOST, cmd_handlers, + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_HIDHOST, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; } -static void free_hid_devices(gpointer data, gpointer user_data) -{ - struct hid_device *dev = data; - - bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); - hid_device_free(dev); -} - void bt_hid_unregister(void) { DBG(""); - g_slist_foreach(devices, free_hid_devices, NULL); + g_slist_free_full(devices, hid_device_free); devices = NULL; if (ctrl_io) { @@ -1311,5 +1458,6 @@ void bt_hid_unregister(void) intr_io = NULL; } - ipc_unregister(HAL_SERVICE_ID_HIDHOST); + ipc_unregister(hal_ipc, HAL_SERVICE_ID_HIDHOST); + hal_ipc = NULL; } diff --git a/android/hidhost.h b/android/hidhost.h index ea14446..e6b87ed 100644 --- a/android/hidhost.h +++ b/android/hidhost.h @@ -2,24 +2,24 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ -bool bt_hid_register(const bdaddr_t *addr); +bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_hid_unregister(void); diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc new file mode 100644 index 0000000..af62121 --- /dev/null +++ b/android/init.bluetooth.rc @@ -0,0 +1,38 @@ +# required permissions +on boot + chown bluetooth bluetooth /data/misc/bluetooth + chown bluetooth bluetooth /dev/uhid + chown system bluetooth /dev/uinput + +# services +on property:bluetooth.start=daemon + setprop bluetooth.start none + start bluetoothd + +on property:bluetooth.stop=daemon + setprop bluetooth.stop none + stop bluetoothd + +on property:bluetooth.start=snoop + setprop bluetooth.start none + start bluetoothd-snoop + +on property:bluetooth.stop=snoop + setprop bluetooth.stop none + stop bluetoothd-snoop + +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd + class main + # init does not yet support setting capabilities so run as root, + # bluetoothd drop uid to bluetooth with the right linux capabilities + group bluetooth + disabled + oneshot + +service bluetoothd-snoop /system/bin/logwrapper /system/bin/bluetoothd-snoop + class main + # init does not yet support setting capabilities so run as root, + # bluetoothd-snoop drops unneeded linux capabilities + group nobody + disabled + oneshot diff --git a/android/ipc-common.h b/android/ipc-common.h new file mode 100644 index 0000000..27736e4 --- /dev/null +++ b/android/ipc-common.h @@ -0,0 +1,38 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 IPC_MTU 1024 + +#define IPC_STATUS_SUCCESS 0x00 + +struct ipc_hdr { + uint8_t service_id; + uint8_t opcode; + uint16_t len; + uint8_t payload[0]; +} __attribute__((packed)); + +#define IPC_OP_STATUS 0x00 +struct ipc_status { + uint8_t code; +} __attribute__((packed)); diff --git a/android/ipc-tester.c b/android/ipc-tester.c new file mode 100644 index 0000000..63fd105 --- /dev/null +++ b/android/ipc-tester.c @@ -0,0 +1,1261 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/mgmt.h" + +#include "src/shared/tester.h" +#include "src/shared/mgmt.h" +#include "src/shared/hciemu.h" + +#include "hal-msg.h" +#include "ipc-common.h" + +#include + +#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */ +#define EMULATOR_SIGNAL "emulator_started" + +struct test_data { + struct mgmt *mgmt; + uint16_t mgmt_index; + struct hciemu *hciemu; + enum hciemu_type hciemu_type; + pid_t bluetoothd_pid; + bool setup_done; +}; + +struct ipc_data { + void *buffer; + size_t len; +}; + +struct generic_data { + struct ipc_data ipc_data; + + unsigned int num_services; + int init_services[]; +}; + +struct regmod_msg { + struct ipc_hdr header; + struct hal_cmd_register_module cmd; +} __attribute__((packed)); + +#define CONNECT_TIMEOUT (5 * 1000) +#define SERVICE_NAME "bluetoothd" + +static char exec_dir[PATH_MAX + 1]; + +static int cmd_sk = -1; +static int notif_sk = -1; + +static void read_info_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct mgmt_rp_read_info *rp = param; + char addr[18]; + uint16_t manufacturer; + uint32_t supported_settings, current_settings; + + tester_print("Read Info callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + ba2str(&rp->bdaddr, addr); + manufacturer = btohs(rp->manufacturer); + supported_settings = btohl(rp->supported_settings); + current_settings = btohl(rp->current_settings); + + tester_print(" Address: %s", addr); + tester_print(" Version: 0x%02x", rp->version); + tester_print(" Manufacturer: 0x%04x", manufacturer); + tester_print(" Supported settings: 0x%08x", supported_settings); + tester_print(" Current settings: 0x%08x", current_settings); + tester_print(" Class: 0x%02x%02x%02x", + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); + tester_print(" Name: %s", rp->name); + tester_print(" Short name: %s", rp->short_name); + + if (strcmp(hciemu_get_address(data->hciemu), addr)) { + tester_pre_setup_failed(); + return; + } + + tester_pre_setup_complete(); +} + +static void index_added_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Added callback"); + tester_print(" Index: 0x%04x", index); + + data->mgmt_index = index; + + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, + read_info_callback, NULL, NULL); +} + +static void index_removed_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Removed callback"); + tester_print(" Index: 0x%04x", index); + + if (index != data->mgmt_index) + return; + + mgmt_unregister_index(data->mgmt, data->mgmt_index); + + mgmt_unref(data->mgmt); + data->mgmt = NULL; + + tester_post_teardown_complete(); +} + +static void read_index_list_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Read Index List callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + index_added_callback, NULL, NULL); + + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + index_removed_callback, NULL, NULL); + + data->hciemu = hciemu_new(data->hciemu_type); + if (!data->hciemu) { + tester_warn("Failed to setup HCI emulation"); + tester_pre_setup_failed(); + return; + } + + tester_print("New hciemu instance created"); +} + +static void test_pre_setup(const void *data) +{ + struct test_data *test_data = tester_get_data(); + + if (!tester_use_debug()) + fclose(stderr); + + test_data->mgmt = mgmt_new_default(); + if (!test_data->mgmt) { + tester_warn("Failed to setup management interface"); + tester_pre_setup_failed(); + return; + } + + mgmt_send(test_data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, + NULL, read_index_list_callback, NULL, NULL); +} + +static void test_post_teardown(const void *data) +{ + struct test_data *test_data = tester_get_data(); + + if (test_data->hciemu) { + hciemu_unref(test_data->hciemu); + test_data->hciemu = NULL; + } +} + +static void bluetoothd_start(int hci_index) +{ + char prg_name[PATH_MAX + 1]; + char index[8]; + char *prg_argv[4]; + + snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); + snprintf(index, sizeof(index), "%d", hci_index); + + prg_argv[0] = prg_name; + prg_argv[1] = "-i"; + prg_argv[2] = index; + prg_argv[3] = NULL; + + if (!tester_use_debug()) + fclose(stderr); + + execve(prg_argv[0], prg_argv, NULL); +} + +static void emulator(int pipe, int hci_index) +{ + static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; + char buf[1024]; + struct sockaddr_un addr; + struct timeval tv; + int fd; + ssize_t len; + + fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) + goto failed; + + tv.tv_sec = WAIT_FOR_SIGNAL_TIME; + tv.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to bind system socket"); + goto failed; + } + + len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL)); + + if (len != sizeof(EMULATOR_SIGNAL)) + goto failed; + + memset(buf, 0, sizeof(buf)); + + len = read(fd, buf, sizeof(buf)); + if (len <= 0 || strcmp(buf, "ctl.start=bluetoothd")) + goto failed; + + close(pipe); + close(fd); + bluetoothd_start(hci_index); + +failed: + close(pipe); + close(fd); +} + +static int accept_connection(int sk) +{ + int err; + struct pollfd pfd; + int new_sk; + + memset(&pfd, 0 , sizeof(pfd)); + pfd.fd = sk; + pfd.events = POLLIN; + + err = poll(&pfd, 1, CONNECT_TIMEOUT); + if (err < 0) { + err = errno; + tester_warn("Failed to poll: %d (%s)", err, strerror(err)); + return -errno; + } + + if (err == 0) { + tester_warn("bluetoothd connect timeout"); + return -errno; + } + + new_sk = accept(sk, NULL, NULL); + if (new_sk < 0) { + err = errno; + tester_warn("Failed to accept socket: %d (%s)", + err, strerror(err)); + return -errno; + } + + return new_sk; +} + +static bool init_ipc(void) +{ + struct sockaddr_un addr; + + int sk; + int err; + + sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + err = errno; + tester_warn("Failed to create socket: %d (%s)", err, + strerror(err)); + return false; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + tester_warn("Failed to bind socket: %d (%s)", err, + strerror(err)); + close(sk); + return false; + } + + if (listen(sk, 2) < 0) { + err = errno; + tester_warn("Failed to listen on socket: %d (%s)", err, + strerror(err)); + close(sk); + return false; + } + + /* Start Android Bluetooth daemon service */ + if (property_set("ctl.start", SERVICE_NAME) < 0) { + tester_warn("Failed to start service %s", SERVICE_NAME); + close(sk); + return false; + } + + cmd_sk = accept_connection(sk); + if (cmd_sk < 0) { + close(sk); + return false; + } + + notif_sk = accept_connection(sk); + if (notif_sk < 0) { + close(sk); + close(cmd_sk); + cmd_sk = -1; + return false; + } + + tester_print("bluetoothd connected"); + + close(sk); + + return true; +} + +static void cleanup_ipc(void) +{ + if (cmd_sk < 0) + return; + + close(cmd_sk); + cmd_sk = -1; +} + +static gboolean check_for_daemon(gpointer user_data) +{ + int status; + struct test_data *data = user_data; + + if ((waitpid(data->bluetoothd_pid, &status, WNOHANG)) + != data->bluetoothd_pid) + return true; + + if (data->setup_done) { + if (WIFEXITED(status) && + (WEXITSTATUS(status) == EXIT_SUCCESS)) { + tester_test_passed(); + return false; + } + tester_test_failed(); + } else { + tester_setup_failed(); + test_post_teardown(data); + } + + tester_warn("Unexpected Daemon shutdown with status %d", status); + return false; +} + +static bool setup_module(int service_id) +{ + struct ipc_hdr response; + struct ipc_hdr expected_response; + + struct regmod_msg btmodule_msg = { + .header = { + .service_id = HAL_SERVICE_ID_CORE, + .opcode = HAL_OP_REGISTER_MODULE, + .len = sizeof(struct hal_cmd_register_module), + }, + .cmd = { + .service_id = service_id, + }, + }; + + if (write(cmd_sk, &btmodule_msg, sizeof(btmodule_msg)) < 0) + goto fail; + + if (read(cmd_sk, &response, sizeof(response)) < 0) + goto fail; + + expected_response = btmodule_msg.header; + expected_response.len = 0; + + if (memcmp(&response, &expected_response, sizeof(response)) == 0) + return true; + +fail: + tester_warn("Module registration failed."); + return false; +} + +static void setup(const void *data) +{ + const struct generic_data *generic_data = data; + struct test_data *test_data = tester_get_data(); + int signal_fd[2]; + char buf[1024]; + pid_t pid; + int len; + unsigned int i; + + if (pipe(signal_fd)) + goto failed; + + pid = fork(); + + if (pid < 0) { + close(signal_fd[0]); + close(signal_fd[1]); + goto failed; + } + + if (pid == 0) { + if (!tester_use_debug()) + fclose(stderr); + + close(signal_fd[0]); + emulator(signal_fd[1], test_data->mgmt_index); + exit(0); + } + + close(signal_fd[1]); + test_data->bluetoothd_pid = pid; + + len = read(signal_fd[0], buf, sizeof(buf)); + if (len <= 0 || (strcmp(buf, EMULATOR_SIGNAL))) { + close(signal_fd[0]); + goto failed; + } + + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, check_for_daemon, test_data, + NULL); + + if (!init_ipc()) { + tester_warn("Cannot initialize IPC mechanism!"); + goto failed; + } + tester_print("Will init %d services.", generic_data->num_services); + + for (i = 0; i < generic_data->num_services; i++) + if (!setup_module(generic_data->init_services[i])) { + cleanup_ipc(); + goto failed; + } + + test_data->setup_done = true; + + tester_setup_complete(); + return; + +failed: + g_idle_remove_by_data(test_data); + tester_setup_failed(); + test_post_teardown(data); +} + +static void teardown(const void *data) +{ + struct test_data *test_data = tester_get_data(); + + g_idle_remove_by_data(test_data); + cleanup_ipc(); + + if (test_data->bluetoothd_pid) + waitpid(test_data->bluetoothd_pid, NULL, 0); + + tester_teardown_complete(); +} + +static void ipc_send_tc(const void *data) +{ + const struct generic_data *generic_data = data; + const struct ipc_data *ipc_data = &generic_data->ipc_data; + + if (ipc_data->len) { + if (write(cmd_sk, ipc_data->buffer, ipc_data->len) < 0) + tester_test_failed(); + } +} + +#define service_data(args...) { args } + +#define gen_data(writelen, writebuf, servicelist...) \ + { \ + .ipc_data = { \ + .buffer = writebuf, \ + .len = writelen, \ + }, \ + .init_services = service_data(servicelist), \ + .num_services = sizeof((const int[]) \ + service_data(servicelist)) / \ + sizeof(int), \ + } + +#define test_generic(name, test, setup, teardown, buffer, writelen, \ + services...) \ + do { \ + struct test_data *user; \ + static const struct generic_data data = \ + gen_data(writelen, buffer, services); \ + user = g_malloc0(sizeof(struct test_data)); \ + if (!user) \ + break; \ + user->hciemu_type = HCIEMU_TYPE_BREDRLE; \ + tester_add_full(name, &data, test_pre_setup, setup, \ + test, teardown, test_post_teardown, \ + 3, user, g_free); \ + } while (0) + +#define test_opcode_valid(_name, _service, _opcode, _len, _servicelist...) \ + do { \ + static struct ipc_hdr hdr = { \ + .service_id = _service, \ + .opcode = _opcode, \ + .len = _len, \ + }; \ + \ + test_generic("Opcode out of range: "_name, \ + ipc_send_tc, setup, teardown, \ + &hdr, \ + sizeof(hdr), \ + _servicelist); \ + } while (0) + +struct vardata { + struct ipc_hdr hdr; + uint8_t buf[IPC_MTU]; +} __attribute__((packed)); + +#define test_datasize_valid(_name, _service, _opcode, _hlen, _addatasize, \ + _servicelist...) \ + do { \ + static struct vardata vdata = { \ + .hdr.service_id = _service, \ + .hdr.opcode = _opcode, \ + .hdr.len = (_hlen) + (_addatasize), \ + .buf = {}, \ + }; \ + test_generic("Data size "_name, \ + ipc_send_tc, setup, teardown, \ + &vdata, \ + sizeof(vdata.hdr) + (_hlen) + (_addatasize),\ + _servicelist); \ + } while (0) + +struct regmod_msg register_bt_msg = { + .header = { + .service_id = HAL_SERVICE_ID_CORE, + .opcode = HAL_OP_REGISTER_MODULE, + .len = sizeof(struct hal_cmd_register_module), + }, + .cmd = { + .service_id = HAL_SERVICE_ID_BLUETOOTH, + }, +}; + +struct regmod_msg register_bt_malformed_size_msg = { + .header = { + .service_id = HAL_SERVICE_ID_CORE, + .opcode = HAL_OP_REGISTER_MODULE, + /* wrong payload size declared */ + .len = sizeof(struct hal_cmd_register_module) - 1, + }, + .cmd = { + .service_id = HAL_SERVICE_ID_CORE, + }, +}; + +struct malformed_data3_struct { + struct regmod_msg valid_msg; + int redundant_data; +} __attribute__((packed)); + +static struct malformed_data3_struct malformed_data3_msg = { + /* valid register service message */ + .valid_msg = { + .header = { + .service_id = HAL_SERVICE_ID_CORE, + .opcode = HAL_OP_REGISTER_MODULE, + .len = sizeof(struct hal_cmd_register_module), + }, + .cmd = { + .service_id = HAL_SERVICE_ID_CORE, + }, + }, + /* plus redundant data */ + . redundant_data = 666, +}; + +struct ipc_hdr enable_unknown_service_hdr = { + .service_id = HAL_SERVICE_ID_MAX + 1, + .opcode = HAL_OP_REGISTER_MODULE, + .len = 0, +}; + +struct ipc_hdr enable_bt_service_hdr = { + .service_id = HAL_SERVICE_ID_BLUETOOTH, + .opcode = HAL_OP_ENABLE, + .len = 0, +}; + +struct bt_set_adapter_prop_data { + struct ipc_hdr hdr; + struct hal_cmd_set_adapter_prop prop; + + /* data placeholder for hal_cmd_set_adapter_prop.val[0] */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_set_adapter_prop)]; +} __attribute__((packed)); + +#define set_name "new name" + +static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, + .hdr.opcode = HAL_OP_SET_ADAPTER_PROP, + .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + + sizeof(set_name), + + .prop.type = HAL_PROP_ADAPTER_NAME, + /* declare wrong descriptor length */ + .prop.len = sizeof(set_name) + 1, + /* init prop.val[0] */ + .buf = set_name, +}; + +static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, + .hdr.opcode = HAL_OP_SET_ADAPTER_PROP, + .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + + sizeof(set_name), + + .prop.type = HAL_PROP_ADAPTER_NAME, + /* declare wrong descriptor length */ + .prop.len = sizeof(set_name) - 1, + /* init prop.val[0] */ + .buf = set_name, +}; + +struct bt_set_remote_prop_data { + struct ipc_hdr hdr; + struct hal_cmd_set_remote_device_prop prop; + + /* data placeholder for hal_cmd_set_remote_device_prop.val[0] */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_set_remote_device_prop)]; +} __attribute__((packed)); + +static struct bt_set_remote_prop_data bt_set_remote_prop_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, + .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP, + .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) + + sizeof(set_name), + + .prop.bdaddr = {}, + .prop.type = HAL_PROP_DEVICE_NAME, + /* declare wrong descriptor length */ + .prop.len = sizeof(set_name) + 1, + .buf = set_name, +}; + +static struct bt_set_remote_prop_data bt_set_remote_prop_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, + .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP, + .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) + + sizeof(set_name), + + .prop.bdaddr = {}, + .prop.type = HAL_PROP_DEVICE_NAME, + /* declare wrong descriptor length */ + .prop.len = sizeof(set_name) - 1, + .buf = set_name, +}; + +struct hidhost_set_info_data { + struct ipc_hdr hdr; + struct hal_cmd_hidhost_set_info info; + + /* data placeholder for hal_cmd_hidhost_set_info.descr[0] field */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_hidhost_set_info)]; +} __attribute__((packed)); + +#define set_info_data "some descriptor" + +static struct hidhost_set_info_data hidhost_set_info_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SET_INFO, + .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) + + sizeof(set_info_data), + + /* declare wrong descriptor length */ + .info.descr_len = sizeof(set_info_data) + 1, + /* init .info.descr[0] */ + .buf = set_info_data, +}; + +static struct hidhost_set_info_data hidhost_set_info_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SET_INFO, + .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) + + sizeof(set_info_data), + + /* declare wrong descriptor length */ + .info.descr_len = sizeof(set_info_data) - 1, + /* init .info.descr[0] */ + .buf = set_info_data, +}; + +struct hidhost_set_report_data { + struct ipc_hdr hdr; + struct hal_cmd_hidhost_set_report report; + + /* data placeholder for hal_cmd_hidhost_set_report.data[0] field */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_hidhost_set_report)]; +} __attribute__((packed)); + +#define set_rep_data "1234567890" + +static struct hidhost_set_report_data hidhost_set_report_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT, + .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) + + sizeof(set_rep_data), + + /* declare wrong descriptor length */ + .report.len = sizeof(set_rep_data) + 1, + /* init report.data[0] */ + .buf = set_rep_data, +}; + +static struct hidhost_set_report_data hidhost_set_report_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT, + .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) + + sizeof(set_rep_data), + + /* declare wrong descriptor length */ + .report.len = sizeof(set_rep_data) - 1, + /* init report.data[0] */ + .buf = set_rep_data, +}; + +struct hidhost_send_data_data { + struct ipc_hdr hdr; + struct hal_cmd_hidhost_send_data hiddata; + + /* data placeholder for hal_cmd_hidhost_send_data.data[0] field */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_hidhost_send_data)]; +} __attribute__((packed)); + +#define send_data_data "1234567890" + +static struct hidhost_send_data_data hidhost_send_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA, + .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) + + sizeof(send_data_data), + + /* declare wrong descriptor length */ + .hiddata.len = sizeof(send_data_data) + 1, + /* init .hiddata.data[0] */ + .buf = send_data_data, +}; + +static struct hidhost_send_data_data hidhost_send_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA, + .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) + + sizeof(send_data_data), + + /* declare wrong descriptor length */ + .hiddata.len = sizeof(send_data_data) - 1, + /* init .hiddata.data[0] */ + .buf = send_data_data, +}; + +int main(int argc, char *argv[]) +{ + snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); + + tester_init(&argc, &argv); + + /* check general IPC errors */ + test_generic("Too small data", + ipc_send_tc, setup, teardown, + ®ister_bt_msg, 1); + + test_generic("Malformed data (wrong payload declared)", + ipc_send_tc, setup, teardown, + ®ister_bt_malformed_size_msg, + sizeof(register_bt_malformed_size_msg), + HAL_SERVICE_ID_BLUETOOTH); + + test_generic("Malformed data2 (undersized msg)", + ipc_send_tc, setup, teardown, + ®ister_bt_msg, + sizeof(register_bt_msg) - 1, + HAL_SERVICE_ID_BLUETOOTH); + + test_generic("Malformed data3 (oversized msg)", + ipc_send_tc, setup, teardown, + &malformed_data3_msg, + sizeof(malformed_data3_msg), + HAL_SERVICE_ID_BLUETOOTH); + + test_generic("Invalid service", + ipc_send_tc, setup, teardown, + &enable_unknown_service_hdr, + sizeof(enable_unknown_service_hdr), + HAL_SERVICE_ID_BLUETOOTH); + + test_generic("Enable unregistered service", + ipc_send_tc, setup, teardown, + &enable_bt_service_hdr, + sizeof(enable_bt_service_hdr)); + + /* check service handler's max opcode value */ + test_opcode_valid("CORE", HAL_SERVICE_ID_CORE, 0x03, 0); + + test_opcode_valid("BLUETOOTH", HAL_SERVICE_ID_BLUETOOTH, 0x15, 0, + HAL_SERVICE_ID_BLUETOOTH); + + test_opcode_valid("SOCK", HAL_SERVICE_ID_SOCKET, 0x03, 0, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + + test_opcode_valid("HIDHOST", HAL_SERVICE_ID_HIDHOST, 0x10, 0, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + + test_opcode_valid("PAN", HAL_SERVICE_ID_PAN, 0x05, 0, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + + test_opcode_valid("A2DP", HAL_SERVICE_ID_A2DP, 0x03, 0, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + + /* check for valid data size */ + test_datasize_valid("CORE Register+", HAL_SERVICE_ID_CORE, + HAL_OP_REGISTER_MODULE, + sizeof(struct hal_cmd_register_module), 1); + test_datasize_valid("CORE Register-", HAL_SERVICE_ID_CORE, + HAL_OP_REGISTER_MODULE, + sizeof(struct hal_cmd_register_module), -1); + test_datasize_valid("CORE Unregister+", HAL_SERVICE_ID_CORE, + HAL_OP_UNREGISTER_MODULE, + sizeof(struct hal_cmd_unregister_module), 1); + test_datasize_valid("CORE Unregister-", HAL_SERVICE_ID_CORE, + HAL_OP_UNREGISTER_MODULE, + sizeof(struct hal_cmd_unregister_module), -1); + + /* check for valid data size for BLUETOOTH */ + test_datasize_valid("BT Enable+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_ENABLE, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Disable+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DISABLE, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Adapter Props+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_ADAPTER_PROPS, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_ADAPTER_PROP, + sizeof(struct hal_cmd_get_adapter_prop), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_ADAPTER_PROP, + sizeof(struct hal_cmd_get_adapter_prop), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Set Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_ADAPTER_PROP, + sizeof(struct hal_cmd_set_adapter_prop), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Set Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_ADAPTER_PROP, + sizeof(struct hal_cmd_set_adapter_prop), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_generic("Data size BT Set Adapter Prop Vardata+", + ipc_send_tc, setup, teardown, + &bt_set_adapter_prop_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_set_adapter_prop) + + sizeof(set_name)), + HAL_SERVICE_ID_BLUETOOTH); + test_generic("Data size BT Set Adapter Prop Vardata+", + ipc_send_tc, setup, teardown, + &bt_set_adapter_prop_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_set_adapter_prop) + + sizeof(set_name)), + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Props+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROPS, + sizeof(struct hal_cmd_get_remote_device_props), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Props-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROPS, + sizeof(struct hal_cmd_get_remote_device_props), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Prop+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROP, + sizeof(struct hal_cmd_get_remote_device_prop), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Prop-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROP, + sizeof(struct hal_cmd_get_remote_device_prop), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Set Remote Prop+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_REMOTE_DEVICE_PROP, + sizeof(struct hal_cmd_set_remote_device_prop), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Set Remote Prop-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_REMOTE_DEVICE_PROP, + sizeof(struct hal_cmd_set_remote_device_prop), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_generic("Data size BT Set Remote Prop Vardata+", + ipc_send_tc, setup, teardown, + &bt_set_remote_prop_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_set_remote_device_prop) + + sizeof(set_name)), + HAL_SERVICE_ID_BLUETOOTH); + test_generic("Data size BT Set Remote Prop Vardata-", + ipc_send_tc, setup, teardown, + &bt_set_remote_prop_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_set_remote_device_prop) + + sizeof(set_name)), + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote SV Rec+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICE_REC, + sizeof(struct hal_cmd_get_remote_service_rec), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote SV Rec-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICE_REC, + sizeof(struct hal_cmd_get_remote_service_rec), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Services+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICES, + sizeof(struct hal_cmd_get_remote_services), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Services-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICES, + sizeof(struct hal_cmd_get_remote_services), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Start Discovery+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_START_DISCOVERY, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Cancel Discovery+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CANCEL_DISCOVERY, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Create Bond+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CREATE_BOND, + sizeof(struct hal_cmd_create_bond), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Create Bond-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CREATE_BOND, + sizeof(struct hal_cmd_create_bond), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Remove Bond+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_REMOVE_BOND, + sizeof(struct hal_cmd_remove_bond), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Remove Bond-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_REMOVE_BOND, + sizeof(struct hal_cmd_remove_bond), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Cancel Bond+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CANCEL_BOND, + sizeof(struct hal_cmd_cancel_bond), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Cancel Bond-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CANCEL_BOND, + sizeof(struct hal_cmd_cancel_bond), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Pin Reply+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_PIN_REPLY, + sizeof(struct hal_cmd_pin_reply), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Pin Reply-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_PIN_REPLY, + sizeof(struct hal_cmd_pin_reply), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT SSP Reply+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SSP_REPLY, + sizeof(struct hal_cmd_ssp_reply), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT SSP Reply-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SSP_REPLY, + sizeof(struct hal_cmd_ssp_reply), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT DUT Mode Conf+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DUT_MODE_CONF, + sizeof(struct hal_cmd_dut_mode_conf), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT DUT Mode Conf-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DUT_MODE_CONF, + sizeof(struct hal_cmd_dut_mode_conf), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT DUT Mode Send+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DUT_MODE_SEND, + sizeof(struct hal_cmd_dut_mode_send), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT DUT Mode Send-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DUT_MODE_SEND, + sizeof(struct hal_cmd_dut_mode_send), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT LE Test+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_LE_TEST_MODE, + sizeof(struct hal_cmd_le_test_mode), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT LE Test-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_LE_TEST_MODE, + sizeof(struct hal_cmd_le_test_mode), -1, + HAL_SERVICE_ID_BLUETOOTH); + + /* check for valid data size for SOCK */ + test_datasize_valid("SOCKET Listen+", HAL_SERVICE_ID_SOCKET, + HAL_OP_SOCKET_LISTEN, + sizeof(struct hal_cmd_socket_listen), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + test_datasize_valid("SOCKET Listen-", HAL_SERVICE_ID_SOCKET, + HAL_OP_SOCKET_LISTEN, + sizeof(struct hal_cmd_socket_listen), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + test_datasize_valid("SOCKET Connect+", HAL_SERVICE_ID_SOCKET, + HAL_OP_SOCKET_CONNECT, + sizeof(struct hal_cmd_socket_connect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + test_datasize_valid("SOCKET Connect-", HAL_SERVICE_ID_SOCKET, + HAL_OP_SOCKET_CONNECT, + sizeof(struct hal_cmd_socket_connect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + + /* check for valid data size for HID Host */ + test_datasize_valid("HIDHOST Connect+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_CONNECT, + sizeof(struct hal_cmd_hidhost_connect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Connect-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_CONNECT, + sizeof(struct hal_cmd_hidhost_connect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Disconnect+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_DISCONNECT, + sizeof(struct hal_cmd_hidhost_disconnect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Disconnect-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_DISCONNECT, + sizeof(struct hal_cmd_hidhost_disconnect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Virt. Unplug+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_VIRTUAL_UNPLUG, + sizeof(struct hal_cmd_hidhost_virtual_unplug), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Virt. Unplug-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_VIRTUAL_UNPLUG, + sizeof(struct hal_cmd_hidhost_virtual_unplug), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Info+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_INFO, + sizeof(struct hal_cmd_hidhost_set_info), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Info-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_INFO, + sizeof(struct hal_cmd_hidhost_set_info), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Set Info Vardata+", + ipc_send_tc, setup, teardown, + &hidhost_set_info_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_set_info) + + sizeof(set_info_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Set Info Vardata-", + ipc_send_tc, setup, teardown, + &hidhost_set_info_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_set_info) + + sizeof(set_info_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Get Protocol+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_PROTOCOL, + sizeof(struct hal_cmd_hidhost_get_protocol), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Get Protocol-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_PROTOCOL, + sizeof(struct hal_cmd_hidhost_get_protocol), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Protocol+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_PROTOCOL, + sizeof(struct hal_cmd_hidhost_set_protocol), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Protocol-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_PROTOCOL, + sizeof(struct hal_cmd_hidhost_set_protocol), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Get Report+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_REPORT, + sizeof(struct hal_cmd_hidhost_get_report), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Get Report-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_REPORT, + sizeof(struct hal_cmd_hidhost_get_report), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Report+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_REPORT, + sizeof(struct hal_cmd_hidhost_set_report), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Report-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_REPORT, + sizeof(struct hal_cmd_hidhost_set_report), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Set Report Vardata+", + ipc_send_tc, setup, teardown, + &hidhost_set_report_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_set_report) + + sizeof(set_rep_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Set Report Vardata-", + ipc_send_tc, setup, teardown, + &hidhost_set_report_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_set_report) + + sizeof(set_rep_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Send Data+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SEND_DATA, + sizeof(struct hal_cmd_hidhost_send_data), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Send Data-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SEND_DATA, + sizeof(struct hal_cmd_hidhost_send_data), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Send Vardata+", + ipc_send_tc, setup, teardown, + &hidhost_send_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_send_data) + + sizeof(send_data_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Send Vardata-", + ipc_send_tc, setup, teardown, + &hidhost_send_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_send_data) + + sizeof(send_data_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + + /* check for valid data size for PAN */ + test_datasize_valid("PAN Enable+", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_ENABLE, + sizeof(struct hal_cmd_pan_enable), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Enable-", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_ENABLE, + sizeof(struct hal_cmd_pan_enable), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Get Role+", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_GET_ROLE, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Connect+", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_CONNECT, + sizeof(struct hal_cmd_pan_connect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Connect-", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_CONNECT, + sizeof(struct hal_cmd_pan_connect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Disconnect+", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_DISCONNECT, + sizeof(struct hal_cmd_pan_disconnect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Disconnect-", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_DISCONNECT, + sizeof(struct hal_cmd_pan_disconnect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + + /* check for valid data size for A2DP */ + test_datasize_valid("A2DP Connect+", HAL_SERVICE_ID_A2DP, + HAL_OP_A2DP_CONNECT, + sizeof(struct hal_cmd_a2dp_connect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + test_datasize_valid("A2DP Connect-", HAL_SERVICE_ID_A2DP, + HAL_OP_A2DP_CONNECT, + sizeof(struct hal_cmd_a2dp_connect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + test_datasize_valid("A2DP Disconnect+", HAL_SERVICE_ID_A2DP, + HAL_OP_A2DP_DISCONNECT, + sizeof(struct hal_cmd_a2dp_disconnect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + test_datasize_valid("A2DP Disconnect-", HAL_SERVICE_ID_A2DP, + HAL_OP_A2DP_DISCONNECT, + sizeof(struct hal_cmd_a2dp_disconnect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + + return tester_run(); +} diff --git a/android/ipc.c b/android/ipc.c index 6f940cd..8cd34ea 100644 --- a/android/ipc.c +++ b/android/ipc.c @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -32,91 +32,128 @@ #include #include #include -#include #include #include #include -#include "hal-msg.h" +#include "ipc-common.h" #include "ipc.h" -#include "log.h" +#include "src/log.h" -struct service_handler { - const struct ipc_handler *handler; - uint8_t size; +struct ipc { + struct service_handler *services; + int service_max; + + const char *path; + size_t size; + + GIOChannel *cmd_io; + guint cmd_watch; + + bool notifications; + GIOChannel *notif_io; + guint notif_watch; + + ipc_disconnect_cb disconnect_cb; + void *disconnect_cb_data; }; -static struct service_handler services[HAL_SERVICE_ID_MAX + 1]; +static void ipc_disconnect(struct ipc *ipc, bool in_cleanup) +{ + if (ipc->cmd_watch) { + g_source_remove(ipc->cmd_watch); + ipc->cmd_watch = 0; + } -static GIOChannel *cmd_io = NULL; -static GIOChannel *notif_io = NULL; + if (ipc->cmd_io) { + g_io_channel_shutdown(ipc->cmd_io, TRUE, NULL); + g_io_channel_unref(ipc->cmd_io); + ipc->cmd_io = NULL; + } + + if (ipc->notif_watch) { + g_source_remove(ipc->notif_watch); + ipc->notif_watch = 0; + } -static void ipc_handle_msg(const void *buf, ssize_t len) + if (ipc->notif_io) { + g_io_channel_shutdown(ipc->notif_io, TRUE, NULL); + g_io_channel_unref(ipc->notif_io); + ipc->notif_io = NULL; + } + + if (in_cleanup) + return; + + if (ipc->disconnect_cb) + ipc->disconnect_cb(ipc->disconnect_cb_data); +} + +static int ipc_handle_msg(struct service_handler *handlers, size_t max_index, + const void *buf, ssize_t len) { - const struct hal_hdr *msg = buf; + const struct ipc_hdr *msg = buf; const struct ipc_handler *handler; if (len < (ssize_t) sizeof(*msg)) { - error("IPC: message too small (%zd bytes), terminating", len); - raise(SIGTERM); - return; + DBG("message too small (%zd bytes)", len); + return -EBADMSG; } if (len != (ssize_t) (sizeof(*msg) + msg->len)) { - error("IPC: message malformed (%zd bytes), terminating", len); - raise(SIGTERM); - return; + DBG("message malformed (%zd bytes)", len); + return -EBADMSG; } /* if service is valid */ - if (msg->service_id > HAL_SERVICE_ID_MAX) { - error("IPC: unknown service (0x%x), terminating", - msg->service_id); - raise(SIGTERM); - return; + if (msg->service_id > max_index) { + DBG("unknown service (0x%x)", msg->service_id); + return -EOPNOTSUPP; } /* if service is registered */ - if (!services[msg->service_id].handler) { - error("IPC: unregistered service (0x%x), terminating", - msg->service_id); - raise(SIGTERM); - return; + if (!handlers[msg->service_id].handler) { + DBG("service not registered (0x%x)", msg->service_id); + return -EOPNOTSUPP; } /* if opcode is valid */ - if (msg->opcode == HAL_OP_STATUS || - msg->opcode > services[msg->service_id].size) { - error("IPC: invalid opcode 0x%x for service 0x%x, terminating", - msg->opcode, msg->service_id); - raise(SIGTERM); - return; + if (msg->opcode == IPC_OP_STATUS || + msg->opcode > handlers[msg->service_id].size) { + DBG("invalid opcode 0x%x for service 0x%x", msg->opcode, + msg->service_id); + return -EOPNOTSUPP; } /* opcode is table offset + 1 */ - handler = &services[msg->service_id].handler[msg->opcode - 1]; + handler = &handlers[msg->service_id].handler[msg->opcode - 1]; /* if payload size is valid */ if ((handler->var_len && handler->data_len > msg->len) || (!handler->var_len && handler->data_len != msg->len)) { - error("IPC: size invalid opcode 0x%x service 0x%x, terminating", - msg->service_id, msg->opcode); - raise(SIGTERM); - return; + DBG("invalid size for opcode 0x%x service 0x%x", + msg->opcode, msg->service_id); + return -EMSGSIZE; } handler->handler(msg->payload, msg->len); + + return 0; } static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { - char buf[BLUEZ_HAL_MTU]; + struct ipc *ipc = user_data; + + char buf[IPC_MTU]; ssize_t ret; - int fd; + int fd, err; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - info("IPC: command socket closed, terminating"); + info("IPC: command socket closed"); + + ipc->cmd_watch = 0; goto fail; } @@ -124,29 +161,40 @@ static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond, ret = read(fd, buf, sizeof(buf)); if (ret < 0) { - error("IPC: command read failed, terminating (%s)", - strerror(errno)); + error("IPC: command read failed (%s)", strerror(errno)); + goto fail; + } + + err = ipc_handle_msg(ipc->services, ipc->service_max, buf, ret); + if (err < 0) { + error("IPC: failed to handle message (%s)", strerror(-err)); goto fail; } - ipc_handle_msg(buf, ret); return TRUE; fail: - raise(SIGTERM); + ipc_disconnect(ipc, false); + return FALSE; } static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { - info("IPC: notification socket closed, terminating"); - raise(SIGTERM); + struct ipc *ipc = user_data; + + info("IPC: notification socket closed"); + + ipc->notif_watch = 0; + + ipc_disconnect(ipc, false); return FALSE; } -static GIOChannel *connect_hal(GIOFunc connect_cb) +static GIOChannel *ipc_connect(const char *path, size_t size, + GIOFunc connect_cb, void *user_data) { struct sockaddr_un addr; GIOCondition cond; @@ -168,18 +216,13 @@ static GIOChannel *connect_hal(GIOFunc connect_cb) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); + memcpy(addr.sun_path, path, size); - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("IPC: failed to connect HAL socket: %d (%s)", errno, - strerror(errno)); - g_io_channel_unref(io); - return NULL; - } + connect(sk, (struct sockaddr *) &addr, sizeof(addr)); cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; - g_io_add_watch(io, cond, connect_cb, NULL); + g_io_add_watch(io, cond, connect_cb, user_data); return io; } @@ -187,23 +230,27 @@ static GIOChannel *connect_hal(GIOFunc connect_cb) static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { + struct ipc *ipc = user_data; + DBG(""); if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - error("IPC: notification socket connect failed, terminating"); - raise(SIGTERM); + error("IPC: notification socket connect failed"); + + ipc_disconnect(ipc, false); + return FALSE; } cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL; - g_io_add_watch(io, cond, notif_watch_cb, NULL); + ipc->notif_watch = g_io_add_watch(io, cond, notif_watch_cb, ipc); cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; - g_io_add_watch(cmd_io, cond, cmd_watch_cb, NULL); + ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc); - info("IPC: successfully connected"); + info("IPC: successfully connected (with notifications)"); return FALSE; } @@ -211,41 +258,70 @@ static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond, static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { + struct ipc *ipc = user_data; + DBG(""); if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - error("IPC: command socket connect failed, terminating"); - raise(SIGTERM); + error("IPC: command socket connect failed"); + ipc_disconnect(ipc, false); + return FALSE; } - notif_io = connect_hal(notif_connect_cb); - if (!notif_io) - raise(SIGTERM); + if (ipc->notifications) { + ipc->notif_io = ipc_connect(ipc->path, ipc->size, + notif_connect_cb, ipc); + if (!ipc->notif_io) + ipc_disconnect(ipc, false); + + return FALSE; + } + + cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc); + + info("IPC: successfully connected (without notifications)"); return FALSE; } -void ipc_init(void) +struct ipc *ipc_init(const char *path, size_t size, int max_service_id, + bool notifications, + ipc_disconnect_cb cb, void *cb_data) { - cmd_io = connect_hal(cmd_connect_cb); - if (!cmd_io) - raise(SIGTERM); + struct ipc *ipc; + + ipc = g_new0(struct ipc, 1); + + ipc->services = g_new0(struct service_handler, max_service_id + 1); + ipc->service_max = max_service_id; + + ipc->path = path; + ipc->size = size; + + ipc->notifications = notifications; + + ipc->cmd_io = ipc_connect(path, size, cmd_connect_cb, ipc); + if (!ipc->cmd_io) { + g_free(ipc->services); + g_free(ipc); + return NULL; + } + + ipc->disconnect_cb = cb; + ipc->disconnect_cb_data = cb_data; + + return ipc; } -void ipc_cleanup(void) +void ipc_cleanup(struct ipc *ipc) { - if (cmd_io) { - g_io_channel_shutdown(cmd_io, TRUE, NULL); - g_io_channel_unref(cmd_io); - cmd_io = NULL; - } + ipc_disconnect(ipc, true); - if (notif_io) { - g_io_channel_shutdown(notif_io, TRUE, NULL); - g_io_channel_unref(notif_io); - notif_io = NULL; - } + g_free(ipc->services); + g_free(ipc); } static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, @@ -253,7 +329,7 @@ static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, { struct msghdr msg; struct iovec iv[2]; - struct hal_hdr m; + struct ipc_hdr m; char cmsgbuf[CMSG_SPACE(sizeof(int))]; struct cmsghdr *cmsg; @@ -288,54 +364,63 @@ static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, } if (sendmsg(sk, &msg, 0) < 0) { - error("IPC send failed, terminating :%s", strerror(errno)); + error("IPC send failed :%s", strerror(errno)); + + /* TODO disconnect IPC here when this function becomes static */ raise(SIGTERM); } } -void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status) +void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint8_t status) { - struct hal_status s; + struct ipc_status s; int sk; - sk = g_io_channel_unix_get_fd(cmd_io); + sk = g_io_channel_unix_get_fd(ipc->cmd_io); - if (status == HAL_STATUS_SUCCESS) { + if (status == IPC_STATUS_SUCCESS) { ipc_send(sk, service_id, opcode, 0, NULL, -1); return; } s.code = status; - ipc_send(sk, service_id, HAL_OP_STATUS, sizeof(s), &s, -1); + ipc_send(sk, service_id, IPC_OP_STATUS, sizeof(s), &s, -1); } -void ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len, - void *param, int fd) +void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param, int fd) { - ipc_send(g_io_channel_unix_get_fd(cmd_io), service_id, opcode, len, + ipc_send(g_io_channel_unix_get_fd(ipc->cmd_io), service_id, opcode, len, param, fd); } -void ipc_send_notif(uint8_t service_id, uint8_t opcode, uint16_t len, - void *param) +void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param) { - if (!notif_io) + if (!ipc || !ipc->notif_io) return; - ipc_send(g_io_channel_unix_get_fd(notif_io), service_id, opcode, len, - param, -1); + ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode, + len, param, -1); } -void ipc_register(uint8_t service, const struct ipc_handler *handlers, - uint8_t size) +void ipc_register(struct ipc *ipc, uint8_t service, + const struct ipc_handler *handlers, uint8_t size) { - services[service].handler = handlers; - services[service].size = size; + if (service > ipc->service_max) + return; + + ipc->services[service].handler = handlers; + ipc->services[service].size = size; } -void ipc_unregister(uint8_t service) +void ipc_unregister(struct ipc *ipc, uint8_t service) { - services[service].handler = NULL; - services[service].size = 0; + if (service > ipc->service_max) + return; + + ipc->services[service].handler = NULL; + ipc->services[service].size = 0; } diff --git a/android/ipc.h b/android/ipc.h index 6cd102b..cc4e92d 100644 --- a/android/ipc.h +++ b/android/ipc.h @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -26,14 +26,27 @@ struct ipc_handler { bool var_len; size_t data_len; }; -void ipc_init(void); -void ipc_cleanup(void); -void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status); -void ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len, - void *param, int fd); -void ipc_send_notif(uint8_t service_id, uint8_t opcode, uint16_t len, - void *param); -void ipc_register(uint8_t service, const struct ipc_handler *handlers, - uint8_t size); -void ipc_unregister(uint8_t service); +struct service_handler { + const struct ipc_handler *handler; + uint8_t size; +}; + +struct ipc; + +typedef void (*ipc_disconnect_cb) (void *data); + +struct ipc *ipc_init(const char *path, size_t size, int max_service_id, + bool notifications, + ipc_disconnect_cb cb, void *cb_data); +void ipc_cleanup(struct ipc *ipc); + +void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint8_t status); +void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param, int fd); +void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param); +void ipc_register(struct ipc *ipc, uint8_t service, + const struct ipc_handler *handlers, uint8_t size); +void ipc_unregister(struct ipc *ipc, uint8_t service); diff --git a/android/main.c b/android/main.c index 5210b4b..0a0c150 100644 --- a/android/main.c +++ b/android/main.c @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -36,27 +36,30 @@ #include #include +#if defined(ANDROID) +#include +#include +#endif #include -#include "log.h" +#include "src/log.h" #include "src/sdpd.h" #include "lib/bluetooth.h" +#include "ipc-common.h" +#include "ipc.h" #include "bluetooth.h" #include "socket.h" #include "hidhost.h" #include "hal-msg.h" -#include "ipc.h" #include "a2dp.h" #include "pan.h" - -/* TODO: Consider to remove PLATFORM_SDKVERSION check if requirement -* for minimal Android platform version increases. */ -#if defined(ANDROID) && PLATFORM_SDK_VERSION >= 18 -#include -#endif +#include "avrcp.h" +#include "handsfree.h" +#include "gatt.h" +#include "health.h" #define STARTUP_GRACE_SECONDS 5 #define SHUTDOWN_GRACE_SECONDS 10 @@ -67,6 +70,8 @@ static bdaddr_t adapter_bdaddr; static GMainLoop *event_loop; +static struct ipc *hal_ipc = NULL; + static bool services[HAL_SERVICE_ID_MAX + 1] = { false }; static void service_register(const void *buf, uint16_t len) @@ -81,29 +86,60 @@ static void service_register(const void *buf, uint16_t len) switch (m->service_id) { case HAL_SERVICE_ID_BLUETOOTH: - bt_bluetooth_register(); + if (!bt_bluetooth_register(hal_ipc, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } break; - case HAL_SERVICE_ID_SOCK: - bt_socket_register(&adapter_bdaddr); + case HAL_SERVICE_ID_SOCKET: + bt_socket_register(hal_ipc, &adapter_bdaddr, m->mode); break; case HAL_SERVICE_ID_HIDHOST: - if (!bt_hid_register(&adapter_bdaddr)) { + if (!bt_hid_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_A2DP: - if (!bt_a2dp_register(&adapter_bdaddr)) { + if (!bt_a2dp_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } break; case HAL_SERVICE_ID_PAN: - if (!bt_pan_register(&adapter_bdaddr)) { + if (!bt_pan_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_AVRCP: + if (!bt_avrcp_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_HANDSFREE: + if (!bt_handsfree_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_GATT: + if (!bt_gatt_register(hal_ipc, &adapter_bdaddr)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_HEALTH: + if (!bt_health_register(hal_ipc, &adapter_bdaddr, m->mode)) { status = HAL_STATUS_FAILED; goto failed; } @@ -122,7 +158,8 @@ static void service_register(const void *buf, uint16_t len) info("Service ID=%u registered", m->service_id); failed: - ipc_send_rsp(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + status); } static void service_unregister(const void *buf, uint16_t len) @@ -139,7 +176,7 @@ static void service_unregister(const void *buf, uint16_t len) case HAL_SERVICE_ID_BLUETOOTH: bt_bluetooth_unregister(); break; - case HAL_SERVICE_ID_SOCK: + case HAL_SERVICE_ID_SOCKET: bt_socket_unregister(); break; case HAL_SERVICE_ID_HIDHOST: @@ -151,9 +188,23 @@ static void service_unregister(const void *buf, uint16_t len) case HAL_SERVICE_ID_PAN: bt_pan_unregister(); break; + case HAL_SERVICE_ID_AVRCP: + bt_avrcp_unregister(); + break; + case HAL_SERVICE_ID_HANDSFREE: + bt_handsfree_unregister(); + break; + case HAL_SERVICE_ID_GATT: + bt_gatt_unregister(); + break; + case HAL_SERVICE_ID_HEALTH: + bt_health_unregister(); + break; default: - /* This would indicate bug in HAL, as unregister should not be - * called in init failed */ + /* + * This would indicate bug in HAL, as unregister should not be + * called in init failed + */ DBG("service %u not supported", m->service_id); status = HAL_STATUS_FAILED; goto failed; @@ -166,7 +217,8 @@ static void service_unregister(const void *buf, uint16_t len) info("Service ID=%u unregistered", m->service_id); failed: - ipc_send_rsp(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + status); } static const struct ipc_handler cmd_handlers[] = { @@ -204,6 +256,11 @@ static void stop_bluetooth(void) g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL); } +static void ipc_disconnected(void *data) +{ + stop_bluetooth(); +} + static void adapter_ready(int err, const bdaddr_t *addr) { if (err < 0) { @@ -220,7 +277,16 @@ static void adapter_ready(int err, const bdaddr_t *addr) info("Adapter initialized"); - ipc_init(); + hal_ipc = ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH), + HAL_SERVICE_ID_MAX, true, + ipc_disconnected, NULL); + if (!hal_ipc) { + error("Failed to initialize IPC"); + exit(EXIT_FAILURE); + } + + ipc_register(hal_ipc, HAL_SERVICE_ID_CORE, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); } static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, @@ -294,12 +360,19 @@ static guint setup_signalfd(void) static gboolean option_version = FALSE; static gint option_index = -1; +static gboolean option_dbg = FALSE; +static gboolean option_mgmt_dbg = FALSE; static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit", NULL }, { "index", 'i', 0, G_OPTION_ARG_INT, &option_index, "Use specified controller", "INDEX"}, + { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_dbg, + "Enable debug logs", NULL}, + { "mgmt-debug", 0, 0, G_OPTION_ARG_NONE, &option_mgmt_dbg, + "Enable mgmt debug logs", NULL}, + { NULL } }; @@ -309,7 +382,7 @@ static void cleanup_services(void) DBG(""); - for (i = HAL_SERVICE_ID_BLUETOOTH; i < HAL_SERVICE_ID_MAX; i++) { + for (i = HAL_SERVICE_ID_BLUETOOTH; i < HAL_SERVICE_ID_MAX + 1; i++) { if (!services[i]) continue; @@ -317,7 +390,7 @@ static void cleanup_services(void) case HAL_SERVICE_ID_BLUETOOTH: bt_bluetooth_unregister(); break; - case HAL_SERVICE_ID_SOCK: + case HAL_SERVICE_ID_SOCKET: bt_socket_unregister(); break; case HAL_SERVICE_ID_HIDHOST: @@ -326,9 +399,21 @@ static void cleanup_services(void) case HAL_SERVICE_ID_A2DP: bt_a2dp_unregister(); break; + case HAL_SERVICE_ID_AVRCP: + bt_avrcp_unregister(); + break; case HAL_SERVICE_ID_PAN: bt_pan_unregister(); break; + case HAL_SERVICE_ID_HANDSFREE: + bt_handsfree_unregister(); + break; + case HAL_SERVICE_ID_GATT: + bt_gatt_unregister(); + break; + case HAL_SERVICE_ID_HEALTH: + bt_health_unregister(); + break; } services[i] = false; @@ -344,15 +429,29 @@ static bool set_capabilities(void) header.version = _LINUX_CAPABILITY_VERSION; header.pid = 0; - /* CAP_NET_ADMIN: Allow use of MGMT interface + /* + * CAP_NET_ADMIN: Allow use of MGMT interface * CAP_NET_BIND_SERVICE: Allow use of privileged PSM - * CAP_NET_RAW: Allow use of bnep ioctl calls */ + * CAP_NET_RAW: Allow use of bnep ioctl calls + */ cap.effective = cap.permitted = CAP_TO_MASK(CAP_NET_RAW) | CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_BIND_SERVICE); cap.inheritable = 0; + /* don't clear capabilities when dropping root */ + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + error("%s: prctl(): %s", __func__, strerror(errno)); + return false; + } + + /* Android bluetooth user UID=1002 */ + if (setuid(1002) < 0) { + error("%s: setuid(): %s", __func__, strerror(errno)); + return false; + } + /* TODO: Move to cap_set_proc once bionic support it */ if (capset(&header, &cap) < 0) { error("%s: capset(): %s", __func__, strerror(errno)); @@ -402,7 +501,10 @@ int main(int argc, char *argv[]) if (!signal) return EXIT_FAILURE; - __btd_log_init("*", 0); + if (option_dbg || option_mgmt_dbg) + __btd_log_init("*", 0); + else + __btd_log_init(NULL, 0); if (!set_capabilities()) { __btd_log_cleanup(); @@ -419,7 +521,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (!bt_bluetooth_start(option_index, adapter_ready)) { + if (!bt_bluetooth_start(option_index, option_mgmt_dbg, adapter_ready)) { __btd_log_cleanup(); g_source_remove(bluetooth_start_timeout); g_source_remove(signal); @@ -429,9 +531,6 @@ int main(int argc, char *argv[]) /* Use params: mtu = 0, flags = 0 */ start_sdp_server(0, 0); - ipc_register(HAL_SERVICE_ID_CORE, cmd_handlers, - G_N_ELEMENTS(cmd_handlers)); - DBG("Entering main loop"); event_loop = g_main_loop_new(NULL, FALSE); @@ -445,12 +544,15 @@ int main(int argc, char *argv[]) cleanup_services(); - ipc_cleanup(); stop_sdp_server(); bt_bluetooth_cleanup(); g_main_loop_unref(event_loop); - ipc_unregister(HAL_SERVICE_ID_CORE); + /* If no adapter was initialized, hal_ipc is NULL */ + if (hal_ipc) { + ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE); + ipc_cleanup(hal_ipc); + } info("Exit"); diff --git a/android/mcap-lib.c b/android/mcap-lib.c new file mode 100644 index 0000000..b04eaac --- /dev/null +++ b/android/mcap-lib.c @@ -0,0 +1,3177 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. + * Copyright (C) 2010 Signove + * Copyright (C) 2014 Intel Corporation. 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 +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "bluetooth/l2cap.h" +#include "btio/btio.h" +#include "src/log.h" + +#include "mcap-lib.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 + +#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 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 */ + uint8_t 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; +}; + +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 gboolean csp_caps_initialized = FALSE; +struct csp_caps _caps; + +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 int 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 int 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) +{ + struct mcap_mcl *mcl; + + for (; list; list = list->next) { + mcl = list->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 + * successful + */ + 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); + g_free(c); + 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, ¬ify); + 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, ¬ify); + +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; + int 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, ¬ify); + 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, ¬ify); + } + + 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(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(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, &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, &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(const 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(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(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, 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, 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, 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); +} + + +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) +{ + /* + * FIXME: btd_adapter_read_clock(...) always return FALSE, current + * code doesn't support CSP (Clock Synchronization Protocol). To avoid + * build dependancy on struct 'btd_adapter', removing this code. + */ + + return FALSE; +} + +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; + btres = 0; + + 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; + + btclock = 0; + 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; + + btclock = 0; + 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); + cur_btclock = 0; + + 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/android/mcap-lib.h b/android/mcap-lib.h new file mode 100644 index 0000000..548d672 --- /dev/null +++ b/android/mcap-lib.h @@ -0,0 +1,437 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. + * Copyright (C) 2010 Signove + * Copyright (C) 2014 Intel Corporation. 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 + * + */ + +#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 65535 + +/* 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) + +#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 */ + +/* + * 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; + +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; + +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_csp; +struct mcap_mdl_op_cb; +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); + +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 */ + int ref; /* Reference counter */ + + gboolean csp_enabled; /* CSP: functionality enabled */ +}; + +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 */ + int 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 */ +}; + +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 */ + int ref; /* References counter */ +}; + +struct sync_info_ind_data { + uint32_t btclock; + uint64_t timestamp; + uint16_t accuracy; +}; + +/************ 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(const 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); + +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); diff --git a/android/pan.c b/android/pan.c index 6b098b2..9eab932 100644 --- a/android/pan.c +++ b/android/pan.c @@ -1,19 +1,22 @@ /* - * Copyright (C) 2013 Intel Corporation. All rights reserved. * + * BlueZ - Bluetooth protocol stack for Linux * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. * - * This program is distributed in the hope that it will be useful, + * + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -28,25 +31,41 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include "btio/btio.h" #include "lib/bluetooth.h" #include "lib/bnep.h" #include "lib/sdp.h" #include "lib/sdp_lib.h" -#include "src/glib-helper.h" +#include "src/uuid-helper.h" #include "profiles/network/bnep.h" +#include "src/log.h" -#include "log.h" -#include "pan.h" #include "hal-msg.h" +#include "ipc-common.h" #include "ipc.h" #include "utils.h" #include "bluetooth.h" +#include "pan.h" + +#define SVC_HINT_NETWORKING 0x02 + +#define BNEP_BRIDGE "bt-pan" +#define BNEP_PANU_INTERFACE "bt-pan" +#define BNEP_NAP_INTERFACE "bt-pan%d" static bdaddr_t adapter_addr; GSList *devices = NULL; uint8_t local_role = HAL_PAN_ROLE_NONE; +static struct ipc *hal_ipc = NULL; struct pan_device { char iface[16]; @@ -54,9 +73,124 @@ struct pan_device { uint8_t conn_state; uint8_t role; GIOChannel *io; + struct bnep *session; guint watch; }; +static struct { + uint32_t record_id; + GIOChannel *io; + bool bridge; +} nap_dev = { + .record_id = 0, + .io = NULL, + .bridge = false, +}; + +static int set_forward_delay(int sk) +{ + unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0 , 0, 0 }; + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, BNEP_BRIDGE, IFNAMSIZ); + ifr.ifr_data = (char *) args; + + if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) { + error("pan: setting forward delay failed: %d (%s)", + errno, strerror(errno)); + return -1; + } + + return 0; +} + +static int nap_create_bridge(void) +{ + int sk, err; + + DBG("%s", BNEP_BRIDGE); + + if (nap_dev.bridge) + return 0; + + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return -EOPNOTSUPP; + + if (ioctl(sk, SIOCBRADDBR, BNEP_BRIDGE) < 0) { + err = -errno; + if (err != -EEXIST) { + close(sk); + return -EOPNOTSUPP; + } + } + + err = set_forward_delay(sk); + if (err < 0) + ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE); + + close(sk); + + nap_dev.bridge = err == 0; + + return err; +} + +static int bridge_if_down(void) +{ + struct ifreq ifr; + int sk, err; + + sk = socket(AF_INET, SOCK_DGRAM, 0); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, BNEP_BRIDGE, IF_NAMESIZE - 1); + + ifr.ifr_flags &= ~IFF_UP; + + /* Bring down the interface */ + err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr); + + close(sk); + + if (err < 0) { + error("pan: Could not bring down %s", BNEP_BRIDGE); + return err; + } + + return 0; +} + +static int nap_remove_bridge(void) +{ + int sk, err; + + DBG("%s", BNEP_BRIDGE); + + if (!nap_dev.bridge) + return 0; + + bridge_if_down(); + + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return -EOPNOTSUPP; + + err = ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE); + if (err < 0) + err = -errno; + + close(sk); + + if (err < 0) + return err; + + nap_dev.bridge = false; + + return 0; +} + static int device_cmp(gconstpointer s, gconstpointer user_data) { const struct pan_device *dev = s; @@ -65,24 +199,38 @@ static int device_cmp(gconstpointer s, gconstpointer user_data) return bacmp(&dev->dst, dst); } -static void pan_device_free(struct pan_device *dev) +static void pan_device_free(void *data) { - local_role = HAL_PAN_ROLE_NONE; + struct pan_device *dev = data; if (dev->watch > 0) { + bnep_server_delete(BNEP_BRIDGE, dev->iface, &dev->dst); g_source_remove(dev->watch); - dev->watch = 0; } if (dev->io) { + g_io_channel_shutdown(dev->io, FALSE, NULL); g_io_channel_unref(dev->io); - dev->io = NULL; } - devices = g_slist_remove(devices, dev); + if (dev->session) + bnep_free(dev->session); + g_free(dev); } +static void pan_device_remove(struct pan_device *dev) +{ + devices = g_slist_remove(devices, dev); + + if (g_slist_length(devices) == 0) { + local_role = HAL_PAN_ROLE_NONE; + nap_remove_bridge(); + } + + pan_device_free(dev); +} + static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state) { struct hal_ev_pan_conn_state ev; @@ -101,8 +249,10 @@ static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state) ev.remote_role = dev->role; ev.status = HAL_STATUS_SUCCESS; - ipc_send_notif(HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE, sizeof(ev), - &ev); + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE, + sizeof(ev), &ev); + if (dev->conn_state == HAL_PAN_STATE_DISCONNECTED) + pan_device_remove(dev); } static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state) @@ -114,27 +264,28 @@ static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state) ev.state = state; ev.local_role = local_role; ev.status = HAL_STATUS_SUCCESS; + memset(ev.name, 0, sizeof(ev.name)); - memcpy(ev.name, dev->iface, sizeof(dev->iface)); - ipc_send_notif(HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE, sizeof(ev), - &ev); + if (local_role == HAL_PAN_ROLE_NAP) + memcpy(ev.name, BNEP_BRIDGE, sizeof(BNEP_BRIDGE)); + else + memcpy(ev.name, dev->iface, sizeof(dev->iface)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE, + sizeof(ev), &ev); } -static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) +static void bnep_disconn_cb(void *data) { struct pan_device *dev = data; DBG("%s disconnected", dev->iface); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); - pan_device_free(dev); - - return FALSE; } -static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data) +static void bnep_conn_cb(char *iface, int err, void *data) { struct pan_device *dev = data; @@ -143,7 +294,6 @@ static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data) if (err < 0) { error("bnep connect req failed: %s", strerror(-err)); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); - pan_device_free(dev); return; } @@ -153,17 +303,12 @@ static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data) bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED); - - dev->watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - bnep_watchdog_cb, dev); - g_io_channel_unref(dev->io); - dev->io = NULL; } static void connect_cb(GIOChannel *chan, GError *err, gpointer data) { struct pan_device *dev = data; - uint16_t src, dst; + uint16_t l_role, r_role; int perr, sk; DBG(""); @@ -173,21 +318,33 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer data) goto fail; } - src = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU; - dst = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU; + l_role = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : + BNEP_SVC_PANU; + r_role = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU; + sk = g_io_channel_unix_get_fd(dev->io); - perr = bnep_connect(sk, src, dst, bnep_conn_cb, dev); + dev->session = bnep_new(sk, l_role, r_role, BNEP_PANU_INTERFACE); + if (!dev->session) + goto fail; + + perr = bnep_connect(dev->session, bnep_conn_cb, dev); if (perr < 0) { error("bnep connect req failed: %s", strerror(-perr)); goto fail; } + bnep_set_disconnect(dev->session, bnep_disconn_cb, dev); + + if (dev->io) { + g_io_channel_unref(dev->io); + dev->io = NULL; + } + return; fail: bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); - pan_device_free(dev); } static void bt_pan_connect(const void *buf, uint16_t len) @@ -256,10 +413,10 @@ static void bt_pan_connect(const void *buf, uint16_t len) devices = g_slist_append(devices, dev); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING); - status = HAL_STATUS_SUCCESS; + status = HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status); } static void bt_pan_disconnect(const void *buf, uint16_t len) @@ -282,40 +439,251 @@ static void bt_pan_disconnect(const void *buf, uint16_t len) dev = l->data; - if (dev->watch) { - g_source_remove(dev->watch); - dev->watch = 0; - } + if (dev->conn_state == HAL_PAN_STATE_CONNECTED && dev->session) + bnep_disconnect(dev->session); + + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, + status); +} + +static gboolean nap_watchdog_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + struct pan_device *dev = user_data; - bnep_if_down(dev->iface); - bnep_kill_connection(&dst); + DBG("disconnected"); bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); - pan_device_free(dev); - status = HAL_STATUS_SUCCESS; + return FALSE; +} +static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + struct pan_device *dev = user_data; + 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 sk, n, err; + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { + error("Hangup or error or inval 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); + goto failed; + } + + /* Highest known control command id BNEP_FILTER_MULT_ADDR_RSP 0x06 */ + if (req->type == BNEP_CONTROL && + req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) { + error("cmd not understood"); + bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_CMD_NOT_UNDERSTOOD, + req->ctrl); + goto failed; + } + + if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) { + error("cmd is not BNEP_SETUP_CONN_REQ %02X %02X", req->type, + req->ctrl); + goto failed; + } + + rsp = bnep_setup_decode(req, &dst_role, &src_role); + if (rsp) { + error("bnep_setup_decode failed"); + goto failed; + } + + rsp = bnep_setup_chk(dst_role, src_role); + if (rsp) { + error("benp_setup_chk failed"); + goto failed; + } + + err = nap_create_bridge(); + if (err < 0) { + error("pan: Failed to create bridge: %s (%d)", strerror(-err), + -err); + goto failed; + } + + if (bnep_server_add(sk, dst_role, BNEP_BRIDGE, dev->iface, + &dev->dst) < 0) { + nap_remove_bridge(); + error("server_connadd failed"); + rsp = BNEP_CONN_NOT_ALLOWED; + goto failed; + } + + rsp = BNEP_SUCCESS; + bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp); + + dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + nap_watchdog_cb, dev); + g_io_channel_unref(dev->io); + dev->io = NULL; + + bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED); + + return FALSE; failed: - ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, status); + bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp); + pan_device_remove(dev); + + return FALSE; +} + +static void nap_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + struct pan_device *dev = user_data; + + DBG(""); + + if (err) { + error("%s", err->message); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + return; + } + + g_io_channel_set_close_on_unref(chan, TRUE); + dev->watch = g_io_add_watch(chan, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + nap_setup_cb, dev); +} + +static void nap_confirm_cb(GIOChannel *chan, gpointer data) +{ + struct pan_device *dev; + bdaddr_t dst; + char address[18]; + GError *err = NULL; + + DBG(""); + + bt_io_get(chan, &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); + return; + } + + DBG("incoming connect request from %s", address); + dev = g_new0(struct pan_device, 1); + bacpy(&dev->dst, &dst); + local_role = HAL_PAN_ROLE_NAP; + dev->role = HAL_PAN_ROLE_PANU; + + strncpy(dev->iface, BNEP_NAP_INTERFACE, 16); + dev->iface[15] = '\0'; + + dev->io = g_io_channel_ref(chan); + g_io_channel_set_close_on_unref(dev->io, TRUE); + + if (!bt_io_accept(dev->io, nap_connect_cb, dev, NULL, &err)) { + error("bt_io_accept: %s", err->message); + g_error_free(err); + goto failed; + } + + devices = g_slist_append(devices, dev); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING); + + return; + +failed: + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); +} + +static void destroy_nap_device(void) +{ + DBG(""); + + nap_remove_bridge(); + + if (nap_dev.io) { + g_io_channel_shutdown(nap_dev.io, FALSE, NULL); + g_io_channel_unref(nap_dev.io); + nap_dev.io = NULL; + } +} + +static int register_nap_server(void) +{ + GError *gerr = NULL; + + DBG(""); + + nap_dev.io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, BNEP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_OMTU, BNEP_MTU, + BT_IO_OPT_IMTU, BNEP_MTU, + BT_IO_OPT_INVALID); + + if (!nap_dev.io) { + destroy_nap_device(); + error("%s", gerr->message); + g_error_free(gerr); + return -EINVAL; + } + + return 0; } static void bt_pan_enable(const void *buf, uint16_t len) { const struct hal_cmd_pan_enable *cmd = buf; uint8_t status; + int err; + + DBG(""); + + if (local_role == cmd->local_role) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* destroy existing server */ + destroy_nap_device(); switch (cmd->local_role) { - case HAL_PAN_ROLE_PANU: case HAL_PAN_ROLE_NAP: - DBG("Not Implemented"); - status = HAL_STATUS_FAILED; break; + case HAL_PAN_ROLE_NONE: + status = HAL_STATUS_SUCCESS; + goto reply; default: status = HAL_STATUS_UNSUPPORTED; - break; + goto reply; + } + + local_role = cmd->local_role; + err = register_nap_server(); + if (err < 0) { + status = HAL_STATUS_FAILED; + destroy_nap_device(); + goto reply; } - ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status); + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status); } static void bt_pan_get_role(const void *buf, uint16_t len) @@ -325,8 +693,8 @@ static void bt_pan_get_role(const void *buf, uint16_t len) DBG(""); rsp.local_role = local_role; - ipc_send_rsp_full(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, sizeof(rsp), - &rsp, -1); + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, + sizeof(rsp), &rsp, -1); } static const struct ipc_handler cmd_handlers[] = { @@ -340,21 +708,121 @@ static const struct ipc_handler cmd_handlers[] = { { bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) }, }; -bool bt_pan_register(const bdaddr_t *addr) +static sdp_record_t *pan_record(void) { + 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 = 0x0001, type = 0xfffe; + uint32_t rate = 0; + const char *desc = "Network Access Point", *name = "Network Service"; + sdp_record_t *record; + uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ }; + sdp_data_t *head, *pseq, *data; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + record->attrlist = NULL; + record->pattern = NULL; + + 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, &type); + sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, + SDP_UINT32, &rate); + + 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); + + head = sdp_data_alloc(SDP_UINT16, &ptype[0]); + data = sdp_data_alloc(SDP_UINT16, &ptype[1]); + 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(NULL, apseq); + sdp_set_access_protos(record, aproto); + sdp_add_lang_attr(record); + sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security); + + 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; +} + +bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + sdp_record_t *rec; int err; DBG(""); bacpy(&adapter_addr, addr); + rec = pan_record(); + if (!rec) { + error("Failed to allocate PAN record"); + return false; + } + + if (bt_adapter_add_record(rec, SVC_HINT_NETWORKING) < 0) { + error("Failed to register PAN record"); + sdp_record_free(rec); + return false; + } + err = bnep_init(); - if (err) { + if (err < 0) { error("bnep init failed"); + bt_adapter_remove_record(rec->handle); + return false; + } + + err = register_nap_server(); + if (err < 0) { + error("Failed to register NAP"); + bt_adapter_remove_record(rec->handle); + bnep_cleanup(); return false; } - ipc_register(HAL_SERVICE_ID_PAN, cmd_handlers, + nap_dev.record_id = rec->handle; + + hal_ipc = ipc; + ipc_register(hal_ipc, HAL_SERVICE_ID_PAN, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); return true; @@ -364,7 +832,16 @@ void bt_pan_unregister(void) { DBG(""); + g_slist_free_full(devices, pan_device_free); + devices = NULL; + local_role = HAL_PAN_ROLE_NONE; + bnep_cleanup(); - ipc_unregister(HAL_SERVICE_ID_PAN); + ipc_unregister(hal_ipc, HAL_SERVICE_ID_PAN); + hal_ipc = NULL; + + bt_adapter_remove_record(nap_dev.record_id); + nap_dev.record_id = 0; + destroy_nap_device(); } diff --git a/android/pan.h b/android/pan.h index 3178d88..cfbea96 100644 --- a/android/pan.h +++ b/android/pan.h @@ -2,24 +2,24 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ -bool bt_pan_register(const bdaddr_t *addr); +bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_pan_unregister(void); diff --git a/android/pics-a2dp.txt b/android/pics-a2dp.txt new file mode 100644 index 0000000..81debcd --- /dev/null +++ b/android/pics-a2dp.txt @@ -0,0 +1,86 @@ +A2DP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory if such role selected +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_1_1 True Role: Source (C.1) +TSPC_A2DP_1_2 False (*) Role: Sink (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + A2DP SRC Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_2_1 True SRC: Initiate connection establishment (M) +TSPC_A2DP_2_2 True SRC: Accept connection establishment (M) +TSPC_A2DP_2_3 True SRC: Initiate start streaming (M) +TSPC_A2DP_2_4 True SRC: Accept start streaming (M) +TSPC_A2DP_2_5 True SRC: Send audio stream (M) +TSPC_A2DP_2_6 True SRC: Initiate connection release (M) +TSPC_A2DP_2_7 True SRC: Accept connection release (M) +TSPC_A2DP_2_8 True (*) SRC: Initiate suspend (O) +TSPC_A2DP_2_9 True (*) SRC: Accept suspend (O) +TSPC_A2DP_2_10 True SRC: SBC encoder (M) +TSPC_A2DP_2_10a False SRC: Encode Audio Stream (O) +TSPC_A2DP_2_11 False SRC: SBC Configurations in 16 KHz sampling (O) +TSPC_A2DP_2_12 False SRC: SBC Configurations in 32 KHz sampling (O) +TSPC_A2DP_2_13 True SRC: SBC Configurations in 44.1 KHz sampling + (C.1) +TSPC_A2DP_2_14 False (*) SRC: SBC Configurations in 48 KHz sampling + (C.1) +------------------------------------------------------------------------------- +C.1: At least one of the values shall be supported. +------------------------------------------------------------------------------- + + + Supported Codecs in SRC +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_3_1 True SRC: SBC encoder Codec (M) +TSPC_A2DP_3_2 False SRC: Additional encoder Codec (O) +------------------------------------------------------------------------------- + + + A2DP Sink Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_4_1 False SNK: Initiate connection establishment (O) +TSPC_A2DP_4_2 False (*) SNK: Accept connection establishment (M) +TSPC_A2DP_4_3 False SNK: Initiate start streaming (O) +TSPC_A2DP_4_4 False (*) SNK: Accept start streaming (M) +TSPC_A2DP_4_5 False (*) SNK: Receive audio stream (M) +TSPC_A2DP_4_6 False SNK: Initiate connection release (O) +TSPC_A2DP_4_7 False (*) SNK: Accept connection release (M) +TSPC_A2DP_4_8 False SNK: Initiate suspend (O) +TSPC_A2DP_4_9 False SNK: Accept suspend (O) +TSPC_A2DP_4_10 False (*) SNK: SBC decoder (M) +TSPC_A2DP_4_10a False (*) SNK: Decode Audio Stream (O) +TSPC_A2DP_4_11 False SNK: SBC Configurations in 16 KHz sampling (O) +TSPC_A2DP_4_12 False SNK: SBC Configurations in 32 KHz sampling (O) +TSPC_A2DP_4_13 False (*) SNK: SBC Configurations in 44.1 KHz sampling (M) +TSPC_A2DP_4_14 False (*) SNK: SBC Configurations in 48 KHz sampling (M) +------------------------------------------------------------------------------- + + + Supported codecs in SNK +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_5_1 False (*) SNK: SBC decoder Codec (M) +TSPC_A2DP_5_2 False SNK: Additional decoder Coded (O) +TSPC_ALL False Enable all test cases when set to False. +------------------------------------------------------------------------------- diff --git a/android/pics-avctp.txt b/android/pics-avctp.txt new file mode 100644 index 0000000..3490db5 --- /dev/null +++ b/android/pics-avctp.txt @@ -0,0 +1,75 @@ +AVCTP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory if such role selected +O - optional + + Protocol Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVCTP_0_1 False AVCTP 1.0 (C.1) +TSPC_AVCTP_0_2 False AVCTP 1.2 (C.1) +TSPC_AVCTP_0_3 False AVCTP 1.3 (C.1) +TSPC_AVCTP_0_4 True (*) AVCTP 1.4 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support only one Protocol Version. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVCTP_1_1 True (*) Controller (C.1) +TSPC_AVCTP_1_2 True (*) Target (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Controller Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVCTP_2_1 False Message fragmentation (O) +TSPC_AVCTP_2_2 True Transaction label management (M) +TSPC_AVCTP_2_3 True Packet type field management (M) +TSPC_AVCTP_2_4 True Message type field management (M) +TSPC_AVCTP_2_5 True PID field management (M) +TSPC_AVCTP_2_6 True IPID field mangement (M) +TSPC_AVCTP_2_7 True Message information management (M) +TSPC_AVCTP_2_8 False Event registration for message reception (O) +TSPC_AVCTP_2_9 False Event registration for connection request (O) +TSPC_AVCTP_2_10 False Event registration for disconnection (O) +TSPC_AVCTP_2_11 False Connect request (O) +TSPC_AVCTP_2_12 False Disconnect request (O) +TSPC_AVCTP_2_13 False Send message (O) +TSPC_AVCTP_2_14 False Support for multiple AVCTP channel establishment + (O) +------------------------------------------------------------------------------- + + + Target Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVCTP_3_1 False Message fragmentation (O) +TSPC_AVCTP_3_2 True Transaction label management (M) +TSPC_AVCTP_3_3 True Packet type field management (M) +TSPC_AVCTP_3_4 True Message type field management (M) +TSPC_AVCTP_3_5 True PID field management (M) +TSPC_AVCTP_3_6 True IPID field management (M) +TSPC_AVCTP_3_7 True Message information management (M) +TSPC_AVCTP_3_8 True (*) Event registration for message reception (O) +TSPC_AVCTP_3_9 True (*) Event registration for connection request (O) +TSPC_AVCTP_3_10 True (*) Event registration for disconnection request (O) +TSPC_AVCTP_3_11 True (*) Connect request (O) +TSPC_AVCTP_3_12 True (*) Disconnect request (O) +TSPC_AVCTP_3_13 True (*) Send message (O) +TSPC_AVCTP_ALL False Enables all test cases when set to TRUE +------------------------------------------------------------------------------- diff --git a/android/pics-avrcp.txt b/android/pics-avrcp.txt new file mode 100644 index 0000000..969be6a --- /dev/null +++ b/android/pics-avrcp.txt @@ -0,0 +1,626 @@ +AVRCP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory if such role selected +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +SPC_AVRCP_1_1 True Role: Controller (CT) (C.1) +TSPC_AVRCP_1_2 True Role: Target (TG) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Controller Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_2_1 False (*) CT: Initiating connection establishment (M) +TSPC_AVRCP_2_2 False (*) CT: Accepting connection establishment (M) +TSPC_AVRCP_2_3 False (*) CT: Initiating connection release (M) +TSPC_AVRCP_2_4 False (*) CT: Accepting connection release (M) +TSPC_AVRCP_2_5 False CT: Sending UNIT INFO (O) +TSPC_AVRCP_2_6 False CT: Sending SUBUNIT INFO (O) +TSPC_AVRCP_2_7 False (*) CT: Sending PASS THROUGH command category 1 + (C.1) +TSPC_AVRCP_2_8 False CT: Sending PASS THROUGH command category 2 + (C.1) +TSPC_AVRCP_2_9 False CT: Sending PASS THROUGH command category 3 + (C.1) +TSPC_AVRCP_2_10 False CT: Sending PASS THROUGH command category 4 + (C.1) +TSPC_AVRCP_2_11 False CT: Get Capabilities (O) +TSPC_AVRCP_2_12 False CT: List Player Application Setting + Attributes (C.9) +TSPC_AVRCP_2_13 False CT: List Player Application Setting Values (O) +TSPC_AVRCP_2_14 False CT: Get Current Player Application Setting + (C.10) +TSPC_AVRCP_2_15 False CT: Set Player Application Setting Value (C.10) +TSPC_AVRCP_2_16 False CT: Get Player Application Setting + Attribute (O) +TSPC_AVRCP_2_17 False CT: Get Player Application Setting Value (O) +TSPC_AVRCP_2_18 False CT: Inform Displayable Character Set (O) +TSPC_AVRCP_2_19 False CT: Inform Battery Status of CT (O) +TSPC_AVRCP_2_20 False CT: Get Element Attributes (O) +TSPC_AVRCP_2_21 False CT: Get Play Status (O) +TSPC_AVRCP_2_22 False CT: Register Notification (C.11) +TSPC_AVRCP_2_23 False CT: Request Continuing Response (C.2) +TSPC_AVRCP_2_24 False CT: Abort Continuing Response (C.2) +TSPC_AVRCP_2_25 False CT: Next Group (C.12) +TSPC_AVRCP_2_26 False CT: Previous Group (C.12) +TSPC_AVRCP_2_27 False CT: Media Player Selection (O) +TSPC_AVRCP_2_28 False CT: SetAddressedPlayer (O) +TSPC_AVRCP_2_29 False CT: GetFolderItems(MediaPlayerList) (C.5) +TSPC_AVRCP_2_29b False CT: GetTotalNumberOfItems(MediaPlayerList) (C.5) +TSPC_AVRCP_2_30 False CT: EVENT_AVAILABLE_PLAYERS_CHANGED (O) +TSPC_AVRCP_2_31 False CT: EVENT_ADDRESSED_PLAYER_CHANGED (O) +TSPC_AVRCP_2_32 False CT: Browsing (O) +TSPC_AVRCP_2_33 False CT: SetBrowsedPlayer (C.4) +TSPC_AVRCP_2_34 False CT: ChangePath (C.4) +TSPC_AVRCP_2_35 False CT: GetFolderItems(Filesystem) (C.4) +TSPC_AVRCP_2_35b False CT: GetTotalNumberOfItems(Filesystem) (C.4) +TSPC_AVRCP_2_36 False CT: GetItemAttributes (O) +TSPC_AVRCP_2_37 False CT: PlayItem(Filesystem) (C.4) +TSPC_AVRCP_2_38 False CT: EVENT_UIDS_CHANGED (O) +TSPC_AVRCP_2_39 False CT: Searching (O) +TSPC_AVRCP_2_40 False CT: Search (C.7) +TSPC_AVRCP_2_41 False CT: GetFolderItems(Search Results) (C.7) +TSPC_AVRCP_2_41b False CT: GetTotalNumberOfItems(Search Results) (C.7) +TSPC_AVRCP_2_42 False CT: PlayItem(SearchResultList) (C.7) +TSPC_AVRCP_2_43 False CT: NowPlaying (C.8) +TSPC_AVRCP_2_44 False CT: GetFolderItems(NowPlayingList) (C.8) +TSPC_AVRCP_2_44b False CT: GetTotalNumberOfItems(NowPlayingList) (C.8) +TSPC_AVRCP_2_45 False CT: PlayItem(NowPlayingList) (C.8) +TSPC_AVRCP_2_46 False CT: AddToNowPlaying (O) +TSPC_AVRCP_2_47 False CT: EVENT_NOW_PLAYING_CONTENT_CHANGED (O) +TSPC_AVRCP_2_48 False CT: Playable Folders (O) +TSPC_AVRCP_2_49 True (*) CT: Absolute Volume (C.3) +TSPC_AVRCP_2_50 True (*) CT: SetAbsoluteVolume (C.3) +TSPC_AVRCP_2_51 True (*) CT: NotifyVolumeChange (C.3) +TSPC_AVRCP_2_52 False (*) CT: Discoverable Mode (M) +TSPC_AVRCP_2_53 False CT: PASSTHROUGH operation supporting press + and hold (O) +TSPC_AVRCP_2_54 False CT: Cover Art (O) +TSPC_AVRCP_2_55 False CT: GetCapabilities, Cover Art (C.10) +TSPC_AVRCP_2_56 False CT: GetImageProperties, Cover Art (C.10) +TSPC_AVRCP_2_57 False CT: GetImage, Cover Art (C.9) +TSPC_AVRCP_2_58 False CT: GetLinkedThumbnail, CoverArt (C.9) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined categories + (TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10). +C.2: Mandatory if TSPC_AVRCP_2_20 is supported, otherwise Optional. +C.3: Mandatory if TSPC_AVRCP_2_8 is supported, otherwise Excluded. +C.4: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded. +C.5: Mandatory if TSPC_AVRCP_2_27 is supported, otherwise Excluded. +C.7: Mandatory if item TSPC_AVRCP_2_39 is supported, Excluded otherwise. +C.8: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded. +C.9: Mandatory to support if Player Application Settings feature is supported. + If any item TSPC_AVRCP_2_13 through TSPC_AVRCP_2_15 is supported it is + required to claim support for this feature in accordance with Player + Application Settings support requirements, otherwise Optional. +C.10: Mandatory to support either Get or Set Player Application Settings + (TSPC_AVRCP_2_14 or TSPC_AVRCP_2_15) if List Player Application Setting + Attributes (TSPC_AVRCP_2_12) is supported. Either TSPC_AVRCP_2_14 + or TSPC_AVRCP_2_15 must be supported if Player Application Settings + feature is supported, in accordance with Player Application Settings + support requirements. +C.11: Mandatory if TSPC_AVRCP_2_7 or (TSPC_AVRCP_2_8 AND TSPC_AVRCP_2_49) + or TSPC_AVRCP_2_9 is supported, otherwise Optional. +C.12: Mandatory if Basic Group Navigation Feature supported. If any item + TSPC_AVRCP_2_25 or TSPC_AVRCP_2_26 is supported it is mandatory to + support both, in accordance with Basic Group Navigation support + requirements, otherwise Excluded. +------------------------------------------------------------------------------- + + + Controller Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_2b_1 False CT: AVRCP v1.0 (C.1) +TSPC_AVRCP_2b_2 False CT: AVRCP v1.3 (C.1) +TSPC_AVRCP_2b_3 False CT: AVRCP v1.4 (C.1) +TSPC_AVRCP_2b_4 False CT: AVRCP v1.5 (C.1) +TSPC_AVRCP_2b_5 False CT: AVRCP v1.6 (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the profile versions if + Controller role supported (SPC_AVRCP_1_1). +------------------------------------------------------------------------------- + + + Operation_id of Category 1 for CT +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_3_1 False CT: category 1 - Operation id: 0 (C.1) +TSPC_AVRCP_3_2 False CT: category 1 - Operation id: 1 (C.1) +TSPC_AVRCP_3_3 False CT: category 1 - Operation id: 2 (C.1) +TSPC_AVRCP_3_4 False CT: category 1 - Operation id: 3 (C.1) +TSPC_AVRCP_3_5 False CT: category 1 - Operation id: 4 (C.1) +TSPC_AVRCP_3_6 False CT: category 1 - Operation id: 5 (C.1) +TSPC_AVRCP_3_7 False CT: category 1 - Operation id: 6 (C.1) +TSPC_AVRCP_3_8 False CT: category 1 - Operation id: 7 (C.1) +TSPC_AVRCP_3_9 False CT: category 1 - Operation id: 8 (C.1) +TSPC_AVRCP_3_10 False CT: category 1 - Operation id: 9 (C.1) +TSPC_AVRCP_3_11 False CT: category 1 - Operation id: dot (C.1) +TSPC_AVRCP_3_12 False CT: category 1 - Operation id: enter (C.1) +TSPC_AVRCP_3_13 False CT: category 1 - Operation id: clear (C.1) +TSPC_AVRCP_3_14 False CT: category 1 - Operation id: sound_select + (C.1) +TSPC_AVRCP_3_15 False CT: category 1 - Operation id: input_select + (C.1) +TSPC_AVRCP_3_16 False CT: category 1 - Operation id: + display_information (C.1) +TSPC_AVRCP_3_17 False CT: category 1 - Operation id: help (C.1) +TSPC_AVRCP_3_18 False CT: category 1 - Operation id: power (C.1) +TSPC_AVRCP_3_19 False (*) CT: category 1 - Operation id: play (C.1) +TSPC_AVRCP_3_20 False (*) CT: category 1 - Operation id: stop (C.1) +TSPC_AVRCP_3_21 False (*) CT: category 1 - Operation id: pause (C.1) +TSPC_AVRCP_3_22 False CT: category 1 - Operation id: record (C.1) +TSPC_AVRCP_3_23 False CT: category 1 - Operation id: rewind (C.1) +TSPC_AVRCP_3_24 False CT: category 1 - Operation id: fast_forward + (C.1) +TSPC_AVRCP_3_25 False CT: category 1 - Operation id: eject (C.1) +TSPC_AVRCP_3_26 False CT: category 1 - Operation id: forward (C.1) +TSPC_AVRCP_3_27 False CT: category 1 - Operation id: backward (C.1) +TSPC_AVRCP_3_28 False CT: category 1 - Operation id: angle (C.1) +TSPC_AVRCP_3_29 False CT: category 1 - Operation id: subpicture (C.1) +TSPC_AVRCP_3_30 False CT: category 1 - Operation id: F1 (C.1) +TSPC_AVRCP_3_31 False CT: category 1 - Operation id: F2 (C.1) +TSPC_AVRCP_3_32 False CT: category 1 - Operation id: F3 (C.1) +TSPC_AVRCP_3_33 False CT: category 1 - Operation id: F4 (C.1) +TSPC_AVRCP_3_33a False CT: category 1 - Operation id: F5 (C.1) +TSPC_AVRCP_3_34 False CT: category 1 - Operation id: vendor_unique + (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of these operation_ids if the device + supports category 1 (TSPC_AVRCP_2_7). +------------------------------------------------------------------------------- + + + Operation_id of category 2 for CT +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_4_1 False CT: category 2 - Operation id: 0 (C.2) +TSPC_AVRCP_4_2 False CT: category 2 - Operation id: 1 (C.2) +TSPC_AVRCP_4_3 False CT: category 2 - Operation id: 2 (C.2) +TSPC_AVRCP_4_4 False CT: category 2 - Operation id: 3 (C.2) +TSPC_AVRCP_4_5 False CT: category 2 - Operation id: 4 (C.2) +TSPC_AVRCP_4_6 False CT: category 2 - Operation id: 5 (C.2) +TSPC_AVRCP_4_7 False CT: category 2 - Operation id: 6 (C.2) +TSPC_AVRCP_4_8 False CT: category 2 - Operation id: 7 (C.2) +TSPC_AVRCP_4_9 False CT: category 2 - Operation id: 8 (C.2) +TSPC_AVRCP_4_10 False CT: category 2 - Operation id: 9 (C.2) +TSPC_AVRCP_4_11 False CT: category 2 - Operation id: dot (C.2) +TSPC_AVRCP_4_12 False CT: category 2 - Operation id: enter (C.2) +TSPC_AVRCP_4_13 False CT: category 2 - Operation id: clear (C.2) +TSPC_AVRCP_4_14 False CT: category 2 - Operation id: sound_select + (C.2) +TSPC_AVRCP_4_15 False CT: category 2 - Operation id: input_select + (C.2) +TSPC_AVRCP_4_16 False CT: category 2 - Operation id: + display_information (C.2) +TSPC_AVRCP_4_17 False CT: category 2 - Operation id: help (C.2) +TSPC_AVRCP_4_18 False CT: category 2 - Operation id: power (C.2) +TSPC_AVRCP_4_19 False (*) CT: category 2 - Operation id: volume_up (C.2) +TSPC_AVRCP_4_20 False (*) CT: category 2 - Operation id: volume_down (C.2) +TSPC_AVRCP_4_21 False CT: category 2 - Operation id: mute (C.2) +TSPC_AVRCP_4_22 False CT: category 2 - Operation id: F1 (C.2) +TSPC_AVRCP_4_23 False CT: category 2 - Operation id: F2 (C.2) +TSPC_AVRCP_4_24 False CT: category 2 - Operation id: F3 (C.2) +TSPC_AVRCP_4_25 False CT: category 2 - Operation id: F4 (C.2) +TSPC_AVRCP_4_25a False CT: category 2 - Operation id: F5 (C.2) +TSPC_AVRCP_4_26 False CT: category 2 - Operation id: vendor_unique + (C.2) +------------------------------------------------------------------------------- +C.2: Mandatory to support at least one of these operation_ids if the device + supports category 2 (TSPC_AVRCP_2_8). +------------------------------------------------------------------------------- + + + Operation_id of category 3 for CT +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_5_1 False CT: category 3 - Operation id: 0 (C.3) +TSPC_AVRCP_5_2 False CT: category 3 - Operation id: 1 (C.3) +TSPC_AVRCP_5_3 False CT: category 3 - Operation id: 2 (C.3) +TSPC_AVRCP_5_4 False CT: category 3 - Operation id: 3 (C.3) +TSPC_AVRCP_5_5 False CT: category 3 - Operation id: 4 (C.3) +TSPC_AVRCP_5_6 False CT: category 3 - Operation id: 5 (C.3) +TSPC_AVRCP_5_7 False CT: category 3 - Operation id: 6 (C.3) +TSPC_AVRCP_5_8 False CT: category 3 - Operation id: 7 (C.3) +TSPC_AVRCP_5_9 False CT: category 3 - Operation id: 8 (C.3) +TSPC_AVRCP_5_10 False CT: category 3 - Operation id: 9 (C.3) +TSPC_AVRCP_5_11 False CT: category 3 - Operation id: dot (C.3) +TSPC_AVRCP_5_12 False CT: category 3 - Operation id: enter (C.3) +TSPC_AVRCP_5_13 False CT: category 3 - Operation id: clear (C.3) +TSPC_AVRCP_5_14 False CT: category 3 - Operation id: channel up (C.3) +TSPC_AVRCP_5_15 False CT: category 3 - Operation id: channel down + (C.3) +TSPC_AVRCP_5_16 False CT: category 3 - Operation id: previous channel + (C.3) +TSPC_AVRCP_5_17 False CT: category 3 - Operation id: sound_select + (C.3) +TSPC_AVRCP_5_18 False CT: category 3 - Operation id: input_select + (C.3) +TSPC_AVRCP_5_19 False CT: category 3 - Operation id: + display_information (C.3) +TSPC_AVRCP_5_20 False CT: category 3 - Operation id: help (C.3) +TSPC_AVRCP_5_21 False CT: category 3 - Operation id: power (C.3) +TSPC_AVRCP_5_22 False CT: category 3 - Operation id: angle (C.3) +TSPC_AVRCP_5_23 False CT: category 3 - Operation id: subpicture(C.3) +TSPC_AVRCP_5_24 False CT: category 3 - Operation id: F1 (C.3) +TSPC_AVRCP_5_25 False CT: category 3 - Operation id: F2 (C.3) +TSPC_AVRCP_5_26 False CT: category 3 - Operation id: F3 (C.3) +TSPC_AVRCP_5_27 False CT: category 3 - Operation id: F4 (C.3) +TSPC_AVRCP_5_27a False CT: category 3 - Operation id: F5 (C.3) +TSPC_AVRCP_5_28 False CT: category 3 - Operation id: vendor_unique + (C.3) +------------------------------------------------------------------------------- +C.3: Mandatory to support at least one of these operation_ids if the device + supports category 3 (TSPC_AVRCP_2_9). +------------------------------------------------------------------------------- + + + Operation_id of category 4 for CT +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_6_1 False CT: category 4 - Operation id: select (C.4) +TSPC_AVRCP_6_2 False CT: category 4 - Operation id: up (C.4) +TSPC_AVRCP_6_3 False CT: category 4 - Operation id: down (C.4) +TSPC_AVRCP_6_4 False CT: category 4 - Operation id: left (C.4) +TSPC_AVRCP_6_5 False CT: category 4 - Operation id: right (C.4) +TSPC_AVRCP_6_6 False CT: category 4 - Operation id: right up (C.4) +TSPC_AVRCP_6_7 False CT: category 4 - Operation id: right down (C.4) +TSPC_AVRCP_6_8 False CT: category 4 - Operation id: left up (C.4) +TSPC_AVRCP_6_9 False CT: category 4 - Operation id: left down (C.4) +TSPC_AVRCP_6_10 False CT: category 4 - Operation id: root menu (C.4) +TSPC_AVRCP_6_11 False CT: category 4 - Operation id: setup menu (C.4) +TSPC_AVRCP_6_12 False CT: category 4 - Operation id: contents menu + (C.4) +TSPC_AVRCP_6_13 False CT: category 4 - Operation id: favorite menu + (C.4) +TSPC_AVRCP_6_14 False CT: category 4 - Operation id: exit (C.4) +TSPC_AVRCP_6_15 False CT: category 4 - Operation id: 0 (C.4) +TSPC_AVRCP_6_16 False CT: category 4 - Operation id: 1 (C.4) +TSPC_AVRCP_6_17 False CT: category 4 - Operation id: 2 (C.4) +TSPC_AVRCP_6_18 False CT: category 4 - Operation id: 3 (C.4) +TSPC_AVRCP_6_19 False CT: category 4 - Operation id: 4 (C.4) +TSPC_AVRCP_6_20 False CT: category 4 - Operation id: 5 (C.4) +TSPC_AVRCP_6_21 False CT: category 4 - Operation id: 6 (C.4) +TSPC_AVRCP_6_22 False CT: category 4 - Operation id: 7 (C.4) +TSPC_AVRCP_6_23 False CT: category 4 - Operation id: 8 (C.4) +TSPC_AVRCP_6_24 False CT: category 4 - Operation id: 9 (C.4) +TSPC_AVRCP_6_25 False CT: category 4 - Operation id: dot (C.4) +TSPC_AVRCP_6_26 False CT: category 4 - Operation id: enter (C.4) +TSPC_AVRCP_6_27 False CT: category 4 - Operation id: clear (C.4) +TSPC_AVRCP_6_28 False CT: category 4 - Operation id: + display_information (C.4) +TSPC_AVRCP_6_29 False CT: category 4 - Operation id: help (C.4) +TSPC_AVRCP_6_30 False CT: category 4 - Operation id: page up (C.4) +TSPC_AVRCP_6_31 False CT: category 4 - Operation id: page down (C.4) +TSPC_AVRCP_6_32 False CT: category 4 - Operation id: power (C.4) +TSPC_AVRCP_6_33 False CT: category 4 - Operation id: F1 (C.4) +TSPC_AVRCP_6_34 False CT: category 4 - Operation id: F2 (C.4) +TSPC_AVRCP_6_35 False CT: category 4 - Operation id: F3 (C.4) +TSPC_AVRCP_6_36 False CT: category 4 - Operation id: F4 (C.4) +TSPC_AVRCP_6_36a False CT: category 4 - Operation id: F5 (C.4) +TSPC_AVRCP_6_37 False CT: category 4 - Operation id: vendor_unique + (C.4) +------------------------------------------------------------------------------- +C.4: Mandatory to support at least one of these operation_ids if the device + supports category 4 (TSPC_AVRCP_2_9). +------------------------------------------------------------------------------- + + + Target Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_7_1 True (*) TG: Initiating connection establishment (O) +TSPC_AVRCP_7_2 True TG: Accept connection establishment (M) +TSPC_AVRCP_7_3 True TG: Initiating connection release (M) +TSPC_AVRCP_7_4 True TG: Accepting connection release (M) +TSPC_AVRCP_7_5 True TG: Receiving UNIT INFO (M) +TSPC_AVRCP_7_6 True TG: Receiving SUBUNIT INFO (M) +TSPC_AVRCP_7_7 True TG: Receiving PASS THROUGH command category 1 + (C.1) +TSPC_AVRCP_7_8 True (*) TG: Receiving PASS THROUGH command category 2 + (C.1) +TSPC_AVRCP_7_9 False TG: Receiving PASS THROUGH command category 3 + (C.1) +TSPC_AVRCP_7_10 False TG: Receiving PASS THROUGH command category 4 + (C.1) +TSPC_AVRCP_7_11 True (*) TG: Get Capabilities Response (C.3) +TSPC_AVRCP_7_12 False TG: List Player Application Settings (C.14) +TSPC_AVRCP_7_13 False TG: List Player Application Setting Values + (C.14) +TSPC_AVRCP_7_14 False TG: Get Current Player Application Settings + (C.14) +TSPC_AVRCP_7_15 False TG: Set Player Application Setting Value (C.14) +TSPC_AVRCP_7_16 False TG: Get Player Application Setting Attribute + (O) +TSPC_AVRCP_7_17 False TG: Get Player Application Setting Value (O) +TSPC_AVRCP_7_18 False TG: Inform Displayable Character Set (O) +TSPC_AVRCP_7_19 False TG: Inform Battery Status Of CT Response (O) +TSPC_AVRCP_7_20 True (*) TG: Get Element Attributes Response (C.3) +TSPC_AVRCP_7_21 True (*) TG: Get Play Status Response (C.2) +TSPC_AVRCP_7_22 True (*) TG: Register Notification Response (C.12) +TSPC_AVRCP_7_23 True (*) TG: Notify Event Response: + PLAYBACK_STATUS_CHANGED (C.4) +TSPC_AVRCP_7_24 True (*) TG: Notify Event Response: TRACK_CHANGED (C.4) +TSPC_AVRCP_7_25 False TG: Notify Event Response: TRACK_REACHED_END (O) +TSPC_AVRCP_7_26 False TG: Notify Event Response: TRACK_REACHED_START + (O) +TSPC_AVRCP_7_27 False TG: Notify Event Response: PLAYBACK_POS_CHANGED + (O) +TSPC_AVRCP_7_28 False TG: Notify Event Response: BATT_STATUS_CHANGED + (O) +TSPC_AVRCP_7_29 False TG: Notify Event Response: SYSTEM_STATUS_CHANGED + (O) +TSPC_AVRCP_7_30 False TG: Notify Event Response: + PLAYER_APPLICATION_SETTING_CHANGED (O) +TSPC_AVRCP_7_31 True (*) TG: Request ContinuingResponse (C.2) +TSPC_AVRCP_7_32 True (*) TG: Abort ContinuingResponse Response (C.2) +TSPC_AVRCP_7_34 False TG: Next Group (C.15) +TSPC_AVRCP_7_35 False TG: Previous Group (C.15) +TSPC_AVRCP_7_36 False TG: Media Player Selection (C.8) +TSPC_AVRCP_7_37 False TG: SetAddressedPlayer (C.8) +TSPC_AVRCP_7_38 False TG: GetFolderItems(MediaPlayerList) (C.8) +TSPC_AVRCP_7_38b False TG: GetTotalNumberOfItems(MediaPlayerList) (C.8) +TSPC_AVRCP_7_39 False TG: EVENT_AVAILABLE_PLAYERS_CHANGED (C.8) +TSPC_AVRCP_7_40 False TG: EVENT_ADDRESSED_PLAYER_CHANGED (C.8) +TSPC_AVRCP_7_41 False TG: Supports Multiple Players (O) +TSPC_AVRCP_7_42 False TG: Browsing (O) +TSPC_AVRCP_7_42a False TG: Supports initiation of browsing channel + establishment (O) +TSPC_AVRCP_7_43 False TG: SetBrowsedPlayer (C.6) +TSPC_AVRCP_7_44 False TG: ChangePath (C.6) +TSPC_AVRCP_7_45 False TG: GetFolderItems(Filesystem) (C.6) +TSPC_AVRCP_7_45b False TG: GetTotalNumberOfItems(Filesystem) (C.6) +TSPC_AVRCP_7_46 False TG: GetItemAttributes (C.6) +TSPC_AVRCP_7_47 False TG: PlayItem(Filesystem) (C.6) +TSPC_AVRCP_7_48 False TG: EVENT_UIDS_CHANGED (C.9) +TSPC_AVRCP_7_49 False TG: Database Aware Players (O) +TSPC_AVRCP_7_50 False TG: Searching (O) +TSPC_AVRCP_7_51 False TG: Search (C.10) +TSPC_AVRCP_7_52 False TG: GetFolderItems(Search Results) (C.10) +TSPC_AVRCP_7_52b False TG: GetTotalNumberOfItems(Search Results) (C.10) +TSPC_AVRCP_7_53 False TG: PlayItem(SearchResultList) (C.10) +TSPC_AVRCP_7_54 False TG: NowPlaying (C.11) +TSPC_AVRCP_7_55 False TG: GetFolderItems(NowPlayingList) (C.11) +TSPC_AVRCP_7_55b False TG: GetTotalNumberOfItems(NowPlayingList) (C.11) +TSPC_AVRCP_7_56 False TG: PlayItem(NowPlayingList) (C.11) +TSPC_AVRCP_7_57 False TG: AddToNowPlaying (O) +TSPC_AVRCP_7_58 False TG: EVENT_NOW_PLAYING_CONTENT_CHANGED (C.11) +TSPC_AVRCP_7_59 False TG: Playable Folders (O) +TSPC_AVRCP_7_60 False TG: Absolute Volume (C.5) +TSPC_AVRCP_7_61 False TG: SetAbsoluteVolume (C.5) +TSPC_AVRCP_7_62 False TG: NotifyVolumeChange (C.5) +TSPC_AVRCP_7_63 False TG: Error Response (O) +TSPC_AVRCP_7_64 False TG: General Reject (C.13) +TSPC_AVRCP_7_65 True TG: Discoverable Mode (M) +TSPC_AVRCP_7_66 False TG: PASSTHROUGH operation supporting press + and hold (O) +TSPC_AVRCP_7_67 False TG: Cover Art (O) +TSPC_AVRCP_7_68 False TG: GetCapabilities, Cover Art (C.12) +TSPC_AVRCP_7_69 False TG: GetImageProperties, Cover Art (C.12) +TSPC_AVRCP_7_70 False TG: GetImage, Cover Art (C.12) +TSPC_AVRCP_7_71 False TG: GetLinkedThumbnail, Cover Art (C.12) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the categories. Supported + operation_id's are shown in Table 8 to Table 11. +C.2: Mandatory if 7/20 is supported, otherwise Optional. +C.3: Mandatory if 7/7 is supported, otherwise Optional. +C.4: Mandatory if 7/22 and 7/20 is supported, otherwise Optional. +C.5: Mandatory if 7/8 is supported, otherwise Excluded. +C.6: Mandatory if 7/42 is supported, otherwise Excluded. +C.7: Mandatory if 7/36 is supported, otherwise Excluded. +C.8: Mandatory if (7/7 or 7/9) is supported, otherwise Excluded. +C.9: Mandatory if 7/49 is supported, otherwise Optional. +C.10: Mandatory if 7/50 is supported, otherwise Excluded. +C.11: Mandatory if 7/42 is supported, otherwise Optional. +C.12: Mandatory if 7/7 or (7/8 AND 7/60) or 7/9 is supported, otherwise Optional +C.13: Mandatory if 7/7 or 7/9 or 7/42 is supported, otherwise Optional. +C.14: Mandatory if Player Application Settings Feature supported. If any item + 7/12 – 7/15 is supported, all items 7/12 – 7/15 shall be supported, + in accordance with Player Application Settings Feature support + requirements, otherwise Excluded. +C.15: Mandatory if Basic Group Navigation Feature supported. If any item + 7/34 or 7/35 is supported it is mandatory to support both, + in accordance with Basic Group Navigation support requirements, + otherwise Excluded. +------------------------------------------------------------------------------- + + Target Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_7b_1 False TG: AVRCP v1.0 (C.1) +TSPC_AVRCP_7b_2 False TG: AVRCP v1.3 (C.1) +TSPC_AVRCP_7b_3 False TG: AVRCP v1.4 (C.1) +TSPC_AVRCP_7b_4 True (*) TG: AVRCP v1.5 (C.1) +TSPC_AVRCP_7b_5 False TG: AVRCP v1.6 (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the profile versions. +------------------------------------------------------------------------------- + + + operation_id of category 1 for TG +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_8_1 False TG: category 1 - Operation id: 0 (O) +TSPC_AVRCP_8_2 False TG: category 1 - Operation id: 1 (O) +TSPC_AVRCP_8_3 False TG: category 1 - Operation id: 2 (O) +TSPC_AVRCP_8_4 False TG: category 1 - Operation id: 3 (O) +TSPC_AVRCP_8_5 False TG: category 1 - Operation id: 4 (O) +TSPC_AVRCP_8_6 False TG: category 1 - Operation id: 5 (O) +TSPC_AVRCP_8_7 False TG: category 1 - Operation id: 6 (O) +TSPC_AVRCP_8_8 False TG: category 1 - Operation id: 7 (O) +TSPC_AVRCP_8_9 False TG: category 1 - Operation id: 8 (O) +TSPC_AVRCP_8_10 False TG: category 1 - Operation id: 9 (O) +TSPC_AVRCP_8_11 False TG: category 1 - Operation id: dot (O) +TSPC_AVRCP_8_12 False TG: category 1 - Operation id: enter (O) +TSPC_AVRCP_8_13 False TG: category 1 - Operation id: clear (O) +TSPC_AVRCP_8_14 False TG: category 1 - Operation id: sound select (O) +TSPC_AVRCP_8_15 False TG: category 1 - Operation id: input select (O) +TSPC_AVRCP_8_16 False TG: category 1 - Operation id: display + information (O) +TSPC_AVRCP_8_17 False TG: category 1 - Operation id: help (O) +TSPC_AVRCP_8_18 False TG: category 1 - Operation id: power (O) +TSPC_AVRCP_8_19 True TG: category 1 - Operation id: play (M) +TSPC_AVRCP_8_20 True TG: category 1 - Operation id: stop (M) +TSPC_AVRCP_8_21 True TG: category 1 - Operation id: pause (O) +TSPC_AVRCP_8_22 False TG: category 1 - Operation id: record (O) +TSPC_AVRCP_8_23 True (*) TG: category 1 - Operation id: rewind (O) +TSPC_AVRCP_8_24 True (*) TG: category 1 - Operation id: fast forward (O) +TSPC_AVRCP_8_25 False TG: category 1 - Operation id: eject (O) +TSPC_AVRCP_8_26 True (*) TG: category 1 - Operation id: forward (O) +TSPC_AVRCP_8_27 True (*) TG: category 1 - Operation id: backward (O) +TSPC_AVRCP_8_28 False TG: category 1 - Operation id: angle (O) +TSPC_AVRCP_8_29 False TG: category 1 - Operation id: subpicture (O) +TSPC_AVRCP_8_30 False TG: category 1 - Operation id: F1 (O) +TSPC_AVRCP_8_31 False TG: category 1 - Operation id: F2 (O) +TSPC_AVRCP_8_32 False TG: category 1 - Operation id: F3 (O) +TSPC_AVRCP_8_33 False TG: category 1 - Operation id: F4 (O) +TSPC_AVRCP_8_33a False TG: category 1 - Operation id: F5 (O) +TSPC_AVRCP_8_34 False TG: category 1 - Operation id: vendor unique (O) +------------------------------------------------------------------------------- + + + operation_id of category 2 for TG +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_9_1 False TG: category 2 - Operation id: 0 (O) +TSPC_AVRCP_9_2 False TG: category 2 - Operation id: 1 (O) +TSPC_AVRCP_9_3 False TG: category 2 - Operation id: 2 (O) +TSPC_AVRCP_9_4 False TG: category 2 - Operation id: 3 (O) +TSPC_AVRCP_9_5 False TG: category 2 - Operation id: 4 (O) +TSPC_AVRCP_9_6 False TG: category 2 - Operation id: 5 (O) +TSPC_AVRCP_9_7 False TG: category 2 - Operation id: 6 (O) +TSPC_AVRCP_9_8 False TG: category 2 - Operation id: 7 (O) +TSPC_AVRCP_9_9 False TG: category 2 - Operation id: 8 (O) +TSPC_AVRCP_9_10 False TG: category 2 - Operation id: 9 (O) +TSPC_AVRCP_9_11 False TG: category 2 - Operation id: dot (O) +TSPC_AVRCP_9_12 False TG: category 2 - Operation id: enter (O) +TSPC_AVRCP_9_13 False TG: category 2 - Operation id: clear (O) +TSPC_AVRCP_9_14 False TG: category 2 - Operation id: sound select (O) +TSPC_AVRCP_9_15 False TG: category 2 - Operation id: input select (O) +TSPC_AVRCP_9_16 False TG: category 2 - Operation id: display + information (O) +TSPC_AVRCP_9_17 False TG: category 2 - Operation id: help (O) +TSPC_AVRCP_9_18 False TG: category 2 - Operation id: power (O) +TSPC_AVRCP_9_19 True TG: category 2 - Operation id: volume up (C.2) +TSPC_AVRCP_9_20 True TG: category 2 - Operation id: volume down (C.2) +TSPC_AVRCP_9_21 False TG: category 2 - Operation id: mute (O) +TSPC_AVRCP_9_24 False TG: category 2 - Operation id: F1 (O) +TSPC_AVRCP_9_25 False TG: category 2 - Operation id: F2 (O) +TSPC_AVRCP_9_26 False TG: category 2 - Operation id: F3 (O) +TSPC_AVRCP_9_27 False TG: category 2 - Operation id: F4 (O) +TSPC_AVRCP_9_27a False TG: category 2 - Operation id: F5 (O) +TSPC_AVRCP_9_28 False TG: category 2 - Operation id: vendor unique (O) +------------------------------------------------------------------------------- +C.2: Mandatory to support if the device supports category 2 (TSPC_AVRCP_7_8). +------------------------------------------------------------------------------- + + + operation_id of category 3 for TG +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_10_1 False TG: category 3 - Operation id: 0 (O) +TSPC_AVRCP_10_2 False TG: category 3 - Operation id: 1 (O) +TSPC_AVRCP_10_3 False TG: category 3 - Operation id: 2 (O) +TSPC_AVRCP_10_4 False TG: category 3 - Operation id: 3 (O) +TSPC_AVRCP_10_5 False TG: category 3 - Operation id: 4 (O) +TSPC_AVRCP_10_6 False TG: category 3 - Operation id: 5 (O) +TSPC_AVRCP_10_7 False TG: category 3 - Operation id: 6 (O) +TSPC_AVRCP_10_8 False TG: category 3 - Operation id: 7 (O) +TSPC_AVRCP_10_9 False TG: category 3 - Operation id: 8 (O) +TSPC_AVRCP_10_10 False TG: category 3 - Operation id: 9 (O) +TSPC_AVRCP_10_11 False TG: category 3 - Operation id: dot (O) +TSPC_AVRCP_10_12 False TG: category 3 - Operation id: enter (O) +TSPC_AVRCP_10_13 False TG: category 3 - Operation id: clear (O) +TSPC_AVRCP_10_14 False TG: category 3 - Operation id: channel up (C.3) +TSPC_AVRCP_10_15 False TG: category 3 - Operation id: channel down + (C.3) +TSPC_AVRCP_10_16 False TG: category 3 - Operation id: previous channel + (O) +TSPC_AVRCP_10_17 False TG: category 3 - Operation id: sound select (O) +TSPC_AVRCP_10_18 False TG: category 3 - Operation id: input select (O) +TSPC_AVRCP_10_19 False TG: category 3 - Operation id: display + information (O) +TSPC_AVRCP_10_20 False TG: category 3 - Operation id: help (O) +TSPC_AVRCP_10_21 False TG: category 3 - Operation id: power (O) +TSPC_AVRCP_10_21a False TG: category 3 - Operation id: angle (O) +TSPC_AVRCP_10_21b False TG: category 3 - Operation id: subpicture (O) +TSPC_AVRCP_10_22 False TG: category 3 - Operation id: F1 (O) +TSPC_AVRCP_10_23 False TG: category 3 - Operation id: F2 (O) +TSPC_AVRCP_10_24 False TG: category 3 - Operation id: F3 (O) +TSPC_AVRCP_10_25 False TG: category 3 - Operation id: F4 (O) +TSPC_AVRCP_10_25a False TG: category 3 - Operation id: F5 (O) +TSPC_AVRCP_10_26 False TG: category 3 - Operation id: vendor unique (O) +------------------------------------------------------------------------------- +C.3: Mandatory to support if the device supports category 3 (TSPC_AVRCP_7_9). +------------------------------------------------------------------------------- + + + operation_id of category 4 for TG +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_11_1 False TG: category 4 - Operation id: select (C.4) +TSPC_AVRCP_11_2 False TG: category 4 - Operation id: up (C.4) +TSPC_AVRCP_11_3 False TG: category 4 - Operation id: down (C.4) +TSPC_AVRCP_11_4 False TG: category 4 - Operation id: left (C.4) +TSPC_AVRCP_11_5 False TG: category 4 - Operation id: right (C.4) +TSPC_AVRCP_11_6 False TG: category 4 - Operation id: right up (O) +TSPC_AVRCP_11_7 False TG: category 4 - Operation id: right down (O) +TSPC_AVRCP_11_8 False TG: category 4 - Operation id: left up (O) +TSPC_AVRCP_11_9 False TG: category 4 - Operation id: left down (O) +TSPC_AVRCP_11_10 False TG: category 4 - Operation id: root menu (C.4) +TSPC_AVRCP_11_11 False TG: category 4 - Operation id: setup menu (O) +TSPC_AVRCP_11_12 False TG: category 4 - Operation id: contents menu (O) +TSPC_AVRCP_11_13 False TG: category 4 - Operation id: favorite menu (O) +TSPC_AVRCP_11_14 False TG: category 4 - Operation id: exit (O) +TSPC_AVRCP_11_15 False TG: category 4 - Operation id: 0 (O) +TSPC_AVRCP_11_16 False TG: category 4 - Operation id: 1 (O) +TSPC_AVRCP_11_17 False TG: category 4 - Operation id: 2 (O) +TSPC_AVRCP_11_18 False TG: category 4 - Operation id: 3 (O) +TSPC_AVRCP_11_19 False TG: category 4 - Operation id: 4 (O) +TSPC_AVRCP_11_20 False TG: category 4 - Operation id: 5 (O) +TSPC_AVRCP_11_21 False TG: category 4 - Operation id: 6 (O) +TSPC_AVRCP_11_22 False TG: category 4 - Operation id: 7 (O) +TSPC_AVRCP_11_23 False TG: category 4 - Operation id: 8 (O) +TSPC_AVRCP_11_24 False TG: category 4 - Operation id: 9 (O) +TSPC_AVRCP_11_25 False TG: category 4 - Operation id: dot (O) +TSPC_AVRCP_11_26 False TG: category 4 - Operation id: enter (O) +TSPC_AVRCP_11_27 False TG: category 4 - Operation id: clear (O) +TSPC_AVRCP_11_28 False TG: category 4 - Operation id: disply (O) +TSPC_AVRCP_11_29 False TG: category 4 - Operation id: help (O) +TSPC_AVRCP_11_30 False TG: category 4 - Operation id: page up (O) +TSPC_AVRCP_11_31 False TG: category 4 - Operation id: page down (O) +TSPC_AVRCP_11_32 False TG: category 4 - Operation id: power (O) +TSPC_AVRCP_11_33 False TG: category 4 - Operation id: F1 (O) +TSPC_AVRCP_11_34 False TG: category 4 - Operation id: F2 (O) +TSPC_AVRCP_11_35 False TG: category 4 - Operation id: F3 (O) +TSPC_AVRCP_11_36 False TG: category 4 - Operation id: F4 (O) +TSPC_AVRCP_11_36a False TG: category 4 - Operation id: F5 (O) +TSPC_AVRCP_11_37 False TG: category 4 - Operation id: vendor unique (O) +TSPC_AVRCP_ALL False Enables all test cases when set to TRUE. +------------------------------------------------------------------------------- +C.4: Mandatory to support if the device supports category 4 (TSPC_AVRCP_7_10). +------------------------------------------------------------------------------- diff --git a/android/pics-did.txt b/android/pics-did.txt index e8c914a..fe1c03a 100644 --- a/android/pics-did.txt +++ b/android/pics-did.txt @@ -1,21 +1,13 @@ DID PICS for the PTS tool. +PTS version: 5.1 + * - different than PTS defaults # - not yet implemented/supported M - mandatory O - optional - Version -------------------------------------------------------------------------------- -Parameter Name Selected Description -------------------------------------------------------------------------------- -TSPC_DID_0_1 False Device ID 1.2 (C.1) -TSPC_DID_0_2 True Device ID 1.3 (C.1) -------------------------------------------------------------------------------- -C.1: It is mandatory to support one of the profile versions. -------------------------------------------------------------------------------- - SDP Requirements ------------------------------------------------------------------------------- @@ -29,14 +21,3 @@ TSPC_DID_1_5 True Primary Record (M) TSPC_DID_1_6 True Vendor ID Source (M) TSPC_ALL False Turns on all the test cases ------------------------------------------------------------------------------- - - - Required PIXIT settings -------------------------------------------------------------------------------- -Parameter Name Value -------------------------------------------------------------------------------- -TSPX_ClientExecutableURL False -TSPX_ServiceDescription False -TSPX_DocumentationURL False -------------------------------------------------------------------------------- -Other should be set according to Tester's test environment. diff --git a/android/pics-gap.txt b/android/pics-gap.txt index cd274e8..cd48332 100644 --- a/android/pics-gap.txt +++ b/android/pics-gap.txt @@ -1,7 +1,8 @@ GAP PICS for the PTS tool. +PTS version: 5.1 + * - different than PTS defaults -# - not yet implemented/supported M - mandatory O - optional @@ -34,12 +35,18 @@ Note - Only one transport shall be supported. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_0A_1 False Core Specification Addendum 3 (CSA3), - GAP Connection Parameters Changes, +TSPC_GAP_0A_1 True (*) Core Specification Addendum 3 (CSA3), GAP + Connection Parameters Changes, Authentication and Lost Bond Changes, - Private Addressing Changes, - Dual Mode Addressing Changes, + Private Addressing Changes, Dual Mode + Addressing Changes, Adopted 24 July 2012 (C.1) +TSPC_GAP_0A_2 True (*) Core Specification Addendum 4 (CSA4) +TSPC_GAP_0A_3 True (*) Core Spec version 4.1 (Core v4.1) GAP Connection + Parameters Changes, Authentication and + Lost Bond Changes, Private Addressing + Changes, Dual Mode Addressing Changes, + Adopted 03 December 2013 ------------------------------------------------------------------------------- C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded. ------------------------------------------------------------------------------- @@ -50,12 +57,14 @@ C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded. Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_1_1 True (*) Non-discoverable mode (C.1) -TSPC_GAP_1_2 False Limited-discoverable Mode (O) +TSPC_GAP_1_2 True (*) Limited-discoverable Mode (O) TSPC_GAP_1_3 True (*) General-discoverable mode (O) TSPC_GAP_1_4 True (*) Non-connectable mode (O) TSPC_GAP_1_5 True Connectable mode (M) TSPC_GAP_1_6 False Non-bondable mode (O) TSPC_GAP_1_7 True (*) Bondable mode (C.2) +TSPC_GAP_1_8 False Non-Synchronizable Mode (O) +TSPC_GAP_1_9 False Synchronizable Mode (O) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional. C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional. @@ -75,7 +84,8 @@ TSPC_GAP_2_6 False Security mode 3 (C.7) TSPC_GAP_2_7 True (*) Security mode 4 (C.4) TSPC_GAP_2_8 True (*) Support of Authenticated link key (C.6) TSPC_GAP_2_9 True (*) Support of Unauthenticated link key (C.6) -TSPC_GAP_2_10 False No security (C.6) +TSPC_GAP_2_10 True (*) No security (C.6) +TSPC_GAP_2_11 False Secure Connections Only Mode (O) ------------------------------------------------------------------------------- C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise Optional. @@ -105,7 +115,7 @@ C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional. Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_3_1 True (*) Initiation of general inquiry (C.1) -TSPC_GAP_3_2 False Initiation of limited inquiry (C.1) +TSPC_GAP_3_2 True (*) Initiation of limited inquiry (C.1) TSPC_GAP_3_3 True (*) Initiation of name discover (O) TSPC_GAP_3_4 True (*) Initiation of device discovery (O) TSPC_GAP_3_5 True (*) Initiation of general bonding (O) @@ -128,6 +138,8 @@ TSPC_GAP_4_5 True (*) Support connection establishment as initiator (O) TSPC_GAP_4_6 True (*) Support connection establishment as acceptor (O) +TSPC_GAP_4_7 True (*) Support synchronization establishment + as receiver (O) ------------------------------------------------------------------------------- @@ -138,7 +150,7 @@ Parameter Name Selected Description TSPC_GAP_5_1 False (*) Broadcaster (C.1) TSPC_GAP_5_2 False Observer (C.1) TSPC_GAP_5_3 False (*) Peripheral (C.1) -TSPC_GAP_5_4 True (*#) Central (C.1) +TSPC_GAP_5_4 True (*) Central (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined roles. Note: 'LE Roles' is applicable for LE-only configurations, but it appears that @@ -180,7 +192,7 @@ Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_8A_1 False AD Type-Service UUID (O) TSPC_GAP_8A_2 False AD Type-Local Name (O) -TSPC_GAP_8A_3 False (*) AD Type-Flags (M) +TSPC_GAP_8A_3 False (*) AD Type-Flags (O) TSPC_GAP_8A_4 False AD Type-Manufacturer Specific Data (O) TSPC_GAP_8A_5 False AD Type-TX Power Level (O) TSPC_GAP_8A_6 False AD Type-Security Manager Out of Band (OOB) (C.1) @@ -188,6 +200,12 @@ TSPC_GAP_8A_7 False AD Type-Security manager TK Value (O) TSPC_GAP_8A_8 False AD Type-Slave Connection Interval Range (O) TSPC_GAP_8A_9 False AD Type-Service Solicitation (O) TSPC_GAP_8A_10 False AD Type-Service Data (O) +TSPC_GAP_8A_11 False AD Type-Appearance (O) +TSPC_GAP_8A_12 False AD Type-Public Target Address (O) +TSPC_GAP_8A_13 False AD Type-Random Target Address (O) +TSPC_GAP_8A_14 False AD Type-Advertising Interval (O) +TSPC_GAP_8A_15 False AD Type-LE Bluetooth Device Address (O) +TSPC_GAP_8A_16 False AD Type –LE Role (O) ------------------------------------------------------------------------------- C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded. ------------------------------------------------------------------------------- @@ -207,8 +225,11 @@ Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_10_1 False (*) Broadcaster: Broadcast Mode TSPC_GAP_11_1 False Broadcaster: Privacy Feature +TSPC_GAP_11_1A False (*) Broadcaster: Privacy Feature v1.1 (O) TSPC_GAP_11_2 False Broadcaster: Resolvable Private Address Generation Procedure +TSPC_GAP_11_3 False (*) Broadcaster: Non-Resolvable Private Address + Generation Procedure (O) ------------------------------------------------------------------------------- @@ -216,7 +237,7 @@ TSPC_GAP_11_2 False Broadcaster: Resolvable Private Address ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_12_1 True (#) Observer: Receiver +TSPC_GAP_12_1 False (*) Observer: Receiver TSPC_GAP_12_2 False Observer: Transmitter ------------------------------------------------------------------------------- @@ -225,8 +246,8 @@ TSPC_GAP_12_2 False Observer: Transmitter ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_13_1 True (#) Observer: Standby -TSPC_GAP_13_2 True (#) Observer: Scanning +TSPC_GAP_13_1 False (*) Observer: Standby +TSPC_GAP_13_2 False (*) Observer: Scanning ------------------------------------------------------------------------------- @@ -234,7 +255,7 @@ TSPC_GAP_13_2 True (#) Observer: Scanning ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_14_1 True (#) Observer: Passive Scanning +TSPC_GAP_14_1 False (*) Observer: Passive Scanning TSPC_GAP_14_2 False Observer: Active Scanning ------------------------------------------------------------------------------- @@ -243,7 +264,7 @@ TSPC_GAP_14_2 False Observer: Active Scanning ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_15_1 True (#) Observer: Non-Connectable Mode +TSPC_GAP_15_1 False (*) Observer: Non-Connectable Mode ------------------------------------------------------------------------------- @@ -251,7 +272,7 @@ TSPC_GAP_15_1 True (#) Observer: Non-Connectable Mode ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_16_1 True (#) Observer: Observation Procedure +TSPC_GAP_16_1 False (*) Observer: Observation Procedure ------------------------------------------------------------------------------- @@ -260,6 +281,7 @@ TSPC_GAP_16_1 True (#) Observer: Observation Procedure Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_17_1 False Observer: Privacy Feature (O) +TSPC_GAP_17_1A False Observer: Privacy Feature v1.1 (O) TSPC_GAP_17_2 False Observer: Non-Resolvable Private Address Generation Procedure (C.1) TSPC_GAP_17_3 False Observer: Resolvable Private Address Resolution @@ -303,8 +325,9 @@ TSPC_GAP_19_3 False Peripheral: Connection, Slave Role ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_20_1 False Peripheral: Connectable Undirected Event -TSPC_GAP_20_2 False Peripheral: Connectable Directed Event +TSPC_GAP_20_1 False Peripheral: Connectable Undirected Event (C.1) +TSPC_GAP_20_2 False Peripheral: Connectable Directed Event (C.2) +TSPC_GAP_20_2A False Peripheral: Low Duty Directed Advertising (C.3) TSPC_GAP_20_3 False Peripheral: Non-Connectable Undirected Event TSPC_GAP_20_4 False Peripheral: Scannable Undirected Event ------------------------------------------------------------------------------- @@ -324,6 +347,12 @@ TSPC_GAP_20A_7 False AD Type-Security manager TK Value (C.1) TSPC_GAP_20A_8 False AD Type-Slave Connection Interval Range (C.1) TSPC_GAP_20A_9 False AD Type-Service Solicitation (C.1) TSPC_GAP_20A_10 False AD Type-Service Data (C.1) +TSPC_GAP_20A_11 False AD Type –Appearance (C.1) +TSPC_GAP_20A_12 False AD Type-Public Target Address (C.1) +TSPC_GAP_20A_13 False AD Type-Random Target Address (C.1) +TSPC_GAP_20A_14 False AD Type-Advertising Interval (C.1) +TSPC_GAP_20A_15 False AD Type-LE Bluetooth Device Address (C.1) +TSPC_GAP_20A_16 False AD Type – LE Role (C.1) ------------------------------------------------------------------------------- C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is supported, otherwise Excluded. @@ -346,6 +375,11 @@ TSPC_GAP_21_3 False Peripheral: Encryption Procedure (O) TSPC_GAP_21_4 False (*) Peripheral: Feature Exchange Procedure (M) TSPC_GAP_21_5 False (*) Peripheral: Version Exchange Procedure (M) TSPC_GAP_21_6 False (*) Peripheral: Termination Procedure (M) +TSPC_GAP_21_7 False (*) Peripheral: LE Ping Procedure (C.3) +TSPC_GAP_21_8 False (*) Peripheral: Slave Initiated Feature Exchange + Procedure (C.4) +TSPC_GAP_21_9 False (*) Peripheral: Connection Parameter Request + Procedure (C.5) ------------------------------------------------------------------------------- @@ -427,10 +461,13 @@ C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported, Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_26_1 False Peripheral: Privacy Feature (O) +TSPC_GAP_26_1A False Peripheral: Privacy Feature v1.1 (O) TSPC_GAP_26_2 False Peripheral: Non-Resolvable Private Address Generation Procedure (C.1) TSPC_GAP_26_3 False Peripheral: Resolvable Private Address Generation Procedure (C.2) +TSPC_GAP_26_4 False Peripheral: Resolvable Private Address + Generation Procedure (C.4) ------------------------------------------------------------------------------- C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded. C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded. @@ -462,8 +499,8 @@ C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported, ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_28_1 True (*#) Central: Transmitter (M) -TSPC_GAP_28_2 True (*#) Central: Receiver (M) +TSPC_GAP_28_1 True (*) Central: Transmitter (M) +TSPC_GAP_28_2 True (*) Central: Receiver (M) ------------------------------------------------------------------------------- @@ -471,10 +508,10 @@ TSPC_GAP_28_2 True (*#) Central: Receiver (M) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_29_1 True (*#) Central: Standby (M) -TSPC_GAP_29_2 True (*#) Central: Scanning (M) -TSPC_GAP_29_3 True (*#) Central: Initiating (M) -TSPC_GAP_29_4 True (*#) Central: Connection, Master Role (M) +TSPC_GAP_29_1 True (*) Central: Standby (M) +TSPC_GAP_29_2 True (*) Central: Scanning (M) +TSPC_GAP_29_3 True (*) Central: Initiating (M) +TSPC_GAP_29_4 True (*) Central: Connection, Master Role (M) ------------------------------------------------------------------------------- @@ -482,8 +519,8 @@ TSPC_GAP_29_4 True (*#) Central: Connection, Master Role (M) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_30_1 True (*#) Central: Passive Scanning (O) -TSPC_GAP_30_2 True (*#) Central: Active Scanning (C.1) +TSPC_GAP_30_1 True (*) Central: Passive Scanning (O) +TSPC_GAP_30_2 True (*) Central: Active Scanning (C.1) ------------------------------------------------------------------------------- C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported. Optional if TSPC_GAP_30_1 and (TSPC_GAP_5_4 OR TSPC_GAP_38_4) @@ -495,12 +532,17 @@ C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_31_1 True (*#) Central: Connection Update Procedure (M) -TSPC_GAP_31_2 True (*#) Central: Channel Map Update Procedure (M) -TSPC_GAP_31_3 True (*#) Central: Encryption Procedure (O) -TSPC_GAP_31_4 True (*#) Central: Feature Exchange Procedure (M) -TSPC_GAP_31_5 True (*#) Central: Version Exchange Procedure (M) -TSPC_GAP_31_6 True (*#) Central: Termination Procedure (M) +TSPC_GAP_31_1 True () Central: Connection Update Procedure (M) +TSPC_GAP_31_2 True () Central: Channel Map Update Procedure (M) +TSPC_GAP_31_3 True (*) Central: Encryption Procedure (O) +TSPC_GAP_31_4 True () Central: Feature Exchange Procedure (M) +TSPC_GAP_31_5 True () Central: Version Exchange Procedure (M) +TSPC_GAP_31_6 True () Central: Termination Procedure (M) +TSPC_GAP_31_7 True (*) Central: LE Ping Procedure (C.1) +TSPC_GAP_31_8 True (*) Central: Slave Initiated Feature Exchange + Procedure (C.2) +TSPC_GAP_31_9 True (*) Central: Connection Parameter Request Procedure + (C.3) ------------------------------------------------------------------------------- @@ -508,9 +550,9 @@ TSPC_GAP_31_6 True (*#) Central: Termination Procedure (M) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_32_1 False Central: Limited Discovery Procedure (C.2) -TSPC_GAP_32_2 True (*#) Central: General Discovery Procedure (C.1) -TSPC_GAP_32_3 True (*#) Central: Name Discovery Procedure (C.3) +TSPC_GAP_32_1 True (*) Central: Limited Discovery Procedure (C.2) +TSPC_GAP_32_2 True (*) Central: General Discovery Procedure (C.1) +TSPC_GAP_32_3 True (*) Central: Name Discovery Procedure (C.3) ------------------------------------------------------------------------------- C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_1) is supported, else Excluded. C.2: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_2) is supported, @@ -524,17 +566,17 @@ C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported, ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_33_1 True (*#) Central: Auto Connection Establishment +TSPC_GAP_33_1 True (*) Central: Auto Connection Establishment Procedure (C.3) -TSPC_GAP_33_2 True (*#) Central: General Connection Establishment +TSPC_GAP_33_2 True (*) Central: General Connection Establishment Procedure (C.1) -TSPC_GAP_33_3 True (*#) Central: Selective Connection Establishment +TSPC_GAP_33_3 True (*) Central: Selective Connection Establishment Procedure (C.3) -TSPC_GAP_33_4 True (*#) Central: Direct Connectin Establishment +TSPC_GAP_33_4 True (*) Central: Direct Connectin Establishment Procedure (C.2) -TSPC_GAP_33_5 True (*#) Central: Connection Parameter Update Procedure +TSPC_GAP_33_5 True (*) Central: Connection Parameter Update Procedure (C.2) -TSPC_GAP_33_6 True (*#) Central: Terminate Connection Procedure +TSPC_GAP_33_6 True (*) Central: Terminate Connection Procedure (C.2) ------------------------------------------------------------------------------- C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) and TSPC_GAP_36_1 is @@ -550,9 +592,9 @@ C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported, ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_34_1 False Central: Non-Bondable Mode (C.1) -TSPC_GAP_34_2 True (*#) Central: Bondable Mode (C.2) -TSPC_GAP_34_3 True (*#) Central: Bonding Procedure (O) +TSPC_GAP_34_1 True (*) Central: Non-Bondable Mode (C.1) +TSPC_GAP_34_2 True (*) Central: Bondable Mode (C.2) +TSPC_GAP_34_3 True (*) Central: Bonding Procedure (O) ------------------------------------------------------------------------------- C.1: Mandatory if (TSPC_GAP_5_4 or 39/5) is supported, otherwise Excluded. C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded. @@ -563,15 +605,15 @@ C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_35_1 True (*#) Central: Security Mode 1 (O) -TSPC_GAP_35_2 True (*#) Central: Security Mode 2 (O) -TSPC_GAP_35_3 True (*#) Central: Authentication Procedure (O) -TSPC_GAP_35_4 True (*#) Central: Authorization Procedure (O) -TSPC_GAP_35_5 True (*#) Central: Connection Data Signing Procedure (O) -TSPC_GAP_35_6 True (*#) Central: Authenticate Signed Data Procedure (O) -TSPC_GAP_35_7 False Central: Authenticated Pairing +TSPC_GAP_35_1 True (*) Central: Security Mode 1 (O) +TSPC_GAP_35_2 True (*) Central: Security Mode 2 (O) +TSPC_GAP_35_3 True (*) Central: Authentication Procedure (O) +TSPC_GAP_35_4 True (*) Central: Authorization Procedure (O) +TSPC_GAP_35_5 True (*) Central: Connection Data Signing Procedure (O) +TSPC_GAP_35_6 True (*) Central: Authenticate Signed Data Procedure (O) +TSPC_GAP_35_7 True (*) Central: Authenticated Pairing (LE security mode 1 level 3) (C.1) -TSPC_GAP_35_8 False Central: Unauthenticated Pairing +TSPC_GAP_35_8 True (*) Central: Unauthenticated Pairing (LE security mode 1 level 2) (C.1) ------------------------------------------------------------------------------- C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded. @@ -582,13 +624,16 @@ C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_36_1 False Central: Privacy Feature (C.3) -TSPC_GAP_36_2 False Central: Non-Resolvable Private Address +TSPC_GAP_36_1 True (*) Central: Privacy Feature (C.3) +TSPC_GAP_36_1A True (*) Central: Privacy Feature v1.1 (C.4) +TSPC_GAP_36_2 True (*) Central: Non-Resolvable Private Address Generation Procedure (C.1) -TSPC_GAP_36_3 False Central: Resolvable Private Address Resolution +TSPC_GAP_36_3 True (*) Central: Resolvable Private Address Resolution Procedure (C.2) -TSPC_GAP_36_4 False Central: Write to Privacy Characteristic +TSPC_GAP_36_4 True (*) Central: Write to Privacy Characteristic (Enable/Disable Privacy) (O) +TSPC_GAP_36_5 True (*) Central: Resolvable Private Address Generation + Procedure (C.6) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported, otherwise Excluded. @@ -600,8 +645,8 @@ C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_37_1 True (*#) Central: Device Name (M) -TSPC_GAP_37_2 True (*#) Central: Appearance (M) +TSPC_GAP_37_1 True (*) Central: Device Name (M) +TSPC_GAP_37_2 True (*) Central: Appearance (M) ------------------------------------------------------------------------------- @@ -610,9 +655,9 @@ TSPC_GAP_37_2 True (*#) Central: Appearance (M) Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_GAP_38_1 False BR/EDR/LE: Broadcaster (C.1) -TSPC_GAP_38_2 True (*#) BR/EDR/LE: Observer (C.1) +TSPC_GAP_38_2 False BR/EDR/LE: Observer (C.1) TSPC_GAP_38_3 False BR/EDR/LE: Peripheral (C.1) -TSPC_GAP_38_4 True (*#) BR/EDR/LE: Central (C.1) +TSPC_GAP_38_4 True (*) BR/EDR/LE: Central (C.1) ------------------------------------------------------------------------------- C.1: It is mandatory to support at least one of the defined roles. This table is applicable for BR/EDR/LE configurations. For LE-only @@ -624,12 +669,12 @@ configurations, see 'LE Roles' table for role declarations. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_39_1 True (*#) Central BR/EDR/LE: Non-Discoverable Mode (C.1) -TSPC_GAP_39_2 True (*#) Central BR/EDR/LE: Discoverable Mode (C.2) -TSPC_GAP_39_3 True (*#) Central BR/EDR/LE: Non-Connectable Mode (C.3) -TSPC_GAP_39_4 True (#) Central BR/EDR/LE: Connectable Mode (M) +TSPC_GAP_39_1 True (*) Central BR/EDR/LE: Non-Discoverable Mode (C.1) +TSPC_GAP_39_2 True (*) Central BR/EDR/LE: Discoverable Mode (C.2) +TSPC_GAP_39_3 True (*) Central BR/EDR/LE: Non-Connectable Mode (C.3) +TSPC_GAP_39_4 True () Central BR/EDR/LE: Connectable Mode (M) TSPC_GAP_39_5 False Central BR/EDR/LE: Non-Bondable Mode (C.4) -TSPC_GAP_39_6 True (*#) Central BR/EDR/LE: Bondable Mode (C.5) +TSPC_GAP_39_6 True (*) Central BR/EDR/LE: Bondable Mode (C.5) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded. C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR, @@ -644,11 +689,11 @@ C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_40_1 True (*#) Central BR/EDR/LE: General Discovery (C.1) -TSPC_GAP_40_2 False Central BR/EDR/LE: Limited Discovery (C.2) -TSPC_GAP_40_3 True (*#) Central BR/EDR/LE: Device Type Discovery (C.3) -TSPC_GAP_40_4 True (*#) Central BR/EDR/LE: Name Discovery (C.4) -TSPC_GAP_40_5 True (*#) Central BR/EDR/LE: Link Establishment (C.5) +TSPC_GAP_40_1 True (*) Central BR/EDR/LE: General Discovery (C.1) +TSPC_GAP_40_2 True (*) Central BR/EDR/LE: Limited Discovery (C.2) +TSPC_GAP_40_3 True (*) Central BR/EDR/LE: Device Type Discovery (C.3) +TSPC_GAP_40_4 True (*) Central BR/EDR/LE: Name Discovery (C.4) +TSPC_GAP_40_5 True (*) Central BR/EDR/LE: Link Establishment (C.5) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_3_1 is supported over BR/EDR, otherwise Excluded. C.2: Mandatory if TSPC_GAP_3_2 is supported over BR/EDR, otherwise Excluded. @@ -664,7 +709,7 @@ C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR, ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_41_1 True (#) Central BR/EDR/LE: Security Aspects (M) +TSPC_GAP_41_1 True () Central BR/EDR/LE: Security Aspects (M) ------------------------------------------------------------------------------- @@ -672,15 +717,12 @@ TSPC_GAP_41_1 True (#) Central BR/EDR/LE: Security Aspects (M) ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_42_1 False Peripheral BR/EDR/LE: Non-Discoverable Mode - (C.1) -TSPC_GAP_42_2 False Peripheral BR/EDR/LE: Discoverable Mode - (C.2) -TSPC_GAP_42_3 False Peripheral BR/EDR/LE: Non-Connectable Mode - (C.3) -TSPC_GAP_42_4 False (*) Peripheral BR/EDR/LE: Connectable Mode (M) -TSPC_GAP_42_5 False Peripheral BR/EDR/LE: Non-Bondable Mode (C.4) -TSPC_GAP_42_6 False Peripheral BR/EDR/LE: Bondable Mode (C.5) +TSPC_GAP_42_1 False Peripheral BR/EDR/LE: Non-Discoverable Mode (See Spec) +TSPC_GAP_42_2 False Peripheral BR/EDR/LE: Discoverable Mode (See Spec) +TSPC_GAP_42_3 False Peripheral BR/EDR/LE: Non-Connectable Mode (See Spec) +TSPC_GAP_42_4 False (*) Peripheral BR/EDR/LE: Connectable Mode (M) +TSPC_GAP_42_5 False Peripheral BR/EDR/LE: Non-Bondable Mode (See Spec) +TSPC_GAP_42_6 False Peripheral BR/EDR/LE: Bondable Mode (See Spec) ------------------------------------------------------------------------------- C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded. C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR, @@ -695,20 +737,42 @@ C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_GAP_43_1 False (*) Peripheral BR/EDR/LE: Security Aspects (M) +TSPC_GAP_43_1 False (*) Peripheral BR/EDR/LE: Non-Discoverable Mode ------------------------------------------------------------------------------- + Central Simultaneous BR/EDR and LE Transports ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_SM_1_1 False Master Role (Initiator) -TSPC_SM_1_2 False Slave Role (Responder) -TSPC_SM_2_4 False OOB supported (O) -TSPC_ALL False Turns on all +TSPC_GAP_44_1 True (*) Central BR/EDR/LE: Simultaneous BR/EDR and LE + Transports – BR/EDR Slave to the same + device (O) +TSPC_GAP_44_2 True (*) Central BR/EDR/LE: Simultaneous BR/EDR and LE + Transports – BR/EDR Master to the same + device (O) +------------------------------------------------------------------------------- + + + Peripheral Simultaneous BR/EDR and LE Transports +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_45_1 False Simultaneous BR/EDR and LE Transports – BR/EDR + Slave to the same device (C.1) +TSPC_GAP_45_2 False Simultaneous BR/EDR and LE Transports – BR/EDR + Master to the same device (C.1) +------------------------------------------------------------------------------- +C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15 +(Core Spec Version 4.1+HS)) is supported, otherwise Excluded. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -No special PIXIT settings required. All should be set according to Tester's -test environment. +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_1_1 True (*) Master Role (Initiator) +TSPC_SM_1_2 True (*) Slave Role (Responder) +TSPC_SM_2_4 True (*) OOB supported (O) +TSPC_ALL False Turns on all +------------------------------------------------------------------------------- diff --git a/android/pics-gatt.txt b/android/pics-gatt.txt new file mode 100644 index 0000000..f3e8199 --- /dev/null +++ b/android/pics-gatt.txt @@ -0,0 +1,322 @@ +GATT PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults + +M - mandatory +O - optional + + Generic Attribute Profile Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_1_1 True Generic Attribute Profile Client (C.1) +TSPC_GATT_1_2 True Generic Attribute Profile Server (C.2) +TSPC_GATT_1A_1 False (*) Complete GATT client (C.3) +TSPC_GATT_1A_2 False (*) Complete GATT server (C.4) +------------------------------------------------------------------------------- +C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory + to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2 +C.2: Mandatory to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is + mandatory to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2 +C.3: Optional IF TSPC_GATT_1_1 is supported, otherwise Excluded +C.4: Optional IF TSPC_GATT_1_2 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + ATT Bearer Transport +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_2_1 True Attribute Protocol Supported over BR/EDR + (L2CAP fixed channel support) (C.1) +TSPC_GATT_2_2 True Attribute Protocol Supported over LE (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory IF (SUM ICS 12/2 OR SUM ICS 12/9) is supported, otherwise + Excluded +C.2: Mandatory IF (SUM ICS 12/7 OR SUM ICS 12/9) is supported, otherwise + Excluded +------------------------------------------------------------------------------- + + + + Generic Attribute Profile Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_3_1 True Client: Exchange MTU (C.1) +TSPC_GATT_3_2 True Client: Discover All Primary Services (C.1) +TSPC_GATT_3_3 True Client: Discover Primary Services Service + UUID (C.1) +TSPC_GATT_3_4 True Client: Find Included Services (C.1) +TSPC_GATT_3_5 True Client: Discover All characteristics of a + Service (C.1) +TSPC_GATT_3_6 True Client: Discover Characteristics by UUID (C.1) +TSPC_GATT_3_7 True Client: Discover All Characteristic Descriptors + (C.1) +TSPC_GATT_3_8 True Client: Read Characteristic Value (C.1) +TSPC_GATT_3_9 True Client: Read using Characteristic UUID (C.1) +TSPC_GATT_3_10 True Client: Read Long Characteristic Values (C.1) +TSPC_GATT_3_11 True Client: Read Multiple Characteristic + Values (C.1) +TSPC_GATT_3_12 True Client: Write without Response (C.1) +TSPC_GATT_3_13 True Client: Signed Write Without Response (C.1) +TSPC_GATT_3_14 True Client: Write Characteristic Value (C.1) +TSPC_GATT_3_15 True Client: Write Long Characteristic Values (C.1) +TSPC_GATT_3_16 True Client: Characteristic Value Reliable + Writes (C.1) +TSPC_GATT_3_17 True Client: Notifications (C.1) +TSPC_GATT_3_18 True Client: Indications (M) +TSPC_GATT_3_19 True Client: Read Characteristic Descriptors (C.1) +TSPC_GATT_3_20 True Client: Read long Characteristic Descriptors + (C.1) +TSPC_GATT_3_21 True Client: Write Characteristic Descriptors (C.1) +TSPC_GATT_3_22 True Client: Write Long Characteristic Descriptors + (C.1) +TSPC_GATT_3_23 True Client: Service Changed Characteristic (M) +TSPC_GATT_3B_1 True Client: Primary Service Declaration (M) +TSPC_GATT_3B_2 True Client: Secondary Service Declaration (M) +TSPC_GATT_3B_3 True Client: Include Declaration (M) +TSPC_GATT_3B_4 True Client: Characteristic Declaration (M) +TSPC_GATT_3B_5 True Client: Characteristic Value Declaration (M) +TSPC_GATT_3B_6 True Client: Characteristic Extended Properties (M) +TSPC_GATT_3B_7 True Client: Characteristic User Description + Descriptor (M) +TSPC_GATT_3B_8 True Client: Client Characteristic Configuration + Descriptor (M) +TSPC_GATT_3B_9 True Client: Server Characteristic Configuration + Descriptor (M) +TSPC_GATT_3B_10 True Client: Characteristic Format Descriptor (M) +TSPC_GATT_3B_11 True Client: Characteristic Aggregate Format + Descriptor (M) +TSPC_GATT_3B_12 True Client: Characteristic Format: Boolean (M) +TSPC_GATT_3B_13 True Client: Characteristic Format: 2Bit (M) +TSPC_GATT_3B_14 True Client: Characteristic Format: nibble (M) +TSPC_GATT_3B_15 True Client: Characteristic Format: Uint8 (M) +TSPC_GATT_3B_16 True Client: Characteristic Format: Uint12 (M) +TSPC_GATT_3B_17 True Client: Characteristic Format: Uint16 (M) +TSPC_GATT_3B_18 True Client: Characteristic Format: Uint24 (M) +TSPC_GATT_3B_19 True Client: Characteristic Format: Uint32 (M) +TSPC_GATT_3B_20 True Client: Characteristic Format: Uint48 (M) +TSPC_GATT_3B_21 True Client: Characteristic Format: Uint64 (M) +TSPC_GATT_3B_22 True Client: Characteristic Format: Uint128 (M) +TSPC_GATT_3B_23 True Client: Characteristic Format: Sint8 (M) +TSPC_GATT_3B_24 True Client: Characteristic Format: Sint12 (M) +TSPC_GATT_3B_25 True Client: Characteristic Format: Sint16 (M) +TSPC_GATT_3B_26 True Client: Characteristic Format: Sint24 (M) +TSPC_GATT_3B_27 True Client: Characteristic Format: Sint32 (M) +TSPC_GATT_3B_28 True Client: Characteristic Format: Sint48 (M) +TSPC_GATT_3B_29 True Client: Characteristic Format: Sint64 (M) +TSPC_GATT_3B_30 True Client: Characteristic Format: Sint128 (M) +TSPC_GATT_3B_31 True Client: Characteristic Format: Float32 (M) +TSPC_GATT_3B_32 True Client: Characteristic Format: Float64 (M) +TSPC_GATT_3B_33 True Client: Characteristic Format: SFLOAT (M) +TSPC_GATT_3B_34 True Client: Characteristic Format: FLOAT (M) +TSPC_GATT_3B_35 True Client: Characteristic Format: Duint16 (M) +TSPC_GATT_3B_36 True Client: Characteristic Format: utf8s (M) +TSPC_GATT_3B_37 True Client: Characteristic Format: utf16s (M) +TSPC_GATT_3B_38 True Client: Characteristic Format: struct (M) +------------------------------------------------------------------------------- +C.1: Mandatory IF TSPC_GATT_1A_1 is supported, otherwise Optional +------------------------------------------------------------------------------- + + + + Generic Attribute Profile Support, by Server +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_4_1 True Server: Exchange MTU (C.4) +TSPC_GATT_4_2 True Server: Discover All Primary Services (M) +TSPC_GATT_4_3 True Server: Discover Primary Services Service + UUID (M) +TSPC_GATT_4_4 True Server: Find Included Services (M) +TSPC_GATT_4_5 True Server: Discover All characteristics of + a Service (M) +TSPC_GATT_4_6 True Server: Discover Characteristics by UUID (M) +TSPC_GATT_4_7 True Server: Discover All Characteristic + Descriptors (M) +TSPC_GATT_4_8 True Server: Read Characteristic Value (M) +TSPC_GATT_4_9 True Server: Read using Characteristic UUID (M) +TSPC_GATT_4_10 True Server: Read Long Characteristic Values (C.4) +TSPC_GATT_4_11 True Server: Read Multiple Characteristic + Values (C.4) +TSPC_GATT_4_12 True Server: Write without Response (C.2) +TSPC_GATT_4_13 False (*) Server: Signed Write Without Response (C.4) +TSPC_GATT_4_14 True Server: Write Characteristic Value (C.3) +TSPC_GATT_4_15 True Server: Write Long Characteristic Values (C.4) +TSPC_GATT_4_16 True Server: Characteristic Value Reliable + Writes (C.4) +TSPC_GATT_4_17 True Server: Notifications (C.4) +TSPC_GATT_4_18 True Server: Indications (C.1) +TSPC_GATT_4_19 True Server: Read Characteristic Descriptors (C.4) +TSPC_GATT_4_20 True Server: Read long Characteristic + Descriptors (C.4) +TSPC_GATT_4_21 True Server: Write Characteristic Descriptors (C.4) +TSPC_GATT_4_22 True Server: Write Long Characteristic + Descriptors (C.4) +TSPC_GATT_4_23 True Server: Service Changed Characteristic (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory IF service definitions on the server can be added, changed, or + removed, otherwise Optional +C.2: Mandatory IF GATT TSPC_GATT_4_13 is supported, otherwise Optional +C.3: Mandatory IF GATT TSPC_GATT_4_15 is supported, otherwise Optional +C.4: Mandatory IF GATT TSPC_GATT_1A_2 is supported, otherwise Optional +------------------------------------------------------------------------------- + + + Profile Attribute Types and Characteristic Formats +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_4B_1 True Server: Primary Service Declaration (M) +TSPC_GATT_4B_2 True Server: Secondary Service Declaration (M) +TSPC_GATT_4B_3 True Server: Include Declaration (M) +TSPC_GATT_4B_4 True Server: Characteristic Declaration (M) +TSPC_GATT_4B_5 True Server: Characteristic Value Declaration (M) +TSPC_GATT_4B_6 True Server: Characteristic Extended Properties (M) +TSPC_GATT_4B_7 True Server: Characteristic User Description + Descriptor (M) +TSPC_GATT_4B_8 True Server: Client Characteristic Configuration + Descriptor (M) +TSPC_GATT_4B_9 True Server: Server Characteristic Configuration + Descriptor (M) +TSPC_GATT_4B_10 True Server: Characteristic Format Descriptor (M) +TSPC_GATT_4B_11 True Server: Characteristic Aggregate Format + Descriptor (M) +TSPC_GATT_4B_12 True Server: Characteristic Format: Boolean (M) +TSPC_GATT_4B_13 True Server: Characteristic Format: 2Bit (M) +TSPC_GATT_4B_14 True Server: Characteristic Format: nibble (M) +TSPC_GATT_4B_15 True Server: Characteristic Format: Uint8 (M) +TSPC_GATT_4B_16 True Server: Characteristic Format: Uint12 (M) +TSPC_GATT_4B_17 True Server: Characteristic Format: Uint16 (M) +TSPC_GATT_4B_18 True Server: Characteristic Format: Uint24 (M) +TSPC_GATT_4B_19 True Server: Characteristic Format: Uint32 (M) +TSPC_GATT_4B_20 True Server: Characteristic Format: Uint48 (M) +TSPC_GATT_4B_21 True Server: Characteristic Format: Uint64 (M) +TSPC_GATT_4B_22 True Server: Characteristic Format: Uint128 (M) +TSPC_GATT_4B_23 True Server: Characteristic Format: Sint8 (M) +TSPC_GATT_4B_24 True Server: Characteristic Format: Sint12 (M) +TSPC_GATT_4B_25 True Server: Characteristic Format: Sint16 (M) +TSPC_GATT_4B_26 True Server: Characteristic Format: Sint24 (M) +TSPC_GATT_4B_27 True Server: Characteristic Format: Sint32 (M) +TSPC_GATT_4B_28 True Server: Characteristic Format: Sint48 (M) +TSPC_GATT_4B_29 True Server: Characteristic Format: Sint64 (M) +TSPC_GATT_4B_30 True Server: Characteristic Format: Sint128 (M) +TSPC_GATT_4B_31 True Server: Characteristic Format: Float32 (M) +TSPC_GATT_4B_32 True Server: Characteristic Format: Float64 (M) +TSPC_GATT_4B_33 True Server: Characteristic Format: SFLOAT (M) +TSPC_GATT_4B_34 True Server: Characteristic Format: FLOAT (M) +TSPC_GATT_4B_35 True Server: Characteristic Format: Duint16 (M) +TSPC_GATT_4B_36 True Server: Characteristic Format: utf8s (M) +TSPC_GATT_4B_37 True Server: Characteristic Format: utf16s (M) +TSPC_GATT_4B_38 True Server: Characteristic Format: struct (M) +------------------------------------------------------------------------------- + + + Generic Attribute Profile Service +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_6_2 True Discover GATT Services using Service Discovery + Profile (C.1) +TSPC_GATT_6_3 True Publish SDP record for GATT services support + via BR/EDR (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded +C.2: Mandatory IF TSPC_GATT_1_2 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + Attribute Protocol Transport Security +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_7_1 True Security Mode 4 (C.1) +TSPC_GATT_7_2 True LE Security Mode 1 (C.2) +TSPC_GATT_7_3 True LE Security Mode 2 (C.2) +TSPC_GATT_7_4 True LE Authentication Procedure (C.2) +TSPC_GATT_7_5 True LE connection data signing procedure (C.2) +TSPC_GATT_7_6 True LE Authenticate signed data procedure (C.2) +TSPC_GATT_7_7 False (*) LE Authorization Procedure (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded +C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + Attribute Protocol Client Messages +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ATT_3_1 True Attribute Error Response (M) +TSPC_ATT_3_2 False (*) Exchange MTU Request (O) +TSPC_ATT_3_4 False (*) Find Information Request (O) +TSPC_ATT_3_6 False (*) Find by Type Value Request (O) +TSPC_ATT_3_8 False (*) Read by Type Request (O) +TSPC_ATT_3_10 False (*) Read Request (O) +TSPC_ATT_3_12 False (*) Read Blob Request (O) +TSPC_ATT_3_14 False (*) Read Multiple Request (O) +TSPC_ATT_3_16 False (*) Read by Group Type Request (O) +TSPC_ATT_3_17 False (*) Read by Group Type Response (C.6) +TSPC_ATT_3_18 False (*) Write Request (O) +TSPC_ATT_3_20 False (*) Write Command (O) +TSPC_ATT_3_21 False (*) Signed Write Command (O) +TSPC_ATT_3_22 False (*) Prepare Write Request (O) +TSPC_ATT_3_24 False (*) Execute Write Request (C.8) +TSPC_ATT_3_26 True Handle Value Notification (M) +------------------------------------------------------------------------------- +C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded +C.8: Mandatory IF TSPC_ATT_3_22 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + Attribute Protocol Server Messages +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ATT_4_1 True Attribute Error Response (M) +TSPC_ATT_4_3 True Exchange MTU Response (M) +TSPC_ATT_4_5 True Find Information Response (M) +TSPC_ATT_4_7 True Find by Type Value Response (M) +TSPC_ATT_4_8 True Read by Type Request (M) +TSPC_ATT_4_9 True Read by Type Response (M) +TSPC_ATT_4_11 True Read Response (M) +TSPC_ATT_4_15 False (*) Read Multiple Response (C.2) +TSPC_ATT_4_17 True Read by Group Type Response (M) +TSPC_ATT_4_19 False (*) Write Response (C.3) +TSPC_ATT_4_20 False (*) Write Command (O) +TSPC_ATT_4_21 False (*) Signed Write Command (O) +TSPC_ATT_4_23 False (*) Prepare Write Response (C.4) +TSPC_ATT_4_25 False (*) Execute Write Response (C.5) +TSPC_ATT_4_26 False (*) Handle Value Notification (O) +------------------------------------------------------------------------------- +C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded +C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded +C.4: Mandatory IF TSPC_ATT_4_22 is supported, otherwise Excluded +C.5: Mandatory IF TSPC_ATT_4_27 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + Attribute Protocol Transport +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ATT_5_2 False (*) LE Security Mode 1 (C.2) +TSPC_ATT_5_4 False (*) LE Authentication Procedure (C.2) +TSPC_ATT_5_7 False (*) LE Authorization Procedure (C.2) +------------------------------------------------------------------------------- +C.2: Optional to support if 2/2 (Attribute Protocol Supported over LE), + otherwise Excluded +------------------------------------------------------------------------------- + + + Device Configuration +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_0_2 False (*) LE (C.2) +------------------------------------------------------------------------------- +C.2: Mandatory IF (SUM ICS 34/2 (LE GAP) AND NOT SUM ICS 32/3 (BR/EDR GAP)) + is supported, otherwise Excluded +------------------------------------------------------------------------------- diff --git a/android/pics-hdp.txt b/android/pics-hdp.txt new file mode 100644 index 0000000..3d8020d --- /dev/null +++ b/android/pics-hdp.txt @@ -0,0 +1,307 @@ +HDP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + + Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_0_1 False HDP 1.0 (C.1) +TSPC_HDP_0_2 True HDP 1.1(C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support only one Profile version. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_1_1 True Supports Source Role (C.1, C.2) +TSPC_HDP_1_2 True (*) Supports Sink Role (C.1, C.3) +------------------------------------------------------------------------------- +C.1: At least one of the defined roles is Mandatory. +C.2: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded. +C.3: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + GAP Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_2_1 True Supports General-discoverable Mode (M) +TSPC_HDP_2_2 True Supports bondable Mode (M) (C.1) +TSPC_HDP_2_3 True Supports Response to Authentication requests (M) +TSPC_HDP_2_4 True Supports Initiation of Authentication (M) (C.2) +TSPC_HDP_2_5 True Supports Acceptance of Encryption request (M) +TSPC_HDP_2_6 True Supports Initiation of Encryption (M) (C.3) +TSPC_HDP_2_7 True (*) Supports General Inquiry (C.5) (C.4) +TSPC_HDP_2_8 True Supports Acceptance of Bonding requests (M) +TSPC_HDP_2_9 True (*) Supports Initiation of Bonding (O) +TSPC_HDP_2_10 True (*) Supports Extended Inquiry Response (C.7) +TSPC_HDP_2_11 True (*) Supports use of Health Class of Device (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. +C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional. +C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed, otherwise Optional. +C.4: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. +C.5: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. +C.6: Mandatory if Bluetooth Core Specification 2.1 + EDR or later + (Not SUM ICS 31/4) and Table 2/1 (Supports General-discoverable Mode) + is supported, otherwise Optional if Bluetooth Core Specification 2.1 + + EDR or later (Not SUM ICS 31/4) is supported, otherwise Excluded. +C.7: Mandatory if Bluetooth Core specification 2.1 + EDR or later is supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + L2CAP Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_3_1 True Supports Reliable Control Channel (C.1) +TSPC_HDP_3_2 True Uses FCS for Control Channel (M) +TSPC_HDP_3_3 True Supports Reliable Data Channel (C.1) +TSPC_HDP_3_4 True Can send data using SAR in ERTM (C.2) +TSPC_HDP_3_5 False Uses FCS for Reliable Data Channel (O) +TSPC_HDP_3_6 False Supports FCS option of "No FCS" for Reliable + Data Channel (C.3) +TSPC_HDP_3_7 True Supports Streaming Data Channel (C.4) +TSPC_HDP_3_8 False Can send data using SAR in SM (C.5) +TSPC_HDP_3_9 False Uses FCS for Steaming Data Channel (C.6) +TSPC_HDP_3_10 False Supports FCS option of "No FCS" for Streaming + (C.7) +TSPC_HDP_3_11 True Maximum number of simultaneous Data Channels + supported (DCmax) per MCL (C.8) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded. +C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded. +C.4: Optional if TSPC_L2CAP_2_13 is supported, otherwise Excluded. +C.5: Mandatory if TSPC_HDP_3_7 and TSPC_L2CAP_2_23 are supported, otherwise + Excluded. +C.6: Optional if TSPC_HDP_3_7 is supported, otherwise Excluded. +C.7: Optional if TSPC_HDP_3_7 and TSPC_L2CAP_2_14 are supported, otherwise + Excluded. +C.8: >=2 if Table TSPC_HDP_3_7 is claimed, otherwise >=1. +------------------------------------------------------------------------------- + + + SDP Attributes - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_4_1 True Supports advertisement of HDP Service Record + (C.1) (C.4) +TSPC_HDP_4_2 True Service Class ID List (C.2) +TSPC_HDP_4_3 True Protocol Descriptor List (C.2) +TSPC_HDP_4_4 True Bluetooth Profile Descriptor List (C.2) +TSPC_HDP_4_5 False Additional Protocol Descriptor Lists (C.2) +TSPC_HDP_4_6 True (*) Service Name (O) +TSPC_HDP_4_7 True (*) Service Description (O) +TSPC_HDP_4_8 True (*) Provider Name (O) +TSPC_HDP_4_9 True HDP Supported Features (MDEP List) (C.3) +TSPC_HDP_4_10 True MCAP Data Exchange Specification (C.3) +TSPC_HDP_4_11 True MCAP Supported Procedures (C.3) +TSPC_HDP_4_12 False Service Record State (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional. +C.3: Mandatory if TSPC_HDP_4_1 is supported, otherwise Excluded. +C.4: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported. +------------------------------------------------------------------------------- + + + Device Identification - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_5_1 True Device Identification Profile v1.3 or later + (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + HDP Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_6_1 True Supports Standard Op Codes (M) +TSPC_HDP_6_2 True (*) Supports Initiate creation of Control and Data + Channels (C.3) (C.7) (C.1 - MCAP Status) +TSPC_HDP_6_3 True Supports Accept creation of Control and Data + Channels (C.3) (C.8) (C.1 - MCAP Status) +TSPC_HDP_6_4 False Supports Initiate Reconnection of MDL (O) + (C.2 - MCAP Status) +TSPC_HDP_6_5 True Supports Accept Reconnection of MDL (C.4) +TSPC_HDP_6_6 False Supports Clock Synchronization Protocol (O) +TSPC_HDP_6_7 False (*) Supports Sync-Slave (C.5) +TSPC_HDP_6_8 False Supports Sync-Master (C.6) +------------------------------------------------------------------------------- +C.1: If TSPC_HDP_6_1 is supported, at least one is Mandatory, otherwise + Excluded. +C.2: Optional if TSPC_HDP_6_1 is supported, otherwise Excluded. +C.3: Mandatory to support at least one. +C.4: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded. + +C.5: Mandatory if TSPC_HDP_6_6 is supported, otherwise Excluded. +C.6: Optional if TSPC_HDP_6_6 is supported, otherwise Excluded. +C.7: Mandatory to support SDP Client Role (SDP 1b/2) if this item is supported. +C.8: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported. +------------------------------------------------------------------------------- + + + Data Exchange Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_7_1 False Supports Initiation of Echo Test (O) +TSPC_HDP_7_2 True Supports Acceptance of Echo Test (M) +TSPC_HDP_7_3 True Supports IEEE 11073-20601 (M) +TSPC_HDP_7_4 True Supports IEEE 11073-20601 Agent Role (C.1) +TSPC_HDP_7_5 True (*) Supports IEEE 11073-20601 Manager Role (C.1) +TSPC_HDP_7_6 False Supports Initiation of Association Release (O) +------------------------------------------------------------------------------- +C.1: If TSPC_HDP_7_3 is supported, at least one is Mandatory, otherwise + Excluded. +------------------------------------------------------------------------------- + + + GAP Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_8_1 True Supports General-discoverable Mode (M) +TSPC_HDP_8_2 True Supports Bondable Mode (M) (C.1) +TSPC_HDP_8_3 True Supports Response to Authentitaction requests + (M) +TSPC_HDP_8_4 True Supports Initiation of Authentication (M) (C.2) +TSPC_HDP_8_5 True Supports Acceptance of Encryption request (M) +TSPC_HDP_8_6 True Supports Initiation of Encryption (M) (C.3) +TSPC_HDP_8_7 True Supports General Inquiry (M) C.4) +TSPC_HDP_8_8 True Supports Acceptance of Bonding requests (M) +TSPC_HDP_8_9 Ture (*) Supports Initiation of Bonding (O) +TSPC_HDP_8_10 True (*) Supports Extended Inquiry Response (C.5) (C.6) +TSPC_HDP_8_11 True (*) Supports use of Health Class of Device (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional. +C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional. +C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed (Not SUM ICS 31/4), + otherwise Optional. +C.4: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional. +C.5: Mandatory if Bluetooth Core Specification 2.1 + EDR or later + (Not SUM ICS 31/4) and TSPC_HDP_8_1 is supported, otherwise Optional + if Bluetooth Core Specification 2.1 + EDR or later is supported, + otherwise Excluded. +C.6: Mandatory if Bluetooth Core specification 2.1 + EDR or later + (Not SUM ICS 31/4) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + + L2CAP Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_9_1 True Supports Reliable Control Channel (C.1) +TSPC_HDP_9_2 True Uses FCS for Control Channel (M) +TSPC_HDP_9_3 True Supports Reliable Data Channel (C.1) +TSPC_HDP_9_4 True Can send data using SAR in ERTM (C.2) +TSPC_HDP_9_5 False Uses FCS for Reliable Data Channel (O) +TSPC_HDP_9_6 False Supports FCS option of "No FCS" for Reliable + Data Channel (C.3) +TSPC_HDP_9_7 True Supports Streaming Data Channel (C.4) +TSPC_HDP_9_8 True Can send data using SAR in SM (C.5) +TSPC_HDP_9_9 False Uses FCS for Steaming Data Channel (O) +TSPC_HDP_9_10 False Supports FCS option of "No FCS" for Streaming + Data Channel (C.3) +TSPC_HDP_9_11 True Maximum number of simultaneous Data Channels + supported (DCmax) per MCL (M) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded. +C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded. +C.4: Mandatory if TSPC_L2CAP_2_13 is supported, otherwise Excluded. +C.5: Mandatory if TSPC_L2CAP_2_23 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + SDP Attributes - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_10_1 True Supports advertisement of HDP Service Record (C.1) +TSPC_HDP_10_2 True Service Class ID List (M) +TSPC_HDP_10_3 True Protocol Descriptor List (M) +TSPC_HDP_10_4 True Bluetooth Profile Descriptor List (M) +TSPC_HDP_10_5 True Additional Protocol Descriptor Lists (M) +TSPC_HDP_10_6 True (*) Service Name (O) +TSPC_HDP_10_7 False Service Description (O) +TSPC_HDP_10_8 False Provider Name (O) +TSPC_HDP_10_9 True HDP Supported Features (MDEP List) (M) +TSPC_HDP_10_10 True MCAP Data Exchange Specification (M) +TSPC_HDP_10_11 True MCAP Supported Procedures (M) +TSPC_HDP_10_12 False Service Record State (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support 10/1 and SDP Server Role (SDP 1b/1). +------------------------------------------------------------------------------- + + + Device Identification - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_11_1 True Device Identification Profile v1.3 or later + (M) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if 1/2 is supported. +------------------------------------------------------------------------------- + + + HDP Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_12_1 True Supports Standard Op Codes (M) +TSPC_HDP_12_2 True Supports Initiate creation of Control and Data + Channels (C.1) (C.5) +TSPC_HDP_12_3 True Supports Accept creation of Control and Data + Channels (C.1) (C.6) +TSPC_HDP_12_4 False Supports Initiate Reconnection of MDL (O) (C.2) +TSPC_HDP_12_5 True Supports Accept Reconnection of MDL (M) +TSPC_HDP_12_6 False Supports Clock Synchronization Protocol (O) +TSPC_HDP_12_7 False Supports Sync-Slave (C.3) +TSPC_HDP_12_8 False Supports Sync-Master (C.6) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_12_1 is supported, otherwise Excluded. +C.2: Optional if TSPC_HDP_12_1 is supported, otherwise Excluded. +C.3: Mandatory if TSPC_HDP_12_6 is supported, otherwise Excluded. +C.4: Optional if TSPC_HDP_12_6 is supported, otherwise Excluded. +C.5: Mandatory to support 12/2 and SDP Client Role (SDP 1b/2). +C.6: Mandatory to support 12/3 and SDP Server Role (SDP 1b/1). +------------------------------------------------------------------------------- + + + Data Exchange Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_13_1 False Supports Initiation of Echo Test (O) +TSPC_HDP_13_2 True Supports Acceptance of Echo Test (M) +TSPC_HDP_13_3 True Supports IEEE 11073-20601 (M) +TSPC_HDP_13_4 True (*) Supports IEEE 11073-20601 Agent Role (C.1) +TSPC_HDP_13_5 False Supports IEEE 11073-20601 Manager Role (C.1) +TSPC_HDP_13_6 False Supports Initiation of Association Release (O) +------------------------------------------------------------------------------- +C.1: If TSPC_HDP_13_3 is supported, at least one is Mandatory, otherwise + Excluded. +------------------------------------------------------------------------------- diff --git a/android/pics-hfp.txt b/android/pics-hfp.txt new file mode 100644 index 0000000..1a13edc --- /dev/null +++ b/android/pics-hfp.txt @@ -0,0 +1,217 @@ +HFP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_0_1 False Version: Hands-Free Profile v1.5 (O.1) +TSPC_HFP_0_2 True (*) Version: Hands-Free Profile v1.6 (O.1) +------------------------------------------------------------------------------- +O.1: It is mandatory to support only one of the adopted versions. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_1_1 True (*) Role: Audio Gateway (AG) (O.1) +TSPC_HFP_1_2 False Role: Hands-Free (HF) (O.1) +------------------------------------------------------------------------------- +O.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Audio Gateway Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_2_1 True Connection management (M) +TSPC_HFP_2_1a True (*) SLC initiation during active ongoing call (O) +TSPC_HFP_2_2 True Phone Status Information (M) +TSPC_HFP_2_3 True Audio connection handling (M) +TSPC_HFP_2_3a False Audio connection establishment independent of + call processing (O) +TSPC_HFP_2_3b True (*) eSCO support in Audio Connection (C.10) +TSPC_HFP_2_3c True (*) Codec negotiation (C.7) +TSPC_HFP_2_4a False Accept an incoming voice call + (in-band ring) (C.1) +TSPC_HFP_2_4b True (*) Accept an incoming voice call + (no in-band ring) (C.1) +TSPC_HFP_2_4c False Capability to change the "in-band ring" + settings (O) +TSPC_HFP_2_5 True (*) Reject an incoming voice call (O) +TSPC_HFP_2_6 True Terminate a call (M) +TSPC_HFP_2_7 True Audio connection transfer during an ongoing + call (M) +TSPC_HFP_2_7a True (*) HF-initiated Audio transfer to AG during + ongoing call (O) +TSPC_HFP_2_8 True Place a call with a phone number supplied by + the HF (M) +TSPC_HFP_2_9 True Place a call using memory dialing (M) +TSPC_HFP_2_10 True Place a call to the last number dialed (M) +TSPC_HFP_2_11 True Call waiting notification (M) +TSPC_HFP_2_12 True (*) Three Way Calling (O) +TSPC_HFP_2_12a True (*) User Busy (AT+CHLD value 0) (C.3) +TSPC_HFP_2_12b True (*) Call Hold Handling (AT+CHLD value 1,2) (C.2) +TSPC_HFP_2_12c True (*) Three Way Call (AT+CHLD value 3) (C.3) +TSPC_HFP_2_12d False Explicit Call Transfer (AT+CHLD value 4) (C.3) +TSPC_HFP_2_13 True Calling Line Identification (CLI) (M) +TSPC_HFP_2_14 True (*) Echo canceling (EC) and Noise reduction (NR) (O) +TSPC_HFP_2_15 True (*) Voice recognition activation (O) +TSPC_HFP_2_15a True (*) Initiate voice recognition from AG (C.6) +TSPC_HFP_2_15b True (*) Autonomous voice deactivation (C.6) +TSPC_HFP_2_16 False Attach a phone number to a voice tag (O) +TSPC_HFP_2_17 True Ability to transmit DTMF codes (M) +TSPC_HFP_2_18a True (*) Remote audio volume control – speaker (O) +TSPC_HFP_2_18b False Remote audio volume control – microphone (O) +TSPC_HFP_2_18c True (*) Volume Level Synchronization – speaker and + microphone (C.5) +TSPC_HFP_2_19 False Response and hold (O) +TSPC_HFP_2_20 True Subscriber Number Information (M) +TSPC_HFP_2_21a True Enhanced Call Status (C.4) +TSPC_HFP_2_21b False Enhanced Call Control (C.3) +TSPC_HFP_2_21c True (*) Enhanced Call Status with limited network + notification (C.4) +TSPC_HFP_2_22 False Support for automatic link loss recovery (O) +TSPC_HFP_2_23 True (*) Individual Indicator Activation (C.9) +TSPC_HFP_2_24 True (*)s Wide Band Speech service (C.8) +TSPC_HFP_2_25 False Support roaming function (O) +------------------------------------------------------------------------------- +C.1: The AG must support one of item TSPC_HFP_2_4a or TSPC_HFP_2_4b +C.2: Mandatory if TSPC_HFP_2_12is TRUE; otherwise excluded +C.3: Optional if TSPC_HFP_2_12 is TRUE; otherwise excluded +C.4: The AG must support one of item TSPC_HFP_2_21a or TSPC_HFP_2_21c +C.5: Mandatory if TSPC_HFP_2_18a or TSPC_HFP_2_18b; otherwise optional +C.6: Optional if TSPC_HFP_2_15 is supported, otherwise excluded +C.7: Mandatory if TSPC_HFP_2_24 otherwise excluded +C.8: Excluded if TSPC_HFP_0_1 otherwise optional +C.9: Excluded if TSPC_HFP_0_1 otherwise mandatory +C.10: Mandatory if TSPC_HFP_2_24 otherwise optional +------------------------------------------------------------------------------- + + + Hands-Free Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_3_1 False (*) Connection Management (M) +TSPC_HFP_3_2a False (*) Phone Status Information ("service" and "call" + indicators) (M) +TSPC_HFP_3_2b False Phone Status Information ("callsetup" + indicators) (O) +TSPC_HFP_3_2c False Accept indicator of signal strength (O) +TSPC_HFP_3_2d False Accept indicator of roaming state ("roam:") (O) +TSPC_HFP_3_2e False Accept indicator of battery level ("battchg") (O) +TSPC_HFP_3_2f False Accept indicator of operator selection (O) +TSPC_HFP_3_3 False (*) Audio connection handling (M) +TSPC_HFP_3_3a False Audio connection establishment independent + of call processing (O) +TSPC_HFP_3_3b False eSCO support in Audio Connection (C.7) +TSPC_HFP_3_3c False Codec negotiation (C.5) +TSPC_HFP_3_4a False (*) Accept an incoming voice call (in-band ring) (M) +TSPC_HFP_3_4b False (*) Accept an incoming voice call (no in-band + ring) (M) +TSPC_HFP_3_4c False Accept an incoming voice call (in-band ring + muting) (O) +TSPC_HFP_3_5 False (*) Reject an incoming voice call (M) +TSPC_HFP_3_6 False (*) Terminate a call (M) + +TSPC_HFP_3_7 False (*) Audio connection transfer during an ongoing + call (M) +TSPC_HFP_3_7a False HF-initiated Audio transfer to AG during + ongoing call (O) +TSPC_HFP_3_8 False Place a call with a phone number supplied by + the HF (O) +TSPC_HFP_3_9 False Place a call using memory dialing (O) +TSPC_HFP_3_10 False Place a call to the last number dialed (O) +TSPC_HFP_3_11 False Call waiting notification (O) +TSPC_HFP_3_12 False Three Way Calling (O) +TSPC_HFP_3_12a False Three way calling (AT+CHLD values 0) (C.2) +TSPC_HFP_3_12b False Three way calling (AT+CHLD values 1 and 2) (C.1) +TSPC_HFP_3_12c False Three way calling (AT+CHLD value 3) (C.2) +TSPC_HFP_3_12d False Three way calling (AT+CHLD value 4) (C.2) +TSPC_HFP_3_12e False Originate new call with established call in + progress (C.2) +TSPC_HFP_3_13 False Calling Line Identification (CLI) (O) +TSPC_HFP_3_14 False Echo cancelling (EC) and Noise reduction (NR) (O) +TSPC_HFP_3_15 False Voice recognition activation/deactivation (O) +TSPC_HFP_3_16 False Attach a phone number to a voice tag (O) +TSPC_HFP_3_17 False Ability to transmit DTMF codes (O) +TSPC_HFP_3_18a False Remote audio volume control – speaker (O) +TSPC_HFP_3_18b False Remote audio volume control – microphone (O) +TSPC_HFP_3_18c False Volume Level Synchronization – speaker (C.3) +TSPC_HFP_3_18d False Volume Level Synchronization – microphone (C.4) +TSPC_HFP_3_18e False HF informs AG about local changes of audio + volume (O) +TSPC_HFP_3_18f False HF informs AG about local changes of + microphone gain (O) +TSPC_HFP_3_19 False Response and hold (O) +TSPC_HFP_3_20 False Subscriber Number Information (O) +TSPC_HFP_3_21a False Enhanced Call Status (O) +TSPC_HFP_3_21b False Enhanced Call Control (C.2) +TSPC_HFP_3_22 False Support for automatic link loss recovery (O) +TSPC_HFP_3_23 False Individual Indicator Activation (C.6) +TSPC_HFP_3_24 False Wide Band Speech service (C.6) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HFP_3_12; otherwise excluded +C.2: Optional if TSPC_HFP_3_12; otherwise excluded +C.3: Mandatory if TSPC_HFP_3_18a or TSPC_HFP_3_18b, otherwise optional +C.4: Mandatory if TSPC_HFP_3_18a, otherwise optional +C.5: Mandatory if TSPC_HFP_3_24 otherwise excluded +C.6: Excluded if TSPC_HFP_0_1 otherwise optional +C.7: Mandatory if TSPC_HFP_3_24 otherwise optional +------------------------------------------------------------------------------- + + + Audio Coding Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_4_1 True CVSD audio coding over SCO (M) +TSPC_HFP_4_2 True (*) mSBC audio coding over eSCO (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if Wide band speech service is supported TSPC_HFP_2_24 or + TSPC_HFP_3_24, otherwise excluded +------------------------------------------------------------------------------- + + + Supplementary Interoperability Verification +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_8_1 True (*) Multiple audio transfers during call – + AG and HF initiated (C.1) +TSPC_HFP_8_2 True (*) Audio transfer by SLC release during + an active call (C.1) +TSPC_HFP_8_3 True (*) Audio transfer by powering ON HF (O) +TSPC_HFP_8_4 True (*) SLC during SDP response (O) +TSPC_HFP_8_5 True (*) Handle dynamic server channel number for HFP + service (O) +TSPC_HFP_8_6 False HF disallows connections in non-discoverable + mode (C.2) +TSPC_HFP_8_7 True (*) HF connects to AG during incoming call (O) +TSPC_HFP_8_8 True (*) Link loss during incoming call (C.3) +TSPC_HFP_8_9 True (*) SLC release during incoming call (C.3) +TSPC_HFP_8_10 True (*) Voice Recognition Activation (C.4) +TSPC_HFP_8_11 True (*) Place outgoing call by dialing number on + the AG (O) +TSPC_HFP_8_12 True (*) Active call termination – NO CARRIER signal + (C.5) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_HFP_2_7a or TSPC_HFP_3_7a is supported, + otherwise excluded +C.2: Optional if TSPC_HFP_1_2 is supported, otherwise excluded +C.3: Optional if TSPC_HFP_1_1 is supported, otherwise excluded +C.4: Optional if TSPC_HFP_2_15 or TSPC_HFP_3_15 is supported, + otherwise excluded +C.5: Optional if TSPC_HFP_2_6 is supported, otherwise excluded +------------------------------------------------------------------------------- diff --git a/android/pics-hid.txt b/android/pics-hid.txt index ba50626..f1645cc 100644 --- a/android/pics-hid.txt +++ b/android/pics-hid.txt @@ -1,5 +1,7 @@ HID PICS for the PTS tool. +PTS version: 5.1 + * - different than PTS defaults # - not yet implemented/supported @@ -283,8 +285,3 @@ TSPC_ALL False Enables all test cases when set to true. ------------------------------------------------------------------------------- M.1: Mandatory IF (TSPC_HID_1_2) is supported. ------------------------------------------------------------------------------- - - -------------------------------------------------------------------------------- -No special PIXIT settings required. All should be set according to Tester's -test environment. diff --git a/android/pics-hsp.txt b/android/pics-hsp.txt new file mode 100644 index 0000000..3c4962c --- /dev/null +++ b/android/pics-hsp.txt @@ -0,0 +1,103 @@ +HSP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_0_1 False Version: Headset Profile v1.1 (C.1) +TSPC_HSP_0_2 True (*) Version: Headset Profile v1.2 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support one and only one of these versions. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_1_1 True (*) Role: Audio Gateway (AG) (C.1) +TSPC_HSP_1_2 False Role: Headset (HS) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Audio Gateway Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_2_1 True Incoming audio connection establishment (M) +TSPC_HSP_2_2 True (*) Ring (AT command) (C.3) +TSPC_HSP_2_3 False Inband ring tone (O) +TSPC_HSP_2_4 True (*) Outgoing audio connection establishment (O) +TSPC_HSP_2_5 True (*) Audio connection release from HS (C.5) +TSPC_HSP_2_6 True Audio connection release from AG (M) +TSPC_HSP_2_7 True Audio connection transfer: AG to HS (M) +TSPC_HSP_2_8 True Audio connection transfer: HS to AG (M) +TSPC_HSP_2_9 True (*) Remote audio volume control (C.1) +TSPC_HSP_2_10 True (*) HS informs AG about local changes of audio + volume (O) +TSPC_HSP_2_11 True (*) Audio volume setting storage by HS (O) +TSPC_HSP_2_12 False Remote microphone gain control (C.2) +TSPC_HSP_2_13 False HS informs AG about local changes of microphone + gain (O) +TSPC_HSP_2_14 False Microphone gain setting storage by HS (O) +TSPC_HSP_2_15 True Connection handling with Detach/Page (M) +TSPC_HSP_2_16 False Connection handling with Park Mode (C.4) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HSP_2_10 is supported, otherwise optional +C:2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional +C.3: Excluded if TSPC_HSP_2_3 and TSPC_HSP_4_1 ("Show that in-band + ringing and RING are mutually exclusive") are supported, + otherwise optional +C.4: Excluded if TSPC_HSP_0_2 is supported, otherwise optional +C.5: Mandatory if TSPC_HSP_0_1 is supported, otherwise optional +------------------------------------------------------------------------------- + + + Headset Application Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_3_1 False (*) Incoming audio connection establishment (M) +TSPC_HSP_3_2 False (*) Ring (AT command) (M) +TSPC_HSP_3_3 False (*) Inband ring tone (M) +TSPC_HSP_3_4 False (*) Outgoing audio connection establishment (M) +TSPC_HSP_3_5 False (*) Audio connection release from HS (M) +TSPC_HSP_3_6 False (*) Audio connection release from AG (M) +TSPC_HSP_3_7 False (*) Audio connection transfer: AG to HS (M) +TSPC_HSP_3_8 False (*) Audio connection transfer: HS to AG (M) +TSPC_HSP_3_9 False Remote audio volume control (C.1) +TSPC_HSP_3_10 False HS informs AG about local changes of audio + volume (O) +TSPC_HSP_3_11 False Audio volume setting storage by HS (O) +TSPC_HSP_3_12 False Remote microphone gain control (C.2) +TSPC_HSP_3_13 False HS informs AG about local changes of microphone + gain (O) +TSPC_HSP_3_14 False (*) Microphone gain setting storage by HS (O) +TSPC_HSP_3_15 False Connection handling with Detach/Page (M) +TSPC_HSP_3_16 False Connection handling with Park Mode (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HSP_3_10 is supported, otherwise optional +C.2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional +C.3: Excluded if TSPC_HSP_0_2 is supported, otherwise optional +------------------------------------------------------------------------------- + + + Errata Service Releases +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_4_1 False Show that in-band ringing and RING are + mutually exclusive (C.1) +------------------------------------------------------------------------------- +C.1: Excluded if TSPC_HSP_0_2 is supported, otherwise optional +------------------------------------------------------------------------------- diff --git a/android/pics-iopt.txt b/android/pics-iopt.txt new file mode 100644 index 0000000..a1289ef --- /dev/null +++ b/android/pics-iopt.txt @@ -0,0 +1,223 @@ +IOPT PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Profiles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_support_ Support for: Advanced +AdvancedAudioDistributionProfile_Sink False Audio Distribution + Profile. Role: Sink + +TSPC_support_ Support for: Advanced +AdvancedAudioDistributionProfile_Source True (*) Audio Distribution + Profile. Role: Source + +TSPC_support_AVRemoteControlProfile_CT True (*) Support for: Audio\Video + Remote Control Profile. + Role: Controller + +TSPC_support_AVRemoteControlProfile_TG True (*) Support for: Audio\Video + Remote Control Profile. + Role: Target + +TSPC_support_BasicImagingProfile_CLIENT False Support for: Basic + Imaging Profile. + Role: Client + +TSPC_support_BasicImagingProfile_ Support for: Basic +SERVER_ImagingAutomaticArchive False Imaging Profile. Role: + Server Functionality: + Imaging autoarchive + +TSPC_support_BasicImagingProfile_ False Support for: Basic +SERVER_ImagingReferencedObjects Imaging Profile. Role: + Server Functionality: + Imaging ref. objects + +TSPC_support_BasicImagingProfile_ False Support for: Basic +SERVER_ImagingResponder Imaging Profile. Role: + Server Functionality: + Imaging responder + +TSPC_support_ False Support for: Basic +BasicPrintingProfile_PRINTER Printing Profile. Role: + Printer + +TSPC_support_ Support for: Basic +BasicPriProfile_PRINTER_ReflectedUI False Printing Profile. Role: + Printer Functionality: + Reflected UI + +TSPC_support_BasicPrintingProfile_ Support for: Basic +SENDER_Referenced_objects_Service False Printing Profile. Role: + Sender Functionality: + Refe. objects service + +TSPC_support_DialUpNetworkingProfile_DT False Support for: Dial-Up + Networking Profile. + Role: Data Terminal + +TSPC_support_DialUpNetworkingProfile_GW False Support for: Dial-Up + Networking Profile. + Role: Gateway + +TSPC_support_ True (*) Support for: Extended +ExtendedServiceDiscoveryProfile_IP_LAP SDP. Version: IP-LAP + +TSPC_support_ True (*) Support for: Extended +ExtendedServiceDiscoveryProfile_IP_PAN SDP. Version: IP-PAN + +TSPC_support_ True (*) Support for: Extended +ExtendedServiceDiscoveryProfile_L2CAP SDP. Version: L2CAP + +TSPC_support_FAXProfile_DE False Support for: FAX Profile + Role: Data Terminal + +TSPC_support_FAXProfile_GW False Support for: FAX Profile + Role: Gateway + +TSPC_support_FileTransferProfile_CLIENT False Support for: FTP + Role: Client + +TSPC_support_FileTransferProfile_SERVER False Support for: FTP + Role: Server + +TSPC_support_HealthDeviceProfile_Sink False (#) Support for: HDP + Role: Sink + +TSPC_support_HealthDeviceProfile_Source False (#) Support for: HDP + Role: Source + +TSPC_support_NewHandsFreeProfile_AG False Support for: HFP + Role: Audio gateway + +TSPC_support_NewHandsFreeProfile_HF False Support for: HFP + Role: Hands-Free unit + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_ cable Repl. Profile +CLIENT_CR_RegisterNotofication_support Role: Client + Functionality: CR + register notification + support + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_CLIENT_print cable Repl. Profile. + Role: Client + Functionality: Print + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_CLIENT_scan cable Repl. Profile. + Role: Client + Functionality: Scan + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_SERVER_print cable Repl. Profile. + Role: Server + Functionality: Print + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_SERVER_scan cable Repl. Profile. + Role: Server + Functionality: Scan + +TSPC_support_HeadsetProfile_AG True (*) Support for: HSP + Role: Audio Gateway + +TSPC_support_HeadsetProfile_HS False Support for: HSP + Role: Headset + +TSPC_support_ False Support for: HID +HumanInterfaceDeviceProfile Role: Device + +TSPC_support_HID_Host True (*) Support for: HID + Role: Host + +TSPC_support_LANAccessProfile_DT False Support for: LAN Access + Profile. Role: Data + Terminal + +TSPC_support_LANAccessProfile_LAP False Support for: LAN Access + Profile. Role: LAN + Access Point + +TSPC_support_MessaeAccessProfile_MCE False Support for: MAP + Role: MCE + +TSPC_support_MessageAccessProfile_MSE True (*) Support for: MAP + Role: MSE + +TSPC_support_ObjectPushProfile_CLIENT True (*) Support for: OPP + Role: Client + +TSPC_support_ObjectPushProfile_SERVER True (*) Support for: OPP + Role: Server + +TSPC_support_ False Support for: PAN +PersonalAreaNetworkProfile_GN Role: GN + +TSPC_support_ True (*) Support for: PAN +PersonalAreaNetworkProfile_NAP Role: NAP + +TSPC_support_ True (*) Support for: PAN +PersonalAreaNetworkProfile_PANU Role: PANU + +TSPC_support_PhonebookAccessProfile_PCE False Support for: PBAP + Role: PCE + +TSPC_support_PhonebookAccessProfile_PSE True (*) Support for: PBAP + Role: PSE + +TSPC_support_SerialPortProfile_Service False Support for: SPP + Role: Dev B + +TSPC_support_ False Support for: Service +ServiceDiscoveryApplicationProfile Discovery Application + Profile + +TSPC_support_SIMAccessProfile_CLIENT False Support for: SIM access + Profile. Role: Client + +TSPC_support_SIMAccessProfile_SERVER False Support for: SIM access + Profile. Role: Server + +TSPC_support_ False Support for: +SynchronizationProfile_CLIENT Synchronization Profile + Role: Client + +TSPC_support_ False Support for: +SynchronizationProfile_SERVER Synchronization Profile + Role: Server + +TSPC_support_UDIProfile_MT False Support for: UDI Profile + Role: MT + +TSPC_support_UDIProfile_TA False Support for: UDI Profile + Role: TA + +TSPC_support_ False Support for: Video +VideoDistributionProfile_Sink distribution Profile + Role: Sink + +TSPC_support_ False Support for: Video +VideoDistributionProfile_Source distribution Profile + Role: Source + +TSPC_support_WAPOverBluetooth_CLIENT False Support for: WAP over + Bluetooth Profile + Role: Client + +TSPC_support_WAPOverBluetooth_PROXY False Support for: WAP over + Bluetooth Profile + Role: PROXY + +TSPC_support_GNSS_SERVER False Support for: GNSS + Role: Server diff --git a/android/pics-l2cap.txt b/android/pics-l2cap.txt new file mode 100644 index 0000000..096fbf4 --- /dev/null +++ b/android/pics-l2cap.txt @@ -0,0 +1,159 @@ +L2CAP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_L2CAP_1_1 True Data Channel Initiator (C.1) +TSPC_L2CAP_1_2 True Data Channel Acceptor (C.1) +TSPC_L2CAP_1_3 True (#) LE Master (C.2) +TSPC_L2CAP_1_4 True (#) LE Slave (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory IF BR/EDR or BR/EDR/LE is claimed, ELSE Excluded. +C.2: Mandatory to support (at least one of TSPC_L2CAP_1_3 or TSPC_L2CAP_1_4) + IF LE or BR/EDR/LE claimed, ELSE Excluded. +------------------------------------------------------------------------------- + + + General Operation +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_L2CAP_2_1 True Support of L2CAP signaling channel (C.20) +TSPC_L2CAP_2_2 True Support of configuration process (C.20) +TSPC_L2CAP_2_4 True Support of command echo request (C.21) +TSPC_L2CAP_2_3 True Support of connection oriented data + channel (C.20) +TSPC_L2CAP_2_5 True Support of command echo response (C.20) +TSPC_L2CAP_2_6 True (*) Support of command information request (C.21) +TSPC_L2CAP_2_7 True Support of command information response (C.20) +TSPC_L2CAP_2_8 False Support of a channel group (C.21) +TSPC_L2CAP_2_9 False Support of packet for connectionless + channel (C.21) +TSPC_L2CAP_2_10 False Support retransmission mode (C.21) +TSPC_L2CAP_2_11 False Support flow control mode(C.21) +TSPC_L2CAP_2_12 True (*) Enhanced Retransmission Mode (C.1, C.13) +TSPC_L2CAP_2_13 True (*) Streaming Mode (C.1, C.14) +TSPC_L2CAP_2_14 True (*) FCS Option (C.2) +TSPC_L2CAP_2_15 True (*) Generate Local Busy Condition (C.3) +TSPC_L2CAP_2_16 True (*) Send Reject (C.3) +TSPC_L2CAP_2_17 True (*) Send Selective Reject (C.3) +TSPC_L2CAP_2_18 True (*) Mandatory use of ERTM (C.4) +TSPC_L2CAP_2_19 True (*) Mandatory use of Streaming Mode (C.5) +TSPC_L2CAP_2_20 True (*) Optional use of ERTM (C.4) +TSPC_L2CAP_2_21 True (*) Optional use of Streaming Mode (C.5) +TSPC_L2CAP_2_22 True (*) Send data using SAR in ERTM (C.6) +TSPC_L2CAP_2_23 True (*) Send data using SAR in Streaming Mode (C.7) +TSPC_L2CAP_2_24 True (*) Actively request Basic Mode for a PSM that + supports the use of ERTM or Streaming + Mode (C.8) +TSPC_L2CAP_2_25 True (*) Supports performing L2CAP channel mode + configuration fallback from SM + to ERTM (C.9) +TSPC_L2CAP_2_26 True (*) Supports sending more than one unacknowledged + I-Frame when operating in ERTM (C.10) +TSPC_L2CAP_2_27 True (*) Supports sending more than three unacknowledged + I-Frame when operating in ERTM (C.10) +TSPC_L2CAP_2_28 True (*) Supports configuring the peer TxWindow + greater than 1 (C.11) +TSPC_L2CAP_2_29 False AMP Support (C.12) +TSPC_L2CAP_2_30 True (*) Fixed Channel Support (C.12) +TSPC_L2CAP_2_31 False AMP Manager Support (C.12) +TSPC_L2CAP_2_32 False ERTM over AMP (C.12) +TSPC_L2CAP_2_33 False Streaming Mode Source over AMP Support (C.15) +TSPC_L2CAP_2_34 False Streaming Mode Sink over AMP Support (C.15) +TSPC_L2CAP_2_35 False Unicast Connectionless Data, Reception (C.1, C.16) +TSPC_L2CAP_2_36 False Ability to transmit an unencrypted packet over + a Unicast connectionless L2CAP + channel (C.16) +TSPC_L2CAP_2_37 False Ability to transmit an encrypted packet over + a Unicast connectionless L2CAP + channel (C.16) +TSPC_L2CAP_2_38 False Extended Flow Specification for BR/EDR (C.8) +TSPC_L2CAP_2_39 False Extended Window Size (C.8) +TSPC_L2CAP_2_40 True (*) Support of Low Energy signaling channel (C.17) +TSPC_L2CAP_2_41 True (*) Support of command reject (C.17) +TSPC_L2CAP_2_42 True (*) Send Connection Parameter Update Request (C.18) +TSPC_L2CAP_2_43 True (*) Send Connection Parameter Update Response (C.19) +TSPC_L2CAP_2_44 False Extended Flow Specification for AMP (C.22) +TSPC_L2CAP_2_45 False Send disconnect request command (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 OR + TSPC_L2CAP_2_35 IF BR/EDR BR/EDR/LE AND SUM_ICS 31/7 (CSA1) OR + SUM_ICS 31/8 (3.0) OR SUM_ICS 31/9 (3.0+HS) OR SUM_ICS 31/10 (4.0)) + is supported, ELSE Excluded +C.2: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded. +C.3: Optional IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_28 is claimed, ELSE Excluded. +C.4: IF TSPC_L2CAP_2_12 is claimed THEN either TSPC_L2CAP_2_18 + OR TSPC_L2CAP_2_20 are Mandatory, ELSE Excluded. +C.5: IF TSPC_L2CAP_2_13 is claimed THEN either TSPC_L2CAP_2_19 + OR TSPC_L2CAP_2_21 are Mandatory, ELSE Excluded. +C.6: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. +C.7: Optional IF TSPC_L2CAP_2_13 is claimed, ELSE Excluded. +C.8: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded. +C.9: Mandatory IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_13 AND TSPC_L2CAP_2_21 + is claimed, ELSE Excluded. +C.10: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. +C.11: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. +C.12: Mandatory IF SUM_ICS 31/9 (3.0 + HS) is claimed, ELSE Optional. +C.13: Mandatory IF SUM_ICS 31/9 (3.0 + HS) is claimed, ELSE Optional. +C.14: Optional IF SUM_ICS 31/8 OR 31/9 OR 31/10 OR 31/11 is claimed, ELSE Excluded. +C.15: Optional IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded. +C.16: Optional IF (SUM_ICS 31/8 OR SUM_ICS 31/9 OR 31/10 OR 31/11) is claimed, + ELSE Excluded. +C.17: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded. +C.18: Optional IF (SUM_ICS 31/10 AND 1/4) is claimed, ELSE Excluded. +C.19: Mandatory IF (SUM_ICS 31/10 AND 1/3) is claimed, ELSE Excluded. +C.20: Mandatory IF LE OR BR/EDR/LE, is claimed, ELSE Excluded +C.21: Optional IF LE OR BR/EDR/LE, is claimed, ELSE Excluded +C.22: Mandatory IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded. +------------------------------------------------------------------------------- + + + Configurable Parameters +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_L2CAP_3_1 True Support of RTX timer (M) +TSPC_L2CAP_3_2 True Support of ERTX timer (C.4) +TSPC_L2CAP_3_3 True Support minimum MTU size 48 octets (C.4) +TSPC_L2CAP_3_4 True (*) Support MTU size larger than 48 octets (C.5) +TSPC_L2CAP_3_5 True Support of flush timeout value for reliable + channel (C.4) +TSPC_L2CAP_3_6 False Support of flush timeout value for unreliable + channel (C.5) +TSPC_L2CAP_3_7 False Support of bi-directional quality of service + (QoS) option field (C.1) +TSPC_L2CAP_3_8 False Negotiate QoS service type (C.5) +TSPC_L2CAP_3_9 False Negotiate and support service type ‘No + traffic’ (C.2) +TSPC_L2CAP_3_10 False Negotiate and support service type ‘Best + effort’ (C.3) +TSPC_L2CAP_3_11 False Negotiate and support service type + ‘Guaranteed’ (C.2) +TSPC_L2CAP_3_12 True (*) Support minimum MTU size 23 octets (C.6) +TSPC_L2CAP_3_13 False Negotiate and support service type ‘No traffic’ + for Extended Flow Specification (C.7) +TSPC_L2CAP_3_14 False Negotiate and support service type ‘Best Effort' + for Extended Flow Specification (C.8) +TSPC_L2CAP_3_15 False Negotiate and support service type ‘Guaranteed’ + for Extended Flow Specification (C.9) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Optional. +C.2: Optional if TSPC_L2CAP_3_8 is supported, ELSE Excluded. +C.3: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Excluded. +C.4: Mandatory IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded. +C.5: Optional IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded. +C.6: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded. +C.7: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded. +C.8: Mandatory if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded. +C.9: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded. +------------------------------------------------------------------------------- diff --git a/android/pics-map.txt b/android/pics-map.txt new file mode 100644 index 0000000..1e0695f --- /dev/null +++ b/android/pics-map.txt @@ -0,0 +1,175 @@ +MAP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_0_1 False Role: Map 1.0 (C1) +TSPC_MAP_0_2 True (*) Role: Map 1.1 (C1) +TSPC_MAP_0_3 False Role: Map 1.2 (C1) +------------------------------------------------------------------------------- +C.1: Mandatory to support only one Profile version. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_1_1 True (*) Role: Messaging Server Equipment (C1) +TSPC_MAP_1_2 False Role: Messaging Client Equipment (C1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Supported features MCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_2_1 False MCE: Message Notification (C1) +TSPC_MAP_2_1a False MCE: SendEvent (C4) +TSPC_MAP_2_2 False MCE: Message Browsing (C1) +TSPC_MAP_2_2a False MCE: SetFolder (C5) +TSPC_MAP_2_2b False MCE: GetFoldersListing (C5) +TSPC_MAP_2_2c False MCE: GetMessagesListing (C5) +TSPC_MAP_2_2d False MCE: GetMessage (O) +TSPC_MAP_2_2e False MCE: SetMessageStatus (O) +TSPC_MAP_2_2f False MCE: UpdateInbox (O) +TSPC_MAP_2_2g False MCE: Filtering (O) +TSPC_MAP_2_2h False MCE: Multiple simultaneous MAS instances (O) +TSPC_MAP_2_3 False MCE: Message Uploading (O) +TSPC_MAP_2_3a False MCE: SetFolder (C6) +TSPC_MAP_2_3b False MCE: GetFoldersListing (C6) +TSPC_MAP_2_3c False MCE: PushMessage (C6) +TSPC_MAP_2_4 False MCE: Message Delete (O) +TSPC_MAP_2_4a False MCE: SetMessageStatus (C7) +TSPC_MAP_2_5 False MCE: Notification Registration (C2) +TSPC_MAP_2_5a False MCE: SetNotificationRegistration off (O) +TSPC_MAP_2_5b False MCE: SetNotificationRegistration on (C8) +TSPC_MAP_2_6 False MCE: Supported Message Types +TSPC_MAP_2_6a False (*) MCE: EMAIL (C3) +TSPC_MAP_2_6b False (*) MCE: SMS_GSM (C3) +TSPC_MAP_2_6c False (*) MCE: SMS_CDMA (C3) +TSPC_MAP_2_6d False (*) MCE: MMS (C3) +TSPC_MAP_2_7 False MCE: Instance Information (Not Supported) +TSPC_MAP_2_7a False (*) MCE: GetMASInstanceInformation (Not Supported) +TSPC_MAP_2_8 False MCE: Extended MAP-Event-Report (Not Supported) +TSPC_MAP_2_8a False (*) MCE: MAP-Event-Report: Version 1.1 + (Not Supported) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined features TSPC_MAP_2_1 or + TSPC_MAP_2_2. +C.2: Mandatory to support TSPC_MAP_2_5 if TSPC_MAP_2_1 is supported. +C.3: Mandatory to support at least one of the defined message types + TSPC_MAP_2_6a to TSPC_MAP_2_6d IF TSPC_MAP_2_2 or TSPC_MAP_2_3 is + supported. +C.4: Support of functionality TSPC_MAP_2_1a mandatory IF related feature + TSPC_MAP_2_1 supported. +C.5: Support of functionality mandatory IF TSPC_MAP_2_2 supported. +C.6: Support of functionality mandatory IF TSPC_MAP_2_3 supported. +C.7: Support of functionality mandatory IF TSPC_MAP_2_4 supported. +C.8: Mandatory to support IF TSPC_MAP_2_5 (Notification Registration) is + supported, otherwise excluded. +C.9: Optional to support IF TSPC_MAP_0_3 (MAP v1.2) is supported, otherwise + excluded. +C.10: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) and TSPC_MAP_2_1 + (Message Notification) is supported, otherwise excluded. +------------------------------------------------------------------------------- + + + Supported features MSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_3_1 True MSE: Message Notification (M) +TSPC_MAP_3_1a True MSE: SendEvent (M) +TSPC_MAP_3_2 True MSE: Message Browsing (M) +TSPC_MAP_3_2a True MSE: SetFolder (M) +TSPC_MAP_3_2b True MSE: GetFoldersListing (M) +TSPC_MAP_3_2c True MSE: GetMessagesListing (M) +TSPC_MAP_3_2d True MSE: GetMessage (M) +TSPC_MAP_3_2e True MSE: SetMessageStatus (M) +TSPC_MAP_3_2f True MSE: UpdateInbox (M) +TSPC_MAP_3_2g False MSE: Multiple simultaneous MAS instances (O) +TSPC_MAP_3_3 True MSE: Message Uploading (M) +TSPC_MAP_3_3a True MSE: SetFolder (M) +TSPC_MAP_3_3b True MSE: GetFoldersListing (M) +TSPC_MAP_3_3c True MSE: PushMessage (M) +TSPC_MAP_3_4 True MSE: Message Delete (M) +TSPC_MAP_3_4a True MSE: SetMessageStatus (M) +TSPC_MAP_3_5 True MSE: Notification Registration (M) +TSPC_MAP_3_5a True MSE: SetNotificationRegistration (M) +TSPC_MAP_3_6 False MSE: Supported Message Types +TSPC_MAP_3_6a False MSE: EMAIL (C1) +TSPC_MAP_3_6b True MSE: SMS_GSM (C1) +TSPC_MAP_3_6c False MSE: SMS_CDMA (C1) +TSPC_MAP_3_6d False (*) MSE: MMS (C1) +TSPC_MAP_3_7 False MSE: Instance Information (Not Supported) +TSPC_MAP_3_7a False (*) MSE: GetMASInstanceInformation (Not Supported) +TSPC_MAP_3_8 False MSE: Extended MAP-Event-Report (Not Supported) +TSPC_MAP_3_8a False (*) MSE: MAP-Event-Report: Version 1.1 + (Not Supported) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined message types + TSPC_MAP_3_6a to TSPC_MAP_3_6d IF TSPC_MAP_3_2 or TSPC_MAP_3_3 + is supported. +C.2: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) is supported, + otherwise excluded. +------------------------------------------------------------------------------- + + + GOEP v2.0 or later Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_7b_1 False GOEP v2.0 or later (C1) +TSPC_MAP_7b_2 False GOEP v2 Backwards Compatibility (C1) +TSPC_MAP_7b_3 False OBEX over L2CAP (C1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. +------------------------------------------------------------------------------- + + + MCE OBEX Header Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_10_1 False (*) Name (M) +TSPC_MAP_10_2 False (*) Typr (M) +TSPC_MAP_10_3 False (*) Body (M) +TSPC_MAP_10_4 False (*) End of Body (M) +TSPC_MAP_10_5 False (*) Target (M) +TSPC_MAP_10_6 False (*) Who (M) +TSPC_MAP_10_7 False (*) Connection ID (M) +TSPC_MAP_10_8 False (*) Application Parameters (M) +TSPC_MAP_10_9 False SRM (C2) +TSPC_MAP_10_10 False Receive SRMP (C2) +TSPC_MAP_10_11 False Send SRMP (C2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. +C.2: Optional if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. +------------------------------------------------------------------------------- + + + GetMessagesListing Filtering Parameter Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_20_1 False (*) MCE: FilterMessageType (O) +TSPC_MAP_20_2 False (*) MCE: FilterPeriodBegin (O) +TSPC_MAP_20_3 False (*) MCE: FilterPeriodEnd (O) +TSPC_MAP_20_4 False (*) MCE: FilterReadStatus (O) +TSPC_MAP_20_5 False (*) MCE: FilterRecipient (O) +TSPC_MAP_20_6 False (*) MCE: FilterOriginator (O) +TSPC_MAP_20_7 False (*) MCE: FilterPriority (O) +TSPC_ALL False (*) Turns on all the test cases +------------------------------------------------------------------------------- diff --git a/android/pics-mcap.txt b/android/pics-mcap.txt new file mode 100644 index 0000000..3b1086a --- /dev/null +++ b/android/pics-mcap.txt @@ -0,0 +1,141 @@ +MCAP PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Protocols +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_1_A_1 True (*) Supports Standard Op Codes (C.1) +TSPC_MCAP_1_A_2 False Supports Clock Synchronization Protocol (C.1) +------------------------------------------------------------------------------- +C.1: Support for at least one of the defined protocols is Mandatory. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_1_1 True (*) Supports Source Role (C.1) +TSPC_MCAP_1_2 True (*) Supports Sink Role (C.1) +TSPC_MCAP_1_3 False Supports Sync-Slave Role (C.2) +TSPC_MCAP_1_4 False Supports Sync-Master Role (C.3) +------------------------------------------------------------------------------- +C.1: If support for TSPC_MCAP_1_A_1 is supported, at least one of the + defined roles is Mandatory otherwise Excluded. +C.2: Mandatory if TSPC_MCAP_1_A_2 is supported, otherwise Excluded. +C.3: Optional if TSPC_MCAP_1_A_2 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + L2CAP Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_2_1 True (*) Supports L2CAP Control Channel (M) +TSPC_MCAP_2_2 True (*) Supports at least one L2CAP Data Channel (M) +TSPC_MCAP_2_3 True (*) Maximum number of simultaneous L2CAP Data + Channels supported (DCmax) per MCL (M) +TSPC_MCAP_2_4 False Can support multiple simultaneous MCLs with + Standard Op Codes (O) +------------------------------------------------------------------------------- + + + Connection Management - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_3_1 This row inentionally left blank +TSPC_MCAP_3_2 True (*) Initiate creation of Control and Data Channels + (C.1) +TSPC_MCAP_3_3 True (*) Accept creation of Control and Data Channels + (C.1) +TSPC_MCAP_3_4 True (*) Initiate Disconnection of MCL (M) +TSPC_MCAP_3_5 True (*) Accept Disconnection of MCL (M) +TSPC_MCAP_3_6 True (*) Initiate Disconnection of MDL (M) +TSPC_MCAP_3_7 True (*) Accept Disconnection of MDL (M) +TSPC_MCAP_3_8 False Initiate Reconnection of MDL (O) +TSPC_MCAP_3_9 False Accept Reconnection of MDL (C.2) +TSPC_MCAP_3_10 False Initiate Deletion of MDL (O) +TSPC_MCAP_3_11 True (*) Accept Deletion of MDL (M) +TSPC_MCAP_3_12 False Initiate Delete of All MDLs using 0xFFFF (O) +TSPC_MCAP_3_13 True (*) Accept Delete of All MDLs using 0xFFFF (M) +TSPC_MCAP_3_14 False Send MDL Abort request (O) +TSPC_MCAP_3_15 True (*) Accept MDL Abort request (M) +------------------------------------------------------------------------------- +C.1: Support for at least one of TSPC_MCAP_3_2 or TSPC_MCAP_3_3 is Mandatory. +C.2: Mandatory if TSPC_MCAP_3_3 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + L2CAP Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_4_1 True (*) Supports L2CAP Control Channel (M) +TSPC_MCAP_4_2 True (*) Supports at least one L2CAP Data Channel (M) +TSPC_MCAP_4_3 True (*) Maximum number of simultaneous L2CAP Data + Channels supported (DCmax) per MCL (M) +TSPC_MCAP_4_4 False Can support multiple simultaneous MCLs with + Standard Op Codes (O) +------------------------------------------------------------------------------- + + + Connection Management - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_5_1 This row intentionally left blank +TSPC_MCAP_5_2 True (*) Initiate creation of Control and Data Channels + (M) +TSPC_MCAP_5_3 True (*) Accept creation of Control and Data Channels + (M) +TSPC_MCAP_5_4 True (*) Initiate Disconnection of MCL (M) +TSPC_MCAP_5_5 True (*) Accept Disconnection of MCL (M) +TSPC_MCAP_5_6 True (*) Initiate Disconnection of MDL (M) +TSPC_MCAP_5_7 True (*) Accept Disconnection of MDL (M) +TSPC_MCAP_5_8 False Initiate Reconnection of MDL (O) +TSPC_MCAP_5_9 True (*) Accept Reconnection of MDL (M) +TSPC_MCAP_5_10 False Initiate Deletion of MDL (O) +TSPC_MCAP_5_11 True (*) Accept Deletion of MDL (M) +TSPC_MCAP_5_12 False Initiate Delete of All MDLs using 0xFFFF (O) +TSPC_MCAP_5_13 True (*) Accept Delete of All MDLs using 0xFFFF (M) +TSPC_MCAP_5_14 False Send MDL Abort request (O) +TSPC_MCAP_5_15 True (*) Accept MDL Abort request (M) +------------------------------------------------------------------------------- + + + Clock Synchronization Features - Sync-Slave +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_6_1 False (*) Accept MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ and + Initiate MD_SYNC_INFO_IND (C.1) +TSPC_MCAP_6_2 This row intentionally left blank +TSPC_MCAP_6_3 False Can support multiple simultaneous MCLs with CSP + (O) +TSPC_MCAP_6_4 False Can access Bluetooth Clock (O) +------------------------------------------------------------------------------- +C.1: Mandatory if MCAP TSPC_MCAP_1_A_2 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Clock Synchronization Features - Sync-Master +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_7_1 This row intentionally left blank +TSPC_MCAP_7_2 False (*) Initiate MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ + and Accept MD_SYNC_INFO_IND (C.1) +TSPC_MCAP_7_3 False Can support multiple simultaneous MCLs with CSP (O) +TSPC_MCAP_7_4 False (*) Can access Bluetooth Clock (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support IF TSPC_MCAP_1_A_2 is supported. +------------------------------------------------------------------------------- diff --git a/android/pics-mps.txt b/android/pics-mps.txt new file mode 100644 index 0000000..e12cad0 --- /dev/null +++ b/android/pics-mps.txt @@ -0,0 +1,339 @@ +MPS PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_0_1 True MPS v1.0 (M) +------------------------------------------------------------------------------- + + + Profile Version Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_1_1 True (*) A2DP 1.2 or later (O) +TSPC_MPS_1_2 True (*) AVRCP 1.3 or later (O) +TSPC_MPS_1_3 False DUN 1.1 or later (O) +TSPC_MPS_1_4 True (*) HFP 1.5 or later (O) +TSPC_MPS_1_5 False PAN 1.0 or later (O) +TSPC_MPS_1_6 False PBAP 1.1 or later (O) +------------------------------------------------------------------------------- + + + Profile Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_2_1 True (*) A2DP Source (SRC) (C.1) +TSPC_MPS_2_2 False A2DP Sink (SNK) (C.1) +TSPC_MPS_2_3 True (*) AVRCP Controller (CT) (C.1) +TSPC_MPS_2_4 True (*) AVRCP Target (TG) (C.1) +TSPC_MPS_2_5 False DUN Gateway (GW) (C.1) +TSPC_MPS_2_6 False DUN Data Terminal (DT) (C.1) +TSPC_MPS_2_7 True (*) HFP Audio Gateway (AG) (C.1) +TSPC_MPS_2_8 False HFP Hands-Free (HF) (C.1) +TSPC_MPS_2_9 False PAN Network Access Point (NAP) (C.1) +TSPC_MPS_2_10 False PAN Group Ad-hoc Network (GN) (C.1) +TSPC_MPS_2_11 False PAN User (PANU) (C.1) +TSPC_MPS_2_12 False PBAP PCE (C.1) +TSPC_MPS_2_13 False PBAP PSE (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to declare each role as supported within the represented Profile + otherwise Excluded. The roles declared shall match that of the roles + supported within the Profile. +------------------------------------------------------------------------------- + + + Profile Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_3_1 True (*) Receiving PASS THROUGH command in Category 1 + (AVRCP - TG) (C.1) +TSPC_MPS_3_2 True (*) Receiving PASS THROUGH command in Category 1 + (AVRCP - TG) - PAUSE (C.1) +TSPC_MPS_3_3 False Sending PASS THROUGH command in Category 1 + (AVRCP - CT) - PLAY (C.2) +TSPC_MPS_3_4 False Sending PASS THROUGH command in Category 1 + (AVRCP - CT) - PAUSE (C.2) +TSPC_MPS_3_5 True (*) Transfer Control - Suspend (GAVDP - Initiator) + (C.3) +TSPC_MPS_3_6 True (*) Transfer Control - Suspend (GAVDP - Acceptor) + (C.4) +TSPC_MPS_3_7 False Accept an incoming voice call (in-band ring) + (C.5) +TSPC_MPS_3_8 True (*) Accept an incoming voice call (no in-band ring) + (C.5) +TSPC_MPS_3_9 False Place a call with a phone number supplied by + the HF (C.6) +TSPC_MPS_3_10 True (*) Register Notification: PLAYBACK_STATUS_CHANGED + (C.7) +TSPC_MPS_3_11 False Ability to support parallel data and call + operation (O) +TSPC_MPS_3_12 False PBAP Phone Book Download (C.8) +TSPC_MPS_3_13 False Ability to support multiple concurrent device + connections (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_MPS_2_1 (A2DP Source role) and TSPC_MPS_2_4 (AVRCP + Target role) are supported, otherwise Excluded. +C.2: Mandatory if TSPC_MPS_2_2 (A2DP Sink role) and TSPC_MPS_2_3 (AVRCP + Controller role) are supported, otherwise Excluded. +C.3: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source + role) are supported; Optional if TSPC_MPS_1_4 (HFP 1.5 or later) and + TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise Excluded. +C.4: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source + role) or TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise + Excluded. +C.5: Mandatory to support at least one if TSPC_MPS_1_4 (HFP 1.5 or later) is + supported, otherwise Excluded. +C.6: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and HFP 3/8 (Place a call with + a phone number supplied by the HF) are supported, otherwise Excluded. +C.7: Mandatory if TSPC_MPS_2_3 (AVRCP Controller role) is supported, otherwise + Excluded. +C.8: Mandatory if TSPC_MPS_1_6 (PBAP 1.1 or later) and PBAP 2/1 (Phone Book + Download) are supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Device Capability Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_4_1 True Multiple Profiles Single Device (MPSD) (M) +TSPC_MPS_4_2 False Multiple Profiles Multiple Devices (MPMD) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_MPS_3_13 (Ability to support multiple concurrent device + connections), otherwise Excluded. +------------------------------------------------------------------------------- + + + MPSD scenarios +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_6_1 True (*) HFP-AG and A2DP-SRC Implementation Answer + Incoming Call during Audio Streaming (C.1) +TSPC_MPS_6_2 False HFP-HF and A2DP-SNK Implementation Answer + Incoming Call during Audio Streaming (C.2) +TSPC_MPS_6_3 True (*) HFP-AG and A2DP-SRC Implementation Outgoing + Call during Audio Streaming (C.1) +TSPC_MPS_6_4 False HFP-HF and A2DP-SNK Implementation Outgoing + Call during Audio Streaming (C.2) +TSPC_MPS_6_5 True (*) HFP-AG and A2DP-SRC Implementation Reject/Ignore + Incoming Call during Audio Streaming (C.1) +TSPC_MPS_6_6 False HFP-HF and A2DP-SNK Implementation Reject/Ignore + Incoming Call during Audio Streaming (C.2) +TSPC_MPS_6_7 True (*) HFP-AG and A2DP-SRC Implementation HFP Call + Termination during AVP Connection (C.1) +TSPC_MPS_6_8 False HFP-HF and A2DP-SNK Implementation HFP Call + Termination during AVP Connection (C.2) +TSPC_MPS_6_9 True (*) HFP-AG and A2DP-SRC Implementation Press Play + on Audio Player during Active Call (C.1) +TSPC_MPS_6_10 False HFP-HF and A2DP-SNK Implementation Press Play + on Audio Player during Active Call (C.2) +TSPC_MPS_6_11 True (*) HFP-AG and A2DP-SRC Implementation Start Audio + Streaming after AVRCP Play Command (C.1) +TSPC_MPS_6_12 False HFP-HF and A2DP-SNK Implementation Start Audio + Streaming after AVRCP Play Command (C.2) +TSPC_MPS_6_13 True (*) HFP-AG and A2DP-SRC Implementation Suspend Audio + Streaming after AVRCP Pause/Stop (C.1) +TSPC_MPS_6_14 False HFP-HF and A2DP-SNK Implementation Suspend Audio + Streaming after AVRCP Pause/Stop (C.2) +TSPC_MPS_6_15 False HFP-AG and DUN-GW Implementation Data + Communication under PSDM (DUN) during Active + Voice Call (C.3) +TSPC_MPS_6_16 False HFP-HF and DUN-DT Implementation Data + Communication under PSDM (DUN) during Active + Voice call (C.4) +TSPC_MPS_6_17 False HFP-AG and DUN-GW Implementation Outgoing Voice + Call during Data Communication under PSDM (DUN) + (C.3) +TSPC_MPS_6_18 False HFP-HF and DUN-DT Implementation Outgoing Voice + Call during Data Communication under PSDM (DUN) + (C.4) +TSPC_MPS_6_19 False HFP-AG and DUN-GW Implementation Incoming Voice + Call during Data Communication under PSDM (DUN) + (C.3) +TSPC_MPS_6_20 False HFP-HF and DUN-DT Implementation Incoming Voice + Call during Data Communication under PSDM (DUN) + (C.4) +TSPC_MPS_6_21 False A2DP-SRC and DUN-GW Implementation Start Audio + Streaming during Data Communication under PSDM + (DUN) (C.5) +TSPC_MPS_6_22 False A2DP-SNK and DUN-DT Implementation Start Audio + Streaming during Data Communication under PSDM + (DUN) (C.6) +TSPC_MPS_6_23 False A2DP-SRC and DUN-GW Implementation Data + Communication Establishment under PSDM (DUN) + during Audio Streaming (C.5) +TSPC_MPS_6_24 False A2DP-SNK and DUN-DT Implementation Data + Communication Establishment under PSDM (DUN) + during Audio Streaming (C.6) +TSPC_MPS_6_25 False HFP-AG and DUN-GW Implementation Terminate + Voice Call/Data Call during Data Communication + and Voice Call (C.5) +TSPC_MPS_6_26 False HFP-HF and DUN-DT Implementation Terminate + Voice Call/Data Call during Data Communication + and Voice Call (C.6) +TSPC_MPS_6_27 False HFP-AG and PAN-NAP Implementation Data + Communication in Personal Area Network during + Active Voice Call (C.7) +TSPC_MPS_6_28 False HFP-HF and PAN-PANU Implementation Data + Communication in Personal Area Network during + Active Voice Call (C.8) +TSPC_MPS_6_29 False HFP-AG and PAN-NAP Implementation Outgoing + Voice Call during Data Communication in Personal + Area Network (C.7) +TSPC_MPS_6_30 False HFP-HF and PAN-PANU Implementation Outgoing + Voice Call during Data Communication in Personal + Area Network (C.8) +TSPC_MPS_6_31 False HFP-AG and PAN-NAP Implementation Incoming Voice + Call during Data Communication in Personal Area + Network (C.7) +TSPC_MPS_6_32 False HFP-HF and PAN-PANU Implementation Incoming + Voice Call during Data Communication in Personal + Area Network (C.8) +TSPC_MPS_6_33 False A2DP-SRC and PAN-NAP Implementation Start Audio + Streaming during Data Communication in Personal + Area Network (C.9) +TSPC_MPS_6_34 False A2DP-SNK and PAN-PANU Implementation Start Audio + Streaming during Data Communication in Personal + Area Network (C.10) +TSPC_MPS_6_35 False A2DP-SRC and PAN-NAP Implementation Data + Communication Establishment in Personal Area + Network during Audio Streaming (C.9) +TSPC_MPS_6_36 False A2DP-SNK and PAN_PANU Implementation Data + Communication Establishment in Personal Area + Network during Audio Streaming (C.10) +TSPC_MPS_6_37 False A2DP-SRC_PBAP-Server Implementation Phonebook + Download during Audio Streaming (C.11) +TSPC_MPS_6_38 False A2DP-SNK and PBAP-Client Implementation + Phonebook Download during Audio Streaming (C.12) +TSPC_MPS_6_39 False HFP-AG and PBAP-Server Implementation PBAP and + HFP Connection Behavior (C.13) +------------------------------------------------------------------------------- +C.1: Mandatory if 2/1 (A2DP Source role), 2/4 (AVRCP Target role) and 2/7 + (HFP Audio Gateway role) are supported, otherwise Excluded. +C.2: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/8 + (HFP Hands-Free role) are supported, otherwise Excluded. +C.3: Mandatory if 2/5 (DUN Gateway role) and 2/7 (HFP Audio Gateway role) + are supported and 3/9 ( Ability to support parallel data and call + operation), otherwise Excluded. +C.4: Mandatory if 2/6 (DUN Data Terminal role) and 2/8 (HFP Hands-Free role) + are supported, otherwise Excluded. +C.5: Mandatory if 2/1 (A2DP Source role) and 2/5 (DUN Gateway role) are + supported, otherwise Excluded. +C.6: Mandatory if 2/2 (A2DP Sink role) and 2/6 (DUN Data Terminal role) are + supported, otherwise Excluded. +C.7: Mandatory if 2/7 (HFP Audio Gateway role) and 2/9 (PAN Network Access Point + role) and 3/11 (Ability to support parallel data and call operation) are + supported, otherwise Excluded. +C.8: Mandatory if 2/8 (HFP Hands-Free role) and 2/11 (PAN User role) are + supported and 3/11, otherwise Excluded. +C.9: Mandatory if 2/1 (A2DP Source role) and 2/9 (PAN Network Access Point role) + are supported, otherwise Excluded. +C.10: Mandatory if 2/2 (A2DP Sink role) and 2/11 (PAN User role) are supported, + otherwise Excluded. +C.11: Mandatory if 2/1 (A2DP Source role) and 2/13 (PBAP PSE role) are + supported, otherwise Excluded. +C.12: Mandatory if 2/2 (A2DP Sink role) and 2/12 (PBAP PCE role) are supported, + otherwise Excluded. +C.13: Mandatory if 2/7 (HFP Audio Gateway role) and 2/13 (PBAP PSE role) are + supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + MPMD Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_7_1 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + Answer Incoming Call during Audio Streaming + (C.1) +TSPC_MPS_7_2 True (*) A2DP-SRC and AVRCP-TG Implementation Answer + Incoming Call during Audio Streaming (C.2) +TSPC_MPS_7_3 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + Outgoing Call during Audio Streaming (C.1) +TSPC_MPS_7_4 True (*) A2DP-SRC and AVRCP-TG Implementation Outgoing + Call during Audio Streaming (C.2) +TSPC_MPS_7_5 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + Reject/Ignore Incoming Call during Audio + Streaming (C.1) +TSPC_MPS_7_6 True (*) A2DP-SRC and AVRCP-TG Implementation + Reject/Ignore Incoming Call during Audio + Streaming (C.2) +TSPC_MPS_7_7 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + HFP Call Termination during AVP Connection (C.1) +TSPC_MPS_7_8 True (*) A2DP-SRC and AVRCP-TG Implementation HFP Call + Termination during AVP Connection (C.2) +TSPC_MPS_7_9 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + Press Play on Audio Player during Active Call + (C.1) +TSPC_MPS_7_10 True (*) A2DP-SRC and AVRCP-TG Implementation Press Play + on Audio Player during Active Call (C.2) +TSPC_MPS_7_11 True (*) A2DP-SRC and AVRCP-TG Implementation Start Audio + Streaming during Data Communication under PSDM + (C.2) +TSPC_MPS_7_12 False A2DP-SNK and AVRCP-CT and DUN-DT Implementation + Start Audio Streaming during Data Communication + under PSDM (C.3) +TSPC_MPS_7_13 True (*) A2DP-SRC and AVRCP-TG Implementation Start + Packet Data Communication during Audio Streaming + (C.2) +TSPC_MPS_7_14 False A2DP-SNK and AVRCP-CT and DUN-DT Implementation + Start Packet Data Communication during Audio + Streaming (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/8 + (HFP Hands-Free role) are supported, otherwise Excluded. +C.2: Mandatory if 2/1 (A2DP Source role) and 2/4 (AVRCP Target role) are + supported, otherwise Excluded. +C.3: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/6 + (DUN Data Terminal role) supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + MPS Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_8_1 True (*) AVP Suspension (C.1) +TSPC_MPS_8_2 True (*) Profile (Dis-)Connection behavior (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if 1/1 (A2DP 1.2 or later) and 1/2 (AVRCP 1.3 or later) are + supported, otherwise Excluded. +C.2: Mandatory if 1/1 (A2DP 1.2 or later), 1/2 (AVRCP 1.3 or later) and 1/4 (HFP + 1.5 or later) are supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + MPS Dependencies +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_9_1 True Implements Bluetooth Core Specification v2.1 + + EDR or later (M) +------------------------------------------------------------------------------- + + + MPS Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_10_1 True SDP Record (M) +TSPC_MPS_10_2 True (*) Media Stream Suspension (C.1) +TSPC_MPS_10_3 True (*) Sniff Mode during Streaming (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if 1/1 (A2DP1.2 or later) and 1/4 (HFP 1.5 or later) are + supported, otherwise Excluded. +C.2: Mandatory if 1/1 (A2DP 1.2 or later) is supported, otherwise Excluded. +------------------------------------------------------------------------------- diff --git a/android/pics-opp.txt b/android/pics-opp.txt index 11e61ff..0a5919e 100644 --- a/android/pics-opp.txt +++ b/android/pics-opp.txt @@ -1,5 +1,7 @@ OPP PICS for the PTS tool. +PTS version: 5.1 + * - different than PTS defaults # - not yet implemented/supported @@ -10,10 +12,10 @@ O - optional ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_OPP_1_1 True (*) Role: Object Push Client. -TSPC_OPP_1_2 True (*) Role: Object Push Server. +TSPC_OPP_1_1 True (*) Role: Object Push Client (C.1) +TSPC_OPP_1_2 True (*) Role: Object Push Server (C.1) ------------------------------------------------------------------------------- -C.1: It is Mandatory to Support at least one of the defined roles. +C.1: Mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- @@ -32,23 +34,18 @@ C.1: It is mandatory to support at least one of the profile versions. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_OPP_2_1 True Client: Perform Service Discovery request. - (M.1) +TSPC_OPP_2_1 True Client: Perform Service Discovery request (M) TSPC_OPP_2_2 True Client: Authentication/PIN exchange supported. - (M.1) + (M) TSPC_OPP_2_2a True (*) Client: Require Authentication/PIN by default. (O) -TSPC_OPP_2_3 True Client: Object Push is supported. (M.1) -TSPC_OPP_2_4 True (*) Client: vCard 2.1 format is supported for - Object Push. (C.3) -TSPC_OPP_2_5 False Client: vCalender 1.0 format is supported for - Object Push. (O) -TSPC_OPP_2_6 False Client: vMsg as defined in IrMC 1.1 is supported - for Object Push. (O) -TSPC_OPP_2_7 False Client: vNote as defined in IrMC 1.1 is - supported for Object Push. (O) +TSPC_OPP_2_3 True Client: Object Push (M) +TSPC_OPP_2_4 True (*) Client: vCard 2.1 (C.3) +TSPC_OPP_2_5 False Client: vCalender 1.0 (O) +TSPC_OPP_2_6 False Client: vMsg as defined in IrMC 1.1 (O) +TSPC_OPP_2_7 False Client: vNote as defined in IrMC 1.1 (O) TSPC_OPP_2_8 True (*) Client: Support content formats other than those - declared in TSPC_OPP_2_44 through + declared in TSPC_OPP_2_4 through TSPC_OPP_2_7. (O) TSPC_OPP_2_8a False Client: Support specific set of other content formats. (C.4) @@ -59,10 +56,10 @@ TSPC_OPP_2_9a False Client: Push multiple vCard objects using TSPC_OPP_2_9b True (*) Client: Push multiple vCard objects using the same PUT operation. (C.5) TSPC_OPP_2_10 False Client: Push multiple vCalender objects. (O) -TSPC_OPP_2_10a False Client: Push multiple vMsg objects using +TSPC_OPP_2_10a False Client: Push multiple vCalendar objects using different PUT operations. (C.6) -TSPC_OPP_2_10b False Client: Push multiple vMsg objects using the - same PUT operation. (C.6) +TSPC_OPP_2_10b False Client: Push multiple vCalendar objects using + the same PUT operation. (C.6) TSPC_OPP_2_11 False Client: Push multiple vMsg objects. (O) TSPC_OPP_2_11a False Client: Push multiple vMsg objects using different PUT operations. (C.7) @@ -73,17 +70,21 @@ TSPC_OPP_2_12a False Client: Push multiple vNote objects using different PUT operations. (C.8) TSPC_OPP_2_12b False Client: Push multiple vNote objects using the same PUT operation. (C.8) -TSPC_OPP_2_13 False Client: Pull business card is supported. (O) -TSPC_OPP_2_14 False Client: vCard 2.1 format is supported for - Business Card Pull. (C.1) -TSPC_OPP_2_15 False Client: Exchange business card is supported. (O) -TSPC_OPP_2_16 False Client: vCard 2.1 format is supported for - Business Card Exchange (C.2) +TSPC_OPP_2_13 False Client: Pull business card (O) +TSPC_OPP_2_14 False Client: vCard 2.1 (C.1) +TSPC_OPP_2_15 False Client: Exchange business card (O) +TSPC_OPP_2_16 False Client: vCard 2.1 (C.2) +TSPC_OPP_2_17 False GOEP v2 (C.9) +TSPC_OPP_2_18 False GOEP v2 Backward Compability (C.9) +TSPC_OPP_2_19 False OBEX over L2CAP (C.9) +TSPC_OPP_2_20 False OBEX Reliable Session (C.10) +TSPC_OPP_2_21 False OBEX SRM (C.10) +TSPC_OPP_2_22 False Send OBEX SRMP header (C.10) +TSPC_OPP_2_23 False Receive OBEX SRMP header (C.11) ------------------------------------------------------------------------------- C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported. C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is supported. -M.1: Mandatory to Support IF (TSPC_OPP_1_1) supported. C.3: vCard 2.1 support is required for devices containing phonebook applications. vCard 2.1 support optional for other devices. C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8 @@ -96,6 +97,9 @@ C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if TSPC_OPP_2_11 is supported. Otherwise, both items are excluded. C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if TSPC_OPP_2_12 is supported. Otherwise, both items are excluded. +C.9: Mandatory if TSPC_OPP_1b_2 supported. +C.10: Optional to support if TSPC_OPP_1b_2 supported else excluded. +C.11: Mandatory if TSPC_OPP_17 and TSPC_OPP_21 supported else excluded. ------------------------------------------------------------------------------- @@ -119,17 +123,13 @@ TSPC_OPP_3_1 True Server: Provide information on supported request. (M) TSPC_OPP_3_2 True Server: Authentication/PIN exchange supported. (M) -TSPC_OPP_3_3 True Server: Object Push is supported. (M) +TSPC_OPP_3_3 True Server: Object Push (M) TSPC_OPP_3_3a True (*) Server: Receive multiple objects in the same PUT operation. (O) -TSPC_OPP_3_4 True (*) Server: vCard 2.1 format is supported for Object - Push. (C.3) -TSPC_OPP_3_5 False Server: vCalender 1.0 format is supported for - Object Push. (O) -TSPC_OPP_3_6 False Server: vMsg as defined in IrMC 1.1 is supported - for Object Push. (O) -TSPC_OPP_3_7 False Server: vNote as defined in IrMC 1.1 is - supported for Object Push. (O) +TSPC_OPP_3_4 True (*) Server: vCard 2.1 (C.3) +TSPC_OPP_3_5 False Server: vCalender 1.0 format (O) +TSPC_OPP_3_6 False Server: vMsg as defined in IrMC 1.1 (O) +TSPC_OPP_3_7 False Server: vNote as defined in IrMC 1.1 (O) TSPC_OPP_3_8 True (*) Server: Support content formats other than those declared in TSPC_OPP_3_4 through TSPC_OPP_3_7. (O) @@ -137,31 +137,37 @@ TSPC_OPP_3_8a False Server: Support specific set of other content formats. (C.4) TSPC_OPP_3_8b True (*) Server: Support all content formats. (C.4) TSPC_OPP_3_9 True (*) Server: Object Push vCard reject. (O) -TSPC_OPP_3_10 False Server: Object Push vCal rejectt. (O) +TSPC_OPP_3_10 False Server: Object Push vCal reject. (O) TSPC_OPP_3_11 False Server: Object Push vMsg reject. (O) TSPC_OPP_3_12 False Server: Object Push vNote reject. (O) -TSPC_OPP_3_13 False Server: Business card pull is supported. (O.1) -TSPC_OPP_3_14 False Server: vCard 2.1 format is supported for - Business Card Pull. (C.1) +TSPC_OPP_3_13 False Server: Business card pull (O.1) +TSPC_OPP_3_14 False Server: vCard 2.1 (C.1) TSPC_OPP_3_15 False Server: Business card pull reject. (O) -TSPC_OPP_3_16 False Server: Business card exchange is supported. - (O.2) -TSPC_OPP_3_17 False Server: vCard 2.1 format is supported for - Business Card Exchange (C.2) +TSPC_OPP_3_16 False Server: Business card exchange (O.2) +TSPC_OPP_3_17 False Server: vCard 2.1 (C.2) TSPC_OPP_3_18 False Server: Business card exchange reject. (O) +TSPC_OPP_3_19 False GOEP v2 (C.5) +TSPC_OPP_3_20 False GOEP v2 Backward Compability (C.5) +TSPC_OPP_3_21 False OBEX over L2CAP (C.5) +TSPC_OPP_3_22 False OBEX Reliable Session (C.16) +TSPC_OPP_3_23 False OBEX SRM (C.6) +TSPC_OPP_3_24 False Send OBEX SRMP header (C.6) +TSPC_OPP_3_25 False Receive OBEX SRMP header (C.7) ------------------------------------------------------------------------------- -M.1: Mandatory to Support IF (TSPC_OPP_1_2) supported. -C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is - supported. O.1: IF NOT Supported, an error message must be sent on request for Business Card Pull. O.2: IF NOT Supported, an error message must be sent on request for Business Card Exchange. C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported. +C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is + supported. C.3: vCard 2.1 support is required for devices containing phonebook applications. vCard 2.1 support optional for other devices. C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8 is supported. Otherwise, both items are excluded. +C.5: Mandatory if TSPC_OPP_2b_2 supported. +C.6: Optional to support if TSPC_OPP_2b_2 supported, else excluded. +C.7: Mandatory if TSPC_OPP_3_19 and TSPC_OPP_3_23 supported else excluded. ------------------------------------------------------------------------------- @@ -169,21 +175,13 @@ C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8 ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_OPP_4_1 False Abort-Push Operation is supported. (O) -TSPC_OPP_4_2 False Disconnect of OBEX session should be tested. +TSPC_OPP_4_1 False Abort-Push Operation (O) +TSPC_OPP_4_2 False Intentionally Left Blank (N/A) TSPC_OPP_4_3 False Multiple vCards transferred as a single vObject - is supported. (C.1) -TSPC_OPP_4_4 False Multiple vCards transfer is supported. (C.1) -TSPC_OPP_4_5 False vCards with multiple Phone Number Fields is - supported. (C.1) -TSPC_OPP_4_6 False Server supports Push vCal to Different Time - Zone. (C.1) -TSPC_ALL False Turn on all test cases. + (C.1) +TSPC_OPP_4_4 False Multiple vCards transfer (C.1) +TSPC_OPP_4_5 False vCards with multiple Phone Number Fields (C.1) +TSPC_OPP_4_6 False Push vCal to Different Time Zone Server (C.1) ------------------------------------------------------------------------------- C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded. ------------------------------------------------------------------------------- - - -------------------------------------------------------------------------------- -No special PIXIT settings required. All should be set according to Tester's -test environment. diff --git a/android/pics-pan.txt b/android/pics-pan.txt index 406b6a9..f78fc10 100644 --- a/android/pics-pan.txt +++ b/android/pics-pan.txt @@ -1,5 +1,7 @@ PAN PICS for the PTS tool. +PTS version: 5.1 + * - different than PTS defaults # - not yet implemented/supported @@ -10,14 +12,14 @@ O - optional ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_PAN_1_1 True (*#) Role: Network Access Point (O.1) +TSPC_PAN_1_1 True (*) Role: Network Access Point (O.1) TSPC_PAN_1_2 False Role: Group Ad-hoc Network (O.1) -TSPC_PAN_1_3 True (*#) Role: PAN User (O.1) -TSPC_PAN_1a_1 True (#) BNEP: BNEP Connection Setup (M) -TSPC_PAN_1a_2 True (#) BNEP: BNEP Data Packet Reception (M) -TSPC_PAN_1a_3 True (#) BNEP: BNEP Data Packet Transmission (M) -TSPC_PAN_1a_4 True (#) BNEP: BNEP Control Message Processing (M) -TSPC_PAN_1a_5 True (#) BNEP: BNEP Extension Header Processing (M) +TSPC_PAN_1_3 True (*) Role: PAN User (O.1) +TSPC_PAN_1a_1 True BNEP: BNEP Connection Setup (M) +TSPC_PAN_1a_2 True BNEP: BNEP Data Packet Reception (M) +TSPC_PAN_1a_3 True BNEP: BNEP Data Packet Transmission (M) +TSPC_PAN_1a_4 True BNEP: BNEP Control Message Processing (M) +TSPC_PAN_1a_5 True BNEP: BNEP Extension Header Processing (M) TSPC_PAN_1a_6 False BNEP: Network Protocol Filter Message Transmission (O) TSPC_PAN_1a_7 False BNEP: Multicast Address Filter Message @@ -31,21 +33,21 @@ O.1: It is mandatory to support at least one of the defined roles. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_PAN_2_1 True (#) NAP: Support BNEP (M) -TSPC_PAN_2_2 True (#) NAP: Support BNEP Forwarding (M) +TSPC_PAN_2_1 True NAP: Support BNEP (M) +TSPC_PAN_2_2 True NAP: Support BNEP Forwarding (M) TSPC_PAN_2_3 False NAP: Support Layer 2-Bridging between PAN and External Network (C.1) -TSPC_PAN_2_4 True (*#) NAP: Support IP forwarding between PAN and +TSPC_PAN_2_4 True (*) NAP: Support IP forwarding between PAN and External Network (C.1) TSPC_PAN_2_5 False NAP: Support BNEP Packet Filtering (O) -TSPC_PAN_2_6 True (*#) NAP: Support IPv4 (C.2) -TSPC_PAN_2_6a True (*#) NAP: Supports operable routable IPv4 address (O) +TSPC_PAN_2_6 False NAP: Support IPv4 (C.2) +TSPC_PAN_2_6a False NAP: Supports operable routable IPv4 address (O) TSPC_PAN_2_6b False NAP: Support link-local address configuration for IPv4 (C.4) TSPC_PAN_2_7 False NAP: Support ping client for IPv4 (O) TSPC_PAN_2_8 False NAP: Support DHCP Client for IPv4 (O) TSPC_PAN_2_9 False NAP: Support DNS/LLMNR Resolver for IPv4 (O) -TSPC_PAN_2_9a True (#) NAP: Support LLMNR Sender for IPv4 (C.5) +TSPC_PAN_2_9a False NAP: Support LLMNR Sender for IPv4 (C.5) TSPC_PAN_2_9b False NAP: Support LLMNR Responder for IPv4 (O) TSPC_PAN_2_10 False NAP: Support HTTP Client for IPv4 (O) TSPC_PAN_2_11 False NAP: Support WAP Client for IPv4 (O) @@ -56,8 +58,8 @@ TSPC_PAN_2_14a False (*) NAP: Support LLMNR Sender for IPv6 (C.6) TSPC_PAN_2_14b False NAP: Support LLMNR Responder for IPv6 (O) TSPC_PAN_2_15 False NAP: Support HTTP Client for IPv6 (O) TSPC_PAN_2_16 False NAP: Support WAP Client for IPv6 (O) -TSPC_PAN_2_17 True (#) NAP: Supports Connectable Mode (M) -TSPC_PAN_2_18 True (#) NAP: NAP Service Record (M) +TSPC_PAN_2_17 True NAP: Supports Connectable Mode (M) +TSPC_PAN_2_18 True NAP: NAP Service Record (M) TSPC_PAN_2_19 False NAP: Support at least three PANUs (O) TSPC_PAN_2_20 False NAP: Support at least two PANUs (O) ------------------------------------------------------------------------------- @@ -71,7 +73,7 @@ C.3: Mandatory to support IF any IPv6-based transport protocol OR (TSPC_PAN_2_13-16) is supported, ELSE Optional. C.4: Mandatory if TSPC_PAN_2_6 is supported and TSPC_PAN_2_6a is not supported, otherwise optional. -C.5: Mandatory if item (TSPC_PAN_2_6) or item supported. +C.5: Mandatory if item (TSPC_PAN_2_6) supported. C.6: Mandatory if item (TSPC_PAN_2_12) supported ------------------------------------------------------------------------------- @@ -117,12 +119,12 @@ C.4: Mandatory to support if (TSPC_PAN_3_10) is supported. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_PAN_4_1 True (#) PANU: Support BNEP (M) -TSPC_PAN_4_2 True (*#) PANU: Support IPv4 (C.1) -TSPC_PAN_4_3 True (*#) PANU: Support ping client for IPv4 (O) -TSPC_PAN_4_4 True (*#) PANU: Support DHCP client for IPv4 (O) +TSPC_PAN_4_1 True PANU: Support BNEP (M) +TSPC_PAN_4_2 True (*) PANU: Support IPv4 (C.1) +TSPC_PAN_4_3 False PANU: Support ping client for IPv4 (O) +TSPC_PAN_4_4 False PANU: Support DHCP client for IPv4 (O) TSPC_PAN_4_5 False PANU: Support DNS/LLMNR Resolver for IPv4 (O) -TSPC_PAN_4_5a True (#) PANU: Support LLMNR Sender for IPv4 (C.2) +TSPC_PAN_4_5a True PANU: Support LLMNR Sender for IPv4 (C.2) TSPC_PAN_4_5b False PANU: Support LLMNR Responder for IPv4 (O) TSPC_PAN_4_6 False PANU: Support HTTP Client for IPv4 (O) TSPC_PAN_4_7 False PANU: Support WAP Client for IPv4 (O) @@ -144,8 +146,3 @@ C.1: PAN User devices must support at least One of items (TSPC_PAN_4_2) or C.2: Mandatory to support if (TSPC_PAN_4_2) is supported. C.3: Mandatory to support if (TSPC_PAN_4_8) is supported. ------------------------------------------------------------------------------- - - -------------------------------------------------------------------------------- -No special PIXIT settings required. All should be set according to Tester's -test environment. diff --git a/android/pics-pbap.txt b/android/pics-pbap.txt index 2af5fd8..f75a972 100644 --- a/android/pics-pbap.txt +++ b/android/pics-pbap.txt @@ -1,5 +1,7 @@ PBAP PICS for the PTS tool. +PTS version: 5.1 + * - different than PTS defaults # - not yet implemented/supported @@ -12,8 +14,7 @@ Parameter Name Selected Description ------------------------------------------------------------------------------- TSPC_PBAP_0_1 False Role: PBAP 1.0 (C.1) TSPC_PBAP_0_2 True (*) Role: PBAP 1.1 (C.1) -TSPC_PBAP_0_3 False Role: Reserve -TSPC_PBAP_0_4 False (*) Role: PBAP 1.2 (C.1) +TSPC_PBAP_0_3 False Role: PBAP 1.2 (C.1) ------------------------------------------------------------------------------- C.1: Mandatory to support one and only one major profile version. ------------------------------------------------------------------------------- @@ -116,7 +117,7 @@ Parameter Name Selected Description TSPC_PBAP_6_1 False (*) PCE: Connect (M) TSPC_PBAP_6_2 False (*) PCE: Disconnect (M) TSPC_PBAP_6_3 False (*) PCE: Get (M) -TSPC_PBAP_6_4 False (*) PCE: Abort (M) +TSPC_PBAP_6_4 False PCE: Abort (O) TSPC_PBAP_6_5 False (*) PCE: SetPath (C.1) TSPC_PBAP_6_6 False PCE: Support for OBEX authentication initiation (C.2) @@ -195,11 +196,11 @@ TSPC_PBAP_9_10a False PSE: Referencing Contacts (C.3) TSPC_PBAP_9_12 False PSE: Contact Image Default Format (C.1) TSPC_PBAP_9_12a False PSE: Able to request Contact Images (C.2) TSPC_PBAP_9_13 False PSE: Supported Phonebook Objects -TSPC_PBAP_9_13a False (*) PSE: Telecom/pb (M) -TSPC_PBAP_9_13b False PSE: Telecom/ich (O) -TSPC_PBAP_9_13c False PSE: Telecom/och (O) -TSPC_PBAP_9_13d False PSE: Telecom/mch (O) -TSPC_PBAP_9_13e False (*) PSE: Telecom/cch (O) +TSPC_PBAP_9_13a True PSE: Telecom/pb (M) +TSPC_PBAP_9_13b True (*) PSE: Telecom/ich (O) +TSPC_PBAP_9_13c True (*) PSE: Telecom/och (O) +TSPC_PBAP_9_13d True (*) PSE: Telecom/mch (O) +TSPC_PBAP_9_13e True PSE: Telecom/cch (O) TSPC_PBAP_9_13f False PSE: Telecom/spd (C.2) TSPC_PBAP_9_13g False PSE: Telecom/fav (C.2) TSPC_PBAP_9_13h False (*) PSE: SIM1/Telecom/pb (O) @@ -353,7 +354,7 @@ TSPC_PBAP_19_2 True PSE: Initiate LMP-Authentication (M) TSPC_PBAP_19_3 False PSE: Security mode 1 (C.2) TSPC_PBAP_19_4 False PSE: Security mode 2 (C.1) TSPC_PBAP_19_5 False PSE: Security mode 3 (C.1) -TSPC_PBAP_19_6 False PSE: Security mode 4 (C.1) +TSPC_PBAP_19_6 True (*) PSE: Security mode 4 (C.1) ------------------------------------------------------------------------------- C.1: At least one of TSPC_PBAP_19_3, TSPC_PBAP_19_4, TSPC_PBAP_19_5 or TSPC_PBAP_19_6 (security mode 2, 3, or 4) shall be supported. @@ -413,7 +414,7 @@ C.3: Optional if TSPC_PBAP_10_2 is supported, otherwise excluded. ------------------------------------------------------------------------------- Parameter Name Selected Description ------------------------------------------------------------------------------- -TSPC_PBAP_25_1 False (*) PCE: GOEP v2.0 or later (M) +TSPC_PBAP_25_1 False PCE: GOEP v2.0 or later (M) TSPC_PBAP_25_2 False (*) PCE: GOEP v2 Backwards Compatibility (M) TSPC_PBAP_25_3 False PCE: OBEX over L2CAP (M) TSPC_PBAP_25_4 False PCE: OBEX SRM (M) @@ -435,12 +436,7 @@ TSPC_PBAP_26_3 False PSE: OBEX over L2CAP (M) TSPC_PBAP_26_4 False PSE: OBEX SRM (M) TSPC_PBAP_26_5 False (*) PSE: Send OBEX SRMP header (C.1) TSPC_PBAP_26_6 False PSE: Receive OBEX SRMP header (M) -TSPC_ALL False (*) Turns on all the test cases +TSPC_ALL False Turns on all the test cases ------------------------------------------------------------------------------- C.1: Optional if TSPC_PBAP_26_4 (OBEX SRM) is supported, otherwise Excluded. ------------------------------------------------------------------------------- - - -------------------------------------------------------------------------------- -No special PIXIT settings required. All should be set according to Tester's -test environment. diff --git a/android/pics-rfcomm.txt b/android/pics-rfcomm.txt new file mode 100644 index 0000000..09cea18 --- /dev/null +++ b/android/pics-rfcomm.txt @@ -0,0 +1,54 @@ + +* - different than BITE defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + + Protocol Version +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +F.1.0.1 False RFCOMM 1.1 with TS 07.10 (C.1) +F.1.0.2 True RFCOMM 1.2 with TS 07.10 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support one and only one of the protocol versions. +------------------------------------------------------------------------------- + + + Supported Procedures +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +F.1.1.1 True Initialize RFCOMM Session (C.2) +F.1.1.2 True Respond to Initialization of an RFCOMM + Session (C.1) +F.1.1.3 True Shutdown RFCOMM Session (M) +F.1.1.4 True Respond to a Shutdown of an RFCOMM Session (M) +F.1.1.5 True Establish DLC (C.2) +F.1.1.6 True Respond to Establishment of a DLC (C.1) +F.1.1.7 True Disconnect DLC (M) +F.1.1.8 True Respond to Disconnection of a DLC (M) +F.1.1.9 True Respond to and send MSC Command (M) +F.1.1.10 True Initiate Transfer Information (M) +F.1.1.11 True Respond to Test Command (M) +F.1.1.12 True Send Test Command (O) +F.1.1.13 True React to Aggregate Flow Control (M) +F.1.1.14 True Respond to RLS Command (M) +F.1.1.15 False (*) Send RLS Command (O) +F.1.1.16 True Respond to PN Command (M) +F.1.1.17 True Send PN Command (C.3) +F.1.1.18 True Send Non-Supported Command (NSC) response (C.4) +F.1.1.19 True Respond to RPN Command (M) +F.1.1.18 Send RPN Command (O) +F.1.1.21 True Closing Multiplexer by First Sending a DISC + Command (O) +F.1.1.22 True Support of Credit Based Flow Control (M) +F.1.1.23 True IUT Responds to Establishment of a DLC (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if SPP 1/2 (Device B) is supported, otherwise Excluded. +C.2: Mandatory if SPP 1/1 (Device A) is supported, otherwise Excluded. +C.3: Mandatory if SPP 1/1 (Device A) is supported, otherwise Optional +C.4: Mandatory to support if 0/2 (RFCOMM 1.2) is supported, otherwise Optional. +------------------------------------------------------------------------------- diff --git a/android/pics-sdp.txt b/android/pics-sdp.txt new file mode 100644 index 0000000..cb98130 --- /dev/null +++ b/android/pics-sdp.txt @@ -0,0 +1,137 @@ + +* - different than BITE defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + + Support Different Size Capabilities on UUID +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.1.1 True Support for 128 bit UUID (M) +E.1.2 True Support for 32 bit UUID (M) +E.1.3 True Support for 16 bit UUID (M) +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.1b.1 True Support for server role (C.1) +E.1b.2 True Support for client role (C.1) +------------------------------------------------------------------------------- +C.1 Mandatory to support at least one of the roles +------------------------------------------------------------------------------- + + + Valid Service Search Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.2.1 True Support for respond on search of single + Service, using ServiceSearchRequest (C.2) +E.2.2 True Support for respond on search of Service, + using continuation state (O) +E.2.3 True Search for services using the continuation + state (C.1) +------------------------------------------------------------------------------- +C.1 Mandatory to support if the client role is supported (1b/2) +C.2 Mandatory to support if the server role is supported (1b/1) +------------------------------------------------------------------------------- + + + Invalid Service Search Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.3.1 True Support for error response on Service search + request (M) +------------------------------------------------------------------------------- + + + Valid Service Attribute Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.4.1 True Support for respond on search of + Attribute(s) (M) +E.4.2 True Support for respond on search of + Attribute, using continuation state (O) +E.4.3 True Support for respond on search on + attribute AdditionalProtocolDescriptorList (O) +------------------------------------------------------------------------------- + + + Invalid Service Attribute Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.5.1 True Support for error response on Attribute + search request (M) +------------------------------------------------------------------------------- + + + Valid Service Search Attribute Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.6.1 True Support for respond on search for Service(s) + and Attribute(s) (M) +E.6.2 True Support for respond on search of Attribute, + using continuation state (O) +E.6.3 True Support for respond on search on attribute + AdditionalProtocolDescriptorList on existing + service (O) +------------------------------------------------------------------------------- + + + Invalid Service Search Attribute Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.7.1 True Support for error response on Service and + Attribute request (M) +------------------------------------------------------------------------------- + + + Service Browsing +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.8.1 True Support for browsing, using + SDP_ServiceSearchRequest and + SDP_ServiceAttributeRequest (O) +E.8.2 True Support for browsing, using + SDP_ServiceSearchAttributeRequest (O) +------------------------------------------------------------------------------- + + + Attributes Present in IUT +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.9.1 True ServiceID (O) +E.9.2 True ProtocolDescriptorList (O) +E.9.3 True ServiceRecordState (O) +E.9.4 True ServiceInfoTimeToLive (O) +E.9.5 True BrowseGroupList (O) +E.9.6 True LanguageBaseAttributeIdList (O) +E.9.7 True ServiceAvailability (O) +E.9.8 True IconURL (O) +E.9.9 True ServiceName (O) +E.9.10 True ServiceDescription (O) +E.9.11 True ProviderName (O) +E.9.12 True VersionNumberList (O) +E.9.13 True ServiceDataBaseState (O) +E.9.14 True BluetoothProfileDescriptorList (O) +E.9.15 True DocumentationURL (O) +E.9.16 True ClientExecutableURL (O) +E.9.17 True AdditionalProtocolDescriptorList (C.1) +E.9.18 True ServiceRecordHandle (M) +E.9.19 True ServiceClassIDList (M) +------------------------------------------------------------------------------- +C.1: Optional if 9/2 is supported, otherwise excluded +------------------------------------------------------------------------------- diff --git a/android/pics-sm.txt b/android/pics-sm.txt new file mode 100644 index 0000000..3060d40 --- /dev/null +++ b/android/pics-sm.txt @@ -0,0 +1,91 @@ +SM PICS for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults + +M - mandatory +O - optional + + Connection Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_1_1 True Master Role (Initiator) (C.1) +TSPC_SM_1_2 True Slave Role (Responder) (C.1) +------------------------------------------------------------------------------- +C.1: At least one of these features shall be supported. +------------------------------------------------------------------------------- + + + Security Properties +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_2_1 True Authenticated MITM protection (O) +TSPC_SM_2_2 True Unauthenticated no MITM protection (C.1) +TSPC_SM_2_3 True No security requirements (M) +TSPC_SM_2_4 True OOB supported (O) +------------------------------------------------------------------------------- +C.1: If TSPC_SM_2_1 is supported then Mandatory, else Optional +------------------------------------------------------------------------------- + + + Encryption Key Size +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_3_1 True Encryption Key Size Negotiation (M) +------------------------------------------------------------------------------- + + + Pairing Method +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_4_1 True Just Works (O) +TSPC_SM_4_2 True Passkey Entry (C.1) +TSPC_SM_4_3 True Out of Band (C.1) +------------------------------------------------------------------------------- +C.1: If TSPC_SM_2_1 is supported, at least one of these features shall be + supported. +------------------------------------------------------------------------------- + + + Security Initiation +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_5_1 True Encryption Setup using STK (C.3) +TSPC_SM_5_2 True Encryption Setup using LTK (O) +TSPC_SM_5_3 True Slave Initiated Security (C.1) +TSPC_SM_5_4 True Slave Initiated Security – Master response(C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_SM_1_2 is supported, otherwise Excluded +C.2: Mandatory if TSPC_SM_1_1 is supported, otherwise Excluded +C.3: Mandatory IF TSPC_SM_2_1 OR TSPC_SM_2_1 OR TSPC_SM_2_4 is supported, + otherwise Excluded +------------------------------------------------------------------------------- + + + Signing Algorithm +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_6_1 True Signing Algorithm - Generation (O) +TSPC_SM_6_2 True Signing Algorithm - Resolving (O) +------------------------------------------------------------------------------- + + + Key Distribution +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_7_1 True Encryption Key (C.1) +TSPC_SM_7_2 True Identity Key (C.2) +TSPC_SM_7_3 True Signing Key (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if GAP (24/2 OR 42/6) is supported, ELSE Optional +C.2: Mandatory if GAP (26/3) is supported, ELSE Optional +C.3: Mandatory if GAP (25/6 OR 35/6) is supported, ELSE Optional +------------------------------------------------------------------------------- diff --git a/android/pics-spp.txt b/android/pics-spp.txt new file mode 100644 index 0000000..6057d45 --- /dev/null +++ b/android/pics-spp.txt @@ -0,0 +1,64 @@ + +* - different than BITE defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + + Profile Version +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +K.5.0.1 False SPP v1.1 (C.1) +K.5.0.2 True SPP v1.2 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support only one Profile version. +------------------------------------------------------------------------------- + + Device Role +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +K.5.1.1 True Device A (DevA) (C.1) +K.5.1.2 True Device B (DevB) (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Support of SPP Service +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +K.5.2.1 True Support of Serial Profile Service (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory for devices that support Serial Profile for serial cable + emulation as a Bluetooth service. Irrelevant of devices that only + support Serial Profile for usage by another application profile + e.g. Fax Profile, Dun Profile, Hands free Profile, etc. +------------------------------------------------------------------------------- + + + Application Procedures +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +K.5.3.1 True Establish link and set up virtual serial + connection (C.1) +K.5.3.2 True Accept link and virtual serial connection + establishment (C.2) +K.5.3.3 True Register Service record for application in + local SDP database (C.2) +K.5.3.4 True No release in Sniff mode. Sniff mode enabled + in the Link Manager (O) +K.5.3.5 True No release in Hold mode. Hold mode enabled + in the Link Manager (O) +K.5.3.6 True No release in Park mode. Park mode enabled + in the Link Manager (O) +K.5.3.7 True No release after Master/Slave switch. M/S + switch enabled in the Link manager (O) +------------------------------------------------------------------------------- +C.1: Mandatory for Device A, Irrelevant for Device B. +C.2: Mandatory for Device B, Irrelevant for Device A. +------------------------------------------------------------------------------- diff --git a/android/pixit-a2dp.txt b/android/pixit-a2dp.txt new file mode 100644 index 0000000..807ce3b --- /dev/null +++ b/android/pixit-a2dp.txt @@ -0,0 +1,30 @@ +A2DP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to PTS's bin/audio folder + +Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_SRC_class_of_device 080418 +TSPX_SNK_class_of_device 04041C +TSPX_pin_code 0000 +TSPX_delete_link_key FALSE +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ + bin\audio (#) +TSPX_no_avrcp TRUE +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_rfcomm_channel 8 +TSPX_l2cap_psm 1011 +TSPX_no_confirmations FALSE +TSPX_cover_art_uuid 3EEE +------------------------------------------------------------------------------- diff --git a/android/pixit-avctp.txt b/android/pixit-avctp.txt new file mode 100644 index 0000000..a9b3f4f --- /dev/null +++ b/android/pixit-avctp.txt @@ -0,0 +1,39 @@ +AVCTP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to PTS's bin/audio folder + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_avctp_psm 0017 +TSPX_avctp_profile_id 110E +TSPX_connect_avdtp TRUE +TSPX_avctp_tester_command_data +TSPX_avctp_tester_response_data +TSPX_avctp_iut_command_data +TSPX_avctp_iut_response_data +TSPX_bd_addr_iut 11223344556677 (*&) +TSPX_pin_code 0000 +TSPX_delete_link_key FALSE +TSPX_security_enabled FALSE +TSPX_class_of_device 20050C +TSPX_player_feature_bitmask 0000000000000007FFF00070000000000 +TSPX_avrcp_version +TSPX_establish_avdtp_stream TRUE +TSPX_tester_av_role +TSPX_time_guard 300000 +TSPX_avrcp_only FALSE +TSPX_use_implicit_send TRUE +TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ + bin\audio (#) +TSPX_no_confirmations FALSE +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_rfcomm_channel 8 +TSPX_l2cap_psm 1011 +------------------------------------------------------------------------------- diff --git a/android/pixit-avrcp.txt b/android/pixit-avrcp.txt new file mode 100644 index 0000000..ceae884 --- /dev/null +++ b/android/pixit-avrcp.txt @@ -0,0 +1,36 @@ +AVRCP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to PTS's bin/audio folder + +Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_class_of_device 20050C +TSPX_player_feature_bitmask 0000000000000007FFF00070000000000 +TSPX_pin_code 0000 +TSPX_delete_link_key FALSE +TSPX_time_guard 300000 +TSPX_avrcp_only FALSE +TSPX_search_string tomorrow +TSPX_max_avc_fragments 10 +TSPX_establish_avdtp_stream TRUE +TSPX_use_implicit_send TRUE +TSPX_avrcp_version +TSPX_tester_av_role +TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ + bin\audio (#) +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_rfcomm_channel 8 +TSPX_l2cap_psm 1011 +TSPX_no_confirmations FALSE +TSPX_no_cover_art_folder +TSPX_cover_art_folder +------------------------------------------------------------------------------- diff --git a/android/pixit-did.txt b/android/pixit-did.txt new file mode 100644 index 0000000..c40f938 --- /dev/null +++ b/android/pixit-did.txt @@ -0,0 +1,24 @@ +DID PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled False +TSPX_ClientExecutableURL False (*) +TSPX_ServiceDescription False (*) +TSPX_DocumentationURL False (*) +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_class_of_device_pts 200404 +TSPX_device_search_time 30 +TSPX_delete_link_key False +TSPX_pin_code 0000 +TSPX_time_guard 200000 +TSPX_use_implicit_send True +TSPX_secure_simple_pairing_pass_key_confirmation False +------------------------------------------------------------------------------- diff --git a/android/pixit-gap.txt b/android/pixit-gap.txt new file mode 100644 index 0000000..494e24d --- /dev/null +++ b/android/pixit-gap.txt @@ -0,0 +1,57 @@ +GAP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_bd_addr_PTS 000000000000 +TSPX_broadcaster_class_of_device 100104 +TSPX_observer_class_of_device 100104 +TSPX_peripheral_class_of_device 100104 +TSPX_central_class_of_device 100104 +TSPX_security_enabled True +TSPX_delete_link_key True +TSPX_pin_code 0000 +TSPX_time_guard 300000 +TSPX_use_implicit_send True +TSPX_use_dynamic_pin False +TSPX_secure_simple_pairing_pass_key_confirmation False +TSPX_using_public_device_address True +TSPX_using_random_device_address False +TSPX_lim_adv_timeout 30720 +TSPX_gen_disc_adv_min 30720 +TSPX_lim_disc_scan_min 10240 +TSPX_gen_disc_scan_min 10240 +TSPX_database_file Database-GAP.sig +TSPX_iut_rx_mtu 23 +TSPX_iut_private_address_interval 10000 +TSPX_iut_privacy_enabled False +TSPX_psm 1001 +TSPX_iut_valid_connection_interval_min 00C8 +TSPX_iut_valid_conneciton_interval_max 0960 +TSPX_iut_valid_connection_latency 0007 +TSPX_iut_valid_timeout_multiplier 0960 +TSPX_iut_connection_parameter_timeout 30000 +TSPX_iut_invalid_connection_interval_min 0000 +TSPX_iut_invalid_conneciton_interval_max 0000 +TSPX_iut_invalid_connection_latency 0000 +TSPX_iut_invalid_timeout_multiplier 0000 +TSPX_LE_scan_interval 0010 +TSPX_LE_scan_window 0010 +TSPX_con_interval_min 0032 +TSPX_con_interval_max 0046 +TSPX_con_latency 0000 +TSPX_supervision_timeout 07D0 +TSPX_minimum_ce_length 0000 +TSPX_maximum_ce_length 0000 +TSPX_pairing_before_service_request False +TSPX_iut_mandates_mitm False +TSPX_encryption_before_service_request False +TSPX_tester_appearance 0000 +------------------------------------------------------------------------------- diff --git a/android/pixit-gatt.txt b/android/pixit-gatt.txt new file mode 100644 index 0000000..c073452 --- /dev/null +++ b/android/pixit-gatt.txt @@ -0,0 +1,31 @@ +GATT PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_security_enabled FALSE +TSPX_delete_link_key TRUE +TSPX_time_guard 180000 +TSPX_selected_handle 0012 +TSPX_use_implicit_send TRUE +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_iut_use_dynamic_bd_addr FALSE +TSPX_iut_setup_att_over_br_edr FALSE +TSPX_tester_database_file [Path to GATT Test + Database] +TSPX_iut_is_client_periphral FALSE +TSPX_iut_is_server_central FALSE +TSPX_mtu_size 23 +TSPX_pin_code 0000 +TSPX_use_dynamic_pin FALSE +TSPX_delete_ltk FALSE +TSPX_characteristic_readable FALSE +TSPX_tester_appearance 0000 +------------------------------------------------------------------------------- diff --git a/android/pixit-hdp.txt b/android/pixit-hdp.txt new file mode 100644 index 0000000..24cdcec --- /dev/null +++ b/android/pixit-hdp.txt @@ -0,0 +1,32 @@ +HDP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled TRUE (*) +TSPX_delete_link_key FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_sink_device_class_of_device 00900 +TSPX_source_device_class_of_device 00900 +TSPX_pin_code 0000 +TSPX_use_dynamic_pin FALSE +TSPX_use_implicit_send TRUE +TSPX_MCAP_l2cap_psm_control 1001 +TSPX_MCAP_l2cap_psm_data 1003 +TSPX_MCAP_sync_lead_time 0BB8 +TSPX_MCAP_timestamp_native_resolution 2233 +TSPX_MCAP_timestamp_native_accuracy 1400 +TSPX_MCAP_timestamp_required_accuracy 6400 +TSPX_DC_max 1 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_time_guard 6000000 +TSPX_ieee_device_specialization 10417 +TSPX_ieee_standard_configuration TRUE +TSPX_MCAP_bluetooth_clock_access_resolution 55 +------------------------------------------------------------------------------- diff --git a/android/pixit-hfp.txt b/android/pixit-hfp.txt new file mode 100644 index 0000000..ebf73c8 --- /dev/null +++ b/android/pixit-hfp.txt @@ -0,0 +1,38 @@ +HFP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to the respective phone numbers +^ - should be set according to the reported phone number's type + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled TRUE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_hf_class_of_device 200408 +TSPX_ag_class_of_device 400204 +TSPX_packet_type_sco 00A0 +TSPX_phone_number 1234567 (#) +TSPX_second_phone_number 7654321 (#) +TSPX_phone_number_type 145 (*^) +TSPX_second_phone_number_type 145 (*^) +TSPX_phone_number_memory 1 +TSPX_phone_number_memory_invalid_index 9999 +TSPX_scan_all_memory_dial_locations FALSE +TSPX_pin_code 0000 +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_verbose_implicit_send FALSE +TSPX_delete_link_key FALSE +TSPX_server_channel_tester 01 +TSPX_server_channel_iut 00 +TSPX_verify_CLIP_information TRUE +TSPX_no_fail_verdict FALSE +TSPX_network_supports_correct_call_and_callstatus TRUE +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_AG_match_tester_BRSF_codec_negotiation_bit FALSE +------------------------------------------------------------------------------- diff --git a/android/pixit-hid.txt b/android/pixit-hid.txt new file mode 100644 index 0000000..44e4363 --- /dev/null +++ b/android/pixit-hid.txt @@ -0,0 +1,30 @@ +HID PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled True +TSPX_delete_link_key True +TSPX_query_iut_sdp True +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_pointing_device_class_of_device 002580 +TSPX_keyboard_device_class_of_device 002540 +TSPX_host_class_of_device 100108 +TSPX_pts_device_role MOUSE +TSPX_pin_code 0000 +TSPX_use_dynamic_pin_code False +TSPX_time_guard 6000000 +TSPX_hid_data_interval 1000 +TSPX_use_implicit_send True +TSPX_verbose_implicit_send False +TSPX_no_fail_verdicts False +TSPX_time_reconnect 30000 +TSPX_secure_simple_pairing_pass_key_confirmation False +TSPX_hid_report_id 1 +------------------------------------------------------------------------------- diff --git a/android/pixit-hsp.txt b/android/pixit-hsp.txt new file mode 100644 index 0000000..2d5b14b --- /dev/null +++ b/android/pixit-hsp.txt @@ -0,0 +1,30 @@ +HSP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled TRUE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_hs_class_of_device 200404 +TSPX_ag_class_of_device 400204 +TSPX_packet_type_sco 00A0 +TSPX_pin_code 0000 +TSPX_time_guard 20000000 +TSPX_use_implicit_send TRUE +TSPX_verbose_implicit_send FALSE +TSPX_delete_link_key FALSE +TSPX_server_channel_tester 01 +TSPX_server_channel_iut 00 +TSPX_no_fail_verdict FALSE +TSPX_remote_audio_volume_control TRUE +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_inband_ring_only FALSE +TSPX_no_ring_or_inband_ring_tone FALSE +TSPX_iut_establish_audio_before_RING FALSE +------------------------------------------------------------------------------- diff --git a/android/pixit-iopt.txt b/android/pixit-iopt.txt new file mode 100644 index 0000000..a4bb581 --- /dev/null +++ b/android/pixit-iopt.txt @@ -0,0 +1,23 @@ +IOPT PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled FALSE +TSPX_delete_link_key FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_class_of_device_pts 200404 +TSPX_class_of_device_test_pts_initiator TRUE +TSPX_limited_inquiry_used FALSE +TSPX_pin_code 0000 +TSPX_time_guard 200000 +TSPX_device_search_time 20 +TSPX_use_implicit_send TRUE +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +------------------------------------------------------------------------------- diff --git a/android/pixit-l2cap.txt b/android/pixit-l2cap.txt new file mode 100644 index 0000000..914a995 --- /dev/null +++ b/android/pixit-l2cap.txt @@ -0,0 +1,41 @@ +L2CAP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_client_class_of_device 100104 +TSPX_server_class_of_device 100104 +TSPX_security_enabled TRUE +TSPX_delete_link_key FALSE +TSPX_pin_code 0000 +TSPX_flushto FFFF +TSPX_inmtu 02A0 +TSPX_no_fail_verditcs FALSE +TSPX_oumtu 02A0 +TSPX_iut_role_initiator TRUE +TSPX_psm 1011 (*) +TSPX_time_guard 180000 +TSPX_timer_ertx 120000 +TSPX_timer_ertx_max 300000 +TSPX_timer_ertx_min 60000 +TSPX_timer_rtx 10000 +TSPX_timer_rtx_max 60000 +TSPX_timer_rtx_min 1000 +TSPX_rfc_mode_tx_window_size 08 +TSPX_rfc_mode_max_transmit 03 +TSPX_rfc_mode_retransmission_timeout 07D0 +TSPX_rfc_mode_monitor_timeout 2EE0 +TSPX_rfc_mode_maximum_pdu_size 02A0 +TSPX_extended_window_size 0012 +TSPX_use_implicit_send TRUE +TSPX_use_dynamic_pin FALSE +TSPX_iut_SDU_size_in_bytes 144 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +------------------------------------------------------------------------------- diff --git a/android/pixit-map.txt b/android/pixit-map.txt new file mode 100644 index 0000000..ba687c1 --- /dev/null +++ b/android/pixit-map.txt @@ -0,0 +1,47 @@ +MAP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to tester's phone number +$ - should be set to IUT e-mail address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_client_class_of_device 100204 +TSPX_delete_link_key FALSE +TSPX_get_object_name put.gif +TSPX_initial_path +TSPX_l2cap_psm 1001 +TSPX_no_confirmations FALSE +TSPX_pin_code 0000 +TSPX_rfcomm_channel 8 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_security_enabled TRUE +TSPX_server_class_of_device 100204 +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_Message_Access_rfcomm_channel 1 +TSPX_Message_Notification_rfcomm_channel 2 +TSPX_SPP_rfcomm_channel 03 +TSPX_filter_period_begin 20100101T000000 +TSPX_filter_period_end 20111231T125959 +TSPX_filter_recipient PTS +TSPX_filter_originator PTS +TSPX_default_message_upload_folder_in_msg draft +TSPX_default_test_folder_in_msg inbox +TSPX_use_fixed_MASInstanceID FALSE +TSPX_MASInstanceID_SMS 0 +TSPX_MASInstanceID_MMS 0 +TSPX_MASInstanceID_Email 0 +TSPX_message_notification_l2cap_psm 1003 +TSPX_message_notification_rfcomm_channel 9 +TSPX_upload_msg_phonenumber 123456789 (#) +TSPX_upload_msg_emailaddress IUT-email ($) +------------------------------------------------------------------------------- diff --git a/android/pixit-mcap.txt b/android/pixit-mcap.txt new file mode 100644 index 0000000..ee67a1a --- /dev/null +++ b/android/pixit-mcap.txt @@ -0,0 +1,37 @@ +MCAP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_delete_link_key FALSE +TSPX_MCAP_DC_max 2 +TSPX_MCAP_l2cap_psm_control 1003 +TSPX_MCAP_l2cap_psm_control_B +TSPX_MCAP_l2cap_psm_data 1005 +TSPX_MCAP_l2cap_psm_data_B +TSPX_MCAP_bluetooth_clock_access_resolution 55 +TSPX_MCAP_create_mdl_configuration +TSPX_MCAP_data_channel_setup_mode +TSPX_MCAP_iut_initiate_connection TRUE (*) +TSPX_MCAP_mdep_id 12 +TSPX_MCAP_profile_name +TSPX_MCAP_sync_lead_time 0BB8 +TSPX_tester_role +TSPX_MCAP_timestamp_native_accuracy 1400 +TSPX_MCAP_timestamp_native_resolution 2233 +TSPX_MCAP_timestamp_required_accuracy 6400 +TSPX_host_class_of_device 100108 +TSPX_pin_code 0000 +TSPX_security_enabled TRUE (*) +TSPX_time_guard 6000000 +TSPX_use_dynamic_pin FALSE +TSPX_use_implicit_send TRUE +TSPX_verbose_implicit_send TRUE +------------------------------------------------------------------------------- diff --git a/android/pixit-mps.txt b/android/pixit-mps.txt new file mode 100644 index 0000000..c59c913 --- /dev/null +++ b/android/pixit-mps.txt @@ -0,0 +1,46 @@ +MPS PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +^ - should be set accordingly + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_avrcp_revision +TSPX_class_of_device 20050C +TSPX_establish_avdtp_stream TRUE +TSPX_iut_establishes_initial_condition FALSE +TSPX_tester_device +TSPX_media_directory +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_delete_link_key FALSE +TSPX_pin_code 0000 +TSPX_security_enabled FALSE +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_l2cap_psm 1003 +TSPX_rfcomm_channel 8 +TSPX_no_confirmations FALSE +TSPX_AG_match_tester_BRSF_codec_negotiation_bit FALSE +TSPX_network_supports_correct_call_and_callstatus TRUE +TSPX_no_fail_verdicts FALSE +TSPX_packet_type_sco 00A0 +TSPX_phone_number 1234567 (^) +TSPX_phone_number_memory 1 +TSPX_phone_number_memory_invalid_index 9999 +TSPX_phone_number_type 129 +TSPX_scan_all_memory_dial_locations FALSE +TSPX_second_phone_number 1234567 (^) +TSPX_second_phone_type 129 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_server_channel_iut 00 +TSPX_server_channel_tester 01 +TSPX_verbose_implicit_send FALSE +TSPX_verify_CLIP_information TRUE +------------------------------------------------------------------------------- diff --git a/android/pixit-opp.txt b/android/pixit-opp.txt new file mode 100644 index 0000000..7b0f4f2 --- /dev/null +++ b/android/pixit-opp.txt @@ -0,0 +1,27 @@ +OPP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_supported_extension jpg (*) +TSPX_unsupported_extension pts +TSPX_client_class_of_device 100104 +TSPX_server_class_of_device 100104 +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_l2cap_psm 1003 +TSPX_rfcomm_channel 8 +TSPX_no_confirmations FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_delete_link_key FALSE +TSPX_pin_code 0000 +TSPX_security_enabled FALSE +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +------------------------------------------------------------------------------- diff --git a/android/pixit-pan.txt b/android/pixit-pan.txt new file mode 100644 index 0000000..9053715 --- /dev/null +++ b/android/pixit-pan.txt @@ -0,0 +1,30 @@ +PAN PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT or PTS Bluetooth address respectively + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_GN_class_of_device 020104 +TSPX_NAP_class_of_device 020300 +TSPX_PANU_class_of_device 020104 +TSPX_time_guard 300000 +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_security_enabled False +TSPX_pin_code 0000 +TSPX_delete_link_key False +TSPX_use_implicit_send True +TSPX_iut_ip_address 192.168.167.152 +TSPX_iut_port_number 4242 +TSPX_PTS_ip_address 192.168.168.100 +TSPX_PTS_port_number 4242 +TSPX_bd_addr_PTS 112233445566 (*&) +TSPX_checksum E851 +TSPX_secure_simple_pairing_pass_key_confirmation False +TSPX_iut_friendly_bt_name gprs-pc +TSPX_PTS_role_when_iut_is_PANU default +------------------------------------------------------------------------------- diff --git a/android/pixit-pbap.txt b/android/pixit-pbap.txt new file mode 100644 index 0000000..fc89e15 --- /dev/null +++ b/android/pixit-pbap.txt @@ -0,0 +1,35 @@ +PBAP PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_security_enabled True +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_pin_code 0000 +TSPX_time_guard 100000 +TSPX_use_implicit_send True +TSPX_client_class_of_device 100204 +TSPX_server_class_of_device 100204 +TSPX_PSE_vCardSelector 0000000000000001 +TSPX_delete_link_key False +TSPX_PBAP_profile_version 1 +TSPX_PBAP_supported_repositories 1 +TSPX_PBAP_rfcomm_channel 1 +TSPX_telecom_folder_path telecom +TSPX_secure_simple_pairing_pass_key_confirmation False +TSPX_check_downloaded_contents_after_disconnect False +TSPX_SPP_rfcomm_channel 03 +TSPX_l2cap_psm 1005 +TSPX_rfcomm_channel 2 +TSPX_obex_auth_password 0000 +TSPX_no_confirmations False +TSPX_PSE_vCardSelector 0000000000000001 +------------------------------------------------------------------------------- diff --git a/android/pixit-sm.txt b/android/pixit-sm.txt new file mode 100644 index 0000000..607a3ce --- /dev/null +++ b/android/pixit-sm.txt @@ -0,0 +1,70 @@ +SM PIXIT for the PTS tool. + +PTS version: 5.1 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_SMP_pin_code 111111 +TSPX_OOB_Data 000000000000FE12036E5A + 889F4D +TSPX_peer_addr_type 00 +TSPX_own_addr_type 00 +TSPX_conn_interval_min 0190 +TSPX_conn_interval_max 0190 +TSPX_con_latency 0000 +TSPX_client_class_of_device 100104 +TSPX_server_class_of_device 100104 +TSPX_security_enabled TRUE +TSPX_delete_link_key TRUE +TSPX_pin_code 1234 +TSPX_ATTR_HANDLE 0000 +TSPX_ATTR_VALUE 000000000000000 +TSPX_delay_variation_in FFFFFFFF +TSPX_delay_variation_out FFFFFFFF +TSPX_flushto FFFF +TSPX_inmtu 02A0 +TSPX_inquiry_length 17 +TSPX_latency_in FFFFFFFF +TSPX_latency_out FFFFFFFF +TSPX_linkto 3000 +TSPX_max_nbr_retransmission 10 +TSPX_no_fail_verdicts FALSE +TSPX_outmtu 02A0 +TSPX_tester_role_optional L2CAP_ROLE_INITIATOR +TSPX_page_scan_mode 00 +TSPX_page_scan_repetition_mode 00 +TSPX_peak_bandwidth_in 00000000 +TSPX_peak_bandwidth_out 00000000 +TSPX_psm 0011 +TSPX_service_type_in 01 +TSPX_service_type_out 01 +TSPX_support_retransmissions TRUE +TSPX_time_guard 180000 +TSPX_timer_ertx 120000 +TSPX_timer_ertx_max 300000 +TSPX_timer_ertx_min 60000 +TSPX_timer_rtx 10000 +TSPX_timer_rtx_max 60000 +TSPX_timer_rtx_min 1000 +TSPX_token_bucket_size_in 00000000 +TSPX_token_bucket_size_out 00000000 +TSPX_token_rate_in 00000000 +TSPX_token_rate_out 00000000 +TSPX_rfc_mode_mode 03 +TSPX_rfc_mode_tx_window_size 08 +TSPX_rfc_mode_max_transmit 03 +TSPX_rfc_mode_retransmission_timeout 07D0 +TSPX_rfc_mode_monitor_timeout 2EE0 +TSPX_rfc_mode_maximum_pdu_size 02A0 +TSPX_extended_window_size 0012 +TSPX_use_implicit_send TRUE +TSPX_use_dynamic_pin FALSE +TSPX_iut_SDU_size_in_bytes 144 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +------------------------------------------------------------------------------- diff --git a/android/pts-a2dp.txt b/android/pts-a2dp.txt new file mode 100644 index 0000000..3670dd0 --- /dev/null +++ b/android/pts-a2dp.txt @@ -0,0 +1,60 @@ +PTS test results for A2DP + +PTS version: 5.1 +Tested: 07-April-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + + Source (SRC) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_SRC_CC_BV_09_I PASS Start streaming +TC_SRC_CC_BV_10_I N/A +TC_SRC_REL_BV_01_I PASS Connect to PTS from IUT. When requested + disconnect from IUT +TC_SRC_REL_BV_02_I PASS +TC_SRC_SET_BV_01_I PASS Connect to PTS (open a2dp) +TC_SRC_SET_BV_02_I PASS +TC_SRC_SET_BV_03_I PASS Start streaming +TC_SRC_SET_BV_04_I PASS Start streaming +TC_SRC_SET_BV_05_I PASS IUT must be moved out of range +TC_SRC_SET_BV_06_I PASS IUT must be moved out of range +TC_SRC_SUS_BV_01_I PASS Stop streaming +TC_SRC_SUS_BV_02_I PASS +TC_SRC_SDP_BV_01_I PASS +TC_SRC_AS_BV_01_I PASS Requires checking if the output on the IUT is + correct +------------------------------------------------------------------------------- + + + Sink (SNK) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_SNK_CC_BV_01_I N/A +TC_SNK_CC_BV_02_I N/A +TC_SNK_CC_BV_03_I N/A +TC_SNK_CC_BV_04_I N/A +TC_SNK_CC_BV_05_I N/A +TC_SNK_CC_BV_06_I N/A +TC_SNK_CC_BV_07_I N/A +TC_SNK_CC_BV_08_I N/A +TC_SNK_REL_BV_01_I N/A +TC_SNK_REL_BV_02_I N/A +TC_SNK_SET_BV_01_I N/A +TC_SNK_SET_BV_02_I N/A +TC_SNK_SET_BV_03_I N/A +TC_SNK_SET_BV_04_I N/A +TC_SNK_SET_BV_05_I N/A +TC_SNK_SET_BV_06_I N/A +TC_SNK_SUS_BV_01_I N/A +TC_SNK_SUS_BV_02_I N/A +TC_SNK_SDP_BV_02_I N/A +TC_SNK_AS_BV_01_I N/A +------------------------------------------------------------------------------- diff --git a/android/pts-avctp.txt b/android/pts-avctp.txt new file mode 100644 index 0000000..d8e7230 --- /dev/null +++ b/android/pts-avctp.txt @@ -0,0 +1,42 @@ +PTS test results for AVCTP + +PTS version: 5.1 +Tested: 07-April-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + + Controller (CT) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_CT_CCM_BV_01_C N/A +TC_CT_CCM_BV_02_C N/A +TC_CT_CCM_BV_03_C N/A +TC_CT_CCM_BV_04_C N/A +TC_CT_CCM_BI_01_C N/A +TC_CT_NFR_BV_01_C N/A +TC_CT_NFR_BV_04_C PASS haltest: rc set_volume 5 +TC_CT_FRA_BV_01_C N/A +TC_CT_FRA_BV_04_C N/A +------------------------------------------------------------------------------- + + + Target (TG) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_TG_CCM_BV_01_C PASS +TC_TG_CCM_BV_02_C PASS +TC_TG_CCM_BV_03_C PASS +TC_TG_CCM_BV_04_C PASS +TC_TG_NFR_BV_02_C PASS +TC_TG_NFR_BV_03_C PASS +TC_TG_NFR_BI_01_C PASS +TC_TG_FRA_BV_02_C N/A Fragmentation not supported +TC_TG_FRA_BV_03_C N/A Fragmentation not supported +------------------------------------------------------------------------------- diff --git a/android/pts-avrcp.txt b/android/pts-avrcp.txt new file mode 100644 index 0000000..b05bdbf --- /dev/null +++ b/android/pts-avrcp.txt @@ -0,0 +1,199 @@ +PTS test results for AVRCP + +PTS version: 5.1 +Tested: 07-April-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + + Controller (CT) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_CT_BGN_BV_01_I N/A +TC_CT_BGN_BV_02_I N/A +TC_CT_CEC_BV_01_I N/A +TC_CT_CEC_BV_02_I N/A +TC_CT_CFG_BV_01_C N/A +TC_CT_CON_BV_01_C N/A +TC_CT_CON_BV_03_C N/A +TC_CT_CRC_BV_01_I N/A +TC_CT_CRC_BV_02_I N/A +TC_CT_ICC_BV_01_I N/A +TC_CT_ICC_BV_02_I N/A +TC_CT_MCN_CB_BV_01_C N/A +TC_CT_MCN_CB_BV_01_I N/A +TC_CT_MCN_CB_BV_02_I N/A +TC_CT_MCN_CB_BV_03_I N/A +TC_CT_MCN_CB_BV_04_C N/A +TC_CT_MCN_CB_BV_04_I N/A +TC_CT_MCN_CB_BV_05_I N/A +TC_CT_MCN_CB_BV_06_I N/A +TC_CT_MCN_CB_BV_07_C N/A +TC_CT_MCN_CB_BV_07_I N/A +TC_CT_MCN_NP_BV_01_C N/A +TC_CT_MCN_NP_BV_01_I N/A +TC_CT_MCN_NP_BV_02_I N/A +TC_CT_MCN_NP_BV_03_C N/A +TC_CT_MCN_NP_BV_03_I N/A +TC_CT_MCN_NP_BV_04_I N/A +TC_CT_MCN_NP_BV_05_C N/A +TC_CT_MCN_NP_BV_05_I N/A +TC_CT_MCN_NP_BV_06_I N/A +TC_CT_MCN_NP_BV_07_I N/A +TC_CT_MCN_NP_BV_08_C N/A +TC_CT_MCN_SRC_BV_01_C N/A +TC_CT_MCN_SRC_BV_01_I N/A +TC_CT_MCN_SRC_BV_02_I N/A +TC_CT_MCN_SRC_BV_03_C N/A +TC_CT_MCN_SRC_BV_03_I N/A +TC_CT_MCN_SRC_BV_04_I N/A +TC_CT_MCN_SRC_BV_05_C N/A +TC_CT_MDI_BV_01_C N/A +TC_CT_MDI_BV_03_C N/A +TC_CT_MPS_BV_01_C N/A +TC_CT_MPS_BV_01_I N/A +TC_CT_MPS_BV_02_I N/A +TC_CT_MPS_BV_03_C N/A +TC_CT_MPS_BV_03_I N/A +TC_CT_MPS_BV_08_C N/A +TC_CT_NFY_BV_01_C N/A +TC_CT_PAS_BV_01_C N/A +TC_CT_PAS_BV_03_C N/A +TC_CT_PAS_BV_05_C N/A +TC_CT_PAS_BV_07_C N/A +TC_CT_PAS_BV_09_C N/A +TC_CT_PAS_BV_11_C N/A +TC_CT_PTH_BV_01_C N/A +TC_CT_PTH_BV_02_C N/A +TC_CT_PTT_BV_01_I N/A +TC_CT_PTT_BV_02_I N/A +TC_CT_PTT_BV_03_I N/A +TC_CT_PTT_BV_04_I N/A +TC_CT_PTT_BV_05_I N/A +TC_CT_RCR_BV_01_C N/A +TC_CT_RCR_BV_03_C N/A +TC_CT_VLH_BI_03_C PASS Send SetAbsolute Volume command by pressing + volume up or down buttons then adb logcat and + check VOLUME_CHANGED value +TC_CT_VLH_BI_04_C PASS adb logcat: check VOLUME_CHANGED value +TC_CT_VLH_BV_01_C PASS Send SetAbsolute Volume command by pressing + volume up or down buttons +TC_CT_VLH_BV_01_I PASS adb logcat: check VOLUME_CHANGED value +TC_CT_VLH_BV_02_I PASS Send SetAbsolute Volume command by pressing + volume up or down buttons +TC_CT_VLH_BV_03_C PASS +------------------------------------------------------------------------------- + + + Target (TG) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_TG_BGN_BV_01_I N/A +TC_TG_BGN_BV_02_I N/A +TC_TG_CEC_BV_01_I PASS +TC_TG_CEC_BV_02_I PASS +TC_TG_CFG_BI_01_C PASS +TC_TG_CFG_BV_02_C PASS +TC_TG_CON_BV_02_C N/A +TC_TG_CON_BV_04_C N/A +TC_TG_CON_BV_05_C N/A +TC_TG_CRC_BV_01_I PASS +TC_TG_CRC_BV_02_I PASS Disconnect from PTS +TC_TG_ICC_BV_01_I PASS +TC_TG_ICC_BV_02_I PASS +TC_TG_INV_BI_01_C PASS +TC_TG_INV_BI_02_C N/A +TC_TG_MCN_CB_BI_01_C N/A +TC_TG_MCN_CB_BI_02_C N/A +TC_TG_MCN_CB_BI_03_C N/A +TC_TG_MCN_CB_BI_04_C N/A +TC_TG_MCN_CB_BI_05_C N/A +TC_TG_MCN_CB_BV_01_I N/A +TC_TG_MCN_CB_BV_02_C N/A +TC_TG_MCN_CB_BV_02_I N/A +TC_TG_MCN_CB_BV_03_C N/A +TC_TG_MCN_CB_BV_03_I N/A +TC_TG_MCN_CB_BV_04_I N/A +TC_TG_MCN_CB_BV_05_C N/A +TC_TG_MCN_CB_BV_05_I N/A +TC_TG_MCN_CB_BV_06_C N/A +TC_TG_MCN_CB_BV_06_I N/A +TC_TG_MCN_CB_BV_07_I N/A +TC_TG_MCN_CB_BV_08_C N/A +TC_TG_MCN_CB_BV_09_C N/A +TC_TG_MCN_CB_BV_10_C N/A +TC_TG_MCN_CB_BV_11_C N/A +TC_TG_MCN_NP_BI_01_C N/A +TC_TG_MCN_NP_BI_02_C N/A +TC_TG_MCN_NP_BV_01_I N/A +TC_TG_MCN_NP_BV_02_C N/A +TC_TG_MCN_NP_BV_02_I N/A +TC_TG_MCN_NP_BV_03_I N/A +TC_TG_MCN_NP_BV_04_C N/A +TC_TG_MCN_NP_BV_04_I N/A +TC_TG_MCN_NP_BV_05_I N/A +TC_TG_MCN_NP_BV_06_C N/A +TC_TG_MCN_NP_BV_06_I N/A +TC_TG_MCN_NP_BV_07_C N/A +TC_TG_MCN_NP_BV_07_I N/A +TC_TG_MCN_NP_BV_09_C N/A +TC_TG_MCN_SRC_BV_01_I N/A +TC_TG_MCN_SRC_BV_02_C N/A +TC_TG_MCN_SRC_BV_02_I N/A +TC_TG_MCN_SRC_BV_03_I N/A +TC_TG_MCN_SRC_BV_04_C N/A +TC_TG_MCN_SRC_BV_04_I N/A +TC_TG_MCN_SRC_BV_06_C N/A +TC_TG_MDI_BV_02_C PASS +TC_TG_MDI_BV_04_C PASS +TC_TG_MDI_BV_05_C PASS +TC_TG_MPS_BI_01_C N/A +TC_TG_MPS_BI_02_C N/A +TC_TG_MPS_BV_01_I N/A +TC_TG_MPS_BV_02_C N/A +TC_TG_MPS_BV_02_I N/A +TC_TG_MPS_BV_03_I N/A +TC_TG_MPS_BV_04_C N/A +TC_TG_MPS_BV_05_C N/A +TC_TG_MPS_BV_06_C N/A +TC_TG_MPS_BV_07_C N/A +TC_TG_MPS_BV_09_C N/A +TC_TG_MPS_BV_10_C N/A +TC_TG_NFY_BI_01_C PASS +TC_TG_NFY_BV_02_C PASS Change track when requested +TC_TG_NFY_BV_03_C N/A +TC_TG_NFY_BV_04_C PASS +TC_TG_NFY_BV_05_C PASS +TC_TG_NFY_BV_06_C N/A +TC_TG_NFY_BV_07_C N/A +TC_TG_NFY_BV_08_C PASS +TC_TG_PAS_BI_01_C N/A +TC_TG_PAS_BI_02_C N/A +TC_TG_PAS_BI_03_C N/A +TC_TG_PAS_BI_04_C N/A +TC_TG_PAS_BI_05_C N/A +TC_TG_PAS_BV_02_C N/A +TC_TG_PAS_BV_04_C N/A +TC_TG_PAS_BV_06_C N/A +TC_TG_PAS_BV_08_C N/A +TC_TG_PAS_BV_10_C N/A +TC_TG_PTT_BV_01_I PASS +TC_TG_PTT_BV_02_I PASS +TC_TG_PTT_BV_03_I N/A +TC_TG_PTT_BV_04_I N/A +TC_TG_PTT_BV_05_I N/A +TC_TG_RCR_BV_02_C PASS +TC_TG_RCR_BV_04_C PASS +TC_TG_VLH_BI_01_C N/A +TC_TG_VLH_BI_02_C N/A +TC_TG_VLH_BV_01_I N/A +TC_TG_VLH_BV_02_C N/A +TC_TG_VLH_BV_02_I N/A +TC_TG_VLH_BV_04_C N/A +------------------------------------------------------------------------------- diff --git a/android/pts-did.txt b/android/pts-did.txt new file mode 100644 index 0000000..2a18d3e --- /dev/null +++ b/android/pts-did.txt @@ -0,0 +1,20 @@ +PTS test results for DID + +PTS version: 5.1 +Tested: 07-April-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_SDI_BV_1_I PASS IUT must be discoverable +TC_SDI_BV_2_I PASS IUT must be discoverable +TC_SDI_BV_3_I PASS IUT must be discoverable +TC_SDI_BV_4_I PASS IUT must be discoverable +------------------------------------------------------------------------------- diff --git a/android/pts-gap.txt b/android/pts-gap.txt new file mode 100644 index 0000000..d1f0402 --- /dev/null +++ b/android/pts-gap.txt @@ -0,0 +1,193 @@ +PTS test results for GAP + +PTS version: 5.1 +Tested: 16-May-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_MOD_NDIS_BV_01_C PASS IUT must be non-discoverable +TC_MOD_LDIS_BV_01_C PASS btmgmt discov limited 30 +TC_MOD_LDIS_BV_02_C PASS btmgmt discov limited 30 +TC_MOD_LDIS_BV_03_C PASS btmgmt discov limited 30 +TC_MOD_GDIS_BV_01_C PASS IUT must be discoverable +TC_MOD_GDIS_BV_02_C PASS IUT must be discoverable +TC_MOD_NCON_BV_01_C PASS btmgmt connectable off +TC_MOD_CON_BV_01_C PASS btmgmt connectable on +TC_BROB_BCST_BV_01_C N/A +TC_BROB_BCST_BV_02_C N/A +TC_BROB_BCST_BV_03_C N/A +TC_BROB_OBSV_BV_01_C N/A +TC_BROB_OBSV_BV_02_C N/A +TC_BROB_OBSV_BV_03_C N/A +TC_BROB_OBSV_BV_04_C N/A +TC_BROB_OBSV_BV_05_C N/A +TC_DISC_NONM_BV_01_C N/A +TC_DISC_NONM_BV_02_C N/A +TC_DISC_LIMM_BV_01_C N/A +TC_DISC_LIMM_BV_02_C N/A +TC_DISC_LIMM_BV_03_C N/A +TC_DISC_LIMM_BV_04_C N/A +TC_DISC_GENM_BV_01_C N/A +TC_DISC_GENM_BV_02_C N/A +TC_DISC_GENM_BV_03_C N/A +TC_DISC_GENM_BV_04_C N/A +TC_DISC_LIMP_BV_01_C PASS btmgmt find -l + PTS AD flags must have bit 1 unset and bit 0 set +TC_DISC_LIMP_BV_02_C PASS btmgmt find -l + PTS AD flags must have bit 1 set and bit 0 unset +TC_DISC_LIMP_BV_03_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_LIMP_BV_04_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_LIMP_BV_05_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_GENP_BV_01_C PASS btmgmt find -l + PTS AD flags must have bit 1 set and bit 0 unset +TC_DISC_GENP_BV_02_C PASS btmgmt find -l + PTS AD flags must have bit 1 unset and bit 0 set +TC_DISC_GENP_BV_03_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_GENP_BV_04_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_GENP_BV_05_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_IDLE_GIN_BV_01_C PASS Start discovery from IUT +TC_IDLE_LIN_BV_01_C PASS hcitool scan --iac=liac +TC_IDLE_NAMP_BV_01_C PASS possible to PASS using haltest following steps: + gattc - register client, connect to PTS, search + all services, get characteristic and then read + characteristic (name) +TC_IDLE_NAMP_BV_02_C PASS haltest: gatts connect +TC_CONN_NCON_BV_01_C N/A +TC_CONN_NCON_BV_02_C N/A +TC_CONN_NCON_BV_03_C N/A +TC_CONN_DCON_BV_01_C N/A +TC_CONN_DCON_BV_02_C N/A +TC_CONN_DCON_BV_03_C N/A +TC_CONN_UCON_BV_01_C N/A +TC_CONN_UCON_BV_02_C N/A +TC_CONN_UCON_BV_03_C N/A +TC_CONN_UCON_BV_04_C N/A +TC_CONN_UCON_BV_05_C N/A +TC_CONN_ACEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_ACEP_BV_02_C INC Privacy feature - not implemented +TC_CONN_GCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_GCEP_BV_02_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_GCEP_BV_03_C INC Privacy feature - not implemented +TC_CONN_GCEP_BV_04_C INC Privacy feature - not implemented +TC_CONN_SCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_SCEP_BV_02_C INC Privacy feature - not implemented +TC_CONN_DCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_DCEP_BV_02_C INC Privacy feature - not implemented +TC_CONN_DCEP_BV_03_C PASS +TC_CONN_DCEP_BV_04_C INC Privacy feature - not implemented +TC_CONN_CPUP_BV_01_C N/A +TC_CONN_CPUP_BV_02_C N/A +TC_CONN_CPUP_BV_03_C N/A +TC_CONN_CPUP_BV_04_C PASS +TC_CONN_CPUP_BV_05_C PASS +TC_CONN_CPUP_BV_06_C PASS PTS issue - ID: 12187 + ETS + new pixit file should be added +TC_CONN_TERM_BV_01_C PASS +TC_CONN_PRDA_BV_01_C N/A +TC_CONN_PRDA_BV_02_C INC Privacy feature - not implemented +TC_BOND_NBON_BV_01_C PASS +TC_BOND_NBON_BV_02_C PASS haltest: gattc_register_client + gattc connect
+ bluetooth create_bond
+ bluetooth remove_bond
+TC_BOND_NBON_BV_03_C N/A +TC_BOND_BON_BV_01_C N/A +TC_BOND_BON_BV_02_C PASS +TC_BOND_BON_BV_03_C N/A +TC_BOND_BON_BV_04_C PASS haltest: gattc_register_client + gattc connect
+ bluetooth remove_bond
+ gattc connect
+TC_SEC_AUT_BV_11_C N/A +TC_SEC_AUT_BV_12_C INC JIRA: BZ-29 +TC_SEC_AUT_BV_13_C INC JIRA: BZ-30 +TC_SEC_AUT_BV_14_C N/A +TC_SEC_AUT_BV_15_C N/A +TC_SEC_AUT_BV_16_C INC Not implemented +TC_SEC_AUT_BV_17_C PASS +TC_SEC_AUT_BV_18_C N/A +TC_SEC_AUT_BV_19_C PASS +TC_SEC_AUT_BV_20_C N/A +TC_SEC_AUT_BV_21_C INC Not implemented +TC_SEC_AUT_BV_22_C N/A +TC_SEC_AUT_BV_23_C N/A +TC_SEC_AUT_BV_24_C INC Not implemented +TC_SEC_CSIGN_BV_01_C INC Not yet implemented - signed write +TC_SEC_CSIGN_BV_02_C INC Not implemented +TC_SEC_CSIGN_BI_01_C INC Not implemented +TC_SEC_CSIGN_BI_02_C INC Not implemented +TC_SEC_CSIGN_BI_03_C INC Not implemented +TC_SEC_CSIGN_BI_04_C INC Not implemented +TC_PRIV_CONN_BV_01_C INC Privacy feature - not implemented +TC_PRIV_CONN_BV_02_C INC Privacy feature - not implemented +TC_PRIV_CONN_BV_03_C INC Privacy feature - not implemented +TC_PRIV_CONN_BV_04_C INC Privacy feature - not implemented +TC_PRIV_CONN_BV_05_C N/A +TC_PRIV_CONN_BV_06_C N/A +TC_PRIV_CONN_BV_07_C N/A +TC_PRIV_CONN_BV_08_C N/A +TC_PRIV_CONN_BV_09_C N/A +TC_PRIV_CONN_BV_10_C N/A +TC_PRIV_CONN_BV_11_C INC Privacy feature - not implemented +TC_ADV_BV_01_C N/A +TC_ADV_BV_02_C N/A +TC_ADV_BV_03_C N/A +TC_ADV_BV_04_C N/A +TC_ADV_BV_05_C N/A +TC_ADV_BV_06_C N/A +TC_ADV_BV_07_C N/A +TC_ADV_BV_08_C N/A +TC_ADV_BV_09_C N/A +TC_ADV_BV_10_C N/A +TC_ADV_BV_11_C N/A +TC_ADV_BV_12_C N/A +TC_ADV_BV_13_C N/A +TC_ADV_BV_14_C N/A +TC_ADV_BV_15_C N/A +TC_ADV_BV_16_C N/A +TC_GAT_BV_01_C PASS haltest: + gattc register_client + gattc connect +TC_GAT_BV_02_C N/A +TC_GAT_BV_03_C N/A +TC_GAT_BV_04_C N/A +TC_GAT_BV_05_C N/A +TC_GAT_BV_06_C N/A +TC_GAT_BV_07_C N/A +TC_GAT_BV_08_C N/A +TC_DM_NCON_BV_01_C N/A +TC_DM_CON_BV_01_C N/A +TC_DM_NBON_BV_01_C N/A +TC_DM_BON_BV_01_C PASS haltest: + create_bond and remove_bond when requested +TC_DM_GIN_BV_01_C PASS +TC_DM_LIN_BV_01_C PASS +TC_DM_NAD_BV_01_C PASS Start discovery from IUT +TC_DM_NAD_BV_02_C PASS +TC_DM_LEP_BV_01_C N/A +TC_DM_LEP_BV_02_C N/A +TC_DM_LEP_BV_03_C N/A +TC_DM_LEP_BV_04_C PASS l2test -n +TC_DM_LEP_BV_05_C PASS btmgmt find -b + l2test -n 00:1B:DC:06:06:22 +TC_DM_LEP_BV_06_C PASS +TC_DM_LEP_BV_07_C N/A +TC_DM_LEP_BV_08_C N/A +TC_DM_LEP_BV_09_C N/A +TC_DM_LEP_BV_10_C N/A +TC_DM_LEP_BV_11_C N/A +------------------------------------------------------------------------------- diff --git a/android/pts-gatt.txt b/android/pts-gatt.txt new file mode 100644 index 0000000..342db47 --- /dev/null +++ b/android/pts-gatt.txt @@ -0,0 +1,119 @@ +PTS test results for GAP + +PTS version: 5.1 +Tested: 7-May-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_GAC_CL_BV_01_C PASS Send exchanged MTU +TC_GAC_SR_BV_01_C PASS IUT should accept incoming GATT con request +TC_GAD_CL_BV_01_C PASS Send discover all primary services command to + PTS +TC_GAD_CL_BV_02_C PASS Send discover primary services (UUID=1800) + command to PTS +TC_GAD_CL_BV_03_C PASS Send discover all include service to PTS +TC_GAD_CL_BV_04_C PASS Discover all characteristics of service (UUID= + 180A) +TC_GAD_CL_BV_05_C PASS Send discover characteristics by UUID +TC_GAD_CL_BV_06_C PASS Send discover characteristics descriptor +TC_GAD_CL_BV_07_C PASS +TC_GAD_CL_BV_08_C PASS +TC_GAD_SR_BV_01_C INC +TC_GAD_SR_BV_02_C INC +TC_GAD_SR_BV_03_C INC +TC_GAD_SR_BV_04_C INC +TC_GAD_SR_BV_05_C INC +TC_GAD_SR_BV_06_C INC +TC_GAD_SR_BV_07_C INC +TC_GAD_SR_BV_08_C INC +TC_GAR_CL_BV_01_C PASS +TC_GAR_CL_BI_01_C PASS +TC_GAR_CL_BI_02_C PASS +TC_GAR_CL_BI_03_C PASS +TC_GAR_CL_BI_04_C PASS +TC_GAR_CL_BI_05_C PASS +TC_GAR_CL_BV_03_C PASS +TC_GAR_CL_BI_06_C PASS +TC_GAR_CL_BI_07_C PASS +TC_GAR_CL_BI_09_C PASS +TC_GAR_CL_BI_10_C PASS +TC_GAR_CL_BI_11_C PASS +TC_GAR_CL_BV_04_C PASS +TC_GAR_CL_BI_12_C PASS +TC_GAR_CL_BI_13_C PASS +TC_GAR_CL_BI_14_C PASS +TC_GAR_CL_BI_15_C INC +TC_GAR_CL_BI_16_C INC +TC_GAR_CL_BI_17_C INC +TC_GAR_CL_BV_05_C INC +TC_GAR_CL_BI_18_C INC +TC_GAR_CL_BI_19_C INC +TC_GAR_CL_BI_20_C INC +TC_GAR_CL_BI_21_C INC +TC_GAR_CL_BI_22_C INC +TC_GAR_CL_BV_06_C INC +TC_GAR_CL_BI_23_C INC +TC_GAR_CL_BI_24_C INC +TC_GAR_CL_BI_25_C INC +TC_GAR_CL_BI_26_C INC +TC_GAR_CL_BI_27_C INC +TC_GAR_CL_BV_07_C INC +TC_GAR_CL_BI_28_C INC +TC_GAR_CL_BI_29_C INC +TC_GAR_CL_BI_30_C INC +TC_GAR_CL_BI_31_C INC +TC_GAR_CL_BI_32_C INC +TC_GAR_CL_BI_33_C PASS +TC_GAR_CL_BI_34_C INC +TC_GAR_CL_BI_35_C PASS +TC_GAR_SR_BV_01_C INC +TC_GAR_SR_BI_01_C INC +TC_GAR_SR_BI_02_C INC +TC_GAR_SR_BI_03_C INC +TC_GAR_SR_BI_04_C INC +TC_GAR_SR_BI_05_C INC +TC_GAR_SR_BV_03_C INC +TC_GAR_SR_BI_06_C INC +TC_GAR_SR_BI_07_C INC +TC_GAR_SR_BI_08_C INC +TC_GAR_SR_BI_09_C INC +TC_GAR_SR_BI_10_C INC +TC_GAR_SR_BI_11_C INC +TC_GAR_SR_BV_04_C INC +TC_GAR_SR_BI_12_C INC +TC_GAR_SR_BI_13_C INC +TC_GAR_SR_BI_14_C INC +TC_GAR_SR_BI_15_C INC +TC_GAR_SR_BI_16_C INC +TC_GAR_SR_BI_17_C INC +TC_GAR_SR_BV_05_C INC +TC_GAR_SR_BI_18_C INC +TC_GAR_SR_BI_19_C INC +TC_GAR_SR_BI_20_C INC +TC_GAR_SR_BI_21_C INC +TC_GAR_SR_BI_22_C INC +TC_GAR_SR_BV_06_C INC +TC_GAR_SR_BI_23_C INC +TC_GAR_SR_BI_24_C INC +TC_GAR_SR_BI_25_C INC +TC_GAR_SR_BI_26_C INC +TC_GAR_SR_BI_27_C INC +TC_GAR_SR_BV_07_C INC +TC_GAR_SR_BV_08_C INC +TC_GAR_SR_BI_28_C INC +TC_GAR_SR_BI_29_C INC +TC_GAR_SR_BI_30_C INC +TC_GAR_SR_BI_31_C INC +TC_GAR_SR_BI_32_C INC +TC_GAR_SR_BI_33_C INC +TC_GAR_SR_BI_34_C INC +TC_GAR_SR_BI_35_C INC +------------------------------------------------------------------------------- diff --git a/android/pts-hdp.txt b/android/pts-hdp.txt new file mode 100644 index 0000000..1dec18c --- /dev/null +++ b/android/pts-hdp.txt @@ -0,0 +1,72 @@ +PTS test results for HDP + +PTS version: 5.1 +Tested: not tested +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_SRC_CON_BV_01_I INC +TC_SRC_CON_BV_02_I INC +TC_SRC_CON_BV_03_I INC +TC_SRC_CON_BV_04_I INC +TC_SRC_CON_BV_05_I INC +TC_SRC_CON_BV_06_I INC +TC_SRC_CON_BV_07_I INC +TC_SRC_CON_BV_08_I INC +TC_SRC_CON_BV_09_I INC +TC_SRC_CON_BV_10_I INC +TC_SRC_CC_BV_01_C INC +TC_SRC_CC_BV_02_C INC +TC_SRC_CC_BV_03_C INC +TC_SRC_CC_BV_05_C INC +TC_SRC_CC_BV_07_C INC +TC_SRC_CC_BV_09_C INC +TC_SRC_CC_BI_12_C INC +TC_SRC_HCT_BV_01_I INC +TC_SRC_HCT_BV_02_I INC +TC_SRC_HCT_BV_03_I N/A +TC_SRC_HCT_BV_04_I INC +TC_SRC_HCT_BV_05_C N/A +TC_SRC_HCT_BV_06_C INC +TC_SRC_HCT_BV_07_C INC +TC_SRC_DE_BV_01_I INC +TC_SRC_DE_BV_02_I INC +TC_SRC_DEP_BV_01_I INC +TC_SRC_DEP_BV_02_I N/A +TC_SNK_CON_BV_01_I INC +TC_SNK_CON_BV_02_I INC +TC_SNK_CON_BV_03_I INC +TC_SNK_CON_BV_04_I INC +TC_SNK_CON_BV_05_I INC +TC_SNK_CON_BV_06_I INC +TC_SNK_CON_BV_07_I INC +TC_SNK_CON_BV_08_I INC +TC_SNK_CON_BV_09_I INC +TC_SNK_CON_BV_10_I INC +TC_SNK_CC_BV_01_C INC +TC_SNK_CC_BV_02_C INC +TC_SNK_CC_BV_04_C INC +TC_SNK_CC_BV_06_C INC +TC_SNK_CC_BV_08_C INC +TC_SNK_CC_BV_10_C INC +TC_SNK_CC_BI_11_C INC +TC_SNK_HCT_BV_01_I INC +TC_SNK_HCT_BV_02_I INC +TC_SNK_HCT_BV_03_I N/A +TC_SNK_HCT_BV_04_I INC +TC_SNK_HCT_BV_05_C N/A +TC_SNK_HCT_BV_06_C INC +TC_SNK_HCT_BV_07_C INC +TC_SNK_DE_BV_01_I N/A +TC_SNK_DE_BV_02_I INC +TC_SNK_DEP_BV_03_I INC +TC_SNK_DEP_BV_04_I INC +------------------------------------------------------------------------------- diff --git a/android/pts-hfp.txt b/android/pts-hfp.txt new file mode 100644 index 0000000..196fa14 --- /dev/null +++ b/android/pts-hfp.txt @@ -0,0 +1,248 @@ +PTS test results for HFP + +PTS version: 5.1 +Tested: 25-Mar-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_AG_OOR_BV_01_I PASS +TC_AG_OOR_BV_02_I PASS +TC_AG_TRS_BV_01_I PASS +TC_AG_PSI_BV_01_I PASS +TC_AG_PSI_BV_02_I N/A +TC_AG_PSI_BV_03_I PASS +TC_AG_PSI_BV_04_I PASS +TC_AG_PSI_BV_05_I PASS +TC_AG_ACS_BV_02_I N/A +TC_AG_ACS_BV_04_I PASS +TC_AG_ACS_BV_06_I N/A +TC_AG_ACS_BV_08_I PASS +TC_AG_ACS_BV_10_I N/A +TC_AG_ACS_BV_11_I PASS +TC_AG_ACS_BI_14_I PASS +TC_AG_ACR_BV_01_I PASS +TC_AG_ACR_BV_02_I PASS +TC_AG_CLI_BV_01_I PASS +TC_AG_ICA_BV_01_I N/A +TC_AG_ICA_BV_02_I N/A +TC_AG_ICA_BV_04_I PASS +TC_AG_ICA_BV_05_I N/A +TC_AG_ICA_BV_06_I PASS +TC_AG_ICR_BV_01_I PASS +TC_AG_ICR_BV_02_I PASS +TC_AG_TCA_BV_01_I PASS +TC_AG_TCA_BV_02_I PASS +TC_AG_TCA_BV_03_I PASS +TC_AG_TCA_BV_04_I PASS +TC_AG_TCA_BV_05_I PASS +TC_AG_ATH_BV_03_I PASS +TC_AG_ATH_BV_04_I PASS +TC_AG_ATH_BV_05_I PASS +TC_AG_ATH_BV_06_I PASS +TC_AG_ATA_BV_01_I PASS +TC_AG_ATA_BV_02_I PASS +TC_AG_OCN_BV_01_I PASS +TC_AG_OCM_BV_01_I PASS +TC_AG_OCM_BV_02_I PASS +TC_AG_OCL_BV_01_I PASS +TC_AG_OCL_BV_02_I PASS +TC_AG_TWC_BV_01_I PASS +TC_AG_TWC_BV_02_I PASS +TC_AG_TWC_BV_03_I PASS +TC_AG_TWC_BV_04_I PASS +TC_AG_TWC_BV_05_I PASS +TC_AG_TWC_BV_06_I N/A +TC_AG_CIT_BV_01_I PASS +TC_AG_ENO_BV_01_I PASS +TC_AG_ENO_BV_02_I N/A +TC_AG_VRA_BV_01_I PASS +TC_AG_VRA_BV_02_I PASS +TC_AG_VRA_BI_01_I PASS +TC_AG_VRD_BV_01_I PASS +TC_AG_VTG_BV_01_I N/A +TC_AG_TDC_BV_01_I PASS +TC_AG_RSV_BV_01_I PASS +TC_AG_RSV_BV_02_I PASS +TC_AG_RSV_BV_03_I PASS +TC_AG_RMV_BV_01_I N/A +TC_AG_RMV_BV_02_I N/A +TC_AG_RMV_BV_03_I N/A +TC_AG_ECS_BV_01_I PASS +TC_AG_ECS_BV_02_I PASS +TC_AG_ECS_BV_03_I PASS +TC_AG_ECC_BV_01_I N/A +TC_AG_ECC_BV_02_I N/A +TC_AG_ECC_BI_03_I PASS +TC_AG_ECC_BI_04_I PASS +TC_AG_RHH_BV_01_I N/A +TC_AG_RHH_BV_02_I N/A +TC_AG_RHH_BV_03_I N/A +TC_AG_RHH_BV_04_I N/A +TC_AG_RHH_BV_05_I N/A +TC_AG_RHH_BV_06_I N/A +TC_AG_RHH_BV_07_I N/A +TC_AG_RHH_BV_08_I N/A +TC_AG_NUM_BV_01_I PASS +TC_AG_SLC_BV_01_C PASS +TC_AG_SLC_BV_02_C PASS +TC_AG_SLC_BV_03_C PASS +TC_AG_SLC_BV_04_C PASS +TC_AG_SLC_BV_05_I PASS +TC_AG_SLC_BV_06_I PASS +TC_AG_SLC_BV_07_I PASS +TC_AG_ACC_BV_08_I INC Possible PTS issue #12039 +TC_AG_ACC_BV_09_I PASS +TC_AG_ACC_BV_10_I INC Possible PTS issue #12039 +TC_AG_ACC_BV_11_I INC Possible PTS issue #12039 +TC_AG_ACC_BI_12_I PASS +TC_AG_ACC_BI_13_I PASS +TC_AG_ACC_BI_14_I PASS +TC_AG_ACC_BV_15_I PASS +TC_AG_WBS_BV_01_I PASS +TC_AG_DIS_BV_01_I PASS +TC_AG_SDP_BV_01_I PASS +TC_AG_IIA_BV_01_I PASS +TC_AG_IIA_BV_02_I PASS +TC_AG_IIA_BV_03_I N/A +TC_AG_IIA_BV_05_I PASS +TC_AG_IID_BV_01_I PASS +TC_AG_IID_BV_02_I N/A +TC_AG_IID_BV_03_I PASS +TC_AG_IID_BV_04_I PASS +TC_AG_IIC_BV_01_I PASS +TC_AG_IIC_BV_02_I PASS +TC_AG_IIC_BV_03_I PASS + +TC_HF_OOR_BV_01_I N/A +TC_HF_OOR_BV_02_I N/A +TC_HF_TRS_BV_01_I N/A +TC_HF_PSI_BV_01_I N/A +TC_HF_PSI_BV_02_I N/A +TC_HF_PSI_BV_03_I N/A +TC_HF_PSI_BV_04_I N/A +TC_HF_ACS_BV_01_I N/A +TC_HF_ACS_BV_03_I N/A +TC_HF_ACS_BV_05_I N/A +TC_HF_ACS_BV_07_I N/A +TC_HF_ACS_BV_09_I N/A +TC_HF_ACS_BV_12_I N/A +TC_HF_ACS_BI_13_I N/A +TC_HF_ACR_BV_01_I N/A +TC_HF_ACR_BV_02_I N/A +TC_HF_CLI_BV_01_I N/A +TC_HF_ICA_BV_01_I N/A +TC_HF_ICA_BV_02_I N/A +TC_HF_ICA_BV_03_I N/A +TC_HF_ICA_BV_04_I N/A +TC_HF_ICA_BV_05_I N/A +TC_HF_ICA_BV_06_I N/A +TC_HF_ICA_BV_07_I N/A +TC_HF_ICR_BV_01_I N/A +TC_HF_ICR_BV_02_I N/A +TC_HF_TCA_BV_01_I N/A +TC_HF_TCA_BV_02_I N/A +TC_HF_TCA_BV_03_I N/A +TC_HF_TCA_BV_04_I N/A +TC_HF_ATH_BV_03_I N/A +TC_HF_ATH_BV_04_I N/A +TC_HF_ATH_BV_05_I N/A +TC_HF_ATH_BV_06_I N/A +TC_HF_ATH_BV_09_I N/A +TC_HF_ATA_BV_01_I N/A +TC_HF_ATA_BV_02_I N/A +TC_HF_ATA_BV_03_I N/A +TC_HF_OCN_BV_01_I N/A +TC_HF_OCM_BV_01_I N/A +TC_HF_OCM_BV_02_I N/A +TC_HF_OCL_BV_01_I N/A +TC_HF_OCL_BV_02_I N/A +TC_HF_TWC_BV_01_I N/A +TC_HF_TWC_BV_02_I N/A +TC_HF_TWC_BV_03_I N/A +TC_HF_TWC_BV_04_I N/A +TC_HF_TWC_BV_05_I N/A +TC_HF_TWC_BV_06_I N/A +TC_HF_CIT_BV_01_I N/A +TC_HF_ENO_BV_01_I N/A +TC_HF_VRA_BV_01_I N/A +TC_HF_VRA_BV_02_I N/A +TC_HF_VRA_BV_03_I N/A +TC_HF_VRD_BV_01_I N/A +TC_HF_VTG_BV_01_I N/A +TC_HF_TDC_BV_01_I N/A +TC_HF_RSV_BV_01_I N/A +TC_HF_RSV_BV_02_I N/A +TC_HF_RSV_BV_03_I N/A +TC_HF_RMV_BV_01_I N/A +TC_HF_RMV_BV_02_I N/A +TC_HF_RMV_BV_03_I N/A +TC_HF_ECS_BV_01_I N/A +TC_HF_ECS_BV_02_I N/A +TC_HF_ECS_BV_03_I N/A +TC_HF_ECC_BV_01_I N/A +TC_HF_ECC_BV_02_I N/A +TC_HF_RHH_BV_01_I N/A +TC_HF_RHH_BV_02_I N/A +TC_HF_RHH_BV_03_I N/A +TC_HF_RHH_BV_04_I N/A +TC_HF_RHH_BV_05_I N/A +TC_HF_RHH_BV_06_I N/A +TC_HF_RHH_BV_07_I N/A +TC_HF_RHH_BV_08_I N/A +TC_HF_NUM_BV_01_I N/A +TC_HF_NUM_BI_01_I N/A +TC_HF_SLC_BV_01_C N/A +TC_HF_SLC_BV_02_C N/A +TC_HF_SLC_BV_03_C N/A +TC_HF_SLC_BV_04_C N/A +TC_HF_SLC_BV_05_I N/A +TC_HF_SLC_BV_06_I N/A +TC_HF_SLC_BV_08_I N/A +TC_HF_ACC_BV_01_I N/A +TC_HF_ACC_BV_02_I N/A +TC_HF_ACC_BV_03_I N/A +TC_HF_ACC_BV_04_I N/A +TC_HF_ACC_BV_05_I N/A +TC_HF_ACC_BV_06_I N/A +TC_HF_ACC_BV_07_I N/A +TC_HF_WBS_BV_02_I N/A +TC_HF_WBS_BV_03_I N/A +TC_HF_DIS_BV_01_I N/A +TC_HF_DIS_BV_02_I N/A +TC_HF_SDP_BV_01_I N/A +TC_HF_SDP_BV_02_C N/A +TC_HF_SDP_BV_03_C N/A +TC_HF_ATAH_BV_01_I N/A +TC_HF_OCA_BV_01_I N/A +TC_HF_IIA_BV_04_I N/A + +TC_AG_COD_BV_02_I PASS +TC_AG_ATAH_BV_03_I PASS +TC_AG_ATA_BV_03_I PASS +TC_AG_ATH_BV_09_I PASS +TC_AG_SDP_BV_02_C PASS +TC_AG_SDP_BV_03_C PASS +TC_AG_ICA_BV_07_I PASS +TC_AG_ICA_BV_08_I PASS +TC_AG_ICA_BV_09_I PASS +TC_AG_VRA_BV_03_I PASS +TC_AG_OCA_BV_01_I PASS +TC_AG_TCA_BV_06_I PASS + +TC_HF_ATAH_BV_03_I N/A +TC_HF_ATA_BV_03_I N/A +TC_HF_ATH_BV_09_I N/A +TC_HF_SDP_BV_02_C N/A +TC_HF_SDP_BV_03_C N/A +TC_HF_DIS_BV_02_I N/A +TC_HF_ICA_BV_07_I N/A +TC_HF_VRA_BV_03_I N/A +TC_HF_OCA_BV_01_I N/A diff --git a/android/pts-hid.txt b/android/pts-hid.txt new file mode 100644 index 0000000..a7990e1 --- /dev/null +++ b/android/pts-hid.txt @@ -0,0 +1,76 @@ +PTS test results for HID + +PTS version: 5.1 +Tested: 07-April-2014 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_HOS_HCE_BV_01_I PASS +TC_HOS_HCE_BV_02_I PASS +TC_HOS_HCE_BV_03_I PASS +TC_HOS_HCE_BV_04_I PASS +TC_HOS_HCR_BV_01_I PASS +TC_HOS_HCR_BV_02_I PASS +TC_HOS_HCR_BV_03_I N/A +TC_HOS_HCR_BV_04_I N/A +TC_HOS_HDT_BV_01_I PASS +TC_HOS_HDT_BV_02_I PASS from shell execute: + haltest + bluetooth enable + hidhost connect + hidhost send_data ff00 +TC_HOS_HDT_BV_03_I N/A +TC_HOS_HDT_BV_04_I N/A +TC_HOS_HID_BV_01_C N/A +TC_HOS_HID_BV_02_C N/A +TC_HOS_HID_BV_03_C N/A +TC_HOS_HID_BV_04_C N/A +TC_HOS_HID_BV_05_C N/A +TC_HOS_HID_BV_06_C N/A +TC_HOS_HID_BV_08_C N/A +TC_HOS_HID_BV_09_C N/A +TC_HOS_HID_BV_10_C N/A +TC_HOS_DAT_BV_01_C PASS from shell execute: + haltest + bluetooth enable + hidhost connect + hidhost send_data ff00 +TC_HOS_DAT_BV_02_C N/A +TC_HOS_DAT_BI_01_C N/A +TC_HOS_DAT_BI_02_C N/A +TC_DEV_HCE_BV_01_I N/A +TC_DEV_HCE_BV_02_I N/A +TC_DEV_HCE_BV_03_I N/A +TC_DEV_HCE_BV_04_I N/A +TC_DEV_HCE_BV_05_I N/A +TC_DEV_HCR_BV_01_I N/A +TC_DEV_HCR_BV_02_I N/A +TC_DEV_HCR_BV_03_I N/A +TC_DEV_HCR_BV_04_I N/A +TC_DEV_HDT_BV_01_I N/A +TC_DEV_HDT_BV_02_I N/A +TC_DEV_HDT_BV_03_I N/A +TC_DEV_HDT_BV_04_I N/A +TC_DEV_HID_BV_01_C N/A +TC_DEV_HID_BV_03_C N/A +TC_DEV_HID_BV_04_C N/A +TC_DEV_HID_BV_05_C N/A +TC_DEV_HID_BV_06_C N/A +TC_DEV_HID_BV_08_C N/A +TC_DEV_HID_BV_09_C N/A +TC_DEV_HID_BV_10_C N/A +TC_DEV_HID_BI_01_C N/A +TC_DEV_HID_BI_02_C N/A +TC_DEV_DAT_BV_01_C N/A +TC_DEV_SDD_BV_01_C N/A +TC_DEV_SDD_BV_02_C N/A +TC_DEV_SDD_BV_03_C N/A +TC_DEV_SDD_BV_04_I N/A +------------------------------------------------------------------------------- diff --git a/android/pts-hsp.txt b/android/pts-hsp.txt new file mode 100644 index 0000000..7da71e8 --- /dev/null +++ b/android/pts-hsp.txt @@ -0,0 +1,41 @@ +PTS test results for HSP + +PTS version: 5.1 +Tested: 10-April-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_AG_IAC_BV_01_I PASS +TC_AG_IAC_BV_02_I N/A +TC_AG_OAC_BV_01_I PASS +HSP_TC_AG_ACR_BV_01_I PASS +HSP_TC_AG_ACR_BV_02_I PASS +TC_AG_ACT_BV_01_I PASS +TC_AG_ACT_BV_02_I PASS +TC_AG_RAV_BV_01_I PASS +TC_AG_RAV_BV_02_I PASS +TC_AG_RAV_BV_03_I PASS +TC_AG_RAV_BV_04_I N/A +TC_AG_RAV_BV_05_I N/A +TC_AG_RAV_BV_06_I N/A +TC_HS_IAC_BV_01_I N/A +TC_HS_IAC_BV_02_I N/A +TC_HS_OAC_BV_01_I N/A +TC_HS_ACR_BV_01_I N/A +TC_HS_ACR_BV_02_I N/A +TC_HS_ACT_BV_01_I N/A +TC_HS_ACT_BV_02_I N/A +TC_HS_RAV_BV_01_I N/A +TC_HS_RAV_BV_02_I N/A +TC_HS_RAV_BV_03_I N/A +TC_HS_RAV_BV_04_I N/A +TC_HS_RAV_BV_05_I N/A +TC_HS_RAV_BV_06_I N/A diff --git a/android/pts-iopt.txt b/android/pts-iopt.txt new file mode 100644 index 0000000..815740d --- /dev/null +++ b/android/pts-iopt.txt @@ -0,0 +1,23 @@ +PTS test results for IOPT + +PTS version: 5.1 +Tested: 08-May-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_COD_BV_01_I PASS IUT must be discoverable +TC_COD_BV_02_I N/A Under PTS 5.1 test shall be disabled as there is + matching test case in HFP test suit (test No. 15) + PICS settings for HFP shall be disabled for IOPT +TC_SDSS_BV_02_I PASS +TC_SDSA_BV_03_I PASS +TC_SDR_BV_04_I PASS +------------------------------------------------------------------------------- diff --git a/android/pts-l2cap.txt b/android/pts-l2cap.txt new file mode 100644 index 0000000..b14614a --- /dev/null +++ b/android/pts-l2cap.txt @@ -0,0 +1,165 @@ +PTS test results for L2CAP + +PTS version: 5.1 +Tested: 27-March-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_COS_CED_BV_01_C PASS l2test -n -P + i.e. l2test -n -P 4097 +TC_COS_CED_BV_03_C PASS l2test -y -N + -P +TC_COS_CED_BV_04_C PASS l2test -n -P +TC_COS_CED_BV_05_C PASS l2test -P +TC_COS_CED_BV_07_C PASS l2test -n -P +TC_COS_CED_BV_08_C PASS l2test -n -P +TC_COS_CED_BV_09_C PASS l2test -n -P +TC_COS_CED_BV_10_C N/A +TC_COS_CED_BV_11_C PASS l2test -n -P +TC_COS_CED_BI_01_C PASS + +TC_COS_CFD_BV_01_C PASS l2test -P +TC_COS_CFD_BV_02_C PASS l2test -n -P +TC_COS_CFD_BV_03_C PASS l2test -n -P +TC_COS_CFD_BV_08_C PASS l2test -n -P +TC_COS_CFD_BV_09_C PASS l2test -n -P +TC_COS_CFD_BV_10_C N/A +TC_COS_CFD_BI_11_C PASS l2test -n -P +TC_COS_CFD_BV_12_C PASS l2test -n -P +TC_COS_CFD_BV_13_C N/A +TC_COS_IEX_BV_01_C PASS l2test -n -P +TC_COS_IEX_BV_02_C PASS + +TC_COS_ECH_BV_01_C PASS +TC_COS_ECH_BV_02_C PASS l2ping +TC_CLS_CLR_BV_01_C N/A +TC_CLS_UCD_BV_01_C N/A +TC_CLS_UCD_BV_02_C N/A +TC_CLS_UCD_BV_03_C N/A +TC_EXF_BV_01_C PASS +TC_EXF_BV_02_C PASS +TC_EXF_BV_03_C PASS +TC_EXF_BV_04_C N/A +TC_EXF_BV_05_C PASS +TC_EXF_BV_06_C N/A + +TC_CMC_BV_01_C PASS l2test -X ertm -P +TC_CMC_BV_02_C PASS l2test -X ertm -P +TC_CMC_BV_03_C PASS l2test -X ertm -P +TC_CMC_BV_04_C PASS l2test -X streaming +TC_CMC_BV_05_C PASS l2test -X streaming +TC_CMC_BV_06_C PASS l2test -X streaming +TC_CMC_BV_07_C PASS l2test -X ertm +TC_CMC_BV_08_C PASS l2test -X streaming -P +TC_CMC_BV_09_C PASS l2test -X basic -P +TC_CMC_BV_10_C PASS l2test -n -P +TC_CMC_BV_11_C PASS l2test -n -P +TC_CMC_BV_12_C PASS l2test -s -X ertm -P + -U +TC_CMC_BV_13_C PASS l2test -s -X ertm -P + -U +TC_CMC_BV_14_C PASS l2test -X streaming -P +TC_CMC_BV_15_C PASS l2test -X streaming -P +TC_CMC_BI_01_C PASS l2test -X ertm -P +TC_CMC_BI_02_C PASS l2test -X ertm -P +TC_CMC_BI_03_C PASS l2test -X streaming -P +TC_CMC_BI_04_C PASS l2test -X streaming -P +TC_CMC_BI_05_C PASS l2test -P +TC_CMC_BI_06_C PASS l2test -P +TC_FOC_BV_01_C PASS l2test -X ertm -P -F 0 +TC_FOC_BV_02_C PASS l2test -X ertm -P -F 0 +TC_FOC_BV_03_C PASS l2test -X ertm -P -F 0 +TC_FOC_BV_04_C PASS l2test -X ertm -P -F 0 + +TC_OFS_BV_01_C PASS l2test -x -X ertm -P -F 0 + -N 1 +TC_OFS_BV_02_C PASS l2test -X ertm -P -F 0 +TC_OFS_BV_03_C PASS l2test -X streaming -P -F 0 +TC_OFS_BV_04_C PASS l2test -X streaming -P -F 0 +TC_OFS_BV_05_C PASS l2test -x -X ertm -P -N 1 +TC_OFS_BV_06_C PASS l2test -X ertm -P +TC_OFS_BV_07_C PASS l2test -x -X streaming -P + -F 0 -N 1 +TC_OFS_BV_08_C PASS l2test -X streaming -P +TC_ERM_BV_01_C PASS l2test -x -X ertm -P -N 3 + -Y 3 +TC_ERM_BV_02_C PASS l2test -X ertm -P +TC_ERM_BV_03_C PASS l2test -X ertm -P +TC_ERM_BV_05_C PASS l2test -x -X ertm -P -N 2 + -Y 2 +TC_ERM_BV_06_C PASS l2test -x -X ertm -P -N 2 + -Y 2 +TC_ERM_BV_07_C PASS l2test -X ertm -P +TC_ERM_BV_08_C PASS l2test -x -X ertm -P -N 1 +TC_ERM_BV_09_C PASS l2test -X ertm -P +TC_ERM_BV_10_C PASS l2test -x -X ertm -P -N 1 +TC_ERM_BV_11_C PASS l2test -x -X ertm -P -N 1 + -Q 1 +TC_ERM_BV_12_C PASS l2test -x -X ertm -P -R + -N 1 -Q 1 +TC_ERM_BV_13_C PASS +TC_ERM_BV_14_C PASS +TC_ERM_BV_15_C PASS l2test -X ertm -P -N 4 + -Y 4 +TC_ERM_BV_16_C N/A +TC_ERM_BV_17_C PASS l2test -X ertm -P +TC_ERM_BV_18_C PASS +TC_ERM_BV_19_C PASS l2test -x -X ertm -P -N 1 +TC_ERM_BV_20_C PASS l2test -x -X ertm -P -N 1 +TC_ERM_BV_21_C PASS l2test -w -X ertm -P 4097 -N 3 +TC_ERM_BV_22_C PASS +TC_ERM_BV_23_C PASS +TC_ERM_BI_01_C N/A +TC_ERM_BI_02_C PASS l2test -X ertm -P +TC_ERM_BI_03_C PASS +TC_ERM_BI_04_C PASS +TC_ERM_BI_05_C PASS +TC_STM_BV_01_C PASS l2test -x -X streaming -P + -N 3 -Y 3 +TC_STM_BV_02_C PASS l2test -X streaming -P +TC_STM_BV_03_C PASS +TC_STM_BV_11_C N/A +TC_STM_BV_12_C N/A +TC_STM_BV_13_C N/A +TC_FIX_BV_01_C N/A +TC_FIX_BV_02_C N/A +TC_EWC_BV_01_C N/A +TC_EWC_BV_02_C N/A +TC_EWC_BV_03_C N/A +TC_LSC_BV_01_C N/A +TC_LSC_BV_02_C N/A +TC_LSC_BV_03_C N/A +TC_LSC_BI_04_C N/A +TC_LSC_BI_05_C N/A +TC_LSC_BV_06_C N/A +TC_LSC_BV_07_C N/A +TC_LSC_BV_08_C N/A +TC_LSC_BV_09_C N/A +TC_LSC_BI_10_C N/A +TC_LSC_BI_11_C N/A +TC_LSC_BV_12_C N/A +TC_CCH_BV_01_C N/A +TC_CCH_BV_02_C N/A +TC_CCH_BV_03_C N/A +TC_CCH_BV_04_C N/A +TC_ECF_BV_01_C N/A +TC_ECF_BV_02_C N/A +TC_ECF_BV_03_C N/A +TC_ECF_BV_04_C N/A +TC_ECF_BV_05_C N/A +TC_ECF_BV_06_C N/A +TC_ECF_BV_07_C N/A +TC_ECF_BV_08_C N/A +TC_LE_CPU_BV_01_C N/A +TC_LE_CPU_BV_02_C N/A +TC_LE_CPU_BI_01_C N/A +TC_LE_CPU_BI_02_C N/A +TC_LE_REJ_BV_01_C N/A diff --git a/android/pts-map.txt b/android/pts-map.txt new file mode 100644 index 0000000..b19a678 --- /dev/null +++ b/android/pts-map.txt @@ -0,0 +1,98 @@ +PTS test results for MAP + +PTS version: 5.1 +Tested: 07-April-2014 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_MCE_MSM_BV_01_I N/A +TC_MCE_MSM_BV_02_I N/A +TC_MCE_MSM_BV_03_I N/A +TC_MCE_MSM_BV_04_I N/A +TC_MCE_MSM_BV_13_I N/A +TC_MCE_MSM_BV_14_I N/A +TC_MCE_MNR_BV_01_I N/A +TC_MCE_MNR_BV_02_I N/A +TC_MCE_MMB_BV_01_I N/A +TC_MCE_MMB_BV_02_I N/A +TC_MCE_MMB_BV_03_I N/A +TC_MCE_MMB_BV_19_I N/A +TC_MCE_MMB_BV_04_I N/A +TC_MCE_MMB_BV_17_I N/A +TC_MCE_MMB_BV_06_I N/A +TC_MCE_MMB_BV_07_I N/A +TC_MCE_MMB_BV_08_I N/A +TC_MCE_MMD_BV_01_I N/A +TC_MCE_MMU_BV_01_I N/A +TC_MCE_MMN_BV_01_I N/A +TC_MCE_MMN_BV_03_I N/A +TC_MCE_MMI_BV_01_I N/A +TC_MCE_MFB_BV_01_I N/A +TC_MCE_MFB_BV_03_I N/A +TC_MCE_MFB_BV_04_I N/A +TC_MCE_BC_BV_02_I N/A +TC_MCE_BC_BV_04_I N/A +TC_MCE_CON_BV_01_I N/A +TC_MCE_CON_BV_02_I N/A +TC_MCE_ROB_BV_01_I N/A +TC_MCE_SRM_BV_03_I N/A +TC_MCE_SRM_BV_07_I N/A +TC_MCE_SRMP_BI_01_I N/A +TC_MCE_SRMP_BV_01_I N/A +TC_MCE_SRMP_BV_04_I N/A +TC_MCE_SRMP_BV_05_I N/A +TC_MCE_SRMP_BV_06_I N/A +TC_MSE_MSM_BV_05_I PASS If prompted tester must accept obex request +TC_MSE_MSM_BV_06_I PASS If prompted tester must accept obex request +TC_MSE_MSM_BV_07_I PASS If prompted tester must accept obex request +TC_MSE_MSM_BV_08_I PASS If prompted tester must accept obex request +TC_MSE_MSM_BV_09_I N/A +TC_MSE_MSM_BV_10_I N/A +TC_MSE_MSM_BV_11_I N/A +TC_MSE_MSM_BV_12_I N/A +TC_MSE_MNR_BV_03_I PASS If prompted tester must accept obex request +TC_MSE_MNR_BV_04_I PASS If prompted tester must accept obex request +TC_MSE_MMB_BV_09_I PASS If prompted tester must accept obex request +TC_MSE_MMB_BV_10_I PASS If prompted tester must accept obex request +TC_MSE_MMB_BV_11_I PASS If prompted tester must accept obex request +TC_MSE_MMB_BV_20_I PASS If prompted tester must accept obex request +TC_MSE_MMB_BV_12_I N/A +TC_MSE_MMB_BV_18_I N/A +TC_MSE_MMB_BV_13_I PASS If prompted tester must accept obex request +TC_MSE_MMB_BV_14_I PASS If prompted tester must accept obex request +TC_MSE_MMB_BV_15_I PASS If prompted tester must accept obex request +TC_MSE_MMB_BV_16_I PASS Android MAP server bug, fix provided at + android-review.googlesource.com/#/c/82757 +TC_MSE_MMD_BV_02_I PASS If prompted tester must accept obex request +TC_MSE_MMU_BV_02_I PASS If prompted tester must accept obex request, + at least one SMS must be present in inbox +TC_MSE_MMU_BV_03_I PASS If prompted tester must accept obex request, + when asked by PTS tester must verify that SMS + was successfully delivered onto network +TC_MSE_MMN_BV_02_I PASS If prompted tester must accept obex request, + when asked by PTS tester must send SMS to IUT +TC_MSE_MMN_BV_04_I N/A +TC_MSE_MMI_BV_02_I N/A +TC_MSE_MFB_BV_02_I N/A +TC_MSE_MFB_BV_05_I N/A +TC_MSE_BC_BV_01_I N/A +TC_MSE_BC_BV_03_I N/A +TC_MSE_CON_BV_01_I N/A +TC_MSE_CON_BV_02_I N/A +TC_MSE_ROB_BV_01_I N/A +TC_MSE_SRM_BI_02_I N/A +TC_MSE_SRM_BI_03_I N/A +TC_MSE_SRM_BI_05_I N/A +TC_MSE_SRM_BV_04_I N/A +TC_MSE_SRM_BV_08_I N/A +TC_MSE_SRMP_BI_02_I N/A +TC_MSE_SRMP_BV_02_I N/A +TC_MSE_SRMP_BV_03_I N/A +------------------------------------------------------------------------------- diff --git a/android/pts-mcap.txt b/android/pts-mcap.txt new file mode 100644 index 0000000..9a77839 --- /dev/null +++ b/android/pts-mcap.txt @@ -0,0 +1,77 @@ +PTS test results for MCAP + +PTS version: 5.1 +Tested: not tested +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_MCAP_CE_BV_01_C INC +TC_MCAP_CE_BV_02_C INC +TC_MCAP_CE_BV_03_C INC +TC_MCAP_CE_BV_04_C INC +TC_MCAP_CM_ABT_BV_01_C N/A +TC_MCAP_CM_ABT_BV_02_C INC +TC_MCAP_CM_ABT_BV_03_C N/A +TC_MCAP_CM_DEL_BV_01_C N/A +TC_MCAP_CM_DEL_BV_02_C INC +TC_MCAP_CM_DEL_BV_02_C N/A +TC_MCAP_CM_DEL_BV_04_C INC +TC_MCAP_CM_DIS_BV_01_C INC +TC_MCAP_CM_DIS_BV_02_C INC +TC_MCAP_CM_DIS_BV_03_C INC +TC_MCAP_CM_DIS_BV_04_C INC +TC_MCAP_CM_DIS_BV_05_C INC +TC_MCAP_CM_REC_BV_01_C N/A +TC_MCAP_CM_REC_BV_02_C INC +TC_MCAP_CM_REC_BV_03_C N/A +TC_MCAP_CM_REC_BV_04_C INC +TC_MCAP_CM_REC_BV_05_C N/A +TC_MCAP_CM_REC_BV_06_C INC +TC_MCAP_CS_ERR_BI_01_C N/A +TC_MCAP_CS_ERR_BI_02_C N/A +TC_MCAP_CS_ERR_BI_03_C N/A +TC_MCAP_CS_ERR_BI_04_C N/A +TC_MCAP_CS_I_BV_01_I N/A +TC_MCAP_CS_I_BV_02_I N/A +TC_MCAP_CS_I_BV_03_C N/A +TC_MCAP_CS_I_BV_04_C N/A +TC_MCAP_CS_R_BV_01_I N/A +TC_MCAP_CS_R_BV_02_I N/A +TC_MCAP_CS_R_BV_03_C N/A +TC_MCAP_CS_T_BV_04_C N/A +TC_MCAP_ERR_BI_01_C INC +TC_MCAP_ERR_BI_02_C INC +TC_MCAP_ERR_BI_03_C INC +TC_MCAP_ERR_BI_04_C INC +TC_MCAP_ERR_BI_05_C INC +TC_MCAP_ERR_BI_06_C INC +TC_MCAP_ERR_BI_07_C INC +TC_MCAP_ERR_BI_08_C INC +TC_MCAP_ERR_BI_09_C INC +TC_MCAP_ERR_BI_10_C INC +TC_MCAP_ERR_BI_11_C INC +TC_MCAP_ERR_BI_12_C INC +TC_MCAP_ERR_BI_13_C INC +TC_MCAP_ERR_BI_14_C INC +TC_MCAP_ERR_BI_15_C INC +TC_MCAP_ERR_BI_16_C INC +TC_MCAP_ERR_BI_17_C INC +TC_MCAP_ERR_BI_18_C INC +TC_MCAP_ERR_BI_19_C INC +TC_MCAP_ERR_BI_20_C INC +TC_MCAP_INV_BI_01_C INC +TC_MCAP_INV_BI_02_C INC +TC_MCAP_INV_BI_03_C INC +TC_MCAP_INV_BI_04_C INC +TC_MCAP_INV_BI_05_C INC +TC_MCAP_INV_BI_06_C INC +TC_MCAP_INV_BI_07_C INC +------------------------------------------------------------------------------- diff --git a/android/pts-mps.txt b/android/pts-mps.txt new file mode 100644 index 0000000..005318d --- /dev/null +++ b/android/pts-mps.txt @@ -0,0 +1,53 @@ +PTS test results for MPS + +PTS version: 5.1 +Tested: not tested +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup +NONE test result is none + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_AG_SRC_HFAV_ACT_SD_BV_01_I INC +TC_AG_SRC_HFAV_ACT_SD_BV_02_I INC +TC_AG_SRC_HFAV_ACT_SD_BV_03_I INC +TC_AG_SRC_HFAV_CLH_SD_BV_01_I INC +TC_AG_SRC_HFAV_CLH_SD_BV_02_I N/A +TC_AG_SRC_HFAV_CLH_SD_BV_03_I INC +TC_AG_SRC_HFAV_CLH_SD_BV_04_I INC +TC_AG_SRC_HFAV_CLH_SD_BV_05_I INC +TC_AG_SRC_HFAV_CLH_SD_BV_06_I INC +TC_AVP_CTH_SD_BI_01_I INC +TC_AVP_CTH_SD_BI_02_I INC +TC_HF_SNK_HFAV_ACT_SD_BV_01_I N/A +TC_HF_SNK_HFAV_ACT_SD_BV_02_I N/A +TC_HF_SNK_HFAV_ACT_SD_BV_03_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_01_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_02_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_03_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_04_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_05_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_06_I N/A +TC_HF_SNK_CT_HFAV_ACT_MD_BV_01_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_01_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_02_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_03_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_04_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_05_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_06_I N/A +TC_SDP_CTH_SD_BV_01_I INC +TC_SRC_TG_HFAV_ACT_MD_BV_01_I INC +TC_SRC_TG_HFAV_CLH_MD_BV_01_I INC +TC_SRC_TG_HFAV_CLH_MD_BV_02_I INC +TC_SRC_TG_HFAV_CLH_MD_BV_03_I INC +TC_SRC_TG_HFAV_CLH_MD_BV_04_I INC +TC_SRC_TG_HFAV_CLH_MD_BV_05_I INC +TC_SRC_TG_HFAV_CLH_MD_BV_06_I INC +TC_PAIRING_HF_SNK_CT INC +------------------------------------------------------------------------------- diff --git a/android/pts-opp.txt b/android/pts-opp.txt new file mode 100644 index 0000000..0a070ca --- /dev/null +++ b/android/pts-opp.txt @@ -0,0 +1,133 @@ +PTS test results for OPP + +PTS version: 5.1 +Tested: 17-April-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup +NONE test result is none + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_CLIENT_BC_BV_02_I N/A +TC_CLIENT_BC_BV_04_I N/A +TC_CLIENT_BCE_BV_01_I N/A +TC_CLIENT_BCE_BV_03_I N/A +TC_CLIENT_BCE_BV_04_I N/A +TC_CLIENT_BCE_BV_05_I N/A +TC_CLIENT_BCE_BV_06_I N/A +TC_CLIENT_BCE_BV_07_I N/A +TC_CLIENT_BCP_BV_01_I N/A +TC_CLIENT_BCP_BV_02_I N/A +TC_CLIENT_BCP_BV_03_I N/A +TC_CLIENT_BCP_BV_04_I N/A +TC_CLIENT_BCP_BV_05_I N/A +TC_CLIENT_CON_BV_01_C N/A +TC_CLIENT_OPH_BI_01_C PASS PTS issue: ID: 12081 (bluetooth.org) + ETS-12081 required +TC_CLIENT_OPH_BV_01_I PASS PTS issue: ID: 12081. + ETS-12160 required +TC_CLIENT_OPH_BV_02_I N/A +TC_CLIENT_OPH_BV_03_I PASS ETS-12081 required (bluetooth.org) +TC_CLIENT_OPH_BV_04_I N/A +TC_CLIENT_OPH_BV_05_I PASS +TC_CLIENT_OPH_BV_07_I N/A +TC_CLIENT_OPH_BV_08_I N/A +TC_CLIENT_OPH_BV_09_I N/A +TC_CLIENT_OPH_BV_10_I N/A +TC_CLIENT_OPH_BV_11_I N/A +TC_CLIENT_OPH_BV_12_I N/A +TC_CLIENT_OPH_BV_13_I N/A +TC_CLIENT_OPH_BV_14_I N/A +TC_CLIENT_OPH_BV_15_I N/A +TC_CLIENT_OPH_BV_16_I N/A +TC_CLIENT_OPH_BV_17_I N/A +TC_CLIENT_OPH_BV_18_I N/A +TC_CLIENT_OPH_BV_19_I PASS ETS-12081 required +TC_CLIENT_OPH_BV_20_I PASS +TC_CLIENT_OPH_BV_22_I PASS ETS-12081 required +TC_CLIENT_OPH_BV_23_I PASS ETS-12081 required +TC_CLIENT_OPH_BV_24_I N/A +TC_CLIENT_OPH_BV_25_I N/A +TC_CLIENT_OPH_BV_26_I N/A +TC_CLIENT_SRM_BV_01_C N/A +TC_CLIENT_SRM_BV_03_C N/A +TC_CLIENT_SRM_BV_05_C N/A +TC_CLIENT_SRM_BV_07_C N/A +TC_CLIENT_SRMP_BI_01_C N/A +TC_CLIENT_SRMP_BV_01_C N/A +TC_CLIENT_SRMP_BV_04_C N/A +TC_CLIENT_SRMP_BV_05_C N/A +TC_CLIENT_SRMP_BV_06_C N/A +TC_SERVER_BC_BV_01_I N/A +TC_SERVER_BC_BV_03_I N/A +TC_SERVER_BCE_BV_01_I N/A +TC_SERVER_BCE_BV_03_I N/A +TC_SERVER_BCE_BV_04_I N/A +TC_SERVER_BCE_BV_05_I N/A +TC_SERVER_BCE_BV_06_I N/A +TC_SERVER_BCE_BV_07_I N/A +TC_SERVER_BCP_BV_01_I N/A +TC_SERVER_BCP_BV_02_I PASS ETS-12081 required (bluetooth.org) +TC_SERVER_BCP_BV_03_I N/A +TC_SERVER_BCP_BV_04_I N/A +TC_SERVER_BCP_BV_05_I N/A +TC_SERVER_CON_BV_02_C N/A +TC_SERVER_OPH_BV_01_I PASS +TC_SERVER_OPH_BV_02_I PASS +TC_SERVER_OPH_BV_03_I PASS ETS-12081 required + IUT must be in the connectable mode. Tester must + accept incoming file +TC_SERVER_OPH_BV_04_I PASS ETS-12081 required + IUT must be in the connectable mode. Tester must + accept incoming file +TC_SERVER_OPH_BV_05_I PASS ETS-12081 required + IUT must be in the connectable mode. Tester must + reject incoming file +TC_SERVER_OPH_BV_07_I N/A +TC_SERVER_OPH_BV_08_I N/A +TC_SERVER_OPH_BV_09_I N/A +TC_SERVER_OPH_BV_10_I PASS ETS-12081 required + IUT must be in the connectable mode +TC_SERVER_OPH_BV_11_I N/A +TC_SERVER_OPH_BV_12_I N/A +TC_SERVER_OPH_BV_13_I N/A +TC_SERVER_OPH_BV_14_I PASS ETS-12081 required + IUT must be in the connectable mode +TC_SERVER_OPH_BV_15_I N/A +TC_SERVER_OPH_BV_16_I N/A +TC_SERVER_OPH_BV_17_I N/A +TC_SERVER_OPH_BV_18_I PASS ETS-12081 required + IUT must be in the connectable mode +TC_SERVER_OPH_BV_19_I PASS ETS-12081 required + IUT must be in the connectable mode +TC_SERVER_OPH_BV_21_I N/A +TC_SERVER_OPH_BV_22_I PASS ETS-12081 required + IUT must be in the connectable mode +TC_SERVER_OPH_BV_23_I PASS ETS-12081 required + IUT must be in the connectable mode +TC_SERVER_OPH_BV_24_I N/A +TC_SERVER_OPH_BV_25_I N/A +TC_SERVER_OPH_BV_26_I N/A +TC_SERVER_ROB_BV_01_I N/A +TC_SERVER_ROB_BV_02_I N/A +TC_SERVER_SRM_BI_03_I N/A +TC_SERVER_SRM_BI_05_I N/A +TC_SERVER_SRM_BV_04_I N/A +TC_SERVER_SRM_BV_08_I N/A +TC_SERVER_SRMP_BV_02_I N/A +TC_SERVER_SRMP_BV_03_I N/A +TC_CLIENT_OPH_BV_27_I N/A +TC_CLIENT_OPH_BV_34_I PASS +TC_SERVER_OPH_BV_27_I N/A +TC_SERVER_OPH_BV_30_I N/A +TC_SERVER_OPH_BV_31_I N/A +TC_SERVER_OPH_BV_32_I N/A +TC_SERVER_OPH_BV_33_I N/A +TC_SERVER_OPH_BV_34_I PASS ETS-12081 required +------------------------------------------------------------------------------- diff --git a/android/pts-pan.txt b/android/pts-pan.txt new file mode 100644 index 0000000..88f903d --- /dev/null +++ b/android/pts-pan.txt @@ -0,0 +1,82 @@ +PTS test results for PAN + +PTS version: 5.1 +Tested: 07-April-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +-------------------------------------------------------------------------------- +Test Name Result Notes +-------------------------------------------------------------------------------- +TC_BNEP_GN_BROADCAST_0_BV_03_C N/A +TC_GN_Ipv4_Autonet_BV_01_I N/A +TC_GN_Ipv6_Autonet_BV_02_I N/A +TC_GN_IP_DHCP_BV_03_I N/A +TC_GN_IP_LLMNR_BV_01_I N/A +TC_GN_IP_LLMNR_BV_02_I N/A +TC_GN_IP_DNS_BV_01_I N/A +TC_GN_IP_APP_BV_01_I N/A +TC_GN_IP_APP_BV_02_I N/A +TC_GN_IP_APP_BV_03_I N/A +TC_GN_IP_APP_BV_04_I N/A +TC_GN_IP_APP_BV_05_I N/A +TC_SDP_GN_BV_02_C N/A +TC_MISC_GN_UUID_BV_01_C N/A +TC_MISC_GN_UUID_BV_02_C N/A +TC_BNEP_NAP_BROADCAST_0_BV_01_C N/A +TC_BNEP_NAP_BROADCAST_0_BV_02_C N/A +TC_BNEP_NAP_FORWARD_UNICAST_BV_05_C N/A +TC_BNEP_NAP_FORWARD_UNICAST_BV_06_C N/A +TC_BNEP_NAP_MULTICAST_0_BV_03_C N/A +TC_BNEP_NAP_MULTICAST_0_BV_04_C N/A +TC_BNEP_BRIDGE_RX_BV_02_I PASS Tester needs to send one + forward-unicast BNEP packet + with given data (5 times) + 0x00, 0x01, 0xFF followed by + 0x00, 0x01, 0x9E, 0x9F + sendip -d +TC_BNEP_BRIDGE_TX_BV_01_I PASS +TC_NAP_Ipv4_Autonet_BV_01_I N/A +TC_NAP_Ipv6_Autonet_BV_02_I N/A +TC_NAP_IP_DHCP_BV_03_I N/A +TC_NAP_IP_LLMNR_BV_01_I N/A +TC_NAP_IP_LLMNR_BV_02_I N/A +TC_NAP_IP_DNS_BV_01_I N/A +TC_NAP_IP_APP_BV_01_I N/A +TC_NAP_IP_APP_BV_02_I N/A +TC_NAP_IP_APP_BV_03_I N/A +TC_NAP_IP_APP_BV_04_I N/A +TC_NAP_IP_APP_BV_05_I N/A +TC_SDP_NAP_BV_01_C PASS +TC_MISC_NAP_UUID_BV_01_C PASS +TC_MISC_NAP_UUID_BV_02_C PASS +TC_BNEP_PANU_BROADCAST_0_BV_04_C N/A +TC_PANU_Ipv4_Autonet_BV_01_I PASS When prompted connect to PTS. + From IUT: issue ARP request + iperf -c -p +TC_PANU_Ipv6_Autonet_BV_02_I N/A +TC_PANU_IP_LLMNR_BV_01_I PASS When prompted connect to PTS. + From IUT: send LLMNR request + command + iperf -c -p +TC_PANU_IP_LLMNR_BV_02_I N/A +TC_PANU_IP_DHCP_BV_03_I N/A +TC_PANU_IP_DNS_BV_01_I N/A +TC_PANU_IP_APP_BV_01_I N/A +TC_PANU_IP_APP_BV_02_I N/A +TC_PANU_IP_APP_BV_03_I N/A +TC_PANU_IP_APP_BV_04_I N/A +TC_PANU_IP_APP_BV_05_I PASS When prompted, connect to PTS + and then when prompted terminate + connection (IUT side) +TC_SDP_PANU_BV_01_C N/A +TC_MISC_PANU_UUID_BV_01_C N/A +TC_MISC_PANU_UUID_BV_02_C N/A +TC_MISC_ROLE_BV_01_C N/A +TC_MISC_ROLE_BV_BV_02_C N/A +-------------------------------------------------------------------------------- diff --git a/android/pts-pbap.txt b/android/pts-pbap.txt new file mode 100644 index 0000000..cd6dcb6 --- /dev/null +++ b/android/pts-pbap.txt @@ -0,0 +1,143 @@ +PTS test results for PBAP + +PTS version: 5.1 +Tested: 08-April-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_PCE_SSM_BV_01_C N/A +TC_PCE_SSM_BV_02_C N/A +TC_PCE_SSM_BV_06_C N/A +TC_PCE_SSM_BV_08_C N/A +TC_PCE_SSM_BI_01_C N/A +TC_PCE_SSM_BV_09_C N/A +TC_PCE_SSM_BV_10_C N/A +TC_PCE_PBD_BV_01_C N/A +TC_PCE_PBD_BV_04_C N/A +TC_PCE_PBD_BV_38_C N/A +TC_PCE_PBD_BV_29_C N/A +TC_PCE_PBD_BV_40_C N/A +TC_PCE_PBD_BV_41_C N/A +TC_PCE_PBD_BV_42_C N/A +TC_PCE_PBD_BV_43_C N/A +TC_PCE_PBD_BV_44_C N/A +TC_PCE_PBD_BV_45_C N/A +TC_PCE_PBD_BV_46_C N/A +TC_PCE_PBD_BV_47_C N/A +TC_PCE_PBD_BV_48_C N/A +TC_PCE_PBB_BV_01_C N/A +TC_PCE_PBB_BV_02_C N/A +TC_PCE_PBB_BV_03_C N/A +TC_PCE_PBB_BV_05_C N/A +TC_PCE_PBB_BV_39_C N/A +TC_PCE_PBB_BV_40_C N/A +TC_PCE_PBB_BV_41_C N/A +TC_PCE_PBB_BV_42_C N/A +TC_PCE_PBB_BV_33_C N/A +TC_PCE_PBB_BV_34_C N/A +TC_PCE_PBB_BV_35_C N/A +TC_PCE_PBB_BV_36_C N/A +TC_PCE_PBB_BV_43_C N/A +TC_PCE_PBB_BV_37_C N/A +TC_PCE_PBB_BV_38_C N/A +TC_PCE_PBF_BV_01_I N/A +TC_PCE_PBF_BV_02_I N/A +TC_PCE_PBF_BV_03_I N/A +TC_PCE_PDF_BV_01_I N/A +TC_PCE_PDF_BV_06_I N/A +TC_PSE_SSM_BV_03_C PASS Tester must accept obex request +TC_PSE_SSM_BV_05_C PASS +TC_PSE_SSM_BV_07_C PASS Tester must accept obex request with + TSPX_auth_password set in PIXITs +TC_PSE_SSM_BI_02_C PASS +TC_PSE_SSM_BI_03_C N/A +TC_PSE_SSM_BV_08_I PASS Tester must compare passkey on IUT and PTS +TC_PSE_SSM_BV_11_C N/A + +TC_PSE_PBD_BV_02_C PASS Tester must compare phone book size with the + value given by PTS +TC_PSE_PBD_BV_03_C PASS Tester must compare phone book size with the + value given by PTS +TC_PSE_PBD_BV_05_C N/A +TC_PSE_PBD_BI_01_C FAIL Android PBAP server bug +TC_PSE_PBD_BV_06_C N/A +TC_PSE_PBD_BV_07_C N/A +TC_PSE_PBD_BV_08_C N/A +TC_PSE_PBD_BV_09_C N/A +TC_PSE_PBD_BV_10_C N/A +TC_PSE_PBD_BV_17_C PASS +TC_PSE_PBD_BV_18_C N/A +TC_PSE_PBD_BV_19_C N/A +TC_PSE_PBD_BV_20_C N/A +TC_PSE_PBD_BV_21_C N/A +TC_PSE_PBD_BV_22_C N/A +TC_PSE_PBD_BV_23_C N/A +TC_PSE_PBD_BV_24_C N/A +TC_PSE_PBD_BV_25_C N/A +TC_PSE_PBD_BV_26_C N/A +TC_PSE_PBD_BV_27_C N/A +TC_PSE_PBD_BV_28_C N/A +TC_PSE_PBD_BV_29_C N/A +TC_PSE_PBD_BV_30_C N/A +TC_PSE_PBD_BV_31_C N/A +TC_PSE_PBD_BV_32_C N/A +TC_PSE_PBD_BV_33_C N/A +TC_PSE_PBD_BV_34_C N/A +TC_PSE_PBD_BV_35_C N/A +TC_PSE_PBD_BV_36_C PASS +TC_PSE_PBD_BV_37_C N/A +TC_PSE_PBB_BV_06_C PASS +TC_PSE_PBB_BV_07_C PASS +TC_PSE_PBB_BV_08_C PASS Tester must compare phone book size with the + value given by PTS +TC_PSE_PBB_BV_09_C PASS +TC_PSE_PBB_BV_10_C PASS Tester must verify vcard content received by PTS +TC_PSE_PBB_BV_11_C PASS Tester must verify number of new missed calls + with value given by PTS +TC_PSE_PBB_BI_01_C PASS +TC_PSE_PBB_BI_07_C PASS +TC_PSE_PBB_BV_12_C PASS +TC_PSE_PBB_BV_13_C N/A +TC_PSE_PBB_BV_14_C N/A +TC_PSE_PBB_BV_15_C N/A +TC_PSE_PBB_BV_16_C N/A +TC_PSE_PBB_BV_17_C N/A +TC_PSE_PBB_BV_18_C N/A +TC_PSE_PBB_BV_19_C N/A +TC_PSE_PBB_BV_20_C N/A +TC_PSE_PBB_BV_21_C N/A +TC_PSE_PBB_BV_22_C N/A +TC_PSE_PBB_BV_23_C N/A +TC_PSE_PBB_BV_24_C N/A +TC_PSE_PBB_BV_25_C N/A +TC_PSE_PBB_BV_26_C N/A +TC_PSE_PBB_BV_27_C N/A +TC_PSE_PBB_BV_44_C N/A +TC_PSE_PBB_BV_45_C N/A +TC_PSE_PBB_BV_46_C N/A +TC_PSE_PBB_BV_28_C N/A +TC_PSE_PBB_BV_29_C N/A +TC_PSE_PBB_BV_30_C N/A +TC_PSE_PBB_BV_31_C PASS +TC_PSE_PBB_BV_32_C N/A +TC_PSE_PBF_BV_01_I PASS +TC_PSE_PBF_BV_02_I PASS Tester must verify vcard content received by PTS +TC_PSE_PDF_BV_01_I PASS Tester must compare phone book size with the + value given by PTS +TC_PSE_BC_BV_03_I N/A +TC_PSE_CON_BV_02_I N/A +TC_PSE_ROB_BV_01_I N/A +TC_PSE_SRM_BI_03_I N/A +TC_PSE_SRM_BI_05_I N/A +TC_PSE_SRM_BV_08_I N/A +TC_PSE_SRMP_BI_02_I N/A +TX_PSE_SRMP_BV_02_I N/A +------------------------------------------------------------------------------- diff --git a/android/pts-sm.txt b/android/pts-sm.txt new file mode 100644 index 0000000..0792de8 --- /dev/null +++ b/android/pts-sm.txt @@ -0,0 +1,63 @@ +PTS test results for SM + +PTS version: 5.1 +Tested: 14-May-2014 +Android version: 4.4.2 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup +NONE test result is none + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_PROT_BV_01_C PASS +TC_PROT_BV_02_C FAIL JIRA: BZ-24 + btmgmt advertising on +TC_JW_BV_01_C FAIL JIRA: BZ-25 +TC_JW_BV_02_C INC JIRA: BZ-26 +TC_JW_BV_05_C INC +TC_JW_BI_01_C PASS +TC_JW_BI_02_C INC +TC_JW_BI_03_C INC +TC_JW_BI_04_C FAIL +TC_PKE_BV_01_C FAIL +TC_PKE_BV_02_C INC +TC_PKE_BV_04_C FAIL +TC_PKE_BV_05_C INC +TC_PKE_BI_01_C INC +TC_PKE_BI_02_C PASS +TC_PKE_BI_03_C INC +TC_OOB_BV_01_C FAIL +TC_OOB_BV_02_C INC +TC_OOB_BV_03_C FAIL +TC_OOB_BV_04_C INC +TC_OOB_BV_05_C INC +TC_OOB_BV_06_C INC +TC_OOB_BV_07_C INC +TC_OOB_BV_08_C INC +TC_OOB_BV_09_C INC +TC_OOB_BV_10_C INC +TC_OOB_BI_01_C INC +TC_OOB_BI_02_C INC +TC_EKS_BV_01_C INC +TC_EKS_BV_02_C INC +TC_EKS_BI_01_C INC +TC_EKS_BI_02_C INC +TC_SIGN_BV_01_C INC +TC_SIGN_BV_03_C INC +TC_SIGN_BI_01_C INC +TC_KDU_BV_01_C INC +TC_KDU_BV_02_C INC +TC_KDU_BV_03_C INC +TC_KDU_BV_04_C INC +TC_KDU_BV_05_C INC +TC_KDU_BV_06_C INC +TC_KDU_BV_07_C INC +TC_SIP_BV_01_C INC +TC_SIP_BV_02_C FAIL +TC_SIE_BV_01_C INC +------------------------------------------------------------------------------- diff --git a/android/sco-msg.h b/android/sco-msg.h new file mode 100644 index 0000000..df0d858 --- /dev/null +++ b/android/sco-msg.h @@ -0,0 +1,36 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 + * + */ + +static const char BLUEZ_SCO_SK_PATH[] = "\0bluez_sco_socket"; + +#define SCO_SERVICE_ID 0 + +#define SCO_STATUS_SUCCESS IPC_STATUS_SUCCESS +#define SCO_STATUS_FAILED 0x01 + +#define SCO_OP_STATUS IPC_OP_STATUS + +#define SCO_OP_CONNECT 0x01 +struct sco_rsp_connect { + uint16_t mtu; +} __attribute__((packed)); diff --git a/android/socket.c b/android/socket.c index 9020874..99d6bec 100644 --- a/android/socket.c +++ b/android/socket.c @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -36,106 +36,166 @@ #include "lib/sdp_lib.h" #include "src/sdp-client.h" #include "src/sdpd.h" +#include "src/log.h" -#include "bluetooth.h" -#include "log.h" #include "hal-msg.h" -#include "hal-ipc.h" +#include "ipc-common.h" #include "ipc.h" #include "utils.h" +#include "bluetooth.h" #include "socket.h" -#define SPP_DEFAULT_CHANNEL 3 +#define RFCOMM_CHANNEL_MAX 30 + #define OPP_DEFAULT_CHANNEL 9 +#define HSP_AG_DEFAULT_CHANNEL 12 +#define HFP_AG_DEFAULT_CHANNEL 13 #define PBAP_DEFAULT_CHANNEL 15 -#define MAS_DEFAULT_CHANNEL 16 +#define MAP_MAS_DEFAULT_CHANNEL 16 #define SVC_HINT_OBEX 0x10 -static bdaddr_t adapter_addr; - -/* Simple list of RFCOMM server sockets */ -GList *servers = NULL; +/* Hardcoded MAP stuff needed for MAS SMS Instance.*/ +#define DEFAULT_MAS_INSTANCE 0x00 -/* Simple list of RFCOMM connected sockets */ -GList *connections = NULL; +#define MAP_MSG_TYPE_SMS_GSM 0x02 +#define MAP_MSG_TYPE_SMS_CDMA 0x04 +#define DEFAULT_MAS_MSG_TYPE (MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA) +static struct ipc *hal_ipc = NULL; struct rfcomm_sock { - int fd; /* descriptor for communication with Java framework */ - int real_sock; /* real RFCOMM socket */ int channel; /* RFCOMM channel */ + BtIOSecLevel sec_level; + + /* for socket to BT */ + int bt_sock; + guint bt_watch; - guint rfcomm_watch; - guint stack_watch; + /* for socket to HAL */ + int jv_sock; + guint jv_watch; bdaddr_t dst; uint32_t service_handle; - const struct profile_info *profile; + uint8_t *buf; + int buf_size; }; -static struct rfcomm_sock *create_rfsock(int sock, int *hal_fd) -{ - int fds[2] = {-1, -1}; +struct rfcomm_channel { + bool reserved; struct rfcomm_sock *rfsock; +}; - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) { - error("socketpair(): %s", strerror(errno)); - *hal_fd = -1; - return NULL; +static bdaddr_t adapter_addr; + +static const uint8_t zero_uuid[16] = { 0 }; + +/* Simple list of RFCOMM connected sockets */ +static GList *connections = NULL; + +static struct rfcomm_channel servers[RFCOMM_CHANNEL_MAX + 1]; + +static int rfsock_set_buffer(struct rfcomm_sock *rfsock) +{ + socklen_t len = sizeof(int); + int rcv, snd, size, err; + + err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_RCVBUF, &rcv, &len); + if (err < 0) { + int err = -errno; + error("getsockopt(SO_RCVBUF): %s", strerror(-err)); + return err; } - rfsock = g_new0(struct rfcomm_sock, 1); - rfsock->fd = fds[0]; - *hal_fd = fds[1]; - rfsock->real_sock = sock; + err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_SNDBUF, &snd, &len); + if (err < 0) { + int err = -errno; + error("getsockopt(SO_SNDBUF): %s", strerror(-err)); + return err; + } - return rfsock; + size = MAX(rcv, snd); + + DBG("Set buffer size %d", size); + + rfsock->buf = g_malloc(size); + rfsock->buf_size = size; + + return 0; } static void cleanup_rfsock(gpointer data) { struct rfcomm_sock *rfsock = data; - DBG("rfsock: %p fd %d real_sock %d chan %u", - rfsock, rfsock->fd, rfsock->real_sock, rfsock->channel); + DBG("rfsock %p bt_sock %d jv_sock %d", rfsock, rfsock->bt_sock, + rfsock->jv_sock); - if (rfsock->fd >= 0) - if (close(rfsock->fd) < 0) - error("close() fd %d failed: %s", rfsock->fd, + if (rfsock->jv_sock >= 0) + if (close(rfsock->jv_sock) < 0) + error("close() fd %d failed: %s", rfsock->jv_sock, strerror(errno)); - if (rfsock->real_sock >= 0) - if (close(rfsock->real_sock) < 0) - error("close() fd %d: failed: %s", rfsock->real_sock, + if (rfsock->bt_sock >= 0) + if (close(rfsock->bt_sock) < 0) + error("close() fd %d: failed: %s", rfsock->bt_sock, strerror(errno)); - if (rfsock->rfcomm_watch > 0) - if (!g_source_remove(rfsock->rfcomm_watch)) - error("rfcomm_watch source was not found"); + if (rfsock->bt_watch > 0) + if (!g_source_remove(rfsock->bt_watch)) + error("bt_watch source was not found"); - if (rfsock->stack_watch > 0) - if (!g_source_remove(rfsock->stack_watch)) + if (rfsock->jv_watch > 0) + if (!g_source_remove(rfsock->jv_watch)) error("stack_watch source was not found"); if (rfsock->service_handle) bt_adapter_remove_record(rfsock->service_handle); + if (rfsock->buf) + g_free(rfsock->buf); + g_free(rfsock); } -static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name) +static struct rfcomm_sock *create_rfsock(int bt_sock, int *hal_sock) { - const char *service_name = "OBEX Object Push"; - 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]; - 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 fds[2] = {-1, -1}; + struct rfcomm_sock *rfsock; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) { + error("socketpair(): %s", strerror(errno)); + *hal_sock = -1; + return NULL; + } + + rfsock = g_new0(struct rfcomm_sock, 1); + rfsock->jv_sock = fds[0]; + *hal_sock = fds[1]; + rfsock->bt_sock = bt_sock; + + DBG("rfsock %p", rfsock); + + if (bt_sock < 0) + return rfsock; + + if (rfsock_set_buffer(rfsock) < 0) { + cleanup_rfsock(rfsock); + return NULL; + } + + return rfsock; +} + +static sdp_record_t *create_rfcomm_record(uint8_t chan, uuid_t *uuid, + const char *svc_name, + bool has_obex) +{ + sdp_list_t *svclass_id; + sdp_list_t *seq, *proto_seq, *pbg_seq; + sdp_list_t *proto[3]; + uuid_t l2cap_uuid, rfcomm_uuid, obex_uuid, pbg_uuid; sdp_data_t *channel; sdp_record_t *record; @@ -145,196 +205,165 @@ static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name) record->handle = sdp_next_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(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID); - svclass_id = sdp_list_append(NULL, &opush_uuid); + svclass_id = sdp_list_append(NULL, 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(NULL, profile); - sdp_set_profile_descs(record, pfseq); - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap_uuid); - apseq = sdp_list_append(NULL, proto[0]); + seq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm_uuid); channel = sdp_data_alloc(SDP_UINT8, &chan); proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); + seq = sdp_list_append(seq, proto[1]); - sdp_uuid16_create(&obex_uuid, OBEX_UUID); - proto[2] = sdp_list_append(NULL, &obex_uuid); - apseq = sdp_list_append(apseq, proto[2]); + if (has_obex) { + sdp_uuid16_create(&obex_uuid, OBEX_UUID); + proto[2] = sdp_list_append(NULL, &obex_uuid); + seq = sdp_list_append(seq, proto[2]); + } - aproto = sdp_list_append(NULL, apseq); - sdp_set_access_protos(record, aproto); + proto_seq = sdp_list_append(NULL, seq); + sdp_set_access_protos(record, proto_seq); - 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_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP); + pbg_seq = sdp_list_append(NULL, &pbg_uuid); + sdp_set_browse_groups(record, pbg_seq); if (svc_name) - service_name = svc_name; - - sdp_set_info_attr(record, service_name, NULL, NULL); + sdp_set_info_attr(record, svc_name, NULL, NULL); sdp_data_free(channel); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); - sdp_list_free(proto[2], NULL); - sdp_list_free(apseq, NULL); - sdp_list_free(pfseq, NULL); - sdp_list_free(aproto, NULL); - sdp_list_free(root, NULL); + if (has_obex) + sdp_list_free(proto[2], NULL); + sdp_list_free(seq, NULL); + sdp_list_free(proto_seq, NULL); + sdp_list_free(pbg_seq, NULL); sdp_list_free(svclass_id, NULL); return record; } -static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name) +static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name) { - const char *service_name = "OBEX Phonebook Access Server"; - 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_data_t *channel; - uint8_t formats[] = { 0x01 }; + uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff }; uint8_t dtd = SDP_UINT8; - sdp_data_t *sflist; + uuid_t uuid; + sdp_list_t *seq; + sdp_profile_desc_t profile[1]; + void *dtds[sizeof(formats)], *values[sizeof(formats)]; + sdp_data_t *formats_list; sdp_record_t *record; + size_t i; - record = sdp_record_alloc(); + sdp_uuid16_create(&uuid, OBEX_OBJPUSH_SVCLASS_ID); + + record = create_rfcomm_record(chan, &uuid, svc_name, true); if (!record) return NULL; - record->handle = sdp_next_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(&pbap_uuid, PBAP_PSE_SVCLASS_ID); - svclass_id = sdp_list_append(NULL, &pbap_uuid); - sdp_set_service_classes(record, svclass_id); - - sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID); + sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID); profile[0].version = 0x0100; - pfseq = sdp_list_append(NULL, profile); - sdp_set_profile_descs(record, pfseq); + seq = sdp_list_append(NULL, profile); + sdp_set_profile_descs(record, seq); - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(NULL, &l2cap_uuid); - apseq = sdp_list_append(NULL, proto[0]); + for (i = 0; i < sizeof(formats); i++) { + dtds[i] = &dtd; + values[i] = &formats[i]; + } + formats_list = sdp_seq_alloc(dtds, values, sizeof(formats)); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, formats_list); - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(NULL, &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_list_free(seq, NULL); - sdp_uuid16_create(&obex_uuid, OBEX_UUID); - proto[2] = sdp_list_append(NULL, &obex_uuid); - apseq = sdp_list_append(apseq, proto[2]); + return record; +} - aproto = sdp_list_append(NULL, apseq); - sdp_set_access_protos(record, aproto); +static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name) +{ + sdp_list_t *seq; + sdp_profile_desc_t profile[1]; + uint8_t formats = 0x01; + sdp_record_t *record; + uuid_t uuid; - sflist = sdp_data_alloc(dtd, formats); - sdp_attr_add(record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist); + sdp_uuid16_create(&uuid, PBAP_PSE_SVCLASS_ID); - if (svc_name) - service_name = svc_name; + record = create_rfcomm_record(chan, &uuid, svc_name, true); + if (!record) + return NULL; - sdp_set_info_attr(record, service_name, NULL, NULL); + sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID); + profile[0].version = 0x0101; + seq = sdp_list_append(NULL, profile); + sdp_set_profile_descs(record, seq); - sdp_data_free(channel); - sdp_list_free(proto[0], NULL); - sdp_list_free(proto[1], NULL); - sdp_list_free(proto[2], NULL); - sdp_list_free(apseq, NULL); - sdp_list_free(pfseq, NULL); - sdp_list_free(aproto, NULL); - sdp_list_free(root, NULL); - sdp_list_free(svclass_id, NULL); + sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_REPOSITORIES, SDP_UINT8, + &formats); + + sdp_list_free(seq, NULL); return record; } -static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name) +static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name) { - const char *service_name = "Serial Port"; - sdp_list_t *svclass_id, *apseq, *profiles, *root; - uuid_t root_uuid, sp_uuid, l2cap, rfcomm; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_data_t *channel; + sdp_list_t *seq; + sdp_profile_desc_t profile[1]; + uint8_t minst = DEFAULT_MAS_INSTANCE; + uint8_t mtype = DEFAULT_MAS_MSG_TYPE; sdp_record_t *record; + uuid_t uuid; - record = sdp_record_alloc(); + sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID); + + record = create_rfcomm_record(chan, &uuid, svc_name, true); if (!record) return NULL; - record->handle = sdp_next_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(&profile[0].uuid, MAP_PROFILE_ID); + profile[0].version = 0x0101; + seq = sdp_list_append(NULL, profile); + sdp_set_profile_descs(record, seq); - sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID); - svclass_id = sdp_list_append(NULL, &sp_uuid); - sdp_set_service_classes(record, svclass_id); + sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst); + sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8, + &mtype); - 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(seq, 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); - channel = sdp_data_alloc(SDP_UINT8, &chan); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); + return record; +} - aproto = sdp_list_append(NULL, apseq); - sdp_set_access_protos(record, aproto); +static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name) +{ + sdp_record_t *record; + uuid_t uuid; - sdp_add_lang_attr(record); + sdp_uuid16_create(&uuid, SERIAL_PORT_SVCLASS_ID); - if (svc_name) - service_name = svc_name; + record = create_rfcomm_record(chan, &uuid, svc_name, false); + if (!record) + return NULL; - sdp_set_info_attr(record, service_name, "BlueZ", "COM Port"); + return record; +} - sdp_set_url_attr(record, "http://www.bluez.org/", - "http://www.bluez.org/", "http://www.bluez.org/"); +static sdp_record_t *create_app_record(uint8_t chan, + const uint8_t *app_uuid, + const char *svc_name) +{ + sdp_record_t *record; + uuid_t uuid; - 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); + sdp_uuid128_create(&uuid, app_uuid); - sdp_data_free(channel); - sdp_list_free(proto[0], NULL); - sdp_list_free(proto[1], NULL); - sdp_list_free(apseq, NULL); - sdp_list_free(aproto, NULL); - sdp_list_free(root, NULL); - sdp_list_free(svclass_id, NULL); - sdp_list_free(profiles, NULL); + record = create_rfcomm_record(chan, &uuid, svc_name, false); + if (!record) + return NULL; return record; } @@ -348,6 +377,24 @@ static const struct profile_info { } profiles[] = { { .uuid = { + 0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = HSP_AG_DEFAULT_CHANNEL, + .svc_hint = 0, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = NULL + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x1F, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = HFP_AG_DEFAULT_CHANNEL, + .svc_hint = 0, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = NULL + }, { + .uuid = { 0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, @@ -369,35 +416,40 @@ static const struct profile_info { 0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, - .channel = MAS_DEFAULT_CHANNEL, - .svc_hint = 0, + .channel = MAP_MAS_DEFAULT_CHANNEL, + .svc_hint = SVC_HINT_OBEX, .sec_level = BT_IO_SEC_MEDIUM, - .create_record = NULL + .create_record = create_mas_record }, { .uuid = { 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }, - .channel = SPP_DEFAULT_CHANNEL, + .channel = 0, .svc_hint = 0, .sec_level = BT_IO_SEC_MEDIUM, .create_record = create_spp_record }, }; -static uint32_t sdp_service_register(const struct profile_info *profile, - const void *svc_name) +static uint32_t sdp_service_register(uint8_t channel, const uint8_t *uuid, + const struct profile_info *profile, + const void *svc_name) { - sdp_record_t *record; - - if (!profile || !profile->create_record) - return 0; + sdp_record_t *record = NULL; + uint8_t svc_hint = 0; + + if (profile && profile->create_record) { + record = profile->create_record(channel, svc_name); + svc_hint = profile->svc_hint; + } else if (uuid) { + record = create_app_record(channel, uuid, svc_name); + } - record = profile->create_record(profile->channel, svc_name); if (!record) return 0; - if (bt_adapter_add_record(record, profile->svc_hint) < 0) { + if (bt_adapter_add_record(record, svc_hint) < 0) { error("Failed to register on SDP record"); sdp_record_free(record); return 0; @@ -483,11 +535,10 @@ static int try_write_all(int fd, unsigned char *buf, int len) return sent; } -static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond, +static gboolean jv_sock_client_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct rfcomm_sock *rfsock = data; - unsigned char buf[1024]; int len, sent; if (cond & G_IO_HUP) { @@ -496,19 +547,18 @@ static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond, } if (cond & (G_IO_ERR | G_IO_NVAL)) { - error("Socket error: sock %d cond %d", - g_io_channel_unix_get_fd(io), cond); + error("Socket %d error", g_io_channel_unix_get_fd(io)); goto fail; } - len = read(rfsock->fd, buf, sizeof(buf)); + len = read(rfsock->jv_sock, rfsock->buf, rfsock->buf_size); if (len <= 0) { error("read(): %s", strerror(errno)); /* Read again */ return TRUE; } - sent = try_write_all(rfsock->real_sock, buf, len); + sent = try_write_all(rfsock->bt_sock, rfsock->buf, len); if (sent < 0) { error("write(): %s", strerror(errno)); goto fail; @@ -516,17 +566,18 @@ static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond, return TRUE; fail: + DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond); + connections = g_list_remove(connections, rfsock); cleanup_rfsock(rfsock); return FALSE; } -static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond, +static gboolean bt_sock_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) { struct rfcomm_sock *rfsock = data; - unsigned char buf[1024]; int len, sent; if (cond & G_IO_HUP) { @@ -535,19 +586,18 @@ static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond, } if (cond & (G_IO_ERR | G_IO_NVAL)) { - error("Socket error: sock %d cond %d", - g_io_channel_unix_get_fd(io), cond); + error("Socket %d error", g_io_channel_unix_get_fd(io)); goto fail; } - len = read(rfsock->real_sock, buf, sizeof(buf)); + len = read(rfsock->bt_sock, rfsock->buf, rfsock->buf_size); if (len <= 0) { error("read(): %s", strerror(errno)); /* Read again */ return TRUE; } - sent = try_write_all(rfsock->fd, buf, len); + sent = try_write_all(rfsock->jv_sock, rfsock->buf, len); if (sent < 0) { error("write(): %s", strerror(errno)); goto fail; @@ -555,6 +605,8 @@ static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond, return TRUE; fail: + DBG("rfsock %p bt_sock %d cond %d", rfsock, rfsock->bt_sock, cond); + connections = g_list_remove(connections, rfsock); cleanup_rfsock(rfsock); @@ -574,7 +626,7 @@ static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr, cmd.channel = rfsock->channel; cmd.status = 0; - len = bt_sock_send_fd(rfsock->fd, &cmd, sizeof(cmd), fd_accepted); + len = bt_sock_send_fd(rfsock->jv_sock, &cmd, sizeof(cmd), fd_accepted); if (len != sizeof(cmd)) { error("Error sending accept signal"); return false; @@ -583,16 +635,34 @@ static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr, return true; } +static gboolean jv_sock_server_event_cb(GIOChannel *io, GIOCondition cond, + gpointer data) +{ + struct rfcomm_sock *rfsock = data; + + DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond); + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_ERR | G_IO_HUP)) { + servers[rfsock->channel].rfsock = NULL; + cleanup_rfsock(rfsock); + } + + return FALSE; +} + static void accept_cb(GIOChannel *io, GError *err, gpointer user_data) { struct rfcomm_sock *rfsock = user_data; - struct rfcomm_sock *rfsock_acc; - GIOChannel *io_stack; + struct rfcomm_sock *new_rfsock; + GIOChannel *jv_io; GError *gerr = NULL; bdaddr_t dst; char address[18]; - int sock_acc; - int hal_fd; + int new_sock; + int hal_sock; guint id; GIOCondition cond; @@ -612,76 +682,127 @@ static void accept_cb(GIOChannel *io, GError *err, gpointer user_data) } ba2str(&dst, address); - DBG("Incoming connection from %s rfsock %p", address, rfsock); + DBG("Incoming connection from %s on channel %d (rfsock %p)", address, + rfsock->channel, rfsock); - sock_acc = g_io_channel_unix_get_fd(io); - rfsock_acc = create_rfsock(sock_acc, &hal_fd); - if (!rfsock_acc) { + new_sock = g_io_channel_unix_get_fd(io); + new_rfsock = create_rfsock(new_sock, &hal_sock); + if (!new_rfsock) { g_io_channel_shutdown(io, TRUE, NULL); return; } - DBG("rfsock: fd %d real_sock %d chan %u sock %d", - rfsock->fd, rfsock->real_sock, rfsock->channel, - sock_acc); + DBG("new rfsock %p bt_sock %d jv_sock %d hal_sock %d", new_rfsock, + new_rfsock->bt_sock, new_rfsock->jv_sock, hal_sock); - if (!sock_send_accept(rfsock, &dst, hal_fd)) { - cleanup_rfsock(rfsock_acc); + if (!sock_send_accept(rfsock, &dst, hal_sock)) { + cleanup_rfsock(new_rfsock); return; } - connections = g_list_append(connections, rfsock_acc); + connections = g_list_append(connections, new_rfsock); /* Handle events from Android */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; - io_stack = g_io_channel_unix_new(rfsock_acc->fd); - id = g_io_add_watch(io_stack, cond, sock_stack_event_cb, rfsock_acc); - g_io_channel_unref(io_stack); + jv_io = g_io_channel_unix_new(new_rfsock->jv_sock); + id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, new_rfsock); + g_io_channel_unref(jv_io); - rfsock_acc->stack_watch = id; + new_rfsock->jv_watch = id; /* Handle rfcomm events */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; - id = g_io_add_watch(io, cond, sock_rfcomm_event_cb, rfsock_acc); + id = g_io_add_watch(io, cond, bt_sock_event_cb, new_rfsock); g_io_channel_set_close_on_unref(io, FALSE); - rfsock_acc->rfcomm_watch = id; + new_rfsock->bt_watch = id; +} + +static int find_free_channel(void) +{ + int ch; + + /* channel 0 is reserver so we don't use it */ + for (ch = 1; ch <= RFCOMM_CHANNEL_MAX; ch++) { + struct rfcomm_channel *srv = &servers[ch]; + + if (!srv->reserved && srv->rfsock == NULL) + return ch; + } - DBG("rfsock %p rfsock_acc %p stack_watch %d rfcomm_watch %d", - rfsock, rfsock_acc, rfsock_acc->stack_watch, - rfsock_acc->rfcomm_watch); + return 0; } -static void handle_listen(const void *buf, uint16_t len) +static BtIOSecLevel get_sec_level(uint8_t flags) +{ + /* + * HAL_SOCK_FLAG_AUTH should require MITM but in our case setting + * security to BT_IO_SEC_HIGH would also require 16-digits PIN code + * for pre-2.1 devices which is not what Android expects. For this + * reason we ignore this flag to not break apps which use "secure" + * sockets (have both auth and encrypt flags set, there is no public + * API in Android which should provide proper high security socket). + */ + return flags & HAL_SOCK_FLAG_ENCRYPT ? BT_IO_SEC_MEDIUM : + BT_IO_SEC_LOW; +} + +static uint8_t rfcomm_listen(int chan, const uint8_t *name, const uint8_t *uuid, + uint8_t flags, int *hal_sock) { - const struct hal_cmd_sock_listen *cmd = buf; const struct profile_info *profile; struct rfcomm_sock *rfsock = NULL; BtIOSecLevel sec_level; - GIOChannel *io; + GIOChannel *io, *jv_io; + GIOCondition cond; GError *err = NULL; - int hal_fd = -1; - int chan; + guint id; + uuid_t uu; + char uuid_str[32]; - DBG(""); + sdp_uuid128_create(&uu, uuid); + sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str)); - profile = get_profile_by_uuid(cmd->uuid); - if (!profile) { - if (!cmd->channel) - goto failed; + DBG("chan %d flags 0x%02x uuid %s name %s", chan, flags, uuid_str, + name); - chan = cmd->channel; - sec_level = BT_IO_SEC_MEDIUM; + if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) || + (chan > RFCOMM_CHANNEL_MAX)) { + error("Invalid rfcomm listen params"); + return HAL_STATUS_INVALID; + } + + profile = get_profile_by_uuid(uuid); + if (!profile) { + sec_level = get_sec_level(flags); } else { + if (!profile->create_record) + return HAL_STATUS_INVALID; + chan = profile->channel; sec_level = profile->sec_level; } - DBG("rfcomm channel %d svc_name %s", chan, cmd->name); + if (chan <= 0) + chan = find_free_channel(); + + if (!chan) { + error("No free channels"); + return HAL_STATUS_BUSY; + } + + if (servers[chan].rfsock != NULL) { + error("Channel already registered (%d)", chan); + return HAL_STATUS_BUSY; + } - rfsock = create_rfsock(-1, &hal_fd); + DBG("chan %d sec_level %d", chan, sec_level); + + rfsock = create_rfsock(-1, hal_sock); if (!rfsock) - goto failed; + return HAL_STATUS_FAILED; + + rfsock->channel = chan; io = bt_io_listen(accept_cb, NULL, rfsock, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, @@ -694,36 +815,76 @@ static void handle_listen(const void *buf, uint16_t len) goto failed; } - rfsock->real_sock = g_io_channel_unix_get_fd(io); + rfsock->bt_sock = g_io_channel_unix_get_fd(io); + g_io_channel_set_close_on_unref(io, FALSE); g_io_channel_unref(io); - DBG("real_sock %d fd %d hal_fd %d", rfsock->real_sock, rfsock->fd, - hal_fd); + /* Handle events from Android */ + cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; + jv_io = g_io_channel_unix_new(rfsock->jv_sock); + id = g_io_add_watch_full(jv_io, G_PRIORITY_HIGH, cond, + jv_sock_server_event_cb, rfsock, + NULL); + g_io_channel_unref(jv_io); + + rfsock->jv_watch = id; - if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) { + DBG("rfsock %p bt_sock %d jv_sock %d hal_sock %d", rfsock, + rfsock->bt_sock, + rfsock->jv_sock, + *hal_sock); + + if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) { error("Error sending RFCOMM channel"); goto failed; } - rfsock->service_handle = sdp_service_register(profile, cmd->name); + rfsock->service_handle = sdp_service_register(chan, uuid, profile, + name); - servers = g_list_append(servers, rfsock); + servers[chan].rfsock = rfsock; - ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, 0, NULL, - hal_fd); - close(hal_fd); - return; + return HAL_STATUS_SUCCESS; failed: - ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, - HAL_STATUS_FAILED); - if (rfsock) - cleanup_rfsock(rfsock); + cleanup_rfsock(rfsock); + close(*hal_sock); + return HAL_STATUS_FAILED; +} - if (hal_fd >= 0) - close(hal_fd); +static void handle_listen(const void *buf, uint16_t len) +{ + const struct hal_cmd_socket_listen *cmd = buf; + uint8_t status; + int hal_sock; + + switch (cmd->type) { + case HAL_SOCK_RFCOMM: + status = rfcomm_listen(cmd->channel, cmd->name, cmd->uuid, + cmd->flags, &hal_sock); + break; + case HAL_SOCK_SCO: + case HAL_SOCK_L2CAP: + status = HAL_STATUS_UNSUPPORTED; + break; + default: + status = HAL_STATUS_INVALID; + break; + } + + if (status != HAL_STATUS_SUCCESS) + goto failed; + + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, + 0, NULL, hal_sock); + close(hal_sock); + return; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, + status); } static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr) @@ -739,7 +900,7 @@ static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr) cmd.channel = rfsock->channel; cmd.status = 0; - len = write(rfsock->fd, &cmd, sizeof(cmd)); + len = write(rfsock->jv_sock, &cmd, sizeof(cmd)); if (len < 0) { error("%s", strerror(errno)); return false; @@ -757,7 +918,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) { struct rfcomm_sock *rfsock = user_data; bdaddr_t *dst = &rfsock->dst; - GIOChannel *io_stack; + GIOChannel *jv_io; char address[18]; guint id; GIOCondition cond; @@ -768,29 +929,26 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) } ba2str(dst, address); - DBG("Connected to %s", address); - - DBG("rfsock: fd %d real_sock %d chan %u sock %d", - rfsock->fd, rfsock->real_sock, rfsock->channel, - g_io_channel_unix_get_fd(io)); + DBG("Connected to %s on channel %d (rfsock %p)", address, + rfsock->channel, rfsock); if (!sock_send_connect(rfsock, dst)) goto fail; /* Handle events from Android */ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; - io_stack = g_io_channel_unix_new(rfsock->fd); - id = g_io_add_watch(io_stack, cond, sock_stack_event_cb, rfsock); - g_io_channel_unref(io_stack); + jv_io = g_io_channel_unix_new(rfsock->jv_sock); + id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, rfsock); + g_io_channel_unref(jv_io); - rfsock->stack_watch = id; + rfsock->jv_watch = id; /* Handle rfcomm events */ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; - id = g_io_add_watch(io, cond, sock_rfcomm_event_cb, rfsock); + id = g_io_add_watch(io, cond, bt_sock_event_cb, rfsock); g_io_channel_set_close_on_unref(io, FALSE); - rfsock->rfcomm_watch = id; + rfsock->bt_watch = id; return; fail: @@ -798,13 +956,45 @@ fail: cleanup_rfsock(rfsock); } +static bool do_rfcomm_connect(struct rfcomm_sock *rfsock, int chan) +{ + GIOChannel *io; + GError *gerr = NULL; + + DBG("rfsock %p sec_level %d chan %d", rfsock, rfsock->sec_level, chan); + + io = bt_io_connect(connect_cb, rfsock, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &rfsock->dst, + BT_IO_OPT_CHANNEL, chan, + BT_IO_OPT_SEC_LEVEL, rfsock->sec_level, + BT_IO_OPT_INVALID); + if (!io) { + error("Failed connect: %s", gerr->message); + g_error_free(gerr); + return false; + } + + g_io_channel_set_close_on_unref(io, FALSE); + g_io_channel_unref(io); + + if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) { + error("Error sending RFCOMM channel"); + return false; + } + + rfsock->bt_sock = g_io_channel_unix_get_fd(io); + rfsock_set_buffer(rfsock); + rfsock->channel = chan; + connections = g_list_append(connections, rfsock); + + return true; +} + static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data) { struct rfcomm_sock *rfsock = data; - BtIOSecLevel sec_level = BT_IO_SEC_MEDIUM; - GError *gerr = NULL; sdp_list_t *list; - GIOChannel *io; int chan; DBG(""); @@ -845,101 +1035,146 @@ static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data) DBG("Got RFCOMM channel %d", chan); - if (rfsock->profile) - sec_level = rfsock->profile->sec_level; + if (do_rfcomm_connect(rfsock, chan)) + return; +fail: + cleanup_rfsock(rfsock); +} - io = bt_io_connect(connect_cb, rfsock, NULL, &gerr, - BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, - BT_IO_OPT_DEST_BDADDR, &rfsock->dst, - BT_IO_OPT_CHANNEL, chan, - BT_IO_OPT_SEC_LEVEL, sec_level, - BT_IO_OPT_INVALID); - if (!io) { - error("Failed connect: %s", gerr->message); - g_error_free(gerr); - goto fail; - } +static uint8_t connect_rfcomm(const bdaddr_t *addr, int chan, + const uint8_t *uuid, uint8_t flags, + int *hal_sock) +{ + struct rfcomm_sock *rfsock; + char address[18]; + uuid_t uu; + char uuid_str[32]; - if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) { - error("Error sending RFCOMM channel"); - goto fail; + sdp_uuid128_create(&uu, uuid); + sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str)); + ba2str(addr, address); + + DBG("addr %s chan %d flags 0x%02x uuid %s", address, chan, flags, + uuid_str); + + if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) || + !bacmp(addr, BDADDR_ANY)) { + error("Invalid rfcomm connect params"); + return HAL_STATUS_INVALID; } - rfsock->real_sock = g_io_channel_unix_get_fd(io); - rfsock->channel = chan; - connections = g_list_append(connections, rfsock); + rfsock = create_rfsock(-1, hal_sock); + if (!rfsock) + return HAL_STATUS_FAILED; - g_io_channel_unref(io); + DBG("rfsock %p jv_sock %d hal_sock %d", rfsock, rfsock->jv_sock, + *hal_sock); - return; -fail: - connections = g_list_remove(connections, rfsock); + rfsock->sec_level = get_sec_level(flags); + + bacpy(&rfsock->dst, addr); + + if (!memcmp(uuid, zero_uuid, sizeof(zero_uuid))) { + if (!do_rfcomm_connect(rfsock, chan)) + goto failed; + } else { + + if (bt_search_service(&adapter_addr, &rfsock->dst, &uu, + sdp_search_cb, rfsock, NULL, 0) < 0) { + error("Failed to search SDP records"); + goto failed; + } + } + + return HAL_STATUS_SUCCESS; + +failed: cleanup_rfsock(rfsock); + close(*hal_sock); + return HAL_STATUS_FAILED; } static void handle_connect(const void *buf, uint16_t len) { - const struct hal_cmd_sock_connect *cmd = buf; - struct rfcomm_sock *rfsock; - uuid_t uuid; - int hal_fd = -1; + const struct hal_cmd_socket_connect *cmd = buf; + bdaddr_t bdaddr; + uint8_t status; + int hal_sock; DBG(""); - rfsock = create_rfsock(-1, &hal_fd); - if (!rfsock) - goto failed; - - android2bdaddr(cmd->bdaddr, &rfsock->dst); - - memset(&uuid, 0, sizeof(uuid)); - uuid.type = SDP_UUID128; - memcpy(&uuid.value.uuid128, cmd->uuid, sizeof(uint128_t)); - - rfsock->profile = get_profile_by_uuid(cmd->uuid); + android2bdaddr(cmd->bdaddr, &bdaddr); + + switch (cmd->type) { + case HAL_SOCK_RFCOMM: + status = connect_rfcomm(&bdaddr, cmd->channel, cmd->uuid, + cmd->flags, &hal_sock); + break; + case HAL_SOCK_SCO: + case HAL_SOCK_L2CAP: + status = HAL_STATUS_UNSUPPORTED; + break; + default: + status = HAL_STATUS_INVALID; + break; + } - if (bt_search_service(&adapter_addr, &rfsock->dst, &uuid, - sdp_search_cb, rfsock, NULL) < 0) { - error("Failed to search SDP records"); - cleanup_rfsock(rfsock); + if (status != HAL_STATUS_SUCCESS) goto failed; - } - ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, 0, NULL, - hal_fd); - close(hal_fd); + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, + 0, NULL, hal_sock); + close(hal_sock); return; failed: - ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, - HAL_STATUS_FAILED); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, + status); - if (hal_fd >= 0) - close(hal_fd); } static const struct ipc_handler cmd_handlers[] = { - /* HAL_OP_SOCK_LISTEN */ - { handle_listen, false, sizeof(struct hal_cmd_sock_listen) }, - /* HAL_OP_SOCK_CONNECT */ - { handle_connect, false, sizeof(struct hal_cmd_sock_connect) }, + /* HAL_OP_SOCKET_LISTEN */ + { handle_listen, false, sizeof(struct hal_cmd_socket_listen) }, + /* HAL_OP_SOCKET_CONNECT */ + { handle_connect, false, sizeof(struct hal_cmd_socket_connect) }, }; -void bt_socket_register(const bdaddr_t *addr) +void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) { + size_t i; + DBG(""); + /* + * make sure channels assigned for profiles are reserved and not used + * for app services + */ + for (i = 0; i < G_N_ELEMENTS(profiles); i++) + if (profiles[i].channel) + servers[profiles[i].channel].reserved = true; + bacpy(&adapter_addr, addr); - ipc_register(HAL_SERVICE_ID_SOCK, cmd_handlers, + + hal_ipc = ipc; + ipc_register(hal_ipc, HAL_SERVICE_ID_SOCKET, cmd_handlers, G_N_ELEMENTS(cmd_handlers)); } void bt_socket_unregister(void) { + int ch; + DBG(""); g_list_free_full(connections, cleanup_rfsock); - g_list_free_full(servers, cleanup_rfsock); - ipc_unregister(HAL_SERVICE_ID_SOCK); + for (ch = 0; ch <= RFCOMM_CHANNEL_MAX; ch++) + if (servers[ch].rfsock) + cleanup_rfsock(servers[ch].rfsock); + + memset(servers, 0, sizeof(servers)); + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_SOCKET); + hal_ipc = NULL; } diff --git a/android/socket.h b/android/socket.h index 5150b89..b0e78c6 100644 --- a/android/socket.h +++ b/android/socket.h @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -28,7 +28,5 @@ struct hal_sock_connect_signal { int status; } __attribute__((packed)); -void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len); - -void bt_socket_register(const bdaddr_t *addr); +void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); void bt_socket_unregister(void); diff --git a/android/system-emulator.c b/android/system-emulator.c index 24f2741..c1b1b25 100644 --- a/android/system-emulator.c +++ b/android/system-emulator.c @@ -2,21 +2,21 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -37,25 +37,31 @@ #include #include #include +#include +#include #include "monitor/mainloop.h" static char exec_dir[PATH_MAX + 1]; static pid_t daemon_pid = -1; +static pid_t snoop_pid = -1; static void ctl_start(void) { char prg_name[PATH_MAX + 1]; - char *prg_argv[3]; + char *prg_argv[6]; char *prg_envp[3]; pid_t pid; snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); prg_argv[0] = "/usr/bin/valgrind"; - prg_argv[1] = prg_name; - prg_argv[2] = NULL; + prg_argv[1] = "--leak-check=full"; + prg_argv[2] = "--track-origins=yes"; + prg_argv[3] = prg_name; + prg_argv[4] = "-d"; + prg_argv[5] = NULL; prg_envp[0] = "G_SLICE=always-malloc"; prg_envp[1] = "G_DEBUG=gc-friendly"; @@ -79,6 +85,47 @@ static void ctl_start(void) daemon_pid = pid; } +static void snoop_start(void) +{ + char prg_name[PATH_MAX + 1]; + char *prg_argv[3]; + char *prg_envp[1]; + pid_t pid; + + snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, + "bluetoothd-snoop"); + + prg_argv[0] = prg_name; + prg_argv[1] = "/tmp/btsnoop_hci.log"; + prg_argv[2] = NULL; + + prg_envp[0] = NULL; + + printf("Starting %s\n", prg_name); + + pid = fork(); + if (pid < 0) { + perror("Failed to fork new process"); + return; + } + + if (pid == 0) { + execve(prg_argv[0], prg_argv, prg_envp); + exit(0); + } + + printf("New process %d created\n", pid); + + snoop_pid = pid; +} + +static void snoop_stop(void) +{ + printf("Stoping %s/%s\n", exec_dir, "bluetoothd-snoop"); + + kill(snoop_pid, SIGTERM); +} + static void system_socket_callback(int fd, uint32_t events, void *user_data) { char buf[4096]; @@ -95,13 +142,20 @@ static void system_socket_callback(int fd, uint32_t events, void *user_data) printf("Received %s\n", buf); - if (strcmp(buf, "ctl.start=bluetoothd")) - return; + if (!strcmp(buf, "bluetooth.start=daemon")) { + if (daemon_pid > 0) + return; - if (daemon_pid > 0) - return; + ctl_start(); + } else if (!strcmp(buf, "bluetooth.start=snoop")) { + if (snoop_pid > 0) + return; - ctl_start(); + snoop_start(); + } else if (!strcmp(buf, "bluetooth.stop=snoop")) { + if (snoop_pid > 0) + snoop_stop(); + } } static void signal_callback(int signum, void *user_data) @@ -125,6 +179,8 @@ static void signal_callback(int signum, void *user_data) if (pid == daemon_pid) daemon_pid = -1; + else if (pid == snoop_pid) + snoop_pid = -1; } break; } @@ -132,8 +188,7 @@ static void signal_callback(int signum, void *user_data) int main(int argc, char *argv[]) { - static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; - + const char SYSTEM_SOCKET_PATH[] = "\0android_system"; sigset_t mask; struct sockaddr_un addr; int fd; @@ -169,5 +224,8 @@ int main(int argc, char *argv[]) mainloop_add_fd(fd, EPOLLIN, system_socket_callback, NULL, NULL); + /* Make sure bluetoothd creates files with proper permissions */ + umask(0177); + return mainloop_run(); } diff --git a/android/system/audio.h b/android/system/audio.h new file mode 100644 index 0000000..26a0a3b --- /dev/null +++ b/android/system/audio.h @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_AUDIO_CORE_H +#define ANDROID_AUDIO_CORE_H + +#include +#include +#include +#include + +#define popcount __builtin_popcount + +__BEGIN_DECLS + +/* The enums were moved here mostly from + * frameworks/base/include/media/AudioSystem.h + */ + +/* device address used to refer to the standard remote submix */ +#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0" + +typedef int audio_io_handle_t; + +/* Audio stream types */ +typedef enum { + AUDIO_STREAM_DEFAULT = -1, + AUDIO_STREAM_VOICE_CALL = 0, + AUDIO_STREAM_SYSTEM = 1, + AUDIO_STREAM_RING = 2, + AUDIO_STREAM_MUSIC = 3, + AUDIO_STREAM_ALARM = 4, + AUDIO_STREAM_NOTIFICATION = 5, + AUDIO_STREAM_BLUETOOTH_SCO = 6, + AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user and must be routed to speaker */ + AUDIO_STREAM_DTMF = 8, + AUDIO_STREAM_TTS = 9, + + AUDIO_STREAM_CNT, + AUDIO_STREAM_MAX = AUDIO_STREAM_CNT - 1, +} audio_stream_type_t; + +/* Do not change these values without updating their counterparts + * in media/java/android/media/MediaRecorder.java! + */ +typedef enum { + AUDIO_SOURCE_DEFAULT = 0, + AUDIO_SOURCE_MIC = 1, + AUDIO_SOURCE_VOICE_UPLINK = 2, + AUDIO_SOURCE_VOICE_DOWNLINK = 3, + AUDIO_SOURCE_VOICE_CALL = 4, + AUDIO_SOURCE_CAMCORDER = 5, + AUDIO_SOURCE_VOICE_RECOGNITION = 6, + AUDIO_SOURCE_VOICE_COMMUNICATION = 7, + AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */ + /* An example of remote presentation is Wifi Display */ + /* where a dongle attached to a TV can be used to */ + /* play the mix captured by this audio source. */ + AUDIO_SOURCE_CNT, + AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, + AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for + for background software hotword detection. + Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION. + Used only internally to the framework. Not exposed + at the audio HAL. */ +} audio_source_t; + +/* special audio session values + * (XXX: should this be living in the audio effects land?) + */ +typedef enum { + /* session for effects attached to a particular output stream + * (value must be less than 0) + */ + AUDIO_SESSION_OUTPUT_STAGE = -1, + + /* session for effects applied to output mix. These effects can + * be moved by audio policy manager to another output stream + * (value must be 0) + */ + AUDIO_SESSION_OUTPUT_MIX = 0, +} audio_session_t; + +/* Audio sub formats (see enum audio_format). */ + +/* PCM sub formats */ +typedef enum { + AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */ + AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ + AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ + AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */ +} audio_format_pcm_sub_fmt_t; + +/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 + * frame header to specify bit rate, stereo mode, version... + */ +typedef enum { + AUDIO_FORMAT_MP3_SUB_NONE = 0x0, +} audio_format_mp3_sub_fmt_t; + +/* AMR NB/WB sub format field definition: specify frame block interleaving, + * bandwidth efficient or octet aligned, encoding mode for recording... + */ +typedef enum { + AUDIO_FORMAT_AMR_SUB_NONE = 0x0, +} audio_format_amr_sub_fmt_t; + +/* AAC sub format field definition: specify profile or bitrate for recording... */ +typedef enum { + AUDIO_FORMAT_AAC_SUB_NONE = 0x0, +} audio_format_aac_sub_fmt_t; + +/* VORBIS sub format field definition: specify quality for recording... */ +typedef enum { + AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, +} audio_format_vorbis_sub_fmt_t; + +/* Audio format consists in a main format field (upper 8 bits) and a sub format + * field (lower 24 bits). + * + * The main format indicates the main codec type. The sub format field + * indicates options and parameters for each format. The sub format is mainly + * used for record to indicate for instance the requested bitrate or profile. + * It can also be used for certain formats to give informations not present in + * the encoded audio stream (e.g. octet alignement for AMR). + */ +typedef enum { + AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL, + AUDIO_FORMAT_DEFAULT = 0, + AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */ + AUDIO_FORMAT_MP3 = 0x01000000UL, + AUDIO_FORMAT_AMR_NB = 0x02000000UL, + AUDIO_FORMAT_AMR_WB = 0x03000000UL, + AUDIO_FORMAT_AAC = 0x04000000UL, + AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, + AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, + AUDIO_FORMAT_VORBIS = 0x07000000UL, + AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL, + AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, + + /* Aliases */ + AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_16_BIT), + AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_8_BIT), + AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_32_BIT), + AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_8_24_BIT), +} audio_format_t; + +enum { + /* output channels */ + AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, + AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, + AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4, + AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8, + AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10, + AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20, + AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, + AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100, + AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200, + AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400, + AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800, + AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, + AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, + AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, + AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, + + AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, + AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT), + AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT), + AUDIO_CHANNEL_OUT_SURROUND = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_BACK_CENTER), + AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT), + // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 + AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT | + AUDIO_CHANNEL_OUT_SIDE_LEFT | + AUDIO_CHANNEL_OUT_SIDE_RIGHT), + AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | + AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | + AUDIO_CHANNEL_OUT_BACK_CENTER| + AUDIO_CHANNEL_OUT_SIDE_LEFT| + AUDIO_CHANNEL_OUT_SIDE_RIGHT| + AUDIO_CHANNEL_OUT_TOP_CENTER| + AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT| + AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER| + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT| + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT| + AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| + AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), + + /* input channels */ + AUDIO_CHANNEL_IN_LEFT = 0x4, + AUDIO_CHANNEL_IN_RIGHT = 0x8, + AUDIO_CHANNEL_IN_FRONT = 0x10, + AUDIO_CHANNEL_IN_BACK = 0x20, + AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40, + AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80, + AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100, + AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200, + AUDIO_CHANNEL_IN_PRESSURE = 0x400, + AUDIO_CHANNEL_IN_X_AXIS = 0x800, + AUDIO_CHANNEL_IN_Y_AXIS = 0x1000, + AUDIO_CHANNEL_IN_Z_AXIS = 0x2000, + AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000, + AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000, + + AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT, + AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT), + AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK), + AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT | + AUDIO_CHANNEL_IN_RIGHT | + AUDIO_CHANNEL_IN_FRONT | + AUDIO_CHANNEL_IN_BACK| + AUDIO_CHANNEL_IN_LEFT_PROCESSED | + AUDIO_CHANNEL_IN_RIGHT_PROCESSED | + AUDIO_CHANNEL_IN_FRONT_PROCESSED | + AUDIO_CHANNEL_IN_BACK_PROCESSED| + AUDIO_CHANNEL_IN_PRESSURE | + AUDIO_CHANNEL_IN_X_AXIS | + AUDIO_CHANNEL_IN_Y_AXIS | + AUDIO_CHANNEL_IN_Z_AXIS | + AUDIO_CHANNEL_IN_VOICE_UPLINK | + AUDIO_CHANNEL_IN_VOICE_DNLINK), +}; + +typedef uint32_t audio_channel_mask_t; + +typedef enum { + AUDIO_MODE_INVALID = -2, + AUDIO_MODE_CURRENT = -1, + AUDIO_MODE_NORMAL = 0, + AUDIO_MODE_RINGTONE = 1, + AUDIO_MODE_IN_CALL = 2, + AUDIO_MODE_IN_COMMUNICATION = 3, + + AUDIO_MODE_CNT, + AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1, +} audio_mode_t; + +typedef enum { + AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001, + AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0, + AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002, + AUDIO_IN_ACOUSTICS_NS_DISABLE = 0, + AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004, + AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, +} audio_in_acoustics_t; + +enum { + AUDIO_DEVICE_NONE = 0x0, + /* reserved bits */ + AUDIO_DEVICE_BIT_IN = 0x80000000, + AUDIO_DEVICE_BIT_DEFAULT = 0x40000000, + /* output devices */ + AUDIO_DEVICE_OUT_EARPIECE = 0x1, + AUDIO_DEVICE_OUT_SPEAKER = 0x2, + AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, + AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, + AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, + AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, + AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, + AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, + AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, + AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, + AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, + AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | + AUDIO_DEVICE_OUT_SPEAKER | + AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | + AUDIO_DEVICE_OUT_AUX_DIGITAL | + AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | + AUDIO_DEVICE_OUT_USB_ACCESSORY | + AUDIO_DEVICE_OUT_USB_DEVICE | + AUDIO_DEVICE_OUT_REMOTE_SUBMIX | + AUDIO_DEVICE_OUT_DEFAULT), + AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), + AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), + AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY | + AUDIO_DEVICE_OUT_USB_DEVICE), + + /* input devices */ + AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1, + AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2, + AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8, + AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10, + AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, + AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40, + AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80, + AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100, + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200, + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400, + AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800, + AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000, + AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, + + AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | + AUDIO_DEVICE_IN_AMBIENT | + AUDIO_DEVICE_IN_BUILTIN_MIC | + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_IN_WIRED_HEADSET | + AUDIO_DEVICE_IN_AUX_DIGITAL | + AUDIO_DEVICE_IN_VOICE_CALL | + AUDIO_DEVICE_IN_BACK_MIC | + AUDIO_DEVICE_IN_REMOTE_SUBMIX | + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | + AUDIO_DEVICE_IN_USB_ACCESSORY | + AUDIO_DEVICE_IN_USB_DEVICE | + AUDIO_DEVICE_IN_DEFAULT), + AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, +}; + +typedef uint32_t audio_devices_t; + +/* the audio output flags serve two purposes: + * - when an AudioTrack is created they indicate a "wish" to be connected to an + * output stream with attributes corresponding to the specified flags + * - when present in an output profile descriptor listed for a particular audio + * hardware module, they indicate that an output stream can be opened that + * supports the attributes indicated by the flags. + * the audio policy manager will try to match the flags in the request + * (when getOuput() is called) to an available output stream. + */ +typedef enum { + AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes + AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track + // to one output stream: no software mixer + AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of + // the device. It is unique and must be + // present. It is opened by default and + // receives routing, audio mode and volume + // controls related to voice calls. + AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", + // defined elsewhere + AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed + // streams to hardware codec + AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20 // use non-blocking write +} audio_output_flags_t; + +/* The audio input flags are analogous to audio output flags. + * Currently they are used only when an AudioRecord is created, + * to indicate a preference to be connected to an input stream with + * attributes corresponding to the specified flags. + */ +typedef enum { + AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes + AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks" +} audio_input_flags_t; + +/* Additional information about compressed streams offloaded to + * hardware playback + * The version and size fields must be initialized by the caller by using + * one of the constants defined here. + */ +typedef struct { + uint16_t version; // version of the info structure + uint16_t size; // total size of the structure including version and size + uint32_t sample_rate; // sample rate in Hz + audio_channel_mask_t channel_mask; // channel mask + audio_format_t format; // audio format + audio_stream_type_t stream_type; // stream type + uint32_t bit_rate; // bit rate in bits per second + int64_t duration_us; // duration in microseconds, -1 if unknown + bool has_video; // true if stream is tied to a video stream + bool is_streaming; // true if streaming, false if local playback +} audio_offload_info_t; + +#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \ + ((((maj) & 0xff) << 8) | ((min) & 0xff)) + +#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1) +#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1 + +static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { + version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, + size: sizeof(audio_offload_info_t), +}; + +static inline bool audio_is_output_device(audio_devices_t device) +{ + if (((device & AUDIO_DEVICE_BIT_IN) == 0) && + (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) + return true; + else + return false; +} + +static inline bool audio_is_input_device(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) + return true; + } + return false; +} + +static inline bool audio_is_output_devices(audio_devices_t device) +{ + return (device & AUDIO_DEVICE_BIT_IN) == 0; +} + + +static inline bool audio_is_a2dp_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) + return true; + else + return false; +} + +static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) +{ + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO | + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET))) + return true; + else + return false; +} + +static inline bool audio_is_usb_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB)) + return true; + else + return false; +} + +static inline bool audio_is_remote_submix_device(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX + || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX) + return true; + else + return false; +} + +static inline bool audio_is_input_channel(audio_channel_mask_t channel) +{ + if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0) + return channel != 0; + else + return false; +} + +static inline bool audio_is_output_channel(audio_channel_mask_t channel) +{ + if ((channel & ~AUDIO_CHANNEL_OUT_ALL) == 0) + return channel != 0; + else + return false; +} + +/* Derive an output channel mask from a channel count. + * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel + * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad, + * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC + * for continuity with stereo. + * Returns the matching channel mask, or 0 if the number of channels exceeds that of the + * configurations for which a default channel mask is defined. + */ +static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) +{ + switch(channel_count) { + case 1: + return AUDIO_CHANNEL_OUT_MONO; + case 2: + return AUDIO_CHANNEL_OUT_STEREO; + case 3: + return (AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER); + case 4: // 4.0 + return AUDIO_CHANNEL_OUT_QUAD; + case 5: // 5.0 + return (AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER); + case 6: // 5.1 + return AUDIO_CHANNEL_OUT_5POINT1; + case 7: // 6.1 + return (AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER); + case 8: + return AUDIO_CHANNEL_OUT_7POINT1; + default: + return 0; + } +} + +/* Similar to above, but for input. Currently handles only mono and stereo. */ +static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count) +{ + switch (channel_count) { + case 1: + return AUDIO_CHANNEL_IN_MONO; + case 2: + return AUDIO_CHANNEL_IN_STEREO; + default: + return 0; + } +} + +static inline bool audio_is_valid_format(audio_format_t format) +{ + switch (format & AUDIO_FORMAT_MAIN_MASK) { + case AUDIO_FORMAT_PCM: + if (format != AUDIO_FORMAT_PCM_16_BIT && + format != AUDIO_FORMAT_PCM_8_BIT) { + return false; + } + case AUDIO_FORMAT_MP3: + case AUDIO_FORMAT_AMR_NB: + case AUDIO_FORMAT_AMR_WB: + case AUDIO_FORMAT_AAC: + case AUDIO_FORMAT_HE_AAC_V1: + case AUDIO_FORMAT_HE_AAC_V2: + case AUDIO_FORMAT_VORBIS: + return true; + default: + return false; + } +} + +static inline bool audio_is_linear_pcm(audio_format_t format) +{ + return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); +} + +static inline size_t audio_bytes_per_sample(audio_format_t format) +{ + size_t size = 0; + + switch (format) { + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_8_24_BIT: + size = sizeof(int32_t); + break; + case AUDIO_FORMAT_PCM_16_BIT: + size = sizeof(int16_t); + break; + case AUDIO_FORMAT_PCM_8_BIT: + size = sizeof(uint8_t); + break; + default: + break; + } + return size; +} + +__END_DECLS + +#endif // ANDROID_AUDIO_CORE_H diff --git a/android/test-ipc.c b/android/test-ipc.c new file mode 100644 index 0000000..bb7d15f --- /dev/null +++ b/android/test-ipc.c @@ -0,0 +1,577 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "src/shared/util.h" +#include "src/log.h" +#include "android/ipc-common.h" +#include "android/ipc.h" + +static const char HAL_SK_PATH[] = "\0test_hal_socket"; + +#define SERVICE_ID_MAX 10 + +struct test_data { + bool disconnect; + const void *cmd; + uint16_t cmd_size; + uint8_t service; + const struct ipc_handler *handlers; + uint8_t handlers_size; +}; + +struct context { + GMainLoop *main_loop; + + int sk; + + guint source; + guint cmd_source; + guint notif_source; + + GIOChannel *cmd_io; + GIOChannel *notif_io; + + const struct test_data *data; +}; + + +static struct ipc *ipc = NULL; + +static void context_quit(struct context *context) +{ + g_main_loop_quit(context->main_loop); +} + +static gboolean cmd_watch(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + const struct ipc_hdr *sent_msg = test_data->cmd; + uint8_t buf[128]; + int sk; + + struct ipc_hdr success_resp = { + .service_id = sent_msg->service_id, + .opcode = sent_msg->opcode, + .len = 0, + }; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_assert(test_data->disconnect); + return FALSE; + } + + g_assert(!test_data->disconnect); + + sk = g_io_channel_unix_get_fd(io); + + g_assert(read(sk, buf, sizeof(buf)) == sizeof(struct ipc_hdr)); + g_assert(!memcmp(&success_resp, buf, sizeof(struct ipc_hdr))); + + context_quit(context); + + return TRUE; +} + +static gboolean notif_watch(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_assert(test_data->disconnect); + return FALSE; + } + + g_assert(!test_data->disconnect); + + return TRUE; +} + +static gboolean connect_handler(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + GIOChannel *new_io; + GIOCondition watch_cond; + int sk; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_assert(FALSE); + return FALSE; + } + + g_assert(!context->cmd_source || !context->notif_source); + + sk = accept(context->sk, NULL, NULL); + g_assert(sk >= 0); + + new_io = g_io_channel_unix_new(sk); + + watch_cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + + if (context->cmd_source && !context->notif_source) { + context->notif_source = g_io_add_watch(new_io, watch_cond, + notif_watch, context); + g_assert(context->notif_source > 0); + context->notif_io = new_io; + } + + if (!context->cmd_source) { + context->cmd_source = g_io_add_watch(new_io, watch_cond, + cmd_watch, context); + context->cmd_io = new_io; + } + + if (context->cmd_source && context->notif_source && !test_data->cmd) + context_quit(context); + + return TRUE; +} + +static struct context *create_context(gconstpointer data) +{ + struct context *context = g_new0(struct context, 1); + struct sockaddr_un addr; + GIOChannel *io; + int ret, sk; + + context->main_loop = g_main_loop_new(NULL, FALSE); + g_assert(context->main_loop); + + sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); + g_assert(sk >= 0); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, HAL_SK_PATH, sizeof(HAL_SK_PATH)); + + ret = bind(sk, (struct sockaddr *) &addr, sizeof(addr)); + g_assert(ret == 0); + + ret = listen(sk, 5); + g_assert(ret == 0); + + io = g_io_channel_unix_new(sk); + + g_io_channel_set_close_on_unref(io, TRUE); + + context->source = g_io_add_watch(io, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + connect_handler, context); + g_assert(context->source > 0); + + g_io_channel_unref(io); + + context->sk = sk; + context->data = data; + + return context; +} + +static void execute_context(struct context *context) +{ + g_main_loop_run(context->main_loop); + + g_io_channel_shutdown(context->notif_io, TRUE, NULL); + g_io_channel_shutdown(context->cmd_io, TRUE, NULL); + g_io_channel_unref(context->cmd_io); + g_io_channel_unref(context->notif_io); + + g_source_remove(context->notif_source); + g_source_remove(context->cmd_source); + g_source_remove(context->source); + + g_main_loop_unref(context->main_loop); + + g_free(context); +} + +static void disconnected(void *data) +{ + struct context *context = data; + + g_assert(context->data->disconnect); + + context_quit(context); +} + +static void test_init(gconstpointer data) +{ + struct context *context = create_context(data); + + ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, + true, NULL, NULL); + + g_assert(ipc); + + execute_context(context); + + ipc_cleanup(ipc); + ipc = NULL; +} + +static gboolean send_cmd(gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + int sk; + + sk = g_io_channel_unix_get_fd(context->cmd_io); + g_assert(sk >= 0); + + g_assert(write(sk, test_data->cmd, test_data->cmd_size) == + test_data->cmd_size); + + return FALSE; +} + +static gboolean register_service(gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + + ipc_register(ipc, test_data->service, test_data->handlers, + test_data->handlers_size); + + return FALSE; +} + +static gboolean unregister_service(gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + + ipc_unregister(ipc, test_data->service); + + return FALSE; +} + +static void test_cmd(gconstpointer data) +{ + struct context *context = create_context(data); + + ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, + true, disconnected, context); + + g_assert(ipc); + + g_idle_add(send_cmd, context); + + execute_context(context); + + ipc_cleanup(ipc); + ipc = NULL; +} + +static void test_cmd_reg(gconstpointer data) +{ + struct context *context = create_context(data); + const struct test_data *test_data = context->data; + + ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, + true, disconnected, context); + + g_assert(ipc); + + g_idle_add(register_service, context); + g_idle_add(send_cmd, context); + + execute_context(context); + + ipc_unregister(ipc, test_data->service); + + ipc_cleanup(ipc); + ipc = NULL; +} + +static void test_cmd_reg_1(gconstpointer data) +{ + struct context *context = create_context(data); + + ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, + true, disconnected, context); + + g_assert(ipc); + + g_idle_add(register_service, context); + g_idle_add(unregister_service, context); + g_idle_add(send_cmd, context); + + execute_context(context); + + ipc_cleanup(ipc); + ipc = NULL; +} + +static void test_cmd_handler_1(const void *buf, uint16_t len) +{ + ipc_send_rsp(ipc, 0, 1, 0); +} + +static void test_cmd_handler_2(const void *buf, uint16_t len) +{ + ipc_send_rsp(ipc, 0, 2, 0); +} + +static void test_cmd_handler_invalid(const void *buf, uint16_t len) +{ + g_assert(false); +} + +static const struct test_data test_init_1 = {}; + +static const struct ipc_hdr test_cmd_1_hdr = { + .service_id = 0, + .opcode = 1, + .len = 0 +}; + +static const struct ipc_hdr test_cmd_2_hdr = { + .service_id = 0, + .opcode = 2, + .len = 0 +}; + +static const struct test_data test_cmd_service_invalid_1 = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr), + .disconnect = true, +}; + +static const struct ipc_handler cmd_handlers[] = { + { test_cmd_handler_1, false, 0 } +}; + +static const struct test_data test_cmd_service_valid_1 = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1 +}; + +static const struct test_data test_cmd_service_invalid_2 = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct ipc_handler cmd_handlers_invalid_2[] = { + { test_cmd_handler_1, false, 0 }, + { test_cmd_handler_invalid, false, 0 } +}; + +static const struct ipc_handler cmd_handlers_invalid_1[] = { + { test_cmd_handler_invalid, false, 0 }, + { test_cmd_handler_2, false, 0 }, +}; + +static const struct test_data test_cmd_opcode_valid_1 = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr), + .service = 0, + .handlers = cmd_handlers_invalid_2, + .handlers_size = 2, +}; + +static const struct test_data test_cmd_opcode_valid_2 = { + .cmd = &test_cmd_2_hdr, + .cmd_size = sizeof(test_cmd_2_hdr), + .service = 0, + .handlers = cmd_handlers_invalid_1, + .handlers_size = 2, +}; + +static const struct test_data test_cmd_opcode_invalid_1 = { + .cmd = &test_cmd_2_hdr, + .cmd_size = sizeof(test_cmd_2_hdr), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct test_data test_cmd_hdr_invalid = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr) - 1, + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +#define VARDATA_EX1 "some data example" + +struct vardata { + struct ipc_hdr hdr; + uint8_t data[IPC_MTU - sizeof(struct ipc_hdr)]; +} __attribute__((packed)); + +static const struct vardata test_cmd_vardata = { + .hdr.service_id = 0, + .hdr.opcode = 1, + .hdr.len = sizeof(VARDATA_EX1), + .data = VARDATA_EX1, +}; + +static const struct ipc_handler cmd_vardata_handlers[] = { + { test_cmd_handler_1, true, sizeof(VARDATA_EX1) } +}; + +static const struct test_data test_cmd_vardata_valid = { + .cmd = &test_cmd_vardata, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), + .service = 0, + .handlers = cmd_vardata_handlers, + .handlers_size = 1, +}; + +static const struct ipc_handler cmd_vardata_handlers_valid2[] = { + { test_cmd_handler_1, true, sizeof(VARDATA_EX1) - 1 } +}; + +static const struct test_data test_cmd_vardata_valid_2 = { + .cmd = &test_cmd_vardata, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), + .service = 0, + .handlers = cmd_vardata_handlers_valid2, + .handlers_size = 1, +}; + +static const struct test_data test_cmd_vardata_invalid_1 = { + .cmd = &test_cmd_vardata, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1, + .service = 0, + .handlers = cmd_vardata_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct ipc_hdr test_cmd_service_offrange_hdr = { + .service_id = SERVICE_ID_MAX + 1, + .opcode = 1, + .len = 0 +}; + +static const struct test_data test_cmd_service_offrange = { + .cmd = &test_cmd_service_offrange_hdr, + .cmd_size = sizeof(struct ipc_hdr), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct vardata test_cmd_invalid_data_1 = { + .hdr.service_id = 0, + .hdr.opcode = 1, + .hdr.len = sizeof(VARDATA_EX1), + .data = VARDATA_EX1, +}; + +static const struct test_data test_cmd_msg_invalid_1 = { + .cmd = &test_cmd_invalid_data_1, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1, + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct vardata test_cmd_invalid_data_2 = { + .hdr.service_id = 0, + .hdr.opcode = 1, + .hdr.len = sizeof(VARDATA_EX1) - 1, + .data = VARDATA_EX1, +}; + +static const struct test_data test_cmd_msg_invalid_2 = { + .cmd = &test_cmd_invalid_data_2, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + if (g_test_verbose()) + __btd_log_init("*", 0); + + g_test_add_data_func("/android_ipc/init", &test_init_1, test_init); + g_test_add_data_func("/android_ipc/service_invalid_1", + &test_cmd_service_invalid_1, test_cmd); + g_test_add_data_func("/android_ipc/service_valid_1", + &test_cmd_service_valid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/service_invalid_2", + &test_cmd_service_invalid_2, test_cmd_reg_1); + g_test_add_data_func("/android_ipc/opcode_valid_1", + &test_cmd_opcode_valid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/opcode_valid_2", + &test_cmd_opcode_valid_2, test_cmd_reg); + g_test_add_data_func("/android_ipc/opcode_invalid_1", + &test_cmd_opcode_invalid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/vardata_valid", + &test_cmd_vardata_valid, test_cmd_reg); + g_test_add_data_func("/android_ipc/vardata_valid_2", + &test_cmd_vardata_valid_2, test_cmd_reg); + g_test_add_data_func("/android_ipc/vardata_invalid_1", + &test_cmd_vardata_invalid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/service_offrange", + &test_cmd_service_offrange, test_cmd_reg); + g_test_add_data_func("/android_ipc/hdr_invalid", + &test_cmd_hdr_invalid, test_cmd_reg); + g_test_add_data_func("/android_ipc/msg_invalid_1", + &test_cmd_msg_invalid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/msg_invalid_2", + &test_cmd_msg_invalid_2, test_cmd_reg); + + return g_test_run(); +} diff --git a/android/utils.h b/android/utils.h index 5b009bc..560e991 100644 --- a/android/utils.h +++ b/android/utils.h @@ -2,26 +2,25 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Copyright (C) 2013-2014 Intel Corporation. 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 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ - static inline void android2bdaddr(const void *buf, bdaddr_t *dst) { baswap(dst, buf); diff --git a/attrib/att.c b/attrib/att.c index 753c753..8e9c06d 100644 --- a/attrib/att.c +++ b/attrib/att.c @@ -34,9 +34,19 @@ #include +#include "src/shared/util.h" #include "lib/uuid.h" #include "att.h" +static inline void put_uuid_le(const bt_uuid_t *src, void *dst) +{ + if (src->type == BT_UUID16) + put_le16(src->value.u16, dst); + else + /* Convert from 128-bit BE to LE */ + bswap_128(&src->value.u128, dst); +} + const char *att_ecode2str(uint8_t status) { switch (status) { @@ -120,38 +130,51 @@ struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len) return list; } +static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid) +{ + if (type == BT_UUID16) + bt_uuid16_create(uuid, get_le16(val)); + else { + uint128_t u128; + + /* Convert from 128-bit LE to BE */ + bswap_128(val, &u128); + bt_uuid128_create(uuid, u128); + } +} + uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { - uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); - uint16_t length; + uint16_t uuid_len; if (!uuid) return 0; if (uuid->type == BT_UUID16) - length = 2; + uuid_len = 2; else if (uuid->type == BT_UUID128) - length = 16; + uuid_len = 16; else return 0; - if (len < min_len + length) - return 0; - + /* Attribute Opcode (1 octet) */ 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; + /* Starting Handle (2 octets) */ + put_le16(start, &pdu[1]); + /* Ending Handle (2 octets) */ + put_le16(end, &pdu[3]); + /* Attribute Group Type (2 or 16 octet UUID) */ + put_uuid_le(uuid, &pdu[5]); + + return 5 + uuid_len; } uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid) { const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); + uint8_t type; if (pdu == NULL) return 0; @@ -162,15 +185,17 @@ uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start, if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ) return 0; - if (len < min_len + 2) + if (len == (min_len + 2)) + type = BT_UUID16; + else if (len == (min_len + 16)) + type = BT_UUID128; + else 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]); + *start = get_le16(&pdu[1]); + *end = get_le16(&pdu[3]); + + get_uuid(type, &pdu[5], uuid); return len; } @@ -212,7 +237,25 @@ struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len) if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP) return NULL; + /* PDU must contain at least: + * - Attribute Opcode (1 octet) + * - Length (1 octet) + * - Attribute Data List (at least one entry): + * - Attribute Handle (2 octets) + * - End Group Handle (2 octets) + * - Attribute Value (at least 1 octet) */ + if (len < 7) + return NULL; + elen = pdu[1]; + /* Minimum Attribute Data List size */ + if (elen < 5) + return NULL; + + /* Reject incomplete Attribute Data List */ + if ((len - 2) % elen) + return NULL; + num = (len - 2) / elen; list = att_data_list_alloc(num, elen); if (list == NULL) @@ -244,16 +287,13 @@ uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, 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]); + put_le16(start, &pdu[1]); + put_le16(end, &pdu[3]); + put_le16(uuid->value.u16, &pdu[5]); if (vlen > 0) { memcpy(&pdu[7], value, vlen); @@ -267,39 +307,27 @@ uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid, uint8_t *value, size_t *vlen) { - size_t valuelen; - uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + - sizeof(*end) + sizeof(uint16_t); - if (pdu == NULL) return 0; - if (len < min_len) + if (len < 7) return 0; + /* Attribute Opcode (1 octet) */ 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; + /* First requested handle number (2 octets) */ + *start = get_le16(&pdu[1]); + /* Last requested handle number (2 octets) */ + *end = get_le16(&pdu[3]); + /* 16-bit UUID to find (2 octets) */ + bt_uuid16_create(uuid, get_le16(&pdu[5])); /* Attribute value to find */ - if (valuelen > 0 && value) - memcpy(value, pdu + min_len, valuelen); - - if (vlen) - *vlen = valuelen; + *vlen = len - 7; + if (*vlen > 0) + memcpy(value, pdu + 7, *vlen); return len; } @@ -309,7 +337,7 @@ uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, size_t len) GSList *l; uint16_t offset; - if (pdu == NULL || len < 5) + if (!pdu) return 0; pdu[0] = ATT_OP_FIND_BY_TYPE_RESP; @@ -319,8 +347,8 @@ uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, size_t len) l = l->next, offset += sizeof(uint16_t) * 2) { struct att_range *range = l->data; - att_put_u16(range->start, &pdu[offset]); - att_put_u16(range->end, &pdu[offset + 2]); + put_le16(range->start, &pdu[offset]); + put_le16(range->end, &pdu[offset + 2]); } return offset; @@ -332,18 +360,27 @@ GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len) GSList *matches; off_t offset; + /* PDU should contain at least: + * - Attribute Opcode (1 octet) + * - Handles Information List (at least one entry): + * - Found Attribute Handle (2 octets) + * - Group End Handle (2 octets) */ if (pdu == NULL || len < 5) return NULL; if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP) return NULL; + /* Reject incomplete Handles Information List */ + if ((len - 1) % 4) + return NULL; + for (offset = 1, matches = NULL; len >= (offset + sizeof(uint16_t) * 2); offset += sizeof(uint16_t) * 2) { range = g_new0(struct att_range, 1); - range->start = att_get_u16(&pdu[offset]); - range->end = att_get_u16(&pdu[offset + 2]); + range->start = get_le16(&pdu[offset]); + range->end = get_le16(&pdu[offset + 2]); matches = g_slist_append(matches, range); } @@ -354,35 +391,35 @@ GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len) uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { - uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); - uint16_t length; + uint16_t uuid_len; if (!uuid) return 0; if (uuid->type == BT_UUID16) - length = 2; + uuid_len = 2; else if (uuid->type == BT_UUID128) - length = 16; + uuid_len = 16; else return 0; - if (len < min_len + length) - return 0; - + /* Attribute Opcode (1 octet) */ 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; + /* Starting Handle (2 octets) */ + put_le16(start, &pdu[1]); + /* Ending Handle (2 octets) */ + put_le16(end, &pdu[3]); + /* Attribute Type (2 or 16 octet UUID) */ + put_uuid_le(uuid, &pdu[5]); + + return 5 + uuid_len; } uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid) { const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); + uint8_t type; if (pdu == NULL) return 0; @@ -390,19 +427,20 @@ uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, if (start == NULL || end == NULL || uuid == NULL) return 0; - if (len < min_len + 2) + if (len == (min_len + 2)) + type = BT_UUID16; + else if (len == (min_len + 16)) + type = BT_UUID128; + else 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]); + *start = get_le16(&pdu[1]); + *end = get_le16(&pdu[3]); - if (len == min_len + 2) - *uuid = att_get_uuid16(&pdu[5]); - else - *uuid = att_get_uuid128(&pdu[5]); + get_uuid(type, &pdu[5], uuid); return len; } @@ -444,7 +482,24 @@ struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len) if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP) return NULL; + /* PDU must contain at least: + * - Attribute Opcode (1 octet) + * - Length (1 octet) + * - Attribute Data List (at least one entry): + * - Attribute Handle (2 octets) + * - Attribute Value (at least 1 octet) */ + if (len < 5) + return NULL; + elen = pdu[1]; + /* Minimum Attribute Data List size */ + if (elen < 3) + return NULL; + + /* Reject incomplete Attribute Data List */ + if ((len - 2) % elen) + return NULL; + num = (len - 2) / elen; list = att_data_list_alloc(num, elen); if (list == NULL) @@ -468,14 +523,11 @@ uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen, 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]); + put_le16(handle, &pdu[1]); if (vlen > 0) { memcpy(&pdu[3], value, vlen); @@ -502,7 +554,7 @@ uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle, if (pdu[0] != ATT_OP_WRITE_CMD) return 0; - *handle = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); memcpy(value, pdu + min_len, len - min_len); *vlen = len - min_len; @@ -517,14 +569,11 @@ uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen, 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]); + put_le16(handle, &pdu[1]); if (vlen > 0) { memcpy(&pdu[3], value, vlen); @@ -551,7 +600,7 @@ uint16_t dec_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, if (pdu[0] != ATT_OP_WRITE_REQ) return 0; - *handle = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); *vlen = len - min_len; if (*vlen > 0) memcpy(value, pdu + min_len, *vlen); @@ -582,37 +631,31 @@ uint16_t dec_write_resp(const uint8_t *pdu, size_t len) uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_REQ; - att_put_u16(handle, &pdu[1]); + /* Attribute Handle (2 octets) */ + put_le16(handle, &pdu[1]); - return min_len; + return 3; } uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) + - sizeof(offset); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BLOB_REQ; - att_put_u16(handle, &pdu[1]); - att_put_u16(offset, &pdu[3]); + /* Attribute Handle (2 octets) */ + put_le16(handle, &pdu[1]); + /* Value Offset (2 octets) */ + put_le16(offset, &pdu[3]); - return min_len; + return 5; } uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle) @@ -631,7 +674,7 @@ uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle) if (pdu[0] != ATT_OP_READ_REQ) return 0; - *handle = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); return min_len; } @@ -657,8 +700,8 @@ uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle, if (pdu[0] != ATT_OP_READ_BLOB_REQ) return 0; - *handle = att_get_u16(&pdu[1]); - *offset = att_get_u16(&pdu[3]); + *handle = get_le16(&pdu[1]); + *offset = get_le16(&pdu[3]); return min_len; } @@ -721,38 +764,32 @@ ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value, uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status, uint8_t *pdu, size_t 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); + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_ERROR; + /* Request Opcode In Error (1 octet) */ pdu[1] = opcode; - memcpy(&pdu[2], &u16, sizeof(u16)); + /* Attribute Handle In Error (2 octets) */ + put_le16(handle, &pdu[2]); + /* Error Code (1 octet) */ pdu[4] = status; - return min_len; + return 5; } uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_FIND_INFO_REQ; - att_put_u16(start, &pdu[1]); - att_put_u16(end, &pdu[3]); + /* Starting Handle (2 octets) */ + put_le16(start, &pdu[1]); + /* Ending Handle (2 octets) */ + put_le16(end, &pdu[3]); - return min_len; + return 5; } uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start, @@ -772,8 +809,8 @@ uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start, if (pdu[0] != ATT_OP_FIND_INFO_REQ) return 0; - *start = att_get_u16(&pdu[1]); - *end = att_get_u16(&pdu[3]); + *start = get_le16(&pdu[1]); + *end = get_le16(&pdu[3]); return min_len; } @@ -858,7 +895,7 @@ uint16_t enc_notification(uint16_t handle, uint8_t *value, size_t vlen, return 0; pdu[0] = ATT_OP_HANDLE_NOTIFY; - att_put_u16(handle, &pdu[1]); + put_le16(handle, &pdu[1]); memcpy(&pdu[3], value, vlen); return vlen + min_len; @@ -876,7 +913,7 @@ uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen, return 0; pdu[0] = ATT_OP_HANDLE_IND; - att_put_u16(handle, &pdu[1]); + put_le16(handle, &pdu[1]); memcpy(&pdu[3], value, vlen); return vlen + min_len; @@ -900,7 +937,7 @@ uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle, dlen = MIN(len - min_len, vlen); if (handle) - *handle = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); memcpy(value, &pdu[3], dlen); @@ -909,33 +946,26 @@ uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t enc_confirmation(uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_HANDLE_CNF; - return min_len; + return 1; } uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_MTU_REQ; - att_put_u16(mtu, &pdu[1]); + /* Client Rx MTU (2 octets) */ + put_le16(mtu, &pdu[1]); - return min_len; + return 3; } uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu) @@ -954,25 +984,22 @@ uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu) if (pdu[0] != ATT_OP_MTU_REQ) return 0; - *mtu = att_get_u16(&pdu[1]); + *mtu = get_le16(&pdu[1]); return min_len; } uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_MTU_RESP; - att_put_u16(mtu, &pdu[1]); + /* Server Rx MTU (2 octets) */ + put_le16(mtu, &pdu[1]); - return min_len; + return 3; } uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu) @@ -991,7 +1018,7 @@ uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu) if (pdu[0] != ATT_OP_MTU_RESP) return 0; - *mtu = att_get_u16(&pdu[1]); + *mtu = get_le16(&pdu[1]); return min_len; } @@ -1006,15 +1033,12 @@ uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset, if (pdu == NULL) return 0; - if (len < min_len) - return 0; - if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_PREP_WRITE_REQ; - att_put_u16(handle, &pdu[1]); - att_put_u16(offset, &pdu[3]); + put_le16(handle, &pdu[1]); + put_le16(offset, &pdu[3]); if (vlen > 0) { memcpy(&pdu[5], value, vlen); @@ -1042,8 +1066,8 @@ uint16_t dec_prep_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, if (pdu[0] != ATT_OP_PREP_WRITE_REQ) return 0; - *handle = att_get_u16(&pdu[1]); - *offset = att_get_u16(&pdu[3]); + *handle = get_le16(&pdu[1]); + *offset = get_le16(&pdu[3]); *vlen = len - min_len; if (*vlen > 0) @@ -1062,15 +1086,12 @@ uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset, if (pdu == NULL) return 0; - if (len < min_len) - return 0; - if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_PREP_WRITE_RESP; - att_put_u16(handle, &pdu[1]); - att_put_u16(offset, &pdu[3]); + put_le16(handle, &pdu[1]); + put_le16(offset, &pdu[3]); if (vlen > 0) { memcpy(&pdu[5], value, vlen); @@ -1098,8 +1119,8 @@ uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle, if (pdu[0] != ATT_OP_PREP_WRITE_REQ) return 0; - *handle = att_get_u16(&pdu[1]); - *offset = att_get_u16(&pdu[3]); + *handle = get_le16(&pdu[1]); + *offset = get_le16(&pdu[3]); *vlen = len - min_len; if (*vlen > 0) memcpy(value, pdu + min_len, *vlen); @@ -1109,21 +1130,18 @@ uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(flags); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - if (flags > 1) return 0; + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_EXEC_WRITE_REQ; + /* Flags (1 octet) */ pdu[1] = flags; - return min_len; + return 2; } uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags) @@ -1152,9 +1170,10 @@ uint16_t enc_exec_write_resp(uint8_t *pdu) if (pdu == NULL) return 0; + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_EXEC_WRITE_RESP; - return sizeof(pdu[0]); + return 1; } uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len) diff --git a/attrib/att.h b/attrib/att.h index 28bc944..c612d80 100644 --- a/attrib/att.h +++ b/attrib/att.h @@ -75,16 +75,6 @@ #define ATT_ECODE_TIMEOUT 0x81 #define ATT_ECODE_ABORTED 0x82 -/* 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_VALUE_LEN 512 #define ATT_DEFAULT_L2CAP_MTU 48 #define ATT_DEFAULT_LE_MTU 23 @@ -111,95 +101,6 @@ struct att_range { uint16_t end; }; -/* 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); diff --git a/attrib/gatt-service.c b/attrib/gatt-service.c index a8ab582..874552b 100644 --- a/attrib/gatt-service.c +++ b/attrib/gatt-service.c @@ -27,17 +27,17 @@ #endif #include -#include -#include +#include "src/adapter.h" +#include "src/shared/util.h" #include "lib/uuid.h" -#include "gattrib.h" -#include "att.h" -#include "gatt.h" -#include "att-database.h" -#include "attrib-server.h" -#include "gatt-service.h" -#include "log.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "attrib/att-database.h" +#include "src/attrib-server.h" +#include "attrib/gatt-service.h" +#include "src/log.h" struct gatt_info { bt_uuid_t uuid; @@ -56,6 +56,15 @@ struct attrib_cb { void *user_data; }; +static inline void put_uuid_le(const bt_uuid_t *src, void *dst) +{ + if (src->type == BT_UUID16) + put_le16(src->value.u16, dst); + else + /* Convert from 128-bit BE to LE */ + bswap_128(&src->value.u128, dst); +} + static GSList *parse_opts(gatt_option opt1, va_list args) { gatt_option opt = opt1; @@ -82,8 +91,8 @@ static GSList *parse_opts(gatt_option opt1, va_list args) case GATT_OPT_CHR_PROPS: info->props = va_arg(args, int); - if (info->props & (ATT_CHAR_PROPER_NOTIFY | - ATT_CHAR_PROPER_INDICATE)) + if (info->props & (GATT_CHR_PROP_NOTIFY | + GATT_CHR_PROP_INDICATE)) /* client characteristic configuration */ info->num_attrs += 1; @@ -130,14 +139,8 @@ static struct attribute *add_service_declaration(struct btd_adapter *adapter, uint8_t atval[16]; int len; - if (uuid->type == BT_UUID16) { - att_put_u16(uuid->value.u16, &atval[0]); - len = 2; - } else if (uuid->type == BT_UUID128) { - att_put_u128(uuid->value.u128, &atval[0]); - len = 16; - } else - return NULL; + put_uuid_le(uuid, &atval[0]); + len = bt_uuid_len(uuid); bt_uuid16_create(&bt_uuid, svc); @@ -153,7 +156,7 @@ static int att_read_req(int authorization, int authentication, uint8_t props) else if (authentication == GATT_CHR_VALUE_READ || authentication == GATT_CHR_VALUE_BOTH) return ATT_AUTHENTICATION; - else if (!(props & ATT_CHAR_PROPER_READ)) + else if (!(props & GATT_CHR_PROP_READ)) return ATT_NOT_PERMITTED; return ATT_NONE; @@ -167,8 +170,8 @@ static int att_write_req(int authorization, int authentication, uint8_t props) else if (authentication == GATT_CHR_VALUE_WRITE || authentication == GATT_CHR_VALUE_BOTH) return ATT_AUTHENTICATION; - else if (!(props & (ATT_CHAR_PROPER_WRITE | - ATT_CHAR_PROPER_WRITE_WITHOUT_RESP))) + else if (!(props & (GATT_CHR_PROP_WRITE | + GATT_CHR_PROP_WRITE_WITHOUT_RESP))) return ATT_NOT_PERMITTED; return ATT_NONE; @@ -228,8 +231,8 @@ static gboolean add_characteristic(struct btd_adapter *adapter, /* characteristic declaration */ bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID); atval[0] = info->props; - att_put_u16(h + 1, &atval[1]); - att_put_uuid(info->uuid, &atval[3]); + put_le16(h + 1, &atval[1]); + put_uuid_le(&info->uuid, &atval[3]); if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 3 + info->uuid.type / 8) == NULL) return FALSE; @@ -259,7 +262,7 @@ static gboolean add_characteristic(struct btd_adapter *adapter, *info->value_handle = a->handle; /* client characteristic configuration descriptor */ - if (info->props & (ATT_CHAR_PROPER_NOTIFY | ATT_CHAR_PROPER_INDICATE)) { + if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) { uint8_t cfg_val[2]; bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID); diff --git a/attrib/gatt.c b/attrib/gatt.c index bb2cae1..73eaf7a 100644 --- a/attrib/gatt.c +++ b/attrib/gatt.c @@ -32,12 +32,14 @@ #include #include +#include "src/shared/util.h" #include "lib/uuid.h" #include "att.h" #include "gattrib.h" #include "gatt.h" struct discover_primary { + int ref; GAttrib *attrib; bt_uuid_t uuid; GSList *primaries; @@ -62,6 +64,7 @@ struct included_uuid_query { }; struct discover_char { + int ref; GAttrib *attrib; bt_uuid_t *uuid; uint16_t end; @@ -70,13 +73,38 @@ struct discover_char { void *user_data; }; -static void discover_primary_free(struct discover_primary *dp) +struct discover_desc { + int ref; + GAttrib *attrib; + bt_uuid_t *uuid; + uint16_t end; + GSList *descriptors; + gatt_cb_t cb; + void *user_data; +}; + +static void discover_primary_unref(void *data) { - g_slist_free(dp->primaries); + struct discover_primary *dp = data; + + dp->ref--; + + if (dp->ref > 0) + return; + + g_slist_free_full(dp->primaries, g_free); g_attrib_unref(dp->attrib); g_free(dp); } +static struct discover_primary *discover_primary_ref( + struct discover_primary *dp) +{ + dp->ref++; + + return dp; +} + static struct included_discovery *isd_ref(struct included_discovery *isd) { __sync_fetch_and_add(&isd->refs, 1); @@ -90,23 +118,84 @@ static void isd_unref(struct included_discovery *isd) return; if (isd->err) - isd->cb(NULL, isd->err, isd->user_data); + isd->cb(isd->err, NULL, isd->user_data); else - isd->cb(isd->includes, isd->err, isd->user_data); + isd->cb(isd->err, isd->includes, isd->user_data); g_slist_free_full(isd->includes, g_free); g_attrib_unref(isd->attrib); g_free(isd); } -static void discover_char_free(struct discover_char *dc) +static void discover_char_unref(void *data) { + struct discover_char *dc = data; + + dc->ref--; + + if (dc->ref > 0) + return; + g_slist_free_full(dc->characteristics, g_free); g_attrib_unref(dc->attrib); g_free(dc->uuid); g_free(dc); } +static struct discover_char *discover_char_ref(struct discover_char *dc) +{ + dc->ref++; + + return dc; +} + +static void discover_desc_unref(void *data) +{ + struct discover_desc *dd = data; + + dd->ref--; + + if (dd->ref > 0) + return; + + g_slist_free_full(dd->descriptors, g_free); + g_attrib_unref(dd->attrib); + g_free(dd->uuid); + g_free(dd); +} + +static struct discover_desc *discover_desc_ref(struct discover_desc *dd) +{ + dd->ref++; + + return dd; +} + +static void put_uuid_le(const bt_uuid_t *uuid, void *dst) +{ + if (uuid->type == BT_UUID16) + put_le16(uuid->value.u16, dst); + else + /* Convert from 128-bit BE to LE */ + bswap_128(&uuid->value.u128, dst); +} + +static void get_uuid128(uint8_t type, const void *val, bt_uuid_t *uuid) +{ + if (type == BT_UUID16) { + bt_uuid_t uuid16; + + bt_uuid16_create(&uuid16, get_le16(val)); + bt_uuid_to_uuid128(&uuid16, uuid); + } else { + uint128_t u128; + + /* Convert from 128-bit LE to BE */ + bswap_128(val, &u128); + bt_uuid128_create(uuid, u128); + } +} + static guint16 encode_discover_primary(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { @@ -119,22 +208,12 @@ static guint16 encode_discover_primary(uint16_t start, uint16_t end, /* Discover all primary services */ plen = enc_read_by_grp_req(start, end, &prim, pdu, len); } else { - uint16_t u16; - uint128_t u128; - const void *value; + uint8_t value[16]; size_t vlen; /* Discover primary service by service UUID */ - - 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); - } + put_uuid_le(uuid, value); + vlen = bt_uuid_len(uuid); plen = enc_find_by_type_req(start, end, &prim, value, vlen, pdu, len); @@ -179,12 +258,12 @@ static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu, if (oplen == 0) goto done; - g_attrib_send(dp->attrib, 0, buf, oplen, primary_by_uuid_cb, dp, NULL); + g_attrib_send(dp->attrib, 0, buf, oplen, primary_by_uuid_cb, + discover_primary_ref(dp), discover_primary_unref); return; done: - dp->cb(dp->primaries, err, dp->user_data); - discover_primary_free(dp); + dp->cb(err, dp->primaries, dp->user_data); } static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen, @@ -194,6 +273,7 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen, struct att_data_list *list; unsigned int i, err; uint16_t start, end; + uint8_t type; if (status) { err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status; @@ -206,23 +286,25 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen, goto done; } + if (list->len == 6) + type = BT_UUID16; + else if (list->len == 20) + type = BT_UUID128; + else { + att_data_list_free(list); + err = ATT_ECODE_INVALID_PDU; + goto done; + } + for (i = 0, end = 0; i < list->num; i++) { const uint8_t *data = list->data[i]; struct gatt_primary *primary; - bt_uuid_t uuid; + bt_uuid_t uuid128; - start = att_get_u16(&data[0]); - end = att_get_u16(&data[2]); + start = get_le16(&data[0]); + end = get_le16(&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; - } + get_uuid128(type, &data[4], &uuid128); primary = g_try_new0(struct gatt_primary, 1); if (!primary) { @@ -232,7 +314,7 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen, } primary->range.start = start; primary->range.end = end; - bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid)); + bt_uuid_to_string(&uuid128, primary->uuid, sizeof(primary->uuid)); dp->primaries = g_slist_append(dp->primaries, primary); } @@ -246,14 +328,14 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen, buf, buflen); g_attrib_send(dp->attrib, 0, buf, oplen, primary_all_cb, - dp, NULL); + discover_primary_ref(dp), + discover_primary_unref); return; } done: - dp->cb(dp->primaries, err, dp->user_data); - discover_primary_free(dp); + dp->cb(err, dp->primaries, dp->user_data); } guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, @@ -283,7 +365,9 @@ guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, } else cb = primary_all_cb; - return g_attrib_send(attrib, 0, buf, plen, cb, dp, NULL); + return g_attrib_send(attrib, 0, buf, plen, cb, + discover_primary_ref(dp), + discover_primary_unref); } static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu, @@ -293,7 +377,7 @@ static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu, struct included_discovery *isd = query->isd; struct gatt_included *incl = query->included; unsigned int err = status; - bt_uuid_t uuid; + bt_uuid_t uuid128; size_t buflen; uint8_t *buf; @@ -306,19 +390,23 @@ static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu, goto done; } - uuid = att_get_uuid128(buf); - bt_uuid_to_string(&uuid, incl->uuid, sizeof(incl->uuid)); + get_uuid128(BT_UUID128, buf, &uuid128); + + bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid)); isd->includes = g_slist_append(isd->includes, incl); + query->included = NULL; done: - if (err) - g_free(incl); - if (isd->err == 0) isd->err = err; +} - isd_unref(isd); +static void inc_query_free(void *data) +{ + struct included_uuid_query *query = data; + isd_unref(query->isd); + g_free(query->included); g_free(query); } @@ -335,22 +423,22 @@ static guint resolve_included_uuid(struct included_discovery *isd, query->included = incl; return g_attrib_send(isd->attrib, 0, buf, oplen, - resolve_included_uuid_cb, query, NULL); + resolve_included_uuid_cb, query, + inc_query_free); } static struct gatt_included *included_from_buf(const uint8_t *buf, gsize len) { struct gatt_included *incl = g_new0(struct gatt_included, 1); - incl->handle = att_get_u16(&buf[0]); - incl->range.start = att_get_u16(&buf[2]); - incl->range.end = att_get_u16(&buf[4]); + incl->handle = get_le16(&buf[0]); + incl->range.start = get_le16(&buf[2]); + incl->range.end = get_le16(&buf[4]); if (len == 8) { bt_uuid_t uuid128; - bt_uuid_t uuid16 = att_get_uuid16(&buf[6]); - bt_uuid_to_uuid128(&uuid16, &uuid128); + get_uuid128(BT_UUID16, &buf[6], &uuid128); bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid)); } @@ -448,6 +536,7 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, struct att_data_list *list; unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND; uint16_t last = 0; + uint8_t type; if (status) { err = status; @@ -460,32 +549,34 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, goto done; } + if (list->len == 7) + type = BT_UUID16; + else + type = BT_UUID128; + for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; struct gatt_char *chars; - bt_uuid_t uuid; + bt_uuid_t uuid128; - last = att_get_u16(value); + last = get_le16(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]); + get_uuid128(type, &value[5], &uuid128); - if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid)) + if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid128)) continue; chars = g_try_new0(struct gatt_char, 1); if (!chars) { + att_data_list_free(list); err = ATT_ECODE_INSUFF_RESOURCES; goto done; } 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)); + chars->value_handle = get_le16(&value[3]); + bt_uuid_to_string(&uuid128, chars->uuid, sizeof(chars->uuid)); dc->characteristics = g_slist_append(dc->characteristics, chars); } @@ -509,16 +600,14 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, return; g_attrib_send(dc->attrib, 0, buf, oplen, char_discovered_cb, - dc, NULL); + discover_char_ref(dc), discover_char_unref); return; } done: err = (dc->characteristics ? 0 : err); - - dc->cb(dc->characteristics, err, dc->user_data); - discover_char_free(dc); + dc->cb(err, dc->characteristics, dc->user_data); } guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, @@ -548,7 +637,7 @@ guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t)); return g_attrib_send(attrib, 0, buf, plen, char_discovered_cb, - dc, NULL); + discover_char_ref(dc), discover_char_unref); } guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end, @@ -777,7 +866,7 @@ static guint prepare_write(struct write_long_data *long_write) NULL); } -guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value, +guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; @@ -814,6 +903,30 @@ guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value, return prepare_write(long_write); } +guint gatt_execute_write(GAttrib *attrib, uint8_t flags, + GAttribResultFunc func, gpointer user_data) +{ + return execute_write(attrib, flags, func, user_data); +} + +guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle, + const uint8_t *value, size_t vlen, + GAttribResultFunc func, + gpointer user_data) +{ + uint8_t *buf; + guint16 plen; + size_t buflen; + + buf = g_attrib_get_buffer(attrib, &buflen); + + plen = enc_prep_write_req(handle, 0, value, vlen, buf, buflen); + if (!plen) + return 0; + + return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); +} + guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func, gpointer user_data) { @@ -826,23 +939,122 @@ guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func, return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } -guint gatt_discover_char_desc(GAttrib *attrib, uint16_t start, uint16_t end, - GAttribResultFunc func, gpointer user_data) + +static void desc_discovered_cb(guint8 status, const guint8 *ipdu, + guint16 iplen, gpointer user_data) +{ + struct discover_desc *dd = user_data; + struct att_data_list *list; + unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND; + guint8 format; + uint16_t last = 0xffff; + uint8_t type; + gboolean uuid_found = FALSE; + + if (status) { + err = status; + goto done; + } + + list = dec_find_info_resp(ipdu, iplen, &format); + if (!list) { + err = ATT_ECODE_IO; + goto done; + } + + if (format == ATT_FIND_INFO_RESP_FMT_16BIT) + type = BT_UUID16; + else + type = BT_UUID128; + + for (i = 0; i < list->num; i++) { + uint8_t *value = list->data[i]; + struct gatt_desc *desc; + bt_uuid_t uuid128; + + last = get_le16(value); + + get_uuid128(type, &value[2], &uuid128); + + if (dd->uuid) { + if (bt_uuid_cmp(dd->uuid, &uuid128)) + continue; + else + uuid_found = TRUE; + } + + desc = g_try_new0(struct gatt_desc, 1); + if (!desc) { + att_data_list_free(list); + err = ATT_ECODE_INSUFF_RESOURCES; + goto done; + } + + bt_uuid_to_string(&uuid128, desc->uuid, sizeof(desc->uuid)); + desc->handle = last; + + if (type == BT_UUID16) + desc->uuid16 = get_le16(&value[2]); + + dd->descriptors = g_slist_append(dd->descriptors, desc); + + if (uuid_found) + break; + } + + att_data_list_free(list); + + if (last + 1 < dd->end && !uuid_found) { + guint16 oplen; + size_t buflen; + uint8_t *buf; + + buf = g_attrib_get_buffer(dd->attrib, &buflen); + + oplen = enc_find_info_req(last + 1, dd->end, buf, buflen); + if (oplen == 0) + return; + + g_attrib_send(dd->attrib, 0, buf, oplen, desc_discovered_cb, + discover_desc_ref(dd), discover_desc_unref); + + return; + } + +done: + err = (dd->descriptors ? 0 : err); + dd->cb(err, dd->descriptors, dd->user_data); +} + +guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data) { - uint8_t *buf; size_t buflen; + uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); + struct discover_desc *dd; 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, buf, plen, func, user_data, NULL); + dd = g_try_new0(struct discover_desc, 1); + if (dd == NULL) + return 0; + + dd->attrib = g_attrib_ref(attrib); + dd->cb = func; + dd->user_data = user_data; + dd->end = end; + dd->uuid = g_memdup(uuid, sizeof(bt_uuid_t)); + + return g_attrib_send(attrib, 0, buf, plen, desc_discovered_cb, + discover_desc_ref(dd), discover_desc_unref); } -guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen, - GDestroyNotify notify, gpointer user_data) +guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value, + int vlen, GDestroyNotify notify, gpointer user_data) { uint8_t *buf; size_t buflen; diff --git a/attrib/gatt.h b/attrib/gatt.h index e5abd85..7d055f0 100644 --- a/attrib/gatt.h +++ b/attrib/gatt.h @@ -24,36 +24,29 @@ #include -/* 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 -#define GATT_CHARAC_VALID_RANGE_UUID 0x2906 -#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907 -#define GATT_REPORT_REFERENCE 0x2908 +/* + * GATT Characteristic Property bit field + * Reference: Core SPEC 4.1 page 2183 (Table 3.5: Characteristic Properties + * bit field) defines how the Characteristic Value can be used, or how the + * characteristic descriptors (see Section 3.3.3 - page 2184) can be accessed. + * In the core spec, regular properties are included in the characteristic + * declaration, and the extended properties are defined as descriptor. + */ + +#define GATT_CHR_PROP_BROADCAST 0x01 +#define GATT_CHR_PROP_READ 0x02 +#define GATT_CHR_PROP_WRITE_WITHOUT_RESP 0x04 +#define GATT_CHR_PROP_WRITE 0x08 +#define GATT_CHR_PROP_NOTIFY 0x10 +#define GATT_CHR_PROP_INDICATE 0x20 +#define GATT_CHR_PROP_AUTH 0x40 +#define GATT_CHR_PROP_EXT_PROP 0x80 /* Client Characteristic Configuration bit field */ #define GATT_CLIENT_CHARAC_CFG_NOTIF_BIT 0x0001 #define GATT_CLIENT_CHARAC_CFG_IND_BIT 0x0002 -typedef void (*gatt_cb_t) (GSList *l, guint8 status, gpointer user_data); +typedef void (*gatt_cb_t) (uint8_t status, GSList *l, void *user_data); struct gatt_primary { char uuid[MAX_LEN_UUID_STR + 1]; @@ -74,6 +67,12 @@ struct gatt_char { uint16_t value_handle; }; +struct gatt_desc { + char uuid[MAX_LEN_UUID_STR + 1]; + uint16_t handle; + uint16_t uuid16; +}; + guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data); @@ -87,15 +86,24 @@ guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func, gpointer user_data); -guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value, +guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value, size_t vlen, GAttribResultFunc func, gpointer user_data); -guint gatt_discover_char_desc(GAttrib *attrib, uint16_t start, uint16_t end, +guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data); + +guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle, + const uint8_t *value, size_t vlen, + GAttribResultFunc func, + gpointer user_data); + +guint gatt_execute_write(GAttrib *attrib, uint8_t flags, 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_write_cmd(GAttrib *attrib, uint16_t handle, const 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, diff --git a/attrib/gattrib.c b/attrib/gattrib.c index 609b908..912dffb 100644 --- a/attrib/gattrib.c +++ b/attrib/gattrib.c @@ -34,12 +34,13 @@ #include #include -#include +#include "btio/btio.h" #include "lib/uuid.h" -#include "log.h" -#include "att.h" -#include "gattrib.h" +#include "src/shared/util.h" +#include "src/log.h" +#include "attrib/att.h" +#include "attrib/gattrib.h" #define GATT_TIMEOUT 30 @@ -381,7 +382,7 @@ static bool match_event(struct event *evt, const uint8_t *pdu, gsize len) if (len < 3) return false; - handle = att_get_u16(&pdu[1]); + handle = get_le16(&pdu[1]); if (evt->expected == pdu[0] && evt->handle == handle) return true; @@ -402,7 +403,16 @@ static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + struct command *c; + + while ((c = g_queue_pop_head(attrib->requests))) { + if (c->func) + c->func(ATT_ECODE_IO, NULL, 0, c->user_data); + command_destroy(c); + } + attrib->read_watch = 0; + return FALSE; } diff --git a/attrib/gatttool.c b/attrib/gatttool.c index f211dcd..bca7f69 100644 --- a/attrib/gatttool.c +++ b/attrib/gatttool.c @@ -35,9 +35,10 @@ #include #include +#include "src/shared/util.h" #include "lib/uuid.h" #include "att.h" -#include +#include "btio/btio.h" #include "gattrib.h" #include "gatt.h" #include "gatttool.h" @@ -78,7 +79,7 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) uint16_t handle, i, olen = 0; size_t plen; - handle = att_get_u16(&pdu[1]); + handle = get_le16(&pdu[1]); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: @@ -137,7 +138,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) operation(attrib); } -static void primary_all_cb(GSList *services, guint8 status, gpointer user_data) +static void primary_all_cb(uint8_t status, GSList *services, void *user_data) { GSList *l; @@ -157,8 +158,7 @@ done: g_main_loop_quit(event_loop); } -static void primary_by_uuid_cb(GSList *ranges, guint8 status, - gpointer user_data) +static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data) { GSList *l; @@ -191,8 +191,8 @@ static gboolean primary(gpointer user_data) return FALSE; } -static void char_discovered_cb(GSList *characteristics, guint8 status, - gpointer user_data) +static void char_discovered_cb(uint8_t status, GSList *characteristics, + void *user_data) { GSList *l; @@ -272,7 +272,7 @@ static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, uint8_t *value = list->data[i]; int j; - g_print("handle: 0x%04x \t value: ", att_get_u16(value)); + g_print("handle: 0x%04x \t value: ", get_le16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) g_print("%02x ", *value); @@ -400,44 +400,23 @@ error: return FALSE; } -static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen, - gpointer user_data) +static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data) { - struct att_data_list *list; - guint8 format; - int i; + GSList *l; - if (status != 0) { - g_printerr("Discover all characteristic descriptors failed: " - "%s\n", att_ecode2str(status)); - goto done; + if (status) { + g_printerr("Discover descriptors failed: %s\n", + att_ecode2str(status)); + return; } - 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); + for (l = descriptors; l; l = l->next) { + struct gatt_desc *desc = l->data; - 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); + g_print("handle = 0x%04x, uuid = %s\n", desc->handle, + desc->uuid); } - att_data_list_free(list); - -done: if (!opt_listen) g_main_loop_quit(event_loop); } @@ -446,7 +425,8 @@ static gboolean characteristics_desc(gpointer user_data) { GAttrib *attrib = user_data; - gatt_discover_char_desc(attrib, opt_start, opt_end, char_desc_cb, NULL); + gatt_discover_desc(attrib, opt_start, opt_end, NULL, char_desc_cb, + NULL); return FALSE; } diff --git a/attrib/interactive.c b/attrib/interactive.c index 5bd27af..08f39f7 100644 --- a/attrib/interactive.c +++ b/attrib/interactive.c @@ -38,8 +38,9 @@ #include #include +#include "src/shared/util.h" #include "lib/uuid.h" -#include +#include "btio/btio.h" #include "att.h" #include "gattrib.h" #include "gatt.h" @@ -113,7 +114,7 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) size_t plen; GString *s; - handle = att_get_u16(&pdu[1]); + handle = get_le16(&pdu[1]); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: @@ -180,7 +181,7 @@ static void disconnect_io() set_state(STATE_DISCONNECTED); } -static void primary_all_cb(GSList *services, guint8 status, gpointer user_data) +static void primary_all_cb(uint8_t status, GSList *services, void *user_data) { GSList *l; @@ -202,8 +203,7 @@ static void primary_all_cb(GSList *services, guint8 status, gpointer user_data) } } -static void primary_by_uuid_cb(GSList *ranges, guint8 status, - gpointer user_data) +static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data) { GSList *l; @@ -225,7 +225,7 @@ static void primary_by_uuid_cb(GSList *ranges, guint8 status, } } -static void included_cb(GSList *includes, guint8 status, gpointer user_data) +static void included_cb(uint8_t status, GSList *includes, void *user_data) { GSList *l; @@ -249,7 +249,7 @@ static void included_cb(GSList *includes, guint8 status, gpointer user_data) } } -static void char_cb(GSList *characteristics, guint8 status, gpointer user_data) +static void char_cb(uint8_t status, GSList *characteristics, void *user_data) { GSList *l; @@ -269,46 +269,22 @@ static void char_cb(GSList *characteristics, guint8 status, gpointer user_data) } } -static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen, - gpointer user_data) +static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data) { - struct att_data_list *list; - guint8 format; - uint16_t handle = 0xffff; - int i; + GSList *l; - if (status != 0) { - rl_printf("Discover descriptors finished: %s\n", - att_ecode2str(status)); + if (status) { + error("Discover descriptors failed: %s\n", + att_ecode2str(status)); return; } - list = dec_find_info_resp(pdu, plen, &format); - if (list == NULL) - return; + for (l = descriptors; l; l = l->next) { + struct gatt_desc *desc = l->data; - for (i = 0; i < list->num; i++) { - char uuidstr[MAX_LEN_UUID_STR]; - 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); - rl_printf("handle: 0x%04x, uuid: %s\n", handle, uuidstr); + rl_printf("handle: 0x%04x, uuid: %s\n", desc->handle, + desc->uuid); } - - att_data_list_free(list); - - if (handle != 0xffff && handle < end) - gatt_discover_char_desc(attrib, handle + 1, end, char_desc_cb, - NULL); } static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen, @@ -362,7 +338,7 @@ static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, int j; g_string_printf(s, "handle: 0x%04x \t value: ", - att_get_u16(value)); + get_le16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) g_string_append_printf(s, "%02x ", *value); @@ -559,7 +535,7 @@ static void cmd_char_desc(int argcp, char **argvp) } else end = 0xffff; - gatt_discover_char_desc(attrib, start, end, char_desc_cb, NULL); + gatt_discover_desc(attrib, start, end, NULL, char_desc_cb, NULL); } static void cmd_read_hnd(int argcp, char **argvp) diff --git a/attrib/utils.c b/attrib/utils.c index 77bab27..8f9a4c0 100644 --- a/attrib/utils.c +++ b/attrib/utils.c @@ -31,10 +31,9 @@ #include #include #include -#include #include "lib/uuid.h" -#include +#include "btio/btio.h" #include "att.h" #include "gattrib.h" #include "gatt.h" diff --git a/btio/btio.c b/btio/btio.c index f62a533..5f91171 100644 --- a/btio/btio.c +++ b/btio/btio.c @@ -591,34 +591,60 @@ static gboolean get_key_size(int sock, int *size, GError **err) return FALSE; } -static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, - uint16_t omtu, uint8_t mode, int master, - int flushable, uint32_t priority, GError **err) +static gboolean set_l2opts(int sock, uint16_t imtu, uint16_t omtu, + uint8_t mode, GError **err) { - if (imtu || omtu || mode) { - struct l2cap_options l2o; - socklen_t len; + 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; - } + 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 (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 (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) { + ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno); + return FALSE; + } + + return TRUE; +} + +static gboolean set_le_imtu(int sock, uint16_t imtu, GError **err) +{ + if (setsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, &imtu, + sizeof(imtu)) < 0) { + ERROR_FAILED(err, "setsockopt(BT_RCVMTU)", errno); + return FALSE; + } + + return TRUE; +} + +static gboolean l2cap_set(int sock, uint8_t src_type, int sec_level, + uint16_t imtu, uint16_t omtu, uint8_t mode, + int master, int flushable, uint32_t priority, + GError **err) +{ + if (imtu || omtu || mode) { + gboolean ret; + + if (src_type == BDADDR_BREDR) + ret = set_l2opts(sock, imtu, omtu, mode, err); + else + ret = set_le_imtu(sock, imtu, err); + + if (!ret) + return ret; } if (master >= 0 && l2cap_set_master(sock, master) < 0) { @@ -860,8 +886,7 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err, return TRUE; } -static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst, - socklen_t len, GError **err) +static gboolean get_src(int sock, void *src, socklen_t len, GError **err) { socklen_t olen; @@ -872,6 +897,13 @@ static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst, return FALSE; } + return TRUE; +} + +static gboolean get_dst(int sock, void *dst, socklen_t len, GError **err) +{ + socklen_t olen; + memset(dst, 0, len); olen = len; if (getpeername(sock, dst, &olen) < 0) { @@ -939,20 +971,36 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, uint8_t dev_class[3]; uint16_t handle; socklen_t len; - gboolean flushable = FALSE; + gboolean flushable = FALSE, have_dst = FALSE; uint32_t priority; + if (!get_src(sock, &src, sizeof(src), err)) + return FALSE; + + memset(&l2o, 0, sizeof(l2o)); + + if (src.l2_bdaddr_type != BDADDR_BREDR) { + len = sizeof(l2o.imtu); + if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, + &l2o.imtu, &len) == 0) + goto parse_opts; + + /* Non-LE CoC enabled kernels will return one of these + * in which case we need to fall back to L2CAP_OPTIONS. + */ + if (errno != EPROTONOSUPPORT && errno != ENOPROTOOPT) { + ERROR_FAILED(err, "getsockopt(BT_RCVMTU)", errno); + return FALSE; + } + } + 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; - +parse_opts: while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: @@ -962,14 +1010,29 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr); break; case BT_IO_OPT_DEST: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; ba2str(&dst.l2_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr); break; case BT_IO_OPT_DEST_TYPE: - ERROR_FAILED(err, "Not implemented", EINVAL); - return FALSE; + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + *(va_arg(args, uint8_t *)) = dst.l2_bdaddr_type; + break; case BT_IO_OPT_DEFER_TIMEOUT: len = sizeof(int); if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, @@ -989,14 +1052,46 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, return FALSE; break; case BT_IO_OPT_PSM: - *(va_arg(args, uint16_t *)) = src.l2_psm ? - btohs(src.l2_psm) : btohs(dst.l2_psm); + if (src.l2_psm) { + *(va_arg(args, uint16_t *)) = btohs(src.l2_psm); + break; + } + + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + + *(va_arg(args, uint16_t *)) = btohs(dst.l2_psm); break; case BT_IO_OPT_CID: - *(va_arg(args, uint16_t *)) = src.l2_cid ? - btohs(src.l2_cid) : btohs(dst.l2_cid); + if (src.l2_cid) { + *(va_arg(args, uint16_t *)) = btohs(src.l2_cid); + break; + } + + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + + *(va_arg(args, uint16_t *)) = btohs(dst.l2_cid); break; case BT_IO_OPT_OMTU: + if (src.l2_bdaddr_type == BDADDR_BREDR) { + *(va_arg(args, uint16_t *)) = l2o.omtu; + break; + } + + if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU, + &l2o.omtu, &len) < 0) { + ERROR_FAILED(err, "getsockopt(BT_RCVMTU)", + errno); + return FALSE; + } + *(va_arg(args, uint16_t *)) = l2o.omtu; break; case BT_IO_OPT_IMTU: @@ -1079,13 +1174,13 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1, { BtIOOption opt = opt1; struct sockaddr_rc src, dst; + gboolean have_dst = FALSE; 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)) + if (!get_src(sock, &src, sizeof(src), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { @@ -1097,9 +1192,19 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1, bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr); break; case BT_IO_OPT_DEST: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; ba2str(&dst.rc_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr); break; case BT_IO_OPT_DEFER_TIMEOUT: @@ -1117,13 +1222,29 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1, return FALSE; break; case BT_IO_OPT_CHANNEL: - *(va_arg(args, uint8_t *)) = src.rc_channel ? - src.rc_channel : dst.rc_channel; + if (src.rc_channel) { + *(va_arg(args, uint8_t *)) = src.rc_channel; + break; + } + + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + + *(va_arg(args, uint8_t *)) = 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: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + *(va_arg(args, uint8_t *)) = dst.rc_channel; break; case BT_IO_OPT_MASTER: @@ -1197,8 +1318,10 @@ static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args) return FALSE; } - if (!get_peers(sock, (struct sockaddr *) &src, - (struct sockaddr *) &dst, sizeof(src), err)) + if (!get_src(sock, &src, sizeof(src), err)) + return FALSE; + + if (!get_dst(sock, &dst, sizeof(dst), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { @@ -1320,9 +1443,9 @@ gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...) switch (type) { case BT_IO_L2CAP: - return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu, - opts.mode, opts.master, opts.flushable, - opts.priority, err); + return l2cap_set(sock, opts.src_type, opts.sec_level, opts.imtu, + opts.omtu, opts.mode, opts.master, + opts.flushable, opts.priority, err); case BT_IO_RFCOMM: return rfcomm_set(sock, opts.sec_level, opts.master, err); case BT_IO_SCO: @@ -1368,9 +1491,10 @@ static GIOChannel *create_io(gboolean server, struct set_opts *opts, if (l2cap_bind(sock, &opts->src, opts->src_type, 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, opts->flushable, - opts->priority, err)) + if (!l2cap_set(sock, opts->src_type, opts->sec_level, + opts->imtu, opts->omtu, opts->mode, + opts->master, opts->flushable, opts->priority, + err)) goto failed; break; case BT_IO_RFCOMM: diff --git a/client/agent.c b/client/agent.c index 2d9dffd..eeabd5b 100644 --- a/client/agent.c +++ b/client/agent.c @@ -154,14 +154,11 @@ dbus_bool_t agent_input(DBusConnection *conn, const char *input) return TRUE; } -static DBusMessage *release_agent(DBusConnection *conn, - DBusMessage *msg, void *user_data) +static void agent_release(DBusConnection *conn) { agent_registered = FALSE; agent_capability = NULL; - rl_printf("Agent released\n"); - if (pending_message) { dbus_message_unref(pending_message); pending_message = NULL; @@ -170,6 +167,14 @@ static DBusMessage *release_agent(DBusConnection *conn, agent_release_prompt(); g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE); +} + +static DBusMessage *release_agent(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + rl_printf("Agent released\n"); + + agent_release(conn); return dbus_message_new_method_return(msg); } @@ -418,13 +423,8 @@ static void unregister_agent_reply(DBusMessage *message, void *user_data) dbus_error_init(&error); if (dbus_set_error_from_message(&error, message) == FALSE) { - agent_registered = FALSE; - agent_capability = NULL; rl_printf("Agent unregistered\n"); - - if (g_dbus_unregister_interface(conn, AGENT_PATH, - AGENT_INTERFACE) == FALSE) - rl_printf("Failed to unregister agent object\n"); + agent_release(conn); } else { rl_printf("Failed to unregister agent: %s\n", error.name); dbus_error_free(&error); @@ -438,6 +438,12 @@ void agent_unregister(DBusConnection *conn, GDBusProxy *manager) return; } + if (!manager) { + rl_printf("Agent unregistered\n"); + agent_release(conn); + return; + } + if (g_dbus_proxy_method_call(manager, "UnregisterAgent", unregister_agent_setup, unregister_agent_reply, diff --git a/client/main.c b/client/main.c index ebc85c6..3244d42 100644 --- a/client/main.c +++ b/client/main.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -153,6 +154,8 @@ static void print_iter(const char *label, const char *name, dbus_uint16_t valu16; dbus_int16_t vals16; const char *valstr; + DBusMessageIter subiter; + int type; if (iter == NULL) { rl_printf("%s%s is nil\n", label, name); @@ -185,6 +188,24 @@ static void print_iter(const char *label, const char *name, dbus_message_iter_get_basic(iter, &vals16); rl_printf("%s%s: %d\n", label, name, vals16); break; + case DBUS_TYPE_ARRAY: + dbus_message_iter_recurse(iter, &subiter); + rl_printf("%s%s:\n", label, name); + + do { + type = dbus_message_iter_get_arg_type(&subiter); + if (type == DBUS_TYPE_INVALID) + break; + + if (type == DBUS_TYPE_STRING) { + dbus_message_iter_get_basic(&subiter, &valstr); + rl_printf("\t%s\n", valstr); + } + + dbus_message_iter_next(&subiter); + } while(true); + + break; default: rl_printf("%s%s has unsupported type\n", label, name); break; @@ -315,8 +336,11 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data) dev_list = NULL; } } else if (!strcmp(interface, "org.bluez.AgentManager1")) { - if (agent_manager == proxy) + if (agent_manager == proxy) { agent_manager = NULL; + if (auto_register_agent) + agent_unregister(dbus_conn, NULL); + } } } @@ -851,6 +875,93 @@ static void cmd_trust(const char *arg) g_free(str); } +static void cmd_untrust(const char *arg) +{ + GDBusProxy *proxy; + dbus_bool_t trusted; + char *str; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + trusted = FALSE; + + str = g_strdup_printf("%s untrust", arg); + + if (g_dbus_proxy_set_property_basic(proxy, "Trusted", + DBUS_TYPE_BOOLEAN, &trusted, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void cmd_block(const char *arg) +{ + GDBusProxy *proxy; + dbus_bool_t blocked; + char *str; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + blocked = TRUE; + + str = g_strdup_printf("%s block", arg); + + if (g_dbus_proxy_set_property_basic(proxy, "Blocked", + DBUS_TYPE_BOOLEAN, &blocked, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void cmd_unblock(const char *arg) +{ + GDBusProxy *proxy; + dbus_bool_t blocked; + char *str; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + blocked = FALSE; + + str = g_strdup_printf("%s unblock", arg); + + if (g_dbus_proxy_set_property_basic(proxy, "Blocked", + DBUS_TYPE_BOOLEAN, &blocked, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + static void remove_device_reply(DBusMessage *message, void *user_data) { DBusError error; @@ -1088,6 +1199,12 @@ static const struct { dev_generator }, { "trust", "", cmd_trust, "Trust device", dev_generator }, + { "untrust", "", cmd_untrust, "Untrust device", + dev_generator }, + { "block", "", cmd_block, "Block device", + dev_generator }, + { "unblock", "", cmd_unblock, "Unblock device", + dev_generator }, { "remove", "", cmd_remove, "Remove device", dev_generator }, { "connect", "", cmd_connect, "Connect device", diff --git a/config.h.in b/config.h.in index 9f1f690..3a68c09 100644 --- a/config.h.in +++ b/config.h.in @@ -1,5 +1,8 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* Directory for the Android daemon storage files */ +#undef ANDROID_STORAGEDIR + /* Directory for the configuration files */ #undef CONFIGDIR diff --git a/configure b/configure index 7e58a43..b65515f 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bluez 5.12. +# Generated by GNU Autoconf 2.69 for bluez 5.19. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='bluez' PACKAGE_TARNAME='bluez' -PACKAGE_VERSION='5.12' -PACKAGE_STRING='bluez 5.12' +PACKAGE_VERSION='5.19' +PACKAGE_STRING='bluez 5.19' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -633,6 +633,10 @@ ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +SPEEXDSP_LIBS +SPEEXDSP_CFLAGS +SBC_LIBS +SBC_CFLAGS ANDROID_FALSE ANDROID_TRUE CONFIGDIR @@ -859,7 +863,11 @@ DBUS_LIBS UDEV_CFLAGS UDEV_LIBS ICAL_CFLAGS -ICAL_LIBS' +ICAL_LIBS +SBC_CFLAGS +SBC_LIBS +SPEEXDSP_CFLAGS +SPEEXDSP_LIBS' # Initialize some variables set by options. @@ -1400,7 +1408,7 @@ 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 5.12 to adapt to many kinds of systems. +\`configure' configures bluez 5.19 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1470,7 +1478,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bluez 5.12:";; + short | recursive ) echo "Configuration of bluez 5.19:";; esac cat <<\_ACEOF @@ -1552,6 +1560,12 @@ Some influential environment variables: UDEV_LIBS linker flags for UDEV, overriding pkg-config ICAL_CFLAGS C compiler flags for ICAL, overriding pkg-config ICAL_LIBS linker flags for ICAL, overriding pkg-config + SBC_CFLAGS C compiler flags for SBC, overriding pkg-config + SBC_LIBS linker flags for SBC, overriding pkg-config + SPEEXDSP_CFLAGS + C compiler flags for SPEEXDSP, overriding pkg-config + SPEEXDSP_LIBS + linker flags for SPEEXDSP, 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. @@ -1619,7 +1633,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bluez configure 5.12 +bluez configure 5.19 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1984,7 +1998,7 @@ 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 5.12, which was +It was created by bluez $as_me 5.19, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2839,7 +2853,7 @@ fi # Define the identity of the package. PACKAGE='bluez' - VERSION='5.12' + VERSION='5.19' cat >>confdefs.h <<_ACEOF @@ -13006,12 +13020,12 @@ 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:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.4\""; } >&5 - ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.4") 2>&5 + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.6\""; } >&5 + ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.6") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.4" 2>/dev/null` + pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.6" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -13023,12 +13037,12 @@ 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:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.4\""; } >&5 - ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.4") 2>&5 + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.6\""; } >&5 + ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.6") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.4" 2>/dev/null` + pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.6" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -13049,18 +13063,18 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.4" 2>&1` + DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.6" 2>&1` else - DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.4" 2>&1` + DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.6" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$DBUS_PKG_ERRORS" >&5 - as_fn_error $? "D-Bus >= 1.4 is required" "$LINENO" 5 + as_fn_error $? "D-Bus >= 1.6 is required" "$LINENO" 5 elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - as_fn_error $? "D-Bus >= 1.4 is required" "$LINENO" 5 + as_fn_error $? "D-Bus >= 1.6 is required" "$LINENO" 5 else DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS DBUS_LIBS=$pkg_cv_DBUS_LIBS @@ -13628,6 +13642,164 @@ else fi +if (test "${enable_android}" = "yes"); then + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SBC" >&5 +$as_echo_n "checking for SBC... " >&6; } + +if test -n "$SBC_CFLAGS"; then + pkg_cv_SBC_CFLAGS="$SBC_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sbc >= 1.2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sbc >= 1.2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SBC_CFLAGS=`$PKG_CONFIG --cflags "sbc >= 1.2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$SBC_LIBS"; then + pkg_cv_SBC_LIBS="$SBC_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sbc >= 1.2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sbc >= 1.2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SBC_LIBS=`$PKG_CONFIG --libs "sbc >= 1.2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +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 + SBC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sbc >= 1.2" 2>&1` + else + SBC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sbc >= 1.2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$SBC_PKG_ERRORS" >&5 + + as_fn_error $? "SBC library >= 1.2 is required" "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "SBC library >= 1.2 is required" "$LINENO" 5 +else + SBC_CFLAGS=$pkg_cv_SBC_CFLAGS + SBC_LIBS=$pkg_cv_SBC_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + dummy=yes +fi + + +fi + +if (test "${enable_android}" = "yes"); then + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SPEEXDSP" >&5 +$as_echo_n "checking for SPEEXDSP... " >&6; } + +if test -n "$SPEEXDSP_CFLAGS"; then + pkg_cv_SPEEXDSP_CFLAGS="$SPEEXDSP_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"speexdsp >= 1.2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "speexdsp >= 1.2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SPEEXDSP_CFLAGS=`$PKG_CONFIG --cflags "speexdsp >= 1.2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$SPEEXDSP_LIBS"; then + pkg_cv_SPEEXDSP_LIBS="$SPEEXDSP_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"speexdsp >= 1.2\""; } >&5 + ($PKG_CONFIG --exists --print-errors "speexdsp >= 1.2") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SPEEXDSP_LIBS=`$PKG_CONFIG --libs "speexdsp >= 1.2" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +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 + SPEEXDSP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "speexdsp >= 1.2" 2>&1` + else + SPEEXDSP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "speexdsp >= 1.2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$SPEEXDSP_PKG_ERRORS" >&5 + + as_fn_error $? "SPEEXDSP library >= 1.2 is required" "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "SPEEXDSP library >= 1.2 is required" "$LINENO" 5 +else + SPEEXDSP_CFLAGS=$pkg_cv_SPEEXDSP_CFLAGS + SPEEXDSP_LIBS=$pkg_cv_SPEEXDSP_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + dummy=yes +fi + + +fi + + +cat >>confdefs.h <<_ACEOF +#define ANDROID_STORAGEDIR "${storagedir}/android" +_ACEOF + + ac_config_files="$ac_config_files Makefile src/bluetoothd.8 lib/bluez.pc" cat >confcache <<\_ACEOF @@ -14220,7 +14392,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # 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 5.12, which was +This file was extended by bluez $as_me 5.19, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14286,7 +14458,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -bluez config.status 5.12 +bluez config.status 5.19 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 18d0b55..d858ff6 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(bluez, 5.12) +AC_INIT(bluez, 5.19) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules tar-pax no-dist-gzip dist-xz]) @@ -62,8 +62,8 @@ if (test "${enable_threads}" = "yes"); then GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS" fi -PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes, - AC_MSG_ERROR(D-Bus >= 1.4 is required)) +PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.6, dummy=yes, + AC_MSG_ERROR(D-Bus >= 1.6 is required)) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) @@ -252,4 +252,21 @@ AC_ARG_ENABLE(android, AC_HELP_STRING([--enable-android], [enable_android=${enableval}]) AM_CONDITIONAL(ANDROID, test "${enable_android}" = "yes") +if (test "${enable_android}" = "yes"); then + PKG_CHECK_MODULES(SBC, sbc >= 1.2, dummy=yes, + AC_MSG_ERROR(SBC library >= 1.2 is required)) + AC_SUBST(SBC_CFLAGS) + AC_SUBST(SBC_LIBS) +fi + +if (test "${enable_android}" = "yes"); then + PKG_CHECK_MODULES(SPEEXDSP, speexdsp >= 1.2, dummy=yes, + AC_MSG_ERROR(SPEEXDSP library >= 1.2 is required)) + AC_SUBST(SPEEXDSP_CFLAGS) + AC_SUBST(SPEEXDSP_LIBS) +fi + +AC_DEFINE_UNQUOTED(ANDROID_STORAGEDIR, "${storagedir}/android", + [Directory for the Android daemon storage files]) + AC_OUTPUT(Makefile src/bluetoothd.8 lib/bluez.pc) diff --git a/doc/media-api.txt b/doc/media-api.txt index 05f90e3..07ea991 100644 --- a/doc/media-api.txt +++ b/doc/media-api.txt @@ -128,32 +128,53 @@ Methods void Play() Resume playback. + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + void Pause() Pause playback. + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + void Stop() Stop playback. + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + void Next() Next item. + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + void Previous() Previous item. + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + void FastForward() Fast forward playback, this action is only stopped when another method in this interface is called. + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + void Rewind() Rewind playback, this action is only stopped when another method in this interface is called. + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + Properties string Equalizer [readwrite] Possible values: "off" or "on" @@ -300,10 +321,17 @@ Methods object Search(string value, dict filter) To list the items found use the folder object returned and pass to ChangeFolder. + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + array{objects, properties} ListItems(dict filter) Return a list of items found + Possible Errors: org.bluez.Error.InvalidArguments + org.bluez.Error.NotSupported + org.bluez.Error.Failed + void ChangeFolder(object folder) Change current folder. @@ -313,6 +341,10 @@ Methods object Search(string value, dict filter) exception is NowPlaying folder which should be always present while the player is active. + Possible Errors: org.bluez.Error.InvalidArguments + org.bluez.Error.NotSupported + org.bluez.Error.Failed + Properties uint32 NumberOfItems [readonly] Number of items in the folder @@ -368,10 +400,16 @@ Methods void Play() Play item + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + void AddtoNowPlaying() Add item to now playing list + Possible Errors: org.bluez.Error.NotSupported + org.bluez.Error.Failed + Properties object Player [readonly] Player object path the item belongs to diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt index 371f252..b691ae0 100644 --- a/doc/mgmt-api.txt +++ b/doc/mgmt-api.txt @@ -4,12 +4,43 @@ Bluetooth Management API Copyright (C) 2008-2009 Marcel Holtmann +Overview +======== + This document describes the format of data used for communicating with the kernel using a so-called Bluetooth Management sockets. These sockets -are available starting with Linux kernel version 3.4, and can be created -by setting the hci_channel member of struct sockaddr_hci to -HCI_CHANNEL_CONTROL (3) when creating a raw HCI socket. In C the needed -code would look something like the following: +are available starting with Linux kernel version 3.4 + +The following kernel versions introduced new commands, new events or +important fixes to the Bluetooth Management API: + +Linux kernel v3.4 Version 1.0 +Linux kernel v3.5 Version 1.1 +Linux kernel v3.7 Version 1.2 +Linux kernel v3.9 Version 1.3 +Linux kernel v3.13 Version 1.4 +Linux kernel v3.15 Version 1.5 (not yet released) + +Version 1.1 introduces Set Device ID command. + +Version 1.2 introduces Passkey Notify event. + +Version 1.3 does not introduce any new command or event. + +Version 1.4 introduces Set Advertising, Set BR/EDR, Set Static Address +and Set Scan Parameters commands. + +Version 1.5 introduces Set Secure Connections, Set Debug Keys, Set Privacy +and Load Identity Resolving Keys commands. It also introduces New Identity +Resolving Key and New Signature Resolving Key events. + + +Example +======= + +The Bluetooth management sockets can be created by setting the hci_channel +member of struct sockaddr_hci to HCI_CHANNEL_CONTROL (3) when creating a +raw HCI socket. In C the needed code would look something like the following: int mgmt_create(void) { @@ -194,6 +225,9 @@ Read Controller Information Command 9 High Speed 10 Low Energy 11 Advertising + 12 Secure Connections + 13 Debug Keys + 14 Privacy This command generates a Command Complete event on success or a Command Status event on failure. @@ -650,7 +684,7 @@ Load Link Keys Command This command is used to feed the kernel with currently known link keys. The command does not need to be called again upon the - receiption of new link key events since the kernel updates its + receiption of New Link Key events since the kernel updates its list automatically. The Debug_Keys parameter is used to tell the kernel whether to @@ -658,6 +692,31 @@ Load Link Keys Command this parameter are 0x00 and 0x01. All other values will return an Invalid Parameters response. + Usage of the Debug_Keys parameter is deprecated and has been + replaced with the Set Debug Keys command. When setting the + Debug_Keys option via Load Link Keys command it has the same + affect as setting it via Set Debug Keys and applies to all + keys in the system. + + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 Reserved (not in use) + 2 Reserved (not in use) + + Public and random LE addresses are not valid and will be rejected. + + Currently defined Key_Type values are: + + 0x00 Combination key + 0x01 Local Unit key + 0x02 Remote Unit key + 0x03 Debug Combination key + 0x04 Unauthenticated Combination key from P-192 + 0x05 Authenticated Combination key from P-192 + 0x06 Changed Combination key + 0x07 Unauthenticated Combination key from P-256 + 0x08 Authenticated Combination key from P-256 + This command can be used when the controller is not powered. This command generates a Command Complete event on success or @@ -676,7 +735,7 @@ Load Long Term Keys Command Key1 { Address (6 Octets) Address_Type (1 Octet) - Authenticated (1 Octet) + Key_Type (1 Octet) Master (1 Octet) Encryption_Size (1 Octet) Encryption_Diversifier (2 Octets) @@ -689,9 +748,25 @@ Load Long Term Keys Command This command is used to feed the kernel with currently known (SMP) Long Term Keys. The command does not need to be called - again upon the receiption of new Long Term Key events since the + again upon the receiption of New Long Term Key events since the kernel updates its list automatically. + Possible values for the Address_Type parameter: + 0 Reserved (not in use) + 1 LE Public + 2 LE Random + + The provided Address and Address_Type are the identity of + a device. So either its public address or static random address. + + Unresolvable random addresses and resolvable random addresses are + not valid and will be rejected. + + Currently defined Key_Type values are: + + 0x00 Unauthenticated key + 0x01 Authenticated key + This command can be used when the controller is not powered. This command generates a Command Complete event on success or @@ -714,6 +789,11 @@ Disconnect Command This command is used to force the disconnection of a currently connected device. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -748,6 +828,10 @@ Get Connections Command 1 LE Public 2 LE Random + For devices using resolvable random addresses with a known + identity resolving key, the Address and Address_Type will + contain the identity information. + This command can only be used when the controller is powered. This command generates a Command Complete event on success or @@ -772,6 +856,11 @@ PIN Code Reply Command This command is used to respond to a PIN Code request event. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -796,6 +885,11 @@ PIN Code Negative Reply Command This command is used to return a negative response to a PIN Code Request event. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -850,6 +944,17 @@ Pair Device Command 1 LE Public 2 LE Random + The Address and Address_Type of the return parameters will + return the identity address if know. In case of resolvable + random address given as command parameters and the remote + provides an identity resolving key, the return parameters + will provide the resolved address. + + To allow tracking of which resolvable random address changed + into which identity address, the New Identity Resolving Key + event will be send before receiving Command Complete event + for this command. + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -875,6 +980,11 @@ Cancel Pair Device The Address and Address_Type parameters should match what was given to a preceding Pair Device command. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -898,11 +1008,24 @@ Unpair Device Command Removes all keys associated with the remote device. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + The Disconnect parameter tells the kernel whether to forcefully disconnect any existing connections to the device. It should in practice always be 1 except for some special GAP qualification test-cases where a key removal without disconnecting is needed. + When unpairing a device its link key, long term key and if + provided identity resolving key will be purged. + + For devices using resolvable random addresses where the identity + resolving key was available, after this command they will now no + longer be resolved. The device will essentially become private + again. + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -927,6 +1050,11 @@ User Confirmation Reply Command This command is used to respond to a User Confirmation Request event. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -951,6 +1079,11 @@ User Confirmation Negative Reply Command This command is used to return a negative response to a User Confirmation Request event. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -976,6 +1109,11 @@ User Passkey Reply Command This command is used to respond to a User Confirmation Passkey Request event. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -1000,6 +1138,11 @@ User Passkey Negative Reply Command This command is used to return a negative response to a User Passkey Request event. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -1017,13 +1160,19 @@ Read Local Out Of Band Data Command Command Code: 0x0020 Controller Index: Command Parameters: - Return Parameters: Hash (16 Octets) - Randomizer (16 Octets) + Return Parameters: Hash_192 (16 Octets) + Randomizer_192 (16 Octets) + Hash_256 (16 Octets, Optional) + Randomizer_256 (16 Octets, Optional) This command is used to read the local Out of Band data. This command can only be used when the controller is powered. + If Secure Connections support is enabled, then this command + will return P-192 versions of hash and randomizer as well as + P-256 versions of both. + This command generates a Command Complete event on success or a Command Status event on failure. @@ -1041,16 +1190,36 @@ Add Remote Out Of Band Data Command Controller Index: Command Parameters: Address (6 Octets) Address_Type (1 Octet) - Hash (16 Octets) - Randomizer (16 Octets) + Hash_192 (16 Octets) + Randomizer_192 (16 Octets) + Hash_256 (16 Octets, Optional) + Randomizer_256 (16 Octets, Optional) Return Parameters: Address (6 Octets) Address_Type (1 Octet) This command is used to provide Out of Band data for a remote device. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + Provided Out Of Band data is persistent over power down/up toggles. + This command also accept optional P-256 versions of hash and + randomizer. If they are not provided, then they are set to + zero value. + + The P-256 versions of both can also be provided when the + support for Secure Connections is not enabled. However in + that case they will never be used. + + To only provide the P-256 versions of hash and randomizer, + it is valid to leave both P-192 fields as zero values. If + Secure Connections is disabled, then of course this is the + same as not providing any data at all. + This command generates a Command Complete event on success or failure. @@ -1073,6 +1242,11 @@ Remove Remote Out Of Band Data Command This command is used to remove data added using the Add Remote Out Of Band Data command. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command generates a Command Complete event on success or failure. @@ -1154,6 +1328,16 @@ Confirm Name Command expected for each Device Found event with the Confirm Name flag set. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + + The Name_Known parameter should be set to 0x01 if user space + knows the name for the device and 0x00 if it doesn't. If set to + 0x00 the kernel will perform a name resolving procedure for the + device in question. + This command can only be used when the controller is powered. This command generates a Command Complete event on success @@ -1178,6 +1362,11 @@ Block Device Command which should be blocked from being connect to the local controller. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can be used when the controller is not powered. This command generates a Command Complete event on success @@ -1201,6 +1390,11 @@ Unblock Device Command This command is used to remove a device from the list of blocked devices (where it was added to using the Block Device command). + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + This command can be used when the controller is not powered. This command generates a Command Complete event on success @@ -1317,6 +1511,15 @@ Set Static Address Command The special BDADDR_ANY address (00:00:00:00:00:00) can be used to disable the static address. + When a controller has a public address (which is required for + all dual-mode controllers), this address is not used. Only when + the controller information reports BDADDR_ANY (00:00:00:00:00:00), + it is required to configure a static address first. + + If privacy mode is enabled and the controller is single mode + LE only without a public address, the static random address is + used as identity address. + This command generates a Command Complete event on success or a Command Status event on failure. @@ -1348,14 +1551,171 @@ Set Scan Parameters Command Invalid Index +Set Secure Connections Command +============================== + + Command Code: 0x002D + Controller Index: + Command Parameters: Secure_Connections (1 Octet) + Return Parameters: Current_Settings (4 Octets) + + This command is used to enable/disable Secure Connections + support for a controller. The allowed values for the + Secure_Connections command parameter are 0x00, 0x01 and 0x02. + All other values will return Invalid Parameters. + + The value 0x00 disables Secure Connections, the value 0x01 + enables Secure Connections and the value 0x02 enables Secure + Connections Only mode. + + This command is only available for BR/EDR capable controllers + supporting the core specification version 4.1 or greater + (e.g. not for single-mode LE controllers or pre-4.1 ones). + + This command can be used when the controller is not powered and + all settings will be programmed once powered. + + In case the controller does not support Secure Connections + the command will fail regardless with Not Supported error. + + This command generates a Command Complete event on success or + a Command Status event on failure. + + Possible errors: Busy + Not Supported + Invalid Parameters + Invalid Index + + +Set Debug Keys Command +====================== + + Command Code: 0x002E + Controller Index: + Command Parameters: Debug_Keys (1 Octet) + Return Parameters: Current_Settings (4 Octets) + + This command is used to tell the kernel whether to accept the + usage of debug keys or not. The allowed values for this parameter + are 0x00 and 0x01. All other values will return an Invalid Parameters + response. + + This command generates a Command Complete event on success or + a Command Status event on failure. + + Possible errors: Busy + Not Supported + Invalid Parameters + Invalid Index + + +Set Privacy Command +=================== + + Command Code: 0x002F + Controller Index: + Command Parameters: Privacy (1 Octet) + Identity_Resolving_Key (16 Octets) + Return Parameters: Current_Settings (4 Octets) + + This command is used to enable Low Energy Privacy feature using + resolvable private addresses. + + The value 0x00 disables privacy mode, the value 0x01 enables + privacy mode. + + When the controller has a public address (mandatory for dual-mode + controllers) it is used as identity address. In case the controller + is single mode LE only without a public address, it is required + to configure a static random andress first. The privacy mode can + only be enabled when an identity address is available. + + The Identity_Resolving_Key is the local key assigned for the local + resolvable private address. + + Possible errors: Busy + Not Supported + Invalid Parameters + Invalid Index + + +Load Identity Resolving Keys Command +==================================== + + Command Code: 0x0030 + Controller Index: + Command Parameters: Key_Count (2 Octets) + Key1 { + Address (6 Octets) + Address_Type (1 Octet) + Value (16 Octets) + } + Key2 { } + ... + Return Parameters: + + This command is used to feed the kernel with currently known + identity resolving keys. The command does not need to be called + again upon the receiption of New Identity Resolving Key events + since the kernel updates its list automatically. + + Possible values for the Address_Type parameter: + 0 Reserved (not in use) + 1 LE Public + 2 LE Random + + The provided Address and Address_Type are the identity of + a device. So either its public address or static random address. + + Unresolvable random addresses and resolvable random addresses are + not valid and will be rejected. + + This command can be used when the controller is not powered. + + This command generates a Command Complete event on success or + a Command Status event on failure. + + Possible errors: Invalid Parameters + Invalid Index + + +Get Connection Information Command +================================== + + Command Code: 0x0031 + Controller Index: + Command Parameters: Address (6 Octets) + Address_Type (1 Octet) + Return Parameters: Address (6 Octets) + Address_Type (1 Octet) + RSSI (1 Octet) + TX_Power (1 Octet) + Max_TX_Power (1 Octet) + + This command is used to get connection information. + + TX_Power and Max_TX_Power can be set to 127 if values are invalid or + unknown. + + This command generates a Command Complete event on success and + on failure. In case of failure only Address and Address_Type fields + are valid and values of remaining parameters are considered invalid + and shall be ignored. + + Possible errors: Not Connected + Not Powered + Invalid Parameters + Invalid Index + + Command Complete Event ====================== -Event Code 0x0001 -Controller Index: or -Event Parameters Command_Opcode (2 Octets) - Status (1 Octet) - Return_Parameters + Event Code: 0x0001 + Controller Index: or + Event Parameters: Command_Opcode (2 Octets) + Status (1 Octet) + Return_Parameters This event is an indication that a command has completed. The fixed set of parameters includes the opcode to identify the @@ -1368,10 +1728,10 @@ Event Parameters Command_Opcode (2 Octets) Command Status Event ==================== -Event Code 0x0002 -Controller Index: or -Event Parameters Command_Opcode (2 Octets) - Status (1 Octet) + Event Code: 0x0002 + Controller Index: or + Event Parameters: Command_Opcode (2 Octets) + Status (1 Octet) The command status event is used to indicate an early status for a pending command. In the case that the status indicates failure @@ -1382,9 +1742,9 @@ Event Parameters Command_Opcode (2 Octets) Controller Error Event ====================== -Event Code 0x0003 -Controller Index: -Event Parameters Error_Code (1 Octet) + Event Code: 0x0003 + Controller Index: + Event Parameters: Error_Code (1 Octet) This event maps straight to the HCI Hardware Error event and is used to indicate something wrong with the controller hardware. @@ -1393,9 +1753,9 @@ Event Parameters Error_Code (1 Octet) Index Added Event ================= -Event Code 0x0004 -Controller Index: -Event Parameters + Event Code: 0x0004 + Controller Index: + Event Parameters: This event indicates that a new controller has been added to the system. It is usually followed by a Read Controller Information @@ -1405,9 +1765,9 @@ Event Parameters Index Removed Event =================== -Event Code 0x0005 -Controller Index: -Event Parameters + Event Code: 0x0005 + Controller Index: + Event Parameters: This event indicates that a controller has been removed from the system. @@ -1416,9 +1776,9 @@ Event Parameters New Settings Event ================== -Event Code 0x0006 -Controller Index: -Event Parameters: Current_Settings (4 Octets) + Event Code: 0x0006 + Controller Index: + Event Parameters: Current_Settings (4 Octets) This event indicates that one or more of the settings for a controller has changed. @@ -1427,9 +1787,9 @@ Event Parameters: Current_Settings (4 Octets) Class Of Device Changed Event ============================= -Event Code 0x0007 -Controller Index: -Event Parameters: Class_Of_Device (3 Octets) + Event Code: 0x0007 + Controller Index: + Event Parameters: Class_Of_Device (3 Octets) This event indicates that the Class of Device value for the controller has changed. When the controller is powered off the @@ -1439,10 +1799,10 @@ Event Parameters: Class_Of_Device (3 Octets) Local Name Changed Event ======================== -Event Code 0x0008 -Controller Index -Event Parameters Name (249 Octets) - Short_Name (11 Octets) + Event Code: 0x0008 + Controller Index: + Event Parameters: Name (249 Octets) + Short_Name (11 Octets) This event indicates that the local name of the controller has changed. @@ -1451,80 +1811,132 @@ Event Parameters Name (249 Octets) New Link Key Event ================== -Event Code 0x0009 -Controller Index: -Event Parametersa Store_Hint (1 Octet) - Key { - Address (6 Octets) - Address_Type (1 Octet) - Key_Type (1 Octet) - Value (16 Octets) - PIN_Length (1 Octet) - } + Event Code: 0x0009 + Controller Index: + Event Parameters: Store_Hint (1 Octet) + Key { + Address (6 Octets) + Address_Type (1 Octet) + Key_Type (1 Octet) + Value (16 Octets) + PIN_Length (1 Octet) + } This event indicates that a new link key has bee generated for a - remote device. The Store_Hint parameter indicates whether the - host is expected to store the key persistently or not (e.g. this - would not be set if the authentication requirement was "No - Bonding"). + remote device. + + The Store_Hint parameter indicates whether the host is expected + to store the key persistently or not (e.g. this would not be set + if the authentication requirement was "No Bonding"). + + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 Reserved (not in use) + 2 Reserved (not in use) + + Public and random LE addresses are not valid and will be rejected. + + Currently defined Key_Type values are: + + 0x00 Combination key + 0x01 Local Unit key + 0x02 Remote Unit key + 0x03 Debug Combination key + 0x04 Unauthenticated Combination key from P-192 + 0x05 Authenticated Combination key from P-192 + 0x06 Changed Combination key + 0x07 Unauthenticated Combination key from P-256 + 0x08 Authenticated Combination key from P-256 + + Receiving this event indicates that a pairing procecure has + been completed. New Long Term Key Event ======================= -Event Code 0x000A -Controller Index -Event Parameters Store_Hint (1 Octet) - Key { - Address (6 Octets) - Address_Type (1 Octet) - Authenticated (1 Octet) - Master (1 Octet) - Encryption Size (1 Octet) - Enc. Diversifier (2 Octets) - Random Number (8 Octets) - Value (16 Octets) - } + Event Code: 0x000A + Controller Index: + Event Parameters: Store_Hint (1 Octet) + Key { + Address (6 Octets) + Address_Type (1 Octet) + Key_Type (1 Octet) + Master (1 Octet) + Encryption Size (1 Octet) + Enc. Diversifier (2 Octets) + Random Number (8 Octets) + Value (16 Octets) + } + + This event indicates that a new long term key has been generated + for a remote device. + + The Store_Hint parameter indicates whether the host is expected + to store the key persistently or not (e.g. this would not be set + if the authentication requirement was "No Bonding"). + + Possible values for the Address_Type parameter: + 0 Reserved (not in use) + 1 LE Public + 2 LE Random + + The provided Address and Address_Type are the identity of + a device. So either its public address or static random address. + + For unresolvable random addresses and resolvable random addresses + without identity information and identity resolving key, the + Store_Hint will be set to not store the long term key. - This event indicates that a new long term key has bee generated - for a remote device. The Store_Hint parameter indicates whether - the host is expected to store the key persistently or not (e.g. - this would not be set if the authentication requirement was "No - Bonding"). + Currently defined Key_Type values are: + + 0x00 Unauthenticated key + 0x01 Authenticated key + + Receiving this event indicates that a pairing procecure has + been completed. Device Connected Event ====================== -Event Code 0x000B -Controller Index: -Event Parameters Address (6 Octets) - Address_Type (1 Octet) - Flags (4 Octets) - EIR_Data_Length (2 Octets) - EIR_Data (0-65535 Octets) + Event Code: 0x000B + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) + Flags (4 Octets) + EIR_Data_Length (2 Octets) + EIR_Data (0-65535 Octets) + + This event indicates that a successful baseband connection has + been created to the remote device. Possible values for the Address_Type parameter: 0 BR/EDR 1 LE Public 2 LE Random + For devices using resolvable random addresses with a known + identity resolving key, the Address and Address_Type will + contain the identity information. + + It is possible that devices get connected via its resolvable + random address and after New Identity Resolving Key event + start using its identity. + The following bits are defined for the Flags parameter: 0 Reserved (not in use) 1 Legacy Pairing - This event indicates that a successful baseband connection has - been created to the remote device. - Device Disconnected Event ========================= -Event Code 0x000C -Controller Index: -Event Parameters Address (6 Octets) - Address_Type (1 Octet) - Reason (1 Octet) + Event Code: 0x000C + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) + Reason (1 Octet) This event indicates that the baseband connection was lost to a remote device. @@ -1534,6 +1946,10 @@ Event Parameters Address (6 Octets) 1 LE Public 2 LE Random + For devices using resolvable random addresses with a known + identity resolving key, the Address and Address_Type will + contain the identity information. + Possible values for the Reason parameter: 0 Unspecified 1 Connection timeout @@ -1553,11 +1969,11 @@ Event Parameters Address (6 Octets) Connect Failed Event ==================== -Event Code 0x000D -Controller Index: -Event Parameters Address (6 Octets) - Address_Type (1 Octet) - Status (1 Octet) + Event Code: 0x000D + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) + Status (1 Octet) This event indicates that a connection attempt failed to a remote device. @@ -1567,20 +1983,29 @@ Event Parameters Address (6 Octets) 1 LE Public 2 LE Random + For devices using resolvable random addresses with a known + identity resolving key, the Address and Address_Type will + contain the identity information. + PIN Code Request Event ====================== -Event Code 0x000E -Controller Index: -Event Parameters Address (6 Octets) - Address_Type (1 Octet) - Secure (1 Octet) + Event Code: 0x000E + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) + Secure (1 Octet) This event is used to request a PIN Code reply from user space. The reply should either be returned using the PIN Code Reply or the PIN Code Negative Reply command. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + Secure: 0x01 secure PIN code required 0x00 secure PIN code not required @@ -1588,18 +2013,25 @@ Event Parameters Address (6 Octets) User Confirmation Request Event =============================== -Event Code 0x000F -Controller Index: -Event Parameters Address (6 Octets) - Address_Type (1 Octet) - Confirm_Hint (1 Octet) - Value (4 Octets) + Event Code: 0x000F + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) + Confirm_Hint (1 Octet) + Value (4 Octets) This event is used to request a user confirmation request from - user space. If the Confirm_Hint parameter value is 0x01 this - means that a simple "Yes/No" confirmation should be presented to - the user instead of a full numerical confirmation (in which case - the parameter value will be 0x00). + user space. + + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + + If the Confirm_Hint parameter value is 0x01 this means that + a simple "Yes/No" confirmation should be presented to the user + instead of a full numerical confirmation (in which case the + parameter value will be 0x00). User space should respond to this command either using the User Confirmation Reply or the User Confirmation Negative Reply @@ -1609,40 +2041,50 @@ Event Parameters Address (6 Octets) User Passkey Request Event ========================== -Event Code 0x0010 -Controller Index: -Event Parameters Address (6 Octets) - Address_Type (1 Octet) + Event Code: 0x0010 + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) This event is used to request a passkey from user space. The response to this event should either be the User Passkey Reply command or the User Passkey Negative Reply command. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + Authentication Failed Event =========================== -Event Code 0x0011 -Controller Index: -Event Parameters Address (6 Octets) - Address_Type (1 Octet) - Status (1 Octet) + Event Code: 0x0011 + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) + Status (1 Octet) This event indicates that there was an authentication failure with a remote device. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + Device Found Event ================== -Event Code 0x0012 -Controller Index -Event Parameters Address (6 Octets) - Address_Type (1 Octet) - RSSI (1 Octet) - Flags (4 Octets) - EIR_Data_Length (2 Octets) - EIR_Data (0-65535 Octets) + Event Code: 0x0012 + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) + RSSI (1 Octet) + Flags (4 Octets) + EIR_Data_Length (2 Octets) + EIR_Data (0-65535 Octets) This event indicates that a device was found during device discovery. @@ -1673,10 +2115,10 @@ Event Parameters Address (6 Octets) Discovering Event ================= -Event Code 0x0013 -Controller Index -Event Parameters Address_Type (1 Octet) - Discovering (1 Octet) + Event Code: 0x0013 + Controller Index: + Event Parameters: Address_Type (1 Octet) + Discovering (1 Octet) This event indicates that the controller has started discovering devices. This discovering state can come and go multiple times @@ -1689,59 +2131,169 @@ Event Parameters Address_Type (1 Octet) Device Blocked Event ==================== -Event Code 0x0014 -Controller Index -Event Parameters Address (6 Octets) - Address_Type (1 Octet) + Event Code: 0x0014 + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) This event indicates that a device has been blocked using the - Block Device command. The event will only be sent to Management - sockets other than the one through which the command was sent. + Block Device command. + + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + + The event will only be sent to Management sockets other than the + one through which the command was sent. Device Unblocked Event ====================== -Event Code 0x0015 -Controller Index -Event Parameters Address (6 Octets) - Address_Type (1 Octet) + Event Code: 0x0015 + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) This event indicates that a device has been unblocked using the - Unblock Device command. The event will only be sent to - Management sockets other than the one through which the command - was sent. + Unblock Device command. + + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + + The event will only be sent to Management sockets other than the + one through which the command was sent. Device Unpaired Event ===================== -Event Code 0x0016 -Controller Index -Event Parameters Address (6 Octets) - Address_Type (1 Octet) + Event Code: 0x0016 + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) This event indicates that a device has been unpaired (i.e. all its keys have been removed from the kernel) using the Unpair - Device command. The event will only be sent to Management - sockets other than the one through which the Unpair Device - command was sent. + Device command. + + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + + For devices using resolvable random addresses with a known + identity resolving key, the event paramters will contain + the identity. After receiving this event, the device will + become essentially private again. + + The event will only be sent to Management sockets other than the + one through which the Unpair Device command was sent. Passkey Notify Event ==================== -Event Code 0x0017 -Controller Index -Event Parameters Address (6 Octets) - Address_Type (1 Octet) - Passkey (4 Octets) - Entered (1 Octet) + Event Code: 0x0017 + Controller Index: + Event Parameters: Address (6 Octets) + Address_Type (1 Octet) + Passkey (4 Octets) + Entered (1 Octet) This event is used to request passkey notification to the user. Unlike the other authentication events it does not need responding to using any Management command. + Possible values for the Address_Type parameter: + 0 BR/EDR + 1 LE Public + 2 LE Random + The Passkey parameter indicates the passkey to be shown to the user whereas the Entered parameter indicates how many characters the user has entered on the remote side. + + +New Identity Resolving Key Event +================================ + + Event Code: 0x0018 + Controller Index: + Event Parameters: Store_Hint (1 Octet) + Random_Address (6 Octets) + Key { + Address (6 Octets) + Address_Type (1 Octet) + Value (16 Octets) + } + + This event indicates that a new identity resolving key has been + generated for a remote device. + + The Store_Hint parameter indicates whether the host is expected + to store the key persistently or not. + + The Random_Address provides the resolvable random address that + was resolved into an identity. A value of 00:00:00:00:00:00 + indicates that the identity resolving key was provided for + a public address or static random address. + + Once this event has been send for a resolvable random address, + all further events mapping this device will send out using the + identity address information. + + This event also indicates that now the identity address should + be used for commands instead of the resolvable random address. + + Possible values for the Address_Type parameter: + 0 Reserved (not in use) + 1 LE Public + 2 LE Random + + The provided Address and Address_Type are the identity of + a device. So either its public address or static random address. + + +New Signature Resolving Key Event +================================= + + Event Code: 0x0019 + Controller Index: + Event Parameters: Store_Hint (1 Octet) + Key { + Address (6 Octets) + Address_Type (1 Octet) + Master (1 Octet) + Value (16 Octets) + } + + This event indicates that a new signature resolving key has been + generated for either the master or slave device. + + The Store_Hint parameter indicates whether the host is expected + to store the key persistently or not. + + When the Master parameter is set to 0x01, then the signature + resolving key from the remote peer device is provided. It is + the key that is used for signature verification. + + When the Master parameter is set to 0x00, then it is the local + signature resolving key that is used to sign data. The remote + peer device will be using it for signature verification. + + The local signature resolving key will be generated with each + pairing request. Only after receiving this event with Master + parameter set to 0x00 it is possible to use ATT Signed Write + procedures. + + Possible values for the Address_Type parameter: + 0 Reserved (not in use) + 1 LE Public + 2 LE Random + + The provided Address and Address_Type are the identity of + a device. So either its public address or static random address. diff --git a/doc/obex-api.txt b/doc/obex-api.txt index 1f22fea..9542a30 100644 --- a/doc/obex-api.txt +++ b/doc/obex-api.txt @@ -90,12 +90,34 @@ Methods void Cancel() org.bluez.obex.Error.InProgress org.bluez.obex.Error.Failed + void Suspend() + + Suspend transference. + + Possible errors: org.bluez.obex.Error.NotAuthorized + org.bluez.obex.Error.NotInProgress + + Note that it is not possible to suspend transfers + which are queued which is why NotInProgress is listed + as possible error. + + void Resume() + + Resume transference. + + Possible errors: org.bluez.obex.Error.NotAuthorized + org.bluez.obex.Error.NotInProgress + + Note that it is not possible to resume transfers + which are queued which is why NotInProgress is listed + as possible error. + Properties string Status [readonly] Inform the current status of the transfer. - Possible values: "queued", "active", "complete" or - "error" + Possible values: "queued", "active", "suspended", + "complete" or "error" object Session [readonly] diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt new file mode 100644 index 0000000..7cdecd5 --- /dev/null +++ b/doc/settings-storage.txt @@ -0,0 +1,228 @@ +BlueZ settings storage +********************** + +Purpose +======= + +The purpose of this document is to describe the directory structure of +BlueZ settings storage. In effect, this document will serve as the primary, +up to date source of BlueZ storage information. + +It is intended as reference for developers. Direct access to the storage +outside from bluetoothd is highly discouraged. + +Adapter and remote device info are read form the storage during object +initialization. Write to storage is performed immediately on every value +change. + +Default storage directory is /var/lib/bluetooth. This can be adjusted +by the --localstatedir configure switch. Default is --localstatedir=/var. + +All files are in ini-file format. + + +Storage directory structure +=========================== + +There is one directory per adapter, named by its Bluetooth address, which +contains: + - a settings file for the local adapter + - an attributes file containing attributes of supported LE services + - a cache directory containing: + - one file per device, named by remote device address, which contains + device name + - one directory per remote device, named by remote device address, which + contains: + - an info file + - an attributes file containing attributes of remote LE services + - a ccc file containing persistent Client Characteristic Configuration + (CCC) descriptor information for GATT characteristics + +So the directory structure is: + /var/lib/bluetooth// + ./settings + ./attributes + ./cache/ + ./ + ./ + ... + .// + ./info + ./attributes + ./ccc + .// + ./info + ./attributes + ... + + +Settings file format +==================== + +Settings file contains one [General] group with adapter info like: + + Alias String Friendly user provided name advertised + for this adapter + + This value overwrites the system + name (pretty hostname) + + Discoverable Boolean Discoverability of the adapter + + PairableTimeout Integer How long to stay in pairable mode + before going back to non-pairable. + The value is in seconds. + 0 = disable timer, i.e. stay + pairable forever + + DiscoverableTimeout Integer How long to stay in discoverable mode + before going back to non-discoverable. + The value is in seconds. + 0 = disable timer, i.e. stay + discoverable forever + +Sample: + [General] + Name=My PC + Discoverable=false + Pairable=true + DiscoverableTimeout=0 + + +Attributes file format +====================== + +The attributes file lists all attributes supported by the local adapter or +remote device. + +Attributes are stored using their handle as group name (decimal format). + +Each group contains: + + UUID String 128-bit UUID of the attribute + + Value String Value of the attribute as hexadecimal encoded + string + + EndGroupHandle Integer End group handle in decimal format + +Sample: + [1] + UUID=00002800-0000-1000-8000-00805f9b34fb + Value=0018 + + [4] + UUID=00002803-0000-1000-8000-00805f9b34fb + Value=020600002A + + [6] + UUID=00002a00-0000-1000-8000-00805f9b34fb + Value=4578616D706C6520446576696365 + + +CCC file format +====================== + +The ccc file stores the current CCC descriptor values for GATT characteristics +which have notification/indication enabled by the remote device. + +Information is stored using CCC attribute handle as group name (in decimal +format). + +Each group contains: + + Value String CCC descriptor value encoded in + hexadecimal + + +Cache directory file format +============================ + +Each file, named by remote device address, may includes multiple groups +(General and ServiceRecords). + +In ServiceRecords, SDP records are stored using their handle as key +(hexadecimal format). + +[General] group contains: + + Name String Remote device friendly name + + ShortName String Remote device shortened name + +[ServiceRecords] group contains + + <0x...> String SDP record as hexadecimal encoded + string + + +Info file format +================ + +Info file may includes multiple groups (General, Device ID, Link key and +Long term key) related to a remote device. + +[General] group contains: + + Name String Remote device friendly name + + Alias String Alias name + + Class String Device class in hexadecimal, + i.e. 0x000000 + + Appearance String Device appearance in hexadecimal, + i.e. 0x0000 + + SupportedTechnologies List of List of technologies supported by + strings device, separated by ";" + Technologies can be BR/EDR or LE + + AddressType String An address can be "static" or "public" + + Trusted Boolean True if the remote device is trusted + + Blocked Boolean True if the remote device is blocked + + Services List of List of service UUIDs advertised by + strings remote in 128-bits UUID format, + separated by ";" + + +[DeviceID] group contains: + + Source Integer Assigner of Device ID + + Vendor Integer Device vendor + + Product Integer Device product + + Version Integer Device version + + +[LinkKey] group contains: + + Key String Key in hexadecimal format + + Type Integer Type of link key + + PINLength Integer Length of PIN + + +[LongTermKey] group contains: + + Key String Long term key in hexadecimal format + + Authenticated Boolean True if remote device has been + authenticated + + EncSize Integer Encrypted size + + EDiv Integer Encrypted diversifier + + Rand Integer Randomizer + + +[SlaveLongTermKey] group contains: + + Same as the [LongTermKey] group, except for slave keys. diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt new file mode 100644 index 0000000..7b05ff6 --- /dev/null +++ b/doc/test-coverage.txt @@ -0,0 +1,67 @@ +BlueZ test coverage +******************* + + +Automated unit testing +====================== + +Application Count Description +------------------------------------------- +test-crc 9 Link Layer CRC-24 checksum +test-eir 14 EIR and AD parsing +test-lib 14 SDP library functions +test-sdp 133 SDP qualification test cases +test-uuid 30 UUID conversion handling +test-mgmt 2 Management interface handling +test-textfile 4 Old textfile storage format +test-ringbuf 3 Ring buffer functionality +test-queue 1 Queue handling functionality +test-hfp 13 HFP Audio Gateway functionality +test-avdtp 60 AVDTP qualification test cases +test-avctp 9 AVCTP qualification test cases +test-avrcp 90 AVRCP qualification test cases +test-gobex 31 Generic OBEX functionality +test-gobex-packet 9 OBEX packet handling +test-gobex-header 28 OBEX header handling +test-gobex-apparam 18 OBEX apparam handling +test-gobex-transfer 36 OBEX transfer handling +test-gdbus-client 12 D-Bus client handling + ----- + 506 + + +Automated end-to-end testing +============================ + +Application Count Description +------------------------------------------- +mgmt-tester 184 Kernel management interface testing +l2cap-tester 26 Kernel L2CAP implementation testing +rfcomm-tester 9 Kernel RFCOMM implementation testing +smp-tester 5 Kernel SMP implementation testing +sco-tester 8 Kernel SCO implementation testing +gap-tester 1 Daemon D-Bus API testing +hci-tester 14 Controller hardware testing + ----- + 247 + + +Android end-to-end testing +========================== + +Application Count Description +------------------------------------------- +android-tester 86 Android HAL interface testing +ipc-tester 94 Android IPC resistance testing + ----- + 180 + + +Android automated unit testing +============================== + +Application Count Description +------------------------------------------- +test-ipc 14 Android IPC library functions + ----- + 14 diff --git a/emulator/amp.c b/emulator/amp.c index 714854b..ab7f9de 100644 --- a/emulator/amp.c +++ b/emulator/amp.c @@ -36,16 +36,12 @@ #include #include +#include "src/shared/util.h" #include "monitor/mainloop.h" #include "monitor/bt.h" #include "amp.h" -#define le16_to_cpu(val) (val) -#define le32_to_cpu(val) (val) -#define cpu_to_le16(val) (val) -#define cpu_to_le32(val) (val) - #define PHY_MODE_IDLE 0x00 #define PHY_MODE_INITIATOR 0x01 #define PHY_MODE_ACCEPTOR 0x02 diff --git a/emulator/btdev.c b/emulator/btdev.c index d0dff74..e590efa 100644 --- a/emulator/btdev.c +++ b/emulator/btdev.c @@ -32,14 +32,11 @@ #include #include +#include "src/shared/util.h" +#include "src/shared/timeout.h" #include "monitor/bt.h" #include "btdev.h" -#define le16_to_cpu(val) (val) -#define le32_to_cpu(val) (val) -#define cpu_to_le16(val) (val) -#define cpu_to_le32(val) (val) - #define has_bredr(btdev) (!((btdev)->features[4] & 0x20)) #define has_le(btdev) (!!((btdev)->features[4] & 0x40)) @@ -57,12 +54,24 @@ struct btdev { struct btdev *conn; + bool auth_init; + uint8_t link_key[16]; + uint16_t pin[16]; + uint8_t pin_len; + uint8_t io_cap; + uint8_t auth_req; + bool ssp_auth_complete; + uint8_t ssp_status; + btdev_command_func command_handler; void *command_data; btdev_send_func send_handler; void *send_data; + unsigned int inquiry_id; + unsigned int inquiry_timeout_id; + struct hook *hook_list[MAX_HOOK_ENTRIES]; uint16_t manufacturer; @@ -76,6 +85,7 @@ struct btdev { uint16_t acl_max_pkt; uint8_t country_code; uint8_t bdaddr[6]; + uint8_t random_addr[6]; uint8_t le_features[8]; uint8_t le_states[8]; @@ -100,12 +110,22 @@ struct btdev { uint8_t ext_inquiry_fec; uint8_t ext_inquiry_rsp[240]; uint8_t simple_pairing_mode; + uint8_t ssp_debug_mode; + uint8_t secure_conn_support; uint8_t le_supported; uint8_t le_simultaneous; uint8_t le_event_mask[8]; uint8_t le_adv_data[31]; uint8_t le_adv_data_len; + uint8_t le_adv_type; + uint8_t le_adv_own_addr; + uint8_t le_adv_direct_addr_type; + uint8_t le_adv_direct_addr[6]; + uint8_t le_scan_data[31]; + uint8_t le_scan_data_len; uint8_t le_scan_enable; + uint8_t le_scan_type; + uint8_t le_scan_own_addr_type; uint8_t le_filter_dup; uint8_t le_adv_enable; uint8_t le_ltk[16]; @@ -115,8 +135,22 @@ struct btdev { uint8_t sync_train_service_data; }; +struct inquiry_data { + struct btdev *btdev; + int num_resp; + + int sent_count; + int iter; +}; + +#define DEFAULT_INQUIRY_INTERVAL 100 /* 100 miliseconds */ + #define MAX_BTDEV_ENTRIES 16 +static const uint8_t LINK_KEY_NONE[16] = { 0 }; +static const uint8_t LINK_KEY_DUMMY[16] = { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 0, 1, 2, 3, 4, 5 }; + static struct btdev *btdev_list[MAX_BTDEV_ENTRIES] = { }; static int get_hook_index(struct btdev *btdev, enum btdev_hook_type type, @@ -268,6 +302,12 @@ static void set_bredr_commands(struct btdev *btdev) btdev->commands[0] |= 0x80; /* Cancel Create Connection */ btdev->commands[1] |= 0x01; /* Accept Connection Request */ btdev->commands[1] |= 0x02; /* Reject Connection Request */ + btdev->commands[1] |= 0x04; /* Link Key Request Reply */ + btdev->commands[1] |= 0x08; /* Link Key Request Negative Reply */ + btdev->commands[1] |= 0x10; /* PIN Code Request Reply */ + btdev->commands[1] |= 0x20; /* PIN Code Request Negative Reply */ + btdev->commands[1] |= 0x80; /* Authentication Requested */ + btdev->commands[2] |= 0x01; /* Set Connection Encryption */ btdev->commands[2] |= 0x08; /* Remote Name Request */ btdev->commands[2] |= 0x10; /* Cancel Remote Name Request */ btdev->commands[2] |= 0x20; /* Read Remote Supported Features */ @@ -314,6 +354,7 @@ static void set_bredr_commands(struct btdev *btdev) btdev->commands[17] |= 0x80; /* Read Local OOB Data */ btdev->commands[18] |= 0x01; /* Read Inquiry Response TX Power */ btdev->commands[18] |= 0x02; /* Write Inquiry Response TX Power */ + btdev->commands[18] |= 0x80; /* IO Capability Request Reply */ btdev->commands[23] |= 0x04; /* Read Data Block Size */ } @@ -351,6 +392,11 @@ static void set_bredrle_commands(struct btdev *btdev) */ btdev->commands[22] |= 0x04; /* Set Event Mask Page 2 */ btdev->commands[31] |= 0x80; /* Read Sync Train Parameters */ + btdev->commands[32] |= 0x04; /* Read Secure Connections Support */ + btdev->commands[32] |= 0x08; /* Write Secure Connections Support */ + btdev->commands[32] |= 0x10; /* Read Auth Payload Timeout */ + btdev->commands[32] |= 0x20; /* Write Auth Payload Timeout */ + btdev->commands[32] |= 0x40; /* Read Local OOB Extended Data */ } static void set_amp_commands(struct btdev *btdev) @@ -366,6 +412,7 @@ static void set_bredrle_features(struct btdev *btdev) btdev->features[0] |= 0x20; /* Role switch */ btdev->features[0] |= 0x80; /* Sniff mode */ btdev->features[1] |= 0x08; /* SCO link */ + btdev->features[2] |= 0x08; /* Transparent SCO */ btdev->features[3] |= 0x40; /* RSSI with inquiry results */ btdev->features[3] |= 0x80; /* Extended SCO link */ btdev->features[4] |= 0x08; /* AFH capable slave */ @@ -390,6 +437,8 @@ static void set_bredrle_features(struct btdev *btdev) btdev->feat_page_2[0] |= 0x04; /* Synchronization Train */ btdev->feat_page_2[0] |= 0x08; /* Synchronization Scan */ btdev->feat_page_2[0] |= 0x10; /* Inquiry Response Notification */ + btdev->feat_page_2[1] |= 0x01; /* Secure Connections */ + btdev->feat_page_2[1] |= 0x02; /* Ping */ btdev->max_page = 2; } @@ -501,6 +550,9 @@ void btdev_destroy(struct btdev *btdev) if (!btdev) return; + if (btdev->inquiry_id > 0) + timeout_remove(btdev->inquiry_id); + del_btdev(btdev); free(btdev); @@ -516,6 +568,14 @@ uint8_t *btdev_get_features(struct btdev *btdev) return btdev->features; } +static bool use_ssp(struct btdev *btdev1, struct btdev *btdev2) +{ + if (btdev1->auth_enable || btdev2->auth_enable) + return false; + + return (btdev1->simple_pairing_mode && btdev2->simple_pairing_mode); +} + void btdev_set_command_handler(struct btdev *btdev, btdev_command_func handler, void *user_data) { @@ -649,12 +709,23 @@ static void num_completed_packets(struct btdev *btdev) } } -static void inquiry_complete(struct btdev *btdev, uint8_t status) +static bool inquiry_callback(void *user_data) { + struct inquiry_data *data = user_data; + struct btdev *btdev = data->btdev; struct bt_hci_evt_inquiry_complete ic; + int sent = data->sent_count; int i; - for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { + /*Report devices only once and wait for inquiry timeout*/ + if (data->iter == MAX_BTDEV_ENTRIES) + return true; + + for (i = data->iter; i < MAX_BTDEV_ENTRIES; i++) { + /*Lets sent 10 inquiry results at once */ + if (sent + 10 == data->sent_count) + break; + if (!btdev_list[i] || btdev_list[i] == btdev) continue; @@ -676,6 +747,7 @@ static void inquiry_complete(struct btdev *btdev, uint8_t status) send_event(btdev, BT_HCI_EVT_EXT_INQUIRY_RESULT, &ir, sizeof(ir)); + data->sent_count++; continue; } @@ -692,6 +764,7 @@ static void inquiry_complete(struct btdev *btdev, uint8_t status) send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI, &ir, sizeof(ir)); + data->sent_count++; } else { struct bt_hci_evt_inquiry_result ir; @@ -705,12 +778,119 @@ static void inquiry_complete(struct btdev *btdev, uint8_t status) send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT, &ir, sizeof(ir)); + data->sent_count++; } - } + } + data->iter = i; - ic.status = status; + /* Check if we sent already required amount of responses*/ + if (data->num_resp && data->sent_count == data->num_resp) + goto finish; + + return true; + +finish: + /* Note that destroy will be called */ + ic.status = BT_HCI_ERR_SUCCESS; + send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic)); + + return false; +} + +static void inquiry_destroy(void *user_data) +{ + struct inquiry_data *data = user_data; + struct btdev *btdev = data->btdev; + + if (!btdev) + goto finish; + + btdev->inquiry_id = 0; + + if (btdev->inquiry_timeout_id > 0) { + timeout_remove(btdev->inquiry_timeout_id); + btdev->inquiry_timeout_id = 0; + } + +finish: + free(data); +} + +static bool inquiry_timeout(void *user_data) +{ + struct inquiry_data *data = user_data; + struct btdev *btdev = data->btdev; + struct bt_hci_evt_inquiry_complete ic; + + timeout_remove(btdev->inquiry_id); + btdev->inquiry_timeout_id = 0; + /* Inquiry is stopped, send Inquiry complete event. */ + ic.status = BT_HCI_ERR_SUCCESS; send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic)); + + return false; +} + +static void inquiry_cmd(struct btdev *btdev, const void *cmd) +{ + const struct bt_hci_cmd_inquiry *inq_cmd = cmd; + struct inquiry_data *data; + struct bt_hci_evt_inquiry_complete ic; + int status = BT_HCI_ERR_HARDWARE_FAILURE; + unsigned int inquiry_len_ms; + + if (btdev->inquiry_id > 0) { + status = BT_HCI_ERR_COMMAND_DISALLOWED; + goto failed; + } + + data = malloc(sizeof(*data)); + if (!data) + goto failed; + + memset(data, 0, sizeof(*data)); + data->btdev = btdev; + data->num_resp = inq_cmd->num_resp; + + /* Add timeout to cancel inquiry */ + inquiry_len_ms = 1280 * inq_cmd->length; + if (inquiry_len_ms) + btdev->inquiry_timeout_id = timeout_add(inquiry_len_ms, + inquiry_timeout, + data, NULL); + + btdev->inquiry_id = timeout_add(DEFAULT_INQUIRY_INTERVAL, + inquiry_callback, data, + inquiry_destroy); + /* Return if success */ + if (btdev->inquiry_id > 0) + return; + +failed: + ic.status = status; + send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic)); +} + +static void inquiry_cancel(struct btdev *btdev) +{ + uint8_t status; + + if (!btdev->inquiry_id) { + status = BT_HCI_ERR_COMMAND_DISALLOWED; + cmd_complete(btdev, BT_HCI_CMD_INQUIRY_CANCEL, &status, + sizeof(status)); + return; + } + + timeout_remove(btdev->inquiry_timeout_id); + btdev->inquiry_timeout_id = 0; + timeout_remove(btdev->inquiry_id); + btdev->inquiry_id = 0; + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, BT_HCI_CMD_INQUIRY_CANCEL, &status, + sizeof(status)); } static void conn_complete(struct btdev *btdev, @@ -747,6 +927,21 @@ static void conn_complete(struct btdev *btdev, send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc)); } +static void accept_conn_request_complete(struct btdev *btdev, + const uint8_t *bdaddr) +{ + struct btdev *remote = find_btdev_by_bdaddr(bdaddr); + + if (!remote) + return; + + if (btdev->auth_enable || remote->auth_enable) + send_event(remote, BT_HCI_EVT_LINK_KEY_REQUEST, + btdev->bdaddr, 6); + else + conn_complete(btdev, bdaddr, BT_HCI_ERR_SUCCESS); +} + static void sync_conn_complete(struct btdev *btdev, uint16_t voice_setting, uint8_t status) { @@ -819,11 +1014,47 @@ static void le_conn_complete(struct btdev *btdev, send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf)); } +static const uint8_t *scan_addr(const struct btdev *btdev) +{ + if (btdev->le_scan_own_addr_type == 0x01) + return btdev->random_addr; + + return btdev->bdaddr; +} + +static const uint8_t *adv_addr(const struct btdev *btdev) +{ + if (btdev->le_adv_own_addr == 0x01) + return btdev->random_addr; + + return btdev->bdaddr; +} + +static bool adv_match(struct btdev *scan, struct btdev *adv) +{ + /* Match everything if this is not directed advertising */ + if (adv->le_adv_type != 0x01 && adv->le_adv_type != 0x04) + return true; + + if (scan->le_scan_own_addr_type != adv->le_adv_direct_addr_type) + return false; + + return !memcmp(scan_addr(scan), adv->le_adv_direct_addr, 6); +} + +static bool adv_connectable(struct btdev *btdev) +{ + if (!btdev->le_adv_enable) + return false; + + return btdev->le_adv_type != 0x03; +} + static void le_conn_request(struct btdev *btdev, const uint8_t *bdaddr) { struct btdev *remote = find_btdev_by_bdaddr(bdaddr); - if (remote && remote->le_adv_enable) + if (remote && adv_connectable(remote) && adv_match(btdev, remote)) le_conn_complete(btdev, bdaddr, 0); else le_conn_complete(btdev, bdaddr, @@ -874,6 +1105,223 @@ static void disconnect_complete(struct btdev *btdev, uint16_t handle, send_event(remote, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc)); } +static void link_key_req_reply_complete(struct btdev *btdev, + const uint8_t *bdaddr, + const uint8_t *link_key) +{ + struct btdev *remote = btdev->conn; + struct bt_hci_evt_auth_complete ev; + + memcpy(btdev->link_key, link_key, 16); + + if (!remote) { + remote = find_btdev_by_bdaddr(bdaddr); + if (!remote) + return; + } + + if (!memcmp(remote->link_key, LINK_KEY_NONE, 16)) { + send_event(remote, BT_HCI_EVT_LINK_KEY_REQUEST, + btdev->bdaddr, 6); + return; + } + + ev.handle = cpu_to_le16(42); + + if (!memcmp(btdev->link_key, remote->link_key, 16)) + ev.status = BT_HCI_ERR_SUCCESS; + else + ev.status = BT_HCI_ERR_AUTH_FAILURE; + + send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev)); + send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev)); +} + +static void link_key_req_neg_reply_complete(struct btdev *btdev, + const uint8_t *bdaddr) +{ + struct btdev *remote = btdev->conn; + + if (!remote) { + remote = find_btdev_by_bdaddr(bdaddr); + if (!remote) + return; + } + + if (use_ssp(btdev, remote)) { + struct bt_hci_evt_io_capability_request io_req; + + memcpy(io_req.bdaddr, bdaddr, 6); + send_event(btdev, BT_HCI_EVT_IO_CAPABILITY_REQUEST, &io_req, + sizeof(io_req)); + } else { + struct bt_hci_evt_pin_code_request pin_req; + + memcpy(pin_req.bdaddr, bdaddr, 6); + send_event(btdev, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req, + sizeof(pin_req)); + } +} + +static uint8_t get_link_key_type(struct btdev *btdev) +{ + struct btdev *remote = btdev->conn; + uint8_t auth, unauth; + + if (!remote) + return 0x00; + + if (!btdev->simple_pairing_mode) + return 0x00; + + if (btdev->ssp_debug_mode || remote->ssp_debug_mode) + return 0x03; + + if (btdev->secure_conn_support && remote->secure_conn_support) { + unauth = 0x04; + auth = 0x05; + } else { + unauth = 0x07; + auth = 0x08; + } + + if (btdev->io_cap == 0x03 || remote->io_cap == 0x03) + return unauth; + + if (!(btdev->auth_req & 0x01) && !(remote->auth_req & 0x01)) + return unauth; + + /* DisplayOnly only produces authenticated with KeyboardOnly */ + if (btdev->io_cap == 0x00 && remote->io_cap != 0x02) + return unauth; + + /* DisplayOnly only produces authenticated with KeyboardOnly */ + if (remote->io_cap == 0x00 && btdev->io_cap != 0x02) + return unauth; + + return auth; +} + +static void link_key_notify(struct btdev *btdev, const uint8_t *bdaddr, + const uint8_t *key) +{ + struct bt_hci_evt_link_key_notify ev; + + memcpy(btdev->link_key, key, 16); + + memcpy(ev.bdaddr, bdaddr, 6); + memcpy(ev.link_key, key, 16); + ev.key_type = get_link_key_type(btdev); + + send_event(btdev, BT_HCI_EVT_LINK_KEY_NOTIFY, &ev, sizeof(ev)); +} + +static void encrypt_change(struct btdev *btdev, uint8_t mode, uint8_t status) +{ + struct bt_hci_evt_encrypt_change ev; + + ev.status = status; + ev.handle = cpu_to_le16(42); + ev.encr_mode = mode; + + send_event(btdev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev)); +} + +static void pin_code_req_reply_complete(struct btdev *btdev, + const uint8_t *bdaddr, uint8_t pin_len, + const uint8_t *pin_code) +{ + struct bt_hci_evt_auth_complete ev; + struct btdev *remote = btdev->conn; + + if (!remote) { + remote = find_btdev_by_bdaddr(bdaddr); + if (!remote) + return; + } + + memcpy(btdev->pin, pin_code, pin_len); + btdev->pin_len = pin_len; + + if (!remote->pin_len) { + struct bt_hci_evt_pin_code_request pin_req; + + memcpy(pin_req.bdaddr, btdev->bdaddr, 6); + send_event(remote, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req, + sizeof(pin_req)); + return; + } + + if (btdev->pin_len == remote->pin_len && + !memcmp(btdev->pin, remote->pin, btdev->pin_len)) { + link_key_notify(btdev, remote->bdaddr, LINK_KEY_DUMMY); + link_key_notify(remote, btdev->bdaddr, LINK_KEY_DUMMY); + ev.status = BT_HCI_ERR_SUCCESS; + } else { + ev.status = BT_HCI_ERR_AUTH_FAILURE; + } + + if (remote->conn) { + ev.handle = cpu_to_le16(42); + send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev)); + } else { + conn_complete(remote, btdev->bdaddr, ev.status); + } + + btdev->pin_len = 0; + remote->pin_len = 0; +} + +static void pin_code_req_neg_reply_complete(struct btdev *btdev, + const uint8_t *bdaddr) +{ + struct bt_hci_evt_auth_complete ev; + struct btdev *remote = btdev->conn; + + if (!remote) { + remote = find_btdev_by_bdaddr(bdaddr); + if (!remote) + return; + } + + ev.status = BT_HCI_ERR_PIN_OR_KEY_MISSING; + ev.handle = cpu_to_le16(42); + + if (btdev->conn) + send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev)); + else + conn_complete(btdev, bdaddr, BT_HCI_ERR_PIN_OR_KEY_MISSING); + + if (remote->conn) { + if (remote->pin_len) + send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev, + sizeof(ev)); + } else { + conn_complete(remote, btdev->bdaddr, + BT_HCI_ERR_PIN_OR_KEY_MISSING); + } +} + +static void auth_request_complete(struct btdev *btdev, uint16_t handle) +{ + struct btdev *remote = btdev->conn; + + if (!remote) { + struct bt_hci_evt_auth_complete ev; + + ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID; + ev.handle = cpu_to_le16(handle); + + send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev)); + + return; + } + + btdev->auth_init = true; + + send_event(btdev, BT_HCI_EVT_LINK_KEY_REQUEST, remote->bdaddr, 6); +} + static void name_request_complete(struct btdev *btdev, const uint8_t *bdaddr, uint8_t status) { @@ -914,6 +1362,19 @@ static void remote_features_complete(struct btdev *btdev, uint16_t handle) &rfc, sizeof(rfc)); } +static void btdev_get_host_features(struct btdev *btdev, uint8_t features[8]) +{ + memset(features, 0, 8); + if (btdev->simple_pairing_mode) + features[0] |= 0x01; + if (btdev->le_supported) + features[0] |= 0x02; + if (btdev->le_simultaneous) + features[0] |= 0x04; + if (btdev->secure_conn_support) + features[0] |= 0x08; +} + static void remote_ext_features_complete(struct btdev *btdev, uint16_t handle, uint8_t page) { @@ -931,7 +1392,7 @@ static void remote_ext_features_complete(struct btdev *btdev, uint16_t handle, break; case 0x01: refc.status = BT_HCI_ERR_SUCCESS; - memset(refc.features, 0, 8); + btdev_get_host_features(btdev, refc.features); break; default: refc.status = BT_HCI_ERR_INVALID_PARAMETERS; @@ -972,7 +1433,121 @@ static void remote_version_complete(struct btdev *btdev, uint16_t handle) &rvc, sizeof(rvc)); } -static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote) +static void io_cap_req_reply_complete(struct btdev *btdev, + const uint8_t *bdaddr, + uint8_t capability, uint8_t oob_data, + uint8_t authentication) +{ + struct btdev *remote = btdev->conn; + struct bt_hci_evt_io_capability_response ev; + struct bt_hci_rsp_io_capability_request_reply rsp; + uint8_t status; + + if (!remote) { + status = BT_HCI_ERR_UNKNOWN_CONN_ID; + goto done; + } + + status = BT_HCI_ERR_SUCCESS; + + btdev->io_cap = capability; + btdev->auth_req = authentication; + + memcpy(ev.bdaddr, btdev->bdaddr, 6); + ev.capability = capability; + ev.oob_data = oob_data; + ev.authentication = authentication; + + send_event(remote, BT_HCI_EVT_IO_CAPABILITY_RESPONSE, &ev, sizeof(ev)); + + if (remote->io_cap) { + struct bt_hci_evt_user_confirm_request cfm; + + memcpy(cfm.bdaddr, btdev->bdaddr, 6); + cfm.passkey = 0; + + send_event(remote, BT_HCI_EVT_USER_CONFIRM_REQUEST, + &cfm, sizeof(cfm)); + + memcpy(cfm.bdaddr, bdaddr, 6); + send_event(btdev, BT_HCI_EVT_USER_CONFIRM_REQUEST, + &cfm, sizeof(cfm)); + } else { + send_event(remote, BT_HCI_EVT_IO_CAPABILITY_REQUEST, + btdev->bdaddr, 6); + } + +done: + rsp.status = status; + memcpy(rsp.bdaddr, bdaddr, 6); + cmd_complete(btdev, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY, + &rsp, sizeof(rsp)); +} + +static void io_cap_req_neg_reply_complete(struct btdev *btdev, + const uint8_t *bdaddr) +{ + struct bt_hci_rsp_io_capability_request_neg_reply rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(rsp.bdaddr, bdaddr, 6); + cmd_complete(btdev, BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY, + &rsp, sizeof(rsp)); +} + +static void ssp_complete(struct btdev *btdev, const uint8_t *bdaddr, + uint8_t status, bool wait) +{ + struct bt_hci_evt_simple_pairing_complete iev, aev; + struct bt_hci_evt_auth_complete auth; + struct btdev *remote = btdev->conn; + struct btdev *init, *accp; + + if (!remote) + return; + + btdev->ssp_status = status; + btdev->ssp_auth_complete = true; + + if (!remote->ssp_auth_complete && wait) + return; + + if (status == BT_HCI_ERR_SUCCESS && + remote->ssp_status != BT_HCI_ERR_SUCCESS) + status = remote->ssp_status; + + iev.status = status; + aev.status = status; + + if (btdev->auth_init) { + init = btdev; + accp = remote; + memcpy(iev.bdaddr, bdaddr, 6); + memcpy(aev.bdaddr, btdev->bdaddr, 6); + } else { + init = remote; + accp = btdev; + memcpy(iev.bdaddr, btdev->bdaddr, 6); + memcpy(aev.bdaddr, bdaddr, 6); + } + + send_event(init, BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE, &iev, + sizeof(iev)); + send_event(accp, BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE, &aev, + sizeof(aev)); + + if (status == BT_HCI_ERR_SUCCESS) { + link_key_notify(init, iev.bdaddr, LINK_KEY_DUMMY); + link_key_notify(accp, aev.bdaddr, LINK_KEY_DUMMY); + } + + auth.status = status; + auth.handle = cpu_to_le16(42); + send_event(init, BT_HCI_EVT_AUTH_COMPLETE, &auth, sizeof(auth)); +} + +static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote, + uint8_t type) { struct __packed { uint8_t subevent; @@ -986,20 +1561,45 @@ static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote) memset(&meta_event.lar, 0, sizeof(meta_event.lar)); meta_event.lar.num_reports = 1; - memcpy(meta_event.lar.addr, remote->bdaddr, 6); - meta_event.lar.data_len = remote->le_adv_data_len; - memcpy(meta_event.lar.data, remote->le_adv_data, + meta_event.lar.event_type = type; + meta_event.lar.addr_type = remote->le_adv_own_addr; + memcpy(meta_event.lar.addr, adv_addr(remote), 6); + + /* Scan or advertising response */ + if (type == 0x04) { + meta_event.lar.data_len = remote->le_scan_data_len; + memcpy(meta_event.lar.data, remote->le_scan_data, + meta_event.lar.data_len); + } else { + meta_event.lar.data_len = remote->le_adv_data_len; + memcpy(meta_event.lar.data, remote->le_adv_data, meta_event.lar.data_len); + } /* Not available */ meta_event.raw[10 + meta_event.lar.data_len] = 127; send_event(btdev, BT_HCI_EVT_LE_META_EVENT, &meta_event, 1 + 10 + meta_event.lar.data_len + 1); } +static uint8_t get_adv_report_type(uint8_t adv_type) +{ + /* + * Connectable low duty cycle directed advertising creates a + * connectable directed advertising report type. + */ + if (adv_type == 0x04) + return 0x01; + + return adv_type; +} + static void le_set_adv_enable_complete(struct btdev *btdev) { + uint8_t report_type; int i; + report_type = get_adv_report_type(btdev->le_adv_type); + for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { if (!btdev_list[i] || btdev_list[i] == btdev) continue; @@ -1007,7 +1607,17 @@ static void le_set_adv_enable_complete(struct btdev *btdev) if (!btdev_list[i]->le_scan_enable) continue; - le_send_adv_report(btdev_list[i], btdev); + if (!adv_match(btdev_list[i], btdev)) + continue; + + le_send_adv_report(btdev_list[i], btdev, report_type); + + if (btdev_list[i]->le_scan_type != 0x01) + continue; + + /* ADV_IND & ADV_SCAN_IND generate a scan response */ + if (btdev->le_adv_type == 0x00 || btdev->le_adv_type == 0x02) + le_send_adv_report(btdev_list[i], btdev, 0x04); } } @@ -1016,13 +1626,27 @@ static void le_set_scan_enable_complete(struct btdev *btdev) int i; for (i = 0; i < MAX_BTDEV_ENTRIES; i++) { + uint8_t report_type; + if (!btdev_list[i] || btdev_list[i] == btdev) continue; if (!btdev_list[i]->le_adv_enable) continue; - le_send_adv_report(btdev, btdev_list[i]); + if (!adv_match(btdev, btdev_list[i])) + continue; + + report_type = get_adv_report_type(btdev_list[i]->le_adv_type); + le_send_adv_report(btdev, btdev_list[i], report_type); + + if (btdev->le_scan_type != 0x01) + continue; + + /* ADV_IND & ADV_SCAN_IND generate a scan response */ + if (btdev_list[i]->le_adv_type == 0x00 || + btdev_list[i]->le_adv_type == 0x02) + le_send_adv_report(btdev, btdev_list[i], 0x04); } } @@ -1074,6 +1698,32 @@ static void le_encrypt_complete(struct btdev *btdev) send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev)); } +static void ltk_neg_reply_complete(struct btdev *btdev) +{ + struct bt_hci_rsp_le_ltk_req_neg_reply rp; + struct bt_hci_evt_encrypt_change ev; + struct btdev *remote = btdev->conn; + + memset(&rp, 0, sizeof(rp)); + rp.handle = cpu_to_le16(42); + + if (!remote) { + rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; + cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, &rp, + sizeof(rp)); + return; + } + + rp.status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, &rp, sizeof(rp)); + + memset(&ev, 0, sizeof(ev)); + ev.status = BT_HCI_ERR_PIN_OR_KEY_MISSING; + ev.handle = cpu_to_le16(42); + + send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev)); +} + static void default_cmd(struct btdev *btdev, uint16_t opcode, const void *data, uint8_t len) { @@ -1095,12 +1745,20 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, const struct bt_hci_cmd_write_afh_assessment_mode *waam; const struct bt_hci_cmd_write_ext_inquiry_response *weir; const struct bt_hci_cmd_write_simple_pairing_mode *wspm; + const struct bt_hci_cmd_io_capability_request_reply *icrr; + const struct bt_hci_cmd_io_capability_request_reply *icrnr; const struct bt_hci_cmd_write_le_host_supported *wlhs; + const struct bt_hci_cmd_write_secure_conn_support *wscs; const struct bt_hci_cmd_set_event_mask_page2 *semp2; const struct bt_hci_cmd_le_set_event_mask *lsem; + const struct bt_hci_cmd_le_set_random_address *lsra; + const struct bt_hci_cmd_le_set_adv_parameters *lsap; const struct bt_hci_cmd_le_set_adv_data *lsad; + const struct bt_hci_cmd_le_set_scan_rsp_data *lssrd; const struct bt_hci_cmd_setup_sync_conn *ssc; + const struct bt_hci_cmd_write_ssp_debug_mode *wsdm; const struct bt_hci_cmd_le_set_adv_enable *lsae; + const struct bt_hci_cmd_le_set_scan_parameters *lssp; const struct bt_hci_cmd_le_set_scan_enable *lsse; const struct bt_hci_cmd_le_start_encrypt *lse; const struct bt_hci_cmd_le_ltk_req_reply *llrr; @@ -1128,6 +1786,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, struct bt_hci_rsp_read_local_oob_data rlod; struct bt_hci_rsp_read_inquiry_resp_tx_power rirtp; struct bt_hci_rsp_read_le_host_supported rlhs; + struct bt_hci_rsp_read_secure_conn_support rscs; + struct bt_hci_rsp_read_local_oob_ext_data rloed; struct bt_hci_rsp_read_sync_train_params rstp; struct bt_hci_rsp_read_local_version rlv; struct bt_hci_rsp_read_local_commands rlc; @@ -1147,6 +1807,12 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, struct bt_hci_rsp_le_rand lr; struct bt_hci_rsp_le_test_end lte; struct bt_hci_rsp_remote_name_request_cancel rnrc_rsp; + struct bt_hci_rsp_link_key_request_reply lkrr_rsp; + struct bt_hci_rsp_link_key_request_neg_reply lkrnr_rsp; + struct bt_hci_rsp_pin_code_request_neg_reply pcrr_rsp; + struct bt_hci_rsp_pin_code_request_neg_reply pcrnr_rsp; + struct bt_hci_rsp_user_confirm_request_reply ucrr_rsp; + struct bt_hci_rsp_user_confirm_request_neg_reply ucrnr_rsp; uint8_t status, page; switch (opcode) { @@ -1159,8 +1825,7 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, case BT_HCI_CMD_INQUIRY_CANCEL: if (btdev->type == BTDEV_TYPE_LE) goto unsupported; - status = BT_HCI_ERR_SUCCESS; - cmd_complete(btdev, opcode, &status, sizeof(status)); + inquiry_cancel(btdev); break; case BT_HCI_CMD_CREATE_CONN: @@ -1191,6 +1856,50 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode); break; + case BT_HCI_CMD_LINK_KEY_REQUEST_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + lkrr_rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(lkrr_rsp.bdaddr, data, 6); + cmd_complete(btdev, opcode, &lkrr_rsp, sizeof(lkrr_rsp)); + break; + + case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + lkrnr_rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(lkrnr_rsp.bdaddr, data, 6); + cmd_complete(btdev, opcode, &lkrnr_rsp, sizeof(lkrnr_rsp)); + break; + + case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + pcrr_rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(pcrr_rsp.bdaddr, data, 6); + cmd_complete(btdev, opcode, &pcrr_rsp, sizeof(pcrr_rsp)); + break; + + case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + pcrnr_rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(pcrnr_rsp.bdaddr, data, 6); + cmd_complete(btdev, opcode, &pcrnr_rsp, sizeof(pcrnr_rsp)); + break; + + case BT_HCI_CMD_AUTH_REQUESTED: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode); + break; + + case BT_HCI_CMD_SET_CONN_ENCRYPT: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode); + break; + case BT_HCI_CMD_REMOTE_NAME_REQUEST: if (btdev->type == BTDEV_TYPE_LE) goto unsupported; @@ -1561,6 +2270,43 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, cmd_complete(btdev, opcode, &status, sizeof(status)); break; + case BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + icrr = data; + io_cap_req_reply_complete(btdev, icrr->bdaddr, + icrr->capability, + icrr->oob_data, + icrr->authentication); + break; + + case BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + icrnr = data; + io_cap_req_neg_reply_complete(btdev, icrnr->bdaddr); + ssp_complete(btdev, icrnr->bdaddr, BT_HCI_ERR_AUTH_FAILURE, + false); + break; + + case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + ucrr_rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(ucrr_rsp.bdaddr, data, 6); + cmd_complete(btdev, opcode, &ucrr_rsp, sizeof(ucrr_rsp)); + ssp_complete(btdev, data, BT_HCI_ERR_SUCCESS, true); + break; + + case BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + ucrnr_rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(ucrnr_rsp.bdaddr, data, 6); + cmd_complete(btdev, opcode, &ucrnr_rsp, sizeof(ucrnr_rsp)); + ssp_complete(btdev, data, BT_HCI_ERR_AUTH_FAILURE, true); + break; + case BT_HCI_CMD_READ_LOCAL_OOB_DATA: if (btdev->type == BTDEV_TYPE_LE) goto unsupported; @@ -1595,6 +2341,30 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, cmd_complete(btdev, opcode, &status, sizeof(status)); break; + case BT_HCI_CMD_READ_SECURE_CONN_SUPPORT: + if (btdev->type != BTDEV_TYPE_BREDRLE) + goto unsupported; + rscs.status = BT_HCI_ERR_SUCCESS; + rscs.support = btdev->secure_conn_support; + cmd_complete(btdev, opcode, &rscs, sizeof(rscs)); + break; + + case BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT: + if (btdev->type != BTDEV_TYPE_BREDRLE) + goto unsupported; + wscs = data; + btdev->secure_conn_support = wscs->support; + status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, opcode, &status, sizeof(status)); + break; + + case BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA: + if (btdev->type != BTDEV_TYPE_BREDRLE) + goto unsupported; + rloed.status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, opcode, &rloed, sizeof(rloed)); + break; + case BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS: if (btdev->type != BTDEV_TYPE_BREDRLE) goto unsupported; @@ -1650,13 +2420,7 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, break; case 0x01: rlef.status = BT_HCI_ERR_SUCCESS; - memset(rlef.features, 0, 8); - if (btdev->simple_pairing_mode) - rlef.features[0] |= 0x01; - if (btdev->le_supported) - rlef.features[0] |= 0x02; - if (btdev->le_simultaneous) - rlef.features[0] |= 0x04; + btdev_get_host_features(btdev, rlef.features); break; case 0x02: rlef.status = BT_HCI_ERR_SUCCESS; @@ -1766,13 +2530,32 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, cmd_complete(btdev, opcode, &lrlf, sizeof(lrlf)); break; + case BT_HCI_CMD_LE_SET_RANDOM_ADDRESS: + if (btdev->type == BTDEV_TYPE_BREDR) + goto unsupported; + lsra = data; + memcpy(btdev->random_addr, lsra->addr, 6); + status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, opcode, &status, sizeof(status)); + break; + case BT_HCI_CMD_LE_SET_ADV_PARAMETERS: if (btdev->type == BTDEV_TYPE_BREDR) goto unsupported; - if (btdev->le_adv_enable) + + if (btdev->le_adv_enable) { status = BT_HCI_ERR_COMMAND_DISALLOWED; - else - status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, opcode, &status, sizeof(status)); + break; + } + + lsap = data; + btdev->le_adv_type = lsap->type; + btdev->le_adv_own_addr = lsap->own_addr_type; + btdev->le_adv_direct_addr_type = lsap->direct_addr_type; + memcpy(btdev->le_adv_direct_addr, lsap->direct_addr, 6); + + status = BT_HCI_ERR_SUCCESS; cmd_complete(btdev, opcode, &status, sizeof(status)); break; @@ -1802,10 +2585,17 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, case BT_HCI_CMD_LE_SET_SCAN_PARAMETERS: if (btdev->type == BTDEV_TYPE_BREDR) goto unsupported; + + lssp = data; + if (btdev->le_scan_enable) status = BT_HCI_ERR_COMMAND_DISALLOWED; - else + else { status = BT_HCI_ERR_SUCCESS; + btdev->le_scan_type = lssp->type; + btdev->le_scan_own_addr_type = lssp->own_addr_type; + } + cmd_complete(btdev, opcode, &status, sizeof(status)); break; @@ -1839,6 +2629,13 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, cmd_complete(btdev, opcode, &lrwls, sizeof(lrwls)); break; + case BT_HCI_CMD_LE_CLEAR_WHITE_LIST: + if (btdev->type == BTDEV_TYPE_BREDR) + goto unsupported; + status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, opcode, &status, sizeof(status)); + break; + case BT_HCI_CMD_LE_READ_SUPPORTED_STATES: if (btdev->type == BTDEV_TYPE_BREDR) goto unsupported; @@ -1857,18 +2654,21 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, cmd_complete(btdev, opcode, &status, sizeof(status)); break; + case BT_HCI_CMD_LE_SET_SCAN_RSP_DATA: + if (btdev->type == BTDEV_TYPE_BREDR) + goto unsupported; + lssrd = data; + btdev->le_scan_data_len = lssrd->len; + memcpy(btdev->le_scan_data, lssrd->data, 31); + status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, opcode, &status, sizeof(status)); + break; + case BT_HCI_CMD_LE_RAND: if (btdev->type == BTDEV_TYPE_BREDR) goto unsupported; lr.status = BT_HCI_ERR_SUCCESS; - lr.number[0] = rand(); - lr.number[1] = rand(); - lr.number[2] = rand(); - lr.number[3] = rand(); - lr.number[4] = rand(); - lr.number[5] = rand(); - lr.number[6] = rand(); - lr.number[7] = rand(); + lr.number = rand(); cmd_complete(btdev, opcode, &lr, sizeof(lr)); break; @@ -1888,6 +2688,12 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, le_encrypt_complete(btdev); break; + case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY: + if (btdev->type == BTDEV_TYPE_BREDR) + goto unsupported; + ltk_neg_reply_complete(btdev); + break; + case BT_HCI_CMD_SETUP_SYNC_CONN: if (btdev->type == BTDEV_TYPE_LE) goto unsupported; @@ -1909,6 +2715,15 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, cmd_complete(btdev, opcode, &status, sizeof(status)); break; + case BT_HCI_CMD_WRITE_SSP_DEBUG_MODE: + if (btdev->type == BTDEV_TYPE_LE) + goto unsupported; + wsdm = data; + btdev->ssp_debug_mode = wsdm->mode; + status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, opcode, &status, sizeof(status)); + break; + case BT_HCI_CMD_LE_RECEIVER_TEST: if (btdev->type == BTDEV_TYPE_BREDR) goto unsupported; @@ -1951,6 +2766,12 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode, const struct bt_hci_cmd_create_conn_cancel *ccc; const struct bt_hci_cmd_accept_conn_request *acr; const struct bt_hci_cmd_reject_conn_request *rcr; + const struct bt_hci_cmd_auth_requested *ar; + const struct bt_hci_cmd_set_conn_encrypt *sce; + const struct bt_hci_cmd_link_key_request_reply *lkrr; + const struct bt_hci_cmd_link_key_request_neg_reply *lkrnr; + const struct bt_hci_cmd_pin_code_request_neg_reply *pcrnr; + const struct bt_hci_cmd_pin_code_request_reply *pcrr; const struct bt_hci_cmd_remote_name_request *rnr; const struct bt_hci_cmd_remote_name_request_cancel *rnrc; const struct bt_hci_cmd_read_remote_features *rrf; @@ -1962,7 +2783,7 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode, case BT_HCI_CMD_INQUIRY: if (btdev->type == BTDEV_TYPE_LE) return; - inquiry_complete(btdev, BT_HCI_ERR_SUCCESS); + inquiry_cmd(btdev, data); break; case BT_HCI_CMD_CREATE_CONN: @@ -1988,7 +2809,7 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode, if (btdev->type == BTDEV_TYPE_LE) return; acr = data; - conn_complete(btdev, acr->bdaddr, BT_HCI_ERR_SUCCESS); + accept_conn_request_complete(btdev, acr->bdaddr); break; case BT_HCI_CMD_REJECT_CONN_REQUEST: @@ -1998,6 +2819,54 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode, conn_complete(btdev, rcr->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID); break; + case BT_HCI_CMD_LINK_KEY_REQUEST_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + return; + lkrr = data; + link_key_req_reply_complete(btdev, lkrr->bdaddr, lkrr->link_key); + break; + + case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + return; + lkrnr = data; + link_key_req_neg_reply_complete(btdev, lkrnr->bdaddr); + break; + + case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + return; + pcrr = data; + pin_code_req_reply_complete(btdev, pcrr->bdaddr, pcrr->pin_len, + pcrr->pin_code); + break; + + case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY: + if (btdev->type == BTDEV_TYPE_LE) + return; + pcrnr = data; + pin_code_req_neg_reply_complete(btdev, pcrnr->bdaddr); + break; + + case BT_HCI_CMD_AUTH_REQUESTED: + if (btdev->type == BTDEV_TYPE_LE) + return; + ar = data; + auth_request_complete(btdev, le16_to_cpu(ar->handle)); + break; + + case BT_HCI_CMD_SET_CONN_ENCRYPT: + if (btdev->type == BTDEV_TYPE_LE) + return; + sce = data; + if (btdev->conn) { + encrypt_change(btdev, sce->encr_mode, + BT_HCI_ERR_SUCCESS); + encrypt_change(btdev->conn, sce->encr_mode, + BT_HCI_ERR_SUCCESS); + } + break; + case BT_HCI_CMD_REMOTE_NAME_REQUEST: if (btdev->type == BTDEV_TYPE_LE) return; @@ -2037,6 +2906,7 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode, if (btdev->type == BTDEV_TYPE_BREDR) return; lecc = data; + btdev->le_scan_own_addr_type = lecc->own_addr_type; le_conn_request(btdev, lecc->peer_addr); break; } diff --git a/emulator/bthost.c b/emulator/bthost.c index 10e7a05..298edcf 100644 --- a/emulator/bthost.c +++ b/emulator/bthost.c @@ -35,7 +35,9 @@ #include "bluetooth/bluetooth.h" +#include "src/shared/util.h" #include "monitor/bt.h" +#include "monitor/rfcomm.h" #include "bthost.h" /* ACL handle and flags pack/unpack */ @@ -43,10 +45,67 @@ #define acl_handle(h) (h & 0x0fff) #define acl_flags(h) (h >> 12) -#define le16_to_cpu(val) (val) -#define le32_to_cpu(val) (val) -#define cpu_to_le16(val) (val) -#define cpu_to_le32(val) (val) +/* RFCOMM setters */ +#define RFCOMM_ADDR(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) +#define RFCOMM_CTRL(type, pf) (((type & 0xef) | (pf << 4))) +#define RFCOMM_LEN8(len) (((len) << 1) | 1) +#define RFCOMM_LEN16(len) ((len) << 1) +#define RFCOMM_MCC_TYPE(cr, type) (((type << 2) | (cr << 1) | 0x01)) + +/* RFCOMM FCS calculation */ +#define CRC(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]]) + +static unsigned char rfcomm_crc_table[256] = { + 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, + 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, + 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, + 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, + + 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, + 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, + 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, + 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, + + 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, + 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, + 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, + 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, + + 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, + 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, + 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, + 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, + + 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, + 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, + 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, + 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, + + 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, + 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, + 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, + 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, + + 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, + 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, + 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, + 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, + + 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, + 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, + 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, + 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf +}; + +static uint8_t rfcomm_fcs2(uint8_t *data) +{ + return 0xff - rfcomm_crc_table[CRC(data) ^ data[2]]; +} + +static uint8_t rfcomm_fcs(uint8_t *data) +{ + return 0xff - CRC(data); +} struct cmd { struct cmd *next; @@ -67,21 +126,40 @@ struct cid_hook { struct cid_hook *next; }; +struct rfcomm_chan_hook { + uint8_t channel; + bthost_rfcomm_chan_hook_func_t func; + void *user_data; + struct rfcomm_chan_hook *next; +}; + struct btconn { uint16_t handle; + uint8_t bdaddr[6]; uint8_t addr_type; + uint8_t encr_mode; uint16_t next_cid; struct l2conn *l2conns; + struct rcconn *rcconns; struct cid_hook *cid_hooks; + struct rfcomm_chan_hook *rfcomm_chan_hooks; struct btconn *next; + void *smp_data; }; struct l2conn { uint16_t scid; uint16_t dcid; + uint16_t psm; struct l2conn *next; }; +struct rcconn { + uint8_t channel; + uint16_t scid; + struct rcconn *next; +}; + struct l2cap_pending_req { uint8_t ident; bthost_l2cap_rsp_cb cb; @@ -89,6 +167,27 @@ struct l2cap_pending_req { struct l2cap_pending_req *next; }; +struct l2cap_conn_cb_data { + uint16_t psm; + bthost_l2cap_connect_cb func; + void *user_data; + struct l2cap_conn_cb_data *next; +}; + +struct rfcomm_conn_cb_data { + uint8_t channel; + bthost_rfcomm_connect_cb func; + void *user_data; + struct rfcomm_conn_cb_data *next; +}; + +struct rfcomm_connection_data { + uint8_t channel; + struct btconn *conn; + bthost_rfcomm_connect_cb cb; + void *user_data; +}; + struct bthost { uint8_t bdaddr[6]; bthost_send_func send_handler; @@ -100,8 +199,17 @@ struct bthost { void *cmd_complete_data; bthost_new_conn_cb new_conn_cb; void *new_conn_data; - uint16_t server_psm; + struct rfcomm_connection_data *rfcomm_conn_data; + struct l2cap_conn_cb_data *new_l2cap_conn_data; + struct rfcomm_conn_cb_data *new_rfcomm_conn_data; struct l2cap_pending_req *l2reqs; + uint8_t pin[16]; + uint8_t pin_len; + uint8_t io_capability; + uint8_t auth_req; + bool reject_user_confirm; + void *smp_data; + bool conn_init; }; struct bthost *bthost_create(void) @@ -124,6 +232,9 @@ static void l2conn_free(struct l2conn *conn) static void btconn_free(struct btconn *conn) { + if (conn->smp_data) + smp_conn_del(conn->smp_data); + while (conn->l2conns) { struct l2conn *l2conn = conn->l2conns; @@ -138,6 +249,20 @@ static void btconn_free(struct btconn *conn) free(hook); } + while (conn->rcconns) { + struct rcconn *rcconn = conn->rcconns; + + conn->rcconns = rcconn->next; + free(rcconn); + } + + while (conn->rfcomm_chan_hooks) { + struct rfcomm_chan_hook *hook = conn->rfcomm_chan_hooks; + + conn->rfcomm_chan_hooks = hook->next; + free(hook); + } + free(conn); } @@ -153,22 +278,75 @@ static struct btconn *bthost_find_conn(struct bthost *bthost, uint16_t handle) return NULL; } -static void bthost_add_l2cap_conn(struct bthost *bthost, struct btconn *conn, - uint16_t scid, uint16_t dcid) +static struct btconn *bthost_find_conn_by_bdaddr(struct bthost *bthost, + const uint8_t *bdaddr) +{ + struct btconn *conn; + + for (conn = bthost->conns; conn != NULL; conn = conn->next) { + if (!memcmp(conn->bdaddr, bdaddr, 6)) + return conn; + } + + return NULL; +} + +static struct l2conn *bthost_add_l2cap_conn(struct bthost *bthost, + struct btconn *conn, + uint16_t scid, uint16_t dcid, + uint16_t psm) { struct l2conn *l2conn; l2conn = malloc(sizeof(*l2conn)); if (!l2conn) - return; + return NULL; memset(l2conn, 0, sizeof(*l2conn)); + l2conn->psm = psm; l2conn->scid = scid; l2conn->dcid = dcid; l2conn->next = conn->l2conns; conn->l2conns = l2conn; + + return l2conn; +} + +static struct rcconn *bthost_add_rfcomm_conn(struct bthost *bthost, + struct btconn *conn, + struct l2conn *l2conn, + uint8_t channel) +{ + struct rcconn *rcconn; + + rcconn = malloc(sizeof(*rcconn)); + if (!rcconn) + return NULL; + + memset(rcconn, 0, sizeof(*rcconn)); + + rcconn->channel = channel; + rcconn->scid = l2conn->scid; + + rcconn->next = conn->rcconns; + conn->rcconns = rcconn; + + return rcconn; +} + +static struct rcconn *btconn_find_rfcomm_conn_by_channel(struct btconn *conn, + uint8_t chan) +{ + struct rcconn *rcconn; + + for (rcconn = conn->rcconns; rcconn != NULL; rcconn = rcconn->next) { + if (rcconn->channel == chan) + return rcconn; + } + + return NULL; } static struct l2conn *btconn_find_l2cap_conn_by_scid(struct btconn *conn, @@ -184,15 +362,43 @@ static struct l2conn *btconn_find_l2cap_conn_by_scid(struct btconn *conn, return NULL; } -void bthost_destroy(struct bthost *bthost) +static struct l2cap_conn_cb_data *bthost_find_l2cap_cb_by_psm( + struct bthost *bthost, uint16_t psm) { - struct cmd *cmd; + struct l2cap_conn_cb_data *cb; + + for (cb = bthost->new_l2cap_conn_data; cb != NULL; cb = cb->next) { + if (cb->psm == psm) + return cb; + } + + return NULL; +} + +static struct rfcomm_conn_cb_data *bthost_find_rfcomm_cb_by_channel( + struct bthost *bthost, uint8_t channel) +{ + struct rfcomm_conn_cb_data *cb; + + for (cb = bthost->new_rfcomm_conn_data; cb != NULL; cb = cb->next) { + if (cb->channel == channel) + return cb; + } + return NULL; +} + +void bthost_destroy(struct bthost *bthost) +{ if (!bthost) return; - for (cmd = bthost->cmd_q.tail; cmd != NULL; cmd = cmd->next) + while (bthost->cmd_q.tail) { + struct cmd *cmd = bthost->cmd_q.tail; + + bthost->cmd_q.tail = cmd->prev; free(cmd); + } while (bthost->conns) { struct btconn *conn = bthost->conns; @@ -209,6 +415,23 @@ void bthost_destroy(struct bthost *bthost) free(req); } + while (bthost->new_l2cap_conn_data) { + struct l2cap_conn_cb_data *cb = bthost->new_l2cap_conn_data; + + bthost->new_l2cap_conn_data = cb->next; + free(cb); + } + + while (bthost->new_rfcomm_conn_data) { + struct rfcomm_conn_cb_data *cb = bthost->new_rfcomm_conn_data; + + bthost->new_rfcomm_conn_data = cb->next; + free(cb); + } + + if (bthost->rfcomm_conn_data) + free(bthost->rfcomm_conn_data); + free(bthost); } @@ -239,6 +462,8 @@ static void queue_command(struct bthost *bthost, const void *data, if (cmd_q->tail) cmd_q->tail->next = cmd; + else + cmd_q->head = cmd; cmd->prev = cmd_q->tail; cmd_q->tail = cmd; @@ -374,6 +599,15 @@ bool bthost_l2cap_req(struct bthost *bthost, uint16_t handle, uint8_t code, if (!conn) return false; + if (code == BT_L2CAP_PDU_CONN_REQ && + len == sizeof(struct bt_l2cap_pdu_conn_req)) { + const struct bt_l2cap_pdu_conn_req *req = data; + + bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(req->scid), + le16_to_cpu(req->scid), + le16_to_cpu(req->psm)); + } + ident = l2cap_sig_send(bthost, conn, code, 0, data, len); if (!ident) return false; @@ -431,7 +665,7 @@ static void send_command(struct bthost *bthost, uint16_t opcode, static void next_cmd(struct bthost *bthost) { struct cmd_queue *cmd_q = &bthost->cmd_q; - struct cmd *cmd = cmd_q->tail; + struct cmd *cmd = cmd_q->head; struct cmd *next; if (!cmd) @@ -447,8 +681,10 @@ static void next_cmd(struct bthost *bthost) if (next) next->prev = NULL; + else + cmd_q->tail = NULL; - cmd_q->tail = next; + cmd_q->head = next; free(cmd); } @@ -493,6 +729,24 @@ static void evt_cmd_complete(struct bthost *bthost, const void *data, break; case BT_HCI_CMD_LE_SET_ADV_ENABLE: break; + case BT_HCI_CMD_LE_SET_ADV_PARAMETERS: + break; + case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY: + break; + case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY: + break; + case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY: + break; + case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE: + break; + case BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY: + break; + case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY: + break; + case BT_HCI_CMD_LE_LTK_REQ_REPLY: + break; + case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY: + break; default: printf("Unhandled cmd_complete opcode 0x%04x\n", opcode); break; @@ -541,9 +795,11 @@ static void evt_conn_request(struct bthost *bthost, const void *data, sizeof(cmd)); } -static void init_conn(struct bthost *bthost, uint16_t handle, uint8_t addr_type) +static void init_conn(struct bthost *bthost, uint16_t handle, + const uint8_t *bdaddr, uint8_t addr_type) { struct btconn *conn; + const uint8_t *ia, *ra; conn = malloc(sizeof(*conn)); if (!conn) @@ -551,12 +807,24 @@ static void init_conn(struct bthost *bthost, uint16_t handle, uint8_t addr_type) memset(conn, 0, sizeof(*conn)); conn->handle = handle; + memcpy(conn->bdaddr, bdaddr, 6); conn->addr_type = addr_type; conn->next_cid = 0x0040; conn->next = bthost->conns; bthost->conns = conn; + if (bthost->conn_init) { + ia = bthost->bdaddr; + ra = conn->bdaddr; + } else { + ia = conn->bdaddr; + ra = bthost->bdaddr; + } + + conn->smp_data = smp_conn_add(bthost->smp_data, handle, ia, ra, + bthost->conn_init); + if (bthost->new_conn_cb) bthost->new_conn_cb(conn->handle, bthost->new_conn_data); } @@ -572,7 +840,7 @@ static void evt_conn_complete(struct bthost *bthost, const void *data, if (ev->status) return; - init_conn(bthost, le16_to_cpu(ev->handle), BDADDR_BREDR); + init_conn(bthost, le16_to_cpu(ev->handle), ev->bdaddr, BDADDR_BREDR); } static void evt_disconn_complete(struct bthost *bthost, const void *data, @@ -611,6 +879,163 @@ static void evt_num_completed_packets(struct bthost *bthost, const void *data, return; } +static void evt_auth_complete(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_auth_complete *ev = data; + struct bt_hci_cmd_set_conn_encrypt cp; + + if (len < sizeof(*ev)) + return; + + if (ev->status) + return; + + cp.handle = ev->handle; + cp.encr_mode = 0x01; + + send_command(bthost, BT_HCI_CMD_SET_CONN_ENCRYPT, &cp, sizeof(cp)); +} + +static void evt_pin_code_request(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_pin_code_request *ev = data; + + if (len < sizeof(*ev)) + return; + + if (bthost->pin_len > 0) { + struct bt_hci_cmd_pin_code_request_reply cp; + + memset(&cp, 0, sizeof(cp)); + memcpy(cp.bdaddr, ev->bdaddr, 6); + cp.pin_len = bthost->pin_len; + memcpy(cp.pin_code, bthost->pin, bthost->pin_len); + + send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_REPLY, + &cp, sizeof(cp)); + } else { + struct bt_hci_cmd_pin_code_request_neg_reply cp; + + memcpy(cp.bdaddr, ev->bdaddr, 6); + send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY, + &cp, sizeof(cp)); + } +} + +static void evt_link_key_request(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_link_key_request *ev = data; + struct bt_hci_cmd_link_key_request_neg_reply cp; + + if (len < sizeof(*ev)) + return; + + memset(&cp, 0, sizeof(cp)); + memcpy(cp.bdaddr, ev->bdaddr, 6); + + send_command(bthost, BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY, + &cp, sizeof(cp)); +} + +static void evt_link_key_notify(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_link_key_notify *ev = data; + + if (len < sizeof(*ev)) + return; +} + +static void evt_encrypt_change(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_encrypt_change *ev = data; + struct btconn *conn; + uint16_t handle; + + if (len < sizeof(*ev)) + return; + + handle = acl_handle(ev->handle); + conn = bthost_find_conn(bthost, handle); + if (!conn) + return; + + conn->encr_mode = ev->encr_mode; +} + +static void evt_io_cap_response(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_io_capability_response *ev = data; + struct btconn *conn; + + if (len < sizeof(*ev)) + return; + + conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr); + if (!conn) + return; +} + +static void evt_io_cap_request(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_io_capability_request *ev = data; + struct bt_hci_cmd_io_capability_request_reply cp; + struct btconn *conn; + + if (len < sizeof(*ev)) + return; + + conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr); + if (!conn) + return; + + memcpy(cp.bdaddr, ev->bdaddr, 6); + cp.capability = bthost->io_capability; + cp.oob_data = 0x00; + cp.authentication = bthost->auth_req; + + send_command(bthost, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY, + &cp, sizeof(cp)); +} + +static void evt_user_confirm_request(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_user_confirm_request *ev = data; + struct btconn *conn; + + if (len < sizeof(*ev)) + return; + + conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr); + if (!conn) + return; + + if (bthost->reject_user_confirm) { + send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY, + ev->bdaddr, 6); + return; + } + + send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, + ev->bdaddr, 6); +} + +static void evt_simple_pairing_complete(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_simple_pairing_complete *ev = data; + + if (len < sizeof(*ev)) + return; +} + static void evt_le_conn_complete(struct bthost *bthost, const void *data, uint8_t len) { @@ -628,7 +1053,40 @@ static void evt_le_conn_complete(struct bthost *bthost, const void *data, else addr_type = BDADDR_LE_RANDOM; - init_conn(bthost, le16_to_cpu(ev->handle), addr_type); + init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type); +} + +static void evt_le_ltk_request(struct bthost *bthost, const void *data, + uint8_t len) +{ + const struct bt_hci_evt_le_long_term_key_request *ev = data; + struct bt_hci_cmd_le_ltk_req_reply cp; + struct bt_hci_cmd_le_ltk_req_neg_reply *neg_cp = (void *) &cp; + uint16_t handle, ediv; + uint64_t rand; + struct btconn *conn; + int err; + + if (len < sizeof(*ev)) + return; + + handle = acl_handle(ev->handle); + conn = bthost_find_conn(bthost, handle); + if (!conn) + return; + + rand = le64_to_cpu(ev->rand); + ediv = le16_to_cpu(ev->ediv); + + cp.handle = ev->handle; + + err = smp_get_ltk(conn->smp_data, rand, ediv, cp.ltk); + if (err < 0) + send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, + neg_cp, sizeof(*neg_cp)); + else + send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_REPLY, &cp, + sizeof(cp)); } static void evt_le_meta_event(struct bthost *bthost, const void *data, @@ -644,7 +1102,11 @@ static void evt_le_meta_event(struct bthost *bthost, const void *data, case BT_HCI_EVT_LE_CONN_COMPLETE: evt_le_conn_complete(bthost, evt_data, len - 1); break; + case BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST: + evt_le_ltk_request(bthost, evt_data, len - 1); + break; default: + printf("Unsupported LE Meta event 0x%2.2x\n", *event); break; } } @@ -687,6 +1149,42 @@ static void process_evt(struct bthost *bthost, const void *data, uint16_t len) evt_num_completed_packets(bthost, param, hdr->plen); break; + case BT_HCI_EVT_AUTH_COMPLETE: + evt_auth_complete(bthost, param, hdr->plen); + break; + + case BT_HCI_EVT_PIN_CODE_REQUEST: + evt_pin_code_request(bthost, param, hdr->plen); + break; + + case BT_HCI_EVT_LINK_KEY_REQUEST: + evt_link_key_request(bthost, param, hdr->plen); + break; + + case BT_HCI_EVT_LINK_KEY_NOTIFY: + evt_link_key_notify(bthost, param, hdr->plen); + break; + + case BT_HCI_EVT_ENCRYPT_CHANGE: + evt_encrypt_change(bthost, param, hdr->plen); + break; + + case BT_HCI_EVT_IO_CAPABILITY_RESPONSE: + evt_io_cap_response(bthost, param, hdr->plen); + break; + + case BT_HCI_EVT_IO_CAPABILITY_REQUEST: + evt_io_cap_request(bthost, param, hdr->plen); + break; + + case BT_HCI_EVT_USER_CONFIRM_REQUEST: + evt_user_confirm_request(bthost, param, hdr->plen); + break; + + case BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE: + evt_simple_pairing_complete(bthost, param, hdr->plen); + break; + case BT_HCI_EVT_LE_META_EVENT: evt_le_meta_event(bthost, param, hdr->plen); break; @@ -712,6 +1210,7 @@ static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_conn_req *req = data; + struct l2cap_conn_cb_data *cb_data; struct bt_l2cap_pdu_conn_rsp rsp; uint16_t psm; @@ -723,7 +1222,8 @@ static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn, memset(&rsp, 0, sizeof(rsp)); rsp.scid = req->scid; - if (bthost->server_psm && bthost->server_psm == psm) + cb_data = bthost_find_l2cap_cb_by_psm(bthost, psm); + if (cb_data) rsp.dcid = cpu_to_le16(conn->next_cid++); else rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */ @@ -733,30 +1233,54 @@ static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn, if (!rsp.result) { struct bt_l2cap_pdu_config_req conf_req; + struct l2conn *l2conn; - bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(rsp.dcid), - le16_to_cpu(rsp.scid)); + l2conn = bthost_add_l2cap_conn(bthost, conn, + le16_to_cpu(rsp.dcid), + le16_to_cpu(rsp.scid), + le16_to_cpu(psm)); memset(&conf_req, 0, sizeof(conf_req)); - conf_req.dcid = rsp.dcid; + conf_req.dcid = rsp.scid; l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0, &conf_req, sizeof(conf_req)); + + if (cb_data && l2conn->psm == cb_data->psm && cb_data->func) + cb_data->func(conn->handle, l2conn->dcid, + cb_data->user_data); } return true; } +static void rfcomm_sabm_send(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, uint8_t cr, uint8_t dlci) +{ + struct rfcomm_cmd cmd; + + cmd.address = RFCOMM_ADDR(cr, dlci); + cmd.control = RFCOMM_CTRL(RFCOMM_SABM, 1); + cmd.length = RFCOMM_LEN8(0); + cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd); + + send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd)); +} + static bool l2cap_conn_rsp(struct bthost *bthost, struct btconn *conn, uint8_t ident, const void *data, uint16_t len) { const struct bt_l2cap_pdu_conn_rsp *rsp = data; + struct l2conn *l2conn; if (len < sizeof(*rsp)) return false; - bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(rsp->scid), - le16_to_cpu(rsp->dcid)); + l2conn = btconn_find_l2cap_conn_by_scid(conn, le16_to_cpu(rsp->scid)); + if (l2conn) + l2conn->dcid = le16_to_cpu(rsp->dcid); + else + return false; if (le16_to_cpu(rsp->result) == 0x0001) { struct bt_l2cap_pdu_config_req req; @@ -766,6 +1290,9 @@ static bool l2cap_conn_rsp(struct bthost *bthost, struct btconn *conn, l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0, &req, sizeof(req)); + } else if (l2conn->psm == 0x0003 && !rsp->result && !rsp->status && + bthost->rfcomm_conn_data) { + rfcomm_sabm_send(bthost, conn, l2conn, 1, 0); } return true; @@ -993,7 +1520,7 @@ static bool l2cap_le_conn_req(struct bthost *bthost, struct btconn *conn, rsp.mps = 23; rsp.credits = 1; - if (bthost->server_psm && bthost->server_psm == psm) + if (bthost_find_l2cap_cb_by_psm(bthost, psm)) rsp.dcid = cpu_to_le16(conn->next_cid++); else rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */ @@ -1011,8 +1538,8 @@ static bool l2cap_le_conn_rsp(struct bthost *bthost, struct btconn *conn, if (len < sizeof(*rsp)) return false; - - bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->dcid)); + /* TODO add L2CAP connection before with proper PSM */ + bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->dcid), 0); return true; } @@ -1093,6 +1620,329 @@ static struct cid_hook *find_cid_hook(struct btconn *conn, uint16_t cid) return NULL; } +static struct rfcomm_chan_hook *find_rfcomm_chan_hook(struct btconn *conn, + uint8_t channel) +{ + struct rfcomm_chan_hook *hook; + + for (hook = conn->rfcomm_chan_hooks; hook != NULL; hook = hook->next) + if (hook->channel == channel) + return hook; + + return NULL; +} + +static void rfcomm_ua_send(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, uint8_t cr, uint8_t dlci) +{ + struct rfcomm_cmd cmd; + + cmd.address = RFCOMM_ADDR(cr, dlci); + cmd.control = RFCOMM_CTRL(RFCOMM_UA, 1); + cmd.length = RFCOMM_LEN8(0); + cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd); + + send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd)); +} + +static void rfcomm_dm_send(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, uint8_t cr, uint8_t dlci) +{ + struct rfcomm_cmd cmd; + + cmd.address = RFCOMM_ADDR(cr, dlci); + cmd.control = RFCOMM_CTRL(RFCOMM_DM, 1); + cmd.length = RFCOMM_LEN8(0); + cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd); + + send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd)); +} + +static void rfcomm_sabm_recv(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, const void *data, + uint16_t len) +{ + const struct rfcomm_cmd *hdr = data; + uint8_t dlci; + struct rfcomm_conn_cb_data *cb; + uint8_t chan; + + if (len < sizeof(*hdr)) + return; + + chan = RFCOMM_GET_CHANNEL(hdr->address); + dlci = RFCOMM_GET_DLCI(hdr->address); + + cb = bthost_find_rfcomm_cb_by_channel(bthost, chan); + if (!dlci || cb) { + bthost_add_rfcomm_conn(bthost, conn, l2conn, chan); + rfcomm_ua_send(bthost, conn, l2conn, 1, dlci); + if (cb && cb->func) + cb->func(conn->handle, l2conn->scid, cb->user_data, + true); + } else { + rfcomm_dm_send(bthost, conn, l2conn, 1, dlci); + } +} + +static void rfcomm_disc_recv(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, const void *data, + uint16_t len) +{ + const struct rfcomm_cmd *hdr = data; + uint8_t dlci; + + if (len < sizeof(*hdr)) + return; + + dlci = RFCOMM_GET_DLCI(hdr->address); + + rfcomm_ua_send(bthost, conn, l2conn, 0, dlci); +} + +static void rfcomm_ua_recv(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, const void *data, + uint16_t len) +{ + const struct rfcomm_cmd *ua_hdr = data; + uint8_t channel; + struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data; + uint8_t type; + uint8_t buf[14]; + struct rfcomm_hdr *hdr; + struct rfcomm_mcc *mcc; + struct rfcomm_pn *pn_cmd; + + if (len < sizeof(*ua_hdr)) + return; + + channel = RFCOMM_GET_CHANNEL(ua_hdr->address); + type = RFCOMM_GET_TYPE(ua_hdr->control); + + if (channel && conn_data && conn_data->channel == channel) { + bthost_add_rfcomm_conn(bthost, conn, l2conn, channel); + if (conn_data->cb) + conn_data->cb(conn->handle, l2conn->scid, + conn_data->user_data, true); + free(bthost->rfcomm_conn_data); + bthost->rfcomm_conn_data = NULL; + return; + } + + if (!conn_data || !RFCOMM_TEST_CR(type)) + return; + + bthost_add_rfcomm_conn(bthost, conn, l2conn, channel); + + memset(buf, 0, sizeof(buf)); + + hdr = (struct rfcomm_hdr *) buf; + mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr)); + pn_cmd = (struct rfcomm_pn *) (buf + sizeof(*hdr) + sizeof(*mcc)); + + hdr->address = RFCOMM_ADDR(1, 0); + hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0); + hdr->length = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*pn_cmd)); + + mcc->type = RFCOMM_MCC_TYPE(1, RFCOMM_PN); + mcc->length = RFCOMM_LEN8(sizeof(*pn_cmd)); + + pn_cmd->dlci = conn_data->channel * 2; + pn_cmd->priority = 7; + pn_cmd->ack_timer = 0; + pn_cmd->max_retrans = 0; + pn_cmd->mtu = 667; + pn_cmd->credits = 7; + + buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*pn_cmd)] = rfcomm_fcs(buf); + + send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf)); +} + +static void rfcomm_dm_recv(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, const void *data, + uint16_t len) +{ + const struct rfcomm_cmd *hdr = data; + uint8_t channel; + struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data; + + if (len < sizeof(*hdr)) + return; + + channel = RFCOMM_GET_CHANNEL(hdr->address); + + if (conn_data && conn_data->channel == channel) { + if (conn_data->cb) + conn_data->cb(conn->handle, l2conn->scid, + conn_data->user_data, false); + free(bthost->rfcomm_conn_data); + bthost->rfcomm_conn_data = NULL; + } +} + +static void rfcomm_msc_recv(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, uint8_t cr, + const struct rfcomm_msc *msc) +{ + uint8_t buf[8]; + struct rfcomm_hdr *hdr = (struct rfcomm_hdr *) buf; + struct rfcomm_mcc *mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr)); + struct rfcomm_msc *msc_cmd = (struct rfcomm_msc *) (buf + + sizeof(*hdr) + + sizeof(*mcc)); + + hdr->address = RFCOMM_ADDR(0, 0); + hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0); + hdr->length = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*msc)); + mcc->type = RFCOMM_MCC_TYPE(cr, RFCOMM_MSC); + mcc->length = RFCOMM_LEN8(sizeof(*msc)); + + msc_cmd->dlci = msc->dlci; + msc_cmd->v24_sig = msc->v24_sig; + buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*msc_cmd)] = rfcomm_fcs(buf); + + send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf)); +} + +static void rfcomm_pn_recv(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, uint8_t cr, + const struct rfcomm_pn *pn) +{ + uint8_t buf[14]; + struct rfcomm_hdr *hdr; + struct rfcomm_mcc *mcc; + struct rfcomm_pn *pn_cmd; + + if (!cr) { + rfcomm_sabm_send(bthost, conn, l2conn, 1, + bthost->rfcomm_conn_data->channel * 2); + return; + } + + hdr = (struct rfcomm_hdr *) buf; + mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr)); + pn_cmd = (struct rfcomm_pn *) (buf + sizeof(*hdr) + sizeof(*mcc)); + + memset(buf, 0, sizeof(buf)); + + hdr->address = RFCOMM_ADDR(1, 0); + hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0); + hdr->length = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*pn_cmd)); + + mcc->type = RFCOMM_MCC_TYPE(0, RFCOMM_PN); + mcc->length = RFCOMM_LEN8(sizeof(*pn_cmd)); + + pn_cmd->dlci = pn->dlci; + pn_cmd->priority = pn->priority; + pn_cmd->ack_timer = pn->ack_timer; + pn_cmd->max_retrans = pn->max_retrans; + pn_cmd->mtu = pn->mtu; + pn_cmd->credits = pn->credits; + + buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*pn_cmd)] = rfcomm_fcs(buf); + + send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf)); +} + +static void rfcomm_mcc_recv(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, const void *data, uint16_t len) +{ + const struct rfcomm_mcc *mcc = data; + const struct rfcomm_msc *msc; + const struct rfcomm_pn *pn; + + if (len < sizeof(*mcc)) + return; + + switch (RFCOMM_GET_MCC_TYPE(mcc->type)) { + case RFCOMM_MSC: + if (len - sizeof(*mcc) < sizeof(*msc)) + break; + + msc = data + sizeof(*mcc); + + rfcomm_msc_recv(bthost, conn, l2conn, + RFCOMM_TEST_CR(mcc->type) / 2, msc); + break; + case RFCOMM_PN: + if (len - sizeof(*mcc) < sizeof(*pn)) + break; + + pn = data + sizeof(*mcc); + + rfcomm_pn_recv(bthost, conn, l2conn, + RFCOMM_TEST_CR(mcc->type) / 2, pn); + break; + default: + break; + } +} + +static void rfcomm_uih_recv(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, const void *data, + uint16_t len) +{ + const struct rfcomm_hdr *hdr = data; + uint16_t hdr_len; + const void *p; + + if (len < sizeof(*hdr)) + return; + + if (RFCOMM_TEST_EA(hdr->length)) + hdr_len = sizeof(*hdr); + else + hdr_len = sizeof(*hdr) + sizeof(uint8_t); + + if (len < hdr_len) + return; + + p = data + hdr_len; + + if (RFCOMM_GET_DLCI(hdr->address)) { + struct rfcomm_chan_hook *hook; + + hook = find_rfcomm_chan_hook(conn, + RFCOMM_GET_CHANNEL(hdr->address)); + if (!hook) + return; + + hook->func(p, len - hdr_len - sizeof(uint8_t), + hook->user_data); + } else { + rfcomm_mcc_recv(bthost, conn, l2conn, p, len - hdr_len); + } +} + +static void process_rfcomm(struct bthost *bthost, struct btconn *conn, + struct l2conn *l2conn, const void *data, + uint16_t len) +{ + const struct rfcomm_hdr *hdr = data; + + switch (RFCOMM_GET_TYPE(hdr->control)) { + case RFCOMM_SABM: + rfcomm_sabm_recv(bthost, conn, l2conn, data, len); + break; + case RFCOMM_DISC: + rfcomm_disc_recv(bthost, conn, l2conn, data, len); + break; + case RFCOMM_UA: + rfcomm_ua_recv(bthost, conn, l2conn, data, len); + break; + case RFCOMM_DM: + rfcomm_dm_recv(bthost, conn, l2conn, data, len); + break; + case RFCOMM_UIH: + rfcomm_uih_recv(bthost, conn, l2conn, data, len); + break; + default: + printf("Unknown frame type\n"); + break; + } +} + static void process_acl(struct bthost *bthost, const void *data, uint16_t len) { const struct bt_hci_acl_hdr *acl_hdr = data; @@ -1100,6 +1950,7 @@ static void process_acl(struct bthost *bthost, const void *data, uint16_t len) uint16_t handle, cid, acl_len, l2_len; struct cid_hook *hook; struct btconn *conn; + struct l2conn *l2conn; const void *l2_data; if (len < sizeof(*acl_hdr) + sizeof(*l2_hdr)) @@ -1137,8 +1988,16 @@ static void process_acl(struct bthost *bthost, const void *data, uint16_t len) case 0x0005: l2cap_le_sig(bthost, conn, l2_data, l2_len); break; + case 0x0006: + smp_data(conn->smp_data, l2_data, l2_len); + break; default: - printf("Packet for unknown CID 0x%04x (%u)\n", cid, cid); + l2conn = btconn_find_l2cap_conn_by_scid(conn, cid); + if (l2conn && l2conn->psm == 0x0003) + process_rfcomm(bthost, conn, l2conn, l2_data, l2_len); + else + printf("Packet for unknown CID 0x%04x (%u)\n", cid, + cid); break; } } @@ -1185,6 +2044,8 @@ void bthost_set_connect_cb(struct bthost *bthost, bthost_new_conn_cb cb, void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr, uint8_t addr_type) { + bthost->conn_init = true; + if (addr_type == BDADDR_BREDR) { struct bt_hci_cmd_create_conn cc; @@ -1213,16 +2074,43 @@ void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan) void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable) { + struct bt_hci_cmd_le_set_adv_parameters cp; + + memset(&cp, 0, sizeof(cp)); + send_command(bthost, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + &cp, sizeof(cp)); + send_command(bthost, BT_HCI_CMD_LE_SET_ADV_ENABLE, &enable, 1); } +void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode) +{ + send_command(bthost, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &mode, 1); +} + +void bthost_request_auth(struct bthost *bthost, uint16_t handle) +{ + struct btconn *conn; + + conn = bthost_find_conn(bthost, handle); + if (!conn) + return; + + if (conn->addr_type == BDADDR_BREDR) { + struct bt_hci_cmd_auth_requested cp; + + cp.handle = cpu_to_le16(handle); + send_command(bthost, BT_HCI_CMD_AUTH_REQUESTED, &cp, sizeof(cp)); + } else { + smp_pair(conn->smp_data); + } +} + void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle, const uint8_t ltk[16]) { struct bt_hci_cmd_le_start_encrypt cmd; - printf("bthost_le_start_encrypt(handle %u)\n", handle); - memset(&cmd, 0, sizeof(cmd)); cmd.handle = htobs(handle); memcpy(cmd.ltk, ltk, 16); @@ -1230,9 +2118,60 @@ void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle, send_command(bthost, BT_HCI_CMD_LE_START_ENCRYPT, &cmd, sizeof(cmd)); } -void bthost_set_server_psm(struct bthost *bthost, uint16_t psm) +void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm, + bthost_l2cap_connect_cb func, void *user_data) +{ + struct l2cap_conn_cb_data *data; + + data = malloc(sizeof(struct l2cap_conn_cb_data)); + if (!data) + return; + + data->psm = psm; + data->user_data = user_data; + data->func = func; + data->next = bthost->new_l2cap_conn_data; + + bthost->new_l2cap_conn_data = data; +} + +void bthost_set_pin_code(struct bthost *bthost, const uint8_t *pin, + uint8_t pin_len) +{ + memcpy(bthost->pin, pin, pin_len); + bthost->pin_len = pin_len; +} + +void bthost_set_io_capability(struct bthost *bthost, uint8_t io_capability) +{ + bthost->io_capability = io_capability; +} + +void bthost_set_auth_req(struct bthost *bthost, uint8_t auth_req) +{ + bthost->auth_req = auth_req; +} + +void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject) +{ + bthost->reject_user_confirm = reject; +} + +void bthost_add_rfcomm_server(struct bthost *bthost, uint8_t channel, + bthost_rfcomm_connect_cb func, void *user_data) { - bthost->server_psm = psm; + struct rfcomm_conn_cb_data *data; + + data = malloc(sizeof(struct rfcomm_conn_cb_data)); + if (!data) + return; + + data->channel = channel; + data->user_data = user_data; + data->func = func; + data->next = bthost->new_rfcomm_conn_data; + + bthost->new_rfcomm_conn_data = data; } void bthost_start(struct bthost *bthost) @@ -1240,6 +2179,8 @@ void bthost_start(struct bthost *bthost) if (!bthost) return; + bthost->smp_data = smp_start(bthost); + bthost->ncmd = 1; send_command(bthost, BT_HCI_CMD_RESET, NULL, 0); @@ -1247,6 +2188,113 @@ void bthost_start(struct bthost *bthost) send_command(bthost, BT_HCI_CMD_READ_BD_ADDR, NULL, 0); } +bool bthost_connect_rfcomm(struct bthost *bthost, uint16_t handle, + uint8_t channel, bthost_rfcomm_connect_cb func, + void *user_data) +{ + struct rfcomm_connection_data *data; + struct bt_l2cap_pdu_conn_req req; + struct btconn *conn; + + if (bthost->rfcomm_conn_data) + return false; + + conn = bthost_find_conn(bthost, handle); + if (!conn) + return false; + + data = malloc(sizeof(struct rfcomm_connection_data)); + if (!data) + return false; + + data->channel = channel; + data->conn = conn; + data->cb = func; + data->user_data = user_data; + + bthost->rfcomm_conn_data = data; + + req.psm = cpu_to_le16(0x0003); + req.scid = cpu_to_le16(conn->next_cid++); + + return bthost_l2cap_req(bthost, handle, BT_L2CAP_PDU_CONN_REQ, + &req, sizeof(req), NULL, NULL); +} + +void bthost_add_rfcomm_chan_hook(struct bthost *bthost, uint16_t handle, + uint8_t channel, + bthost_rfcomm_chan_hook_func_t func, + void *user_data) +{ + struct rfcomm_chan_hook *hook; + struct btconn *conn; + + conn = bthost_find_conn(bthost, handle); + if (!conn) + return; + + hook = malloc(sizeof(*hook)); + if (!hook) + return; + + memset(hook, 0, sizeof(*hook)); + + hook->channel = channel; + hook->func = func; + hook->user_data = user_data; + + hook->next = conn->rfcomm_chan_hooks; + conn->rfcomm_chan_hooks = hook; +} + +void bthost_send_rfcomm_data(struct bthost *bthost, uint16_t handle, + uint8_t channel, const void *data, + uint16_t len) +{ + struct btconn *conn; + struct rcconn *rcconn; + struct rfcomm_hdr *hdr; + uint8_t *uih_frame; + uint16_t uih_len; + + conn = bthost_find_conn(bthost, handle); + if (!conn) + return; + + rcconn = btconn_find_rfcomm_conn_by_channel(conn, channel); + if (!rcconn) + return; + + if (len > 127) + uih_len = len + sizeof(struct rfcomm_cmd) + sizeof(uint8_t); + else + uih_len = len + sizeof(struct rfcomm_cmd); + + uih_frame = malloc(uih_len); + if (!uih_frame) + return; + + hdr = (struct rfcomm_hdr *) uih_frame; + hdr->address = RFCOMM_ADDR(1, channel * 2); + hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0); + if (len > 127) { + hdr->length = RFCOMM_LEN16(cpu_to_le16(sizeof(*hdr) + len)); + memcpy(uih_frame + sizeof(*hdr) + 1, data, len); + } else { + hdr->length = RFCOMM_LEN8(sizeof(*hdr) + len); + memcpy(uih_frame + sizeof(*hdr), data, len); + } + + uih_frame[uih_len - 1] = rfcomm_fcs((void *)hdr); + send_acl(bthost, handle, rcconn->scid, uih_frame, uih_len); + + free(uih_frame); +} + void bthost_stop(struct bthost *bthost) { + if (bthost->smp_data) { + smp_stop(bthost->smp_data); + bthost->smp_data = NULL; + } } diff --git a/emulator/bthost.h b/emulator/bthost.h index 474ada9..b00bcd6 100644 --- a/emulator/bthost.h +++ b/emulator/bthost.h @@ -72,10 +72,56 @@ void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan); void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable); +void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode); + +void bthost_request_auth(struct bthost *bthost, uint16_t handle); + void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle, const uint8_t ltk[16]); +typedef void (*bthost_l2cap_connect_cb) (uint16_t handle, uint16_t cid, + void *user_data); + +void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm, + bthost_l2cap_connect_cb func, void *user_data); + +void bthost_set_pin_code(struct bthost *bthost, const uint8_t *pin, + uint8_t pin_len); +void bthost_set_io_capability(struct bthost *bthost, uint8_t io_capability); +void bthost_set_auth_req(struct bthost *bthost, uint8_t auth_req); +void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject); + +typedef void (*bthost_rfcomm_connect_cb) (uint16_t handle, uint16_t cid, + void *user_data, bool status); + +void bthost_add_rfcomm_server(struct bthost *bthost, uint8_t channel, + bthost_rfcomm_connect_cb func, void *user_data); -void bthost_set_server_psm(struct bthost *bthost, uint16_t psm); +bool bthost_connect_rfcomm(struct bthost *bthost, uint16_t handle, + uint8_t channel, bthost_rfcomm_connect_cb func, + void *user_data); + +typedef void (*bthost_rfcomm_chan_hook_func_t) (const void *data, uint16_t len, + void *user_data); + +void bthost_add_rfcomm_chan_hook(struct bthost *bthost, uint16_t handle, + uint8_t channel, + bthost_rfcomm_chan_hook_func_t func, + void *user_data); + +void bthost_send_rfcomm_data(struct bthost *bthost, uint16_t handle, + uint8_t channel, const void *data, + uint16_t len); void bthost_start(struct bthost *bthost); void bthost_stop(struct bthost *bthost); + +/* LE SMP support */ + +void *smp_start(struct bthost *bthost); +void smp_stop(void *smp_data); +void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia, + const uint8_t *ra, bool conn_init); +void smp_conn_del(void *conn_data); +void smp_data(void *conn_data, const void *data, uint16_t len); +int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk); +void smp_pair(void *conn_data); diff --git a/emulator/hfp.c b/emulator/hfp.c new file mode 100644 index 0000000..928a729 --- /dev/null +++ b/emulator/hfp.c @@ -0,0 +1,119 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have 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 +#endif + +#include +#include +#include +#include +#include +#include + +#include "monitor/mainloop.h" +#include "src/shared/hfp.h" + +static void hfp_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + printf("%s%s\n", prefix, str); +} + +static void command_handler(const char *command, void *user_data) +{ + struct hfp_gw *hfp = user_data; + + printf("Command: %s\n", command); + + hfp_gw_send_result(hfp, HFP_RESULT_ERROR); +} + +static bool open_connection(void) +{ + static const char SOCKET_PATH[] = "\0hfp-headset"; + struct hfp_gw *hfp; + struct sockaddr_un addr; + int fd; + + fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("Failed to create Unix socket"); + return false; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SOCKET_PATH, sizeof(SOCKET_PATH)); + + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to connect Unix socket"); + close(fd); + return false; + } + + hfp = hfp_gw_new(fd); + if (!hfp) { + close(fd); + return false; + } + + hfp_gw_set_close_on_unref(hfp, true); + + hfp_gw_set_debug(hfp, hfp_debug, "HFP: ", NULL); + + hfp_gw_set_command_handler(hfp, command_handler, hfp, NULL); + + return true; +} + +static void signal_callback(int signum, void *user_data) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + mainloop_quit(); + break; + } +} + +int main(int argc, char *argv[]) +{ + sigset_t mask; + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + if (!open_connection()) + return EXIT_FAILURE; + + return mainloop_run(); +} diff --git a/emulator/le.c b/emulator/le.c new file mode 100644 index 0000000..0a1b00d --- /dev/null +++ b/emulator/le.c @@ -0,0 +1,809 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "src/shared/util.h" +#include "src/shared/crypto.h" +#include "monitor/mainloop.h" +#include "monitor/bt.h" + +#include "le.h" + +#define WHITE_LIST_SIZE 16 + +struct bt_le { + volatile int ref_count; + int vhci_fd; + struct bt_crypto *crypto; + + uint8_t event_mask[16]; + uint16_t manufacturer; + uint8_t commands[64]; + uint8_t features[8]; + uint8_t bdaddr[6]; + + uint8_t le_event_mask[8]; + uint16_t le_mtu; + uint8_t le_max_pkt; + uint8_t le_features[8]; + uint8_t le_random_addr[6]; + uint16_t le_adv_min_interval; + uint16_t le_adv_max_interval; + uint8_t le_adv_type; + uint8_t le_adv_own_addr_type; + uint8_t le_adv_direct_addr_type; + uint8_t le_adv_direct_addr[6]; + uint8_t le_adv_channel_map; + uint8_t le_adv_filter_policy; + int8_t le_adv_tx_power; + uint8_t le_adv_data_len; + uint8_t le_adv_data[31]; + uint8_t le_scan_rsp_data_len; + uint8_t le_scan_rsp_data[31]; + uint8_t le_adv_enable; + + uint8_t le_white_list_size; + uint8_t le_states[8]; +}; + +static void reset_defaults(struct bt_le *hci) +{ + memset(hci->event_mask, 0, sizeof(hci->event_mask)); + hci->event_mask[0] |= 0x10; /* Disconnection Complete */ + hci->event_mask[0] |= 0x80; /* Encryption Change */ + hci->event_mask[1] |= 0x08; /* Read Remote Version Information Complete */ + hci->event_mask[1] |= 0x20; /* Command Complete */ + hci->event_mask[1] |= 0x40; /* Command Status */ + hci->event_mask[1] |= 0x80; /* Hardware Error */ + hci->event_mask[2] |= 0x04; /* Number of Completed Packets */ + hci->event_mask[3] |= 0x02; /* Data Buffer Overflow */ + hci->event_mask[5] |= 0x80; /* Encryption Key Refresh Complete */ + + hci->manufacturer = 0x003f; /* Bluetooth SIG (63) */ + + memset(hci->commands, 0, sizeof(hci->commands)); + //hci->commands[0] |= 0x20; /* Disconnect */ + //hci->commands[2] |= 0x80; /* Read Remote Version Information */ + hci->commands[5] |= 0x40; /* Set Event Mask */ + hci->commands[5] |= 0x80; /* Reset */ + //hci->commands[10] |= 0x04; /* Read Transmit Power Level */ + hci->commands[14] |= 0x08; /* Read Local Version Information */ + hci->commands[14] |= 0x10; /* Read Local Supported Commands */ + hci->commands[14] |= 0x20; /* Read Local Supported Features */ + hci->commands[14] |= 0x80; /* Read Buffer Size */ + hci->commands[15] |= 0x02; /* Read BD ADDR */ + //hci->commands[15] |= 0x20; /* Read RSSI */ + hci->commands[25] |= 0x01; /* LE Set Event Mask */ + hci->commands[25] |= 0x02; /* LE Read Buffer Size */ + hci->commands[25] |= 0x04; /* LE Read Local Supported Features */ + hci->commands[25] |= 0x10; /* LE Set Random Address */ + hci->commands[25] |= 0x20; /* LE Set Advertising Parameters */ + hci->commands[25] |= 0x40; /* LE Read Advertising Channel TX Power */ + hci->commands[25] |= 0x80; /* LE Set Advertising Data */ + hci->commands[26] |= 0x01; /* LE Set Scan Response Data */ + hci->commands[26] |= 0x02; /* LE Set Advertise Enable */ + //hci->commands[26] |= 0x04; /* LE Set Scan Parameters */ + //hci->commands[26] |= 0x08; /* LE Set Scan Enable */ + //hci->commands[26] |= 0x10; /* LE Create Connection */ + //hci->commands[26] |= 0x20; /* LE Create Connection Cancel */ + hci->commands[26] |= 0x40; /* LE Read White List Size */ + hci->commands[26] |= 0x80; /* LE Clear White List */ + //hci->commands[27] |= 0x01; /* LE Add Device To White List */ + //hci->commands[27] |= 0x02; /* LE Remove Device From White List */ + //hci->commands[27] |= 0x04; /* LE Connection Update */ + //hci->commands[27] |= 0x08; /* LE Set Host Channel Classification */ + //hci->commands[27] |= 0x10; /* LE Read Channel Map */ + //hci->commands[27] |= 0x20; /* LE Read Remote Used Features */ + hci->commands[27] |= 0x40; /* LE Encrypt */ + hci->commands[27] |= 0x80; /* LE Rand */ + //hci->commands[28] |= 0x01; /* LE Start Encryption */ + //hci->commands[28] |= 0x02; /* LE Long Term Key Request Reply */ + //hci->commands[28] |= 0x04; /* LE Long Term Key Request Negative Reply */ + hci->commands[28] |= 0x08; /* LE Read Supported States */ + //hci->commands[28] |= 0x10; /* LE Receiver Test */ + //hci->commands[28] |= 0x20; /* LE Transmitter Test */ + //hci->commands[28] |= 0x40; /* LE Test End */ + //hci->commands[33] |= 0x10; /* LE Remote Connection Parameter Request Reply */ + //hci->commands[33] |= 0x20; /* LE Remote Connection Parameter Request Negative Reply */ + + memset(hci->features, 0, sizeof(hci->features)); + hci->features[4] |= 0x20; /* BR/EDR Not Supported */ + hci->features[4] |= 0x40; /* LE Supported */ + + memset(hci->bdaddr, 0, sizeof(hci->bdaddr)); + + memset(hci->le_event_mask, 0, sizeof(hci->le_event_mask)); + hci->le_event_mask[0] |= 0x01; /* LE Connection Complete */ + hci->le_event_mask[0] |= 0x02; /* LE Advertising Report */ + hci->le_event_mask[0] |= 0x04; /* LE Connection Update Complete */ + hci->le_event_mask[0] |= 0x08; /* LE Read Remote Used Features Complete */ + hci->le_event_mask[0] |= 0x10; /* LE Long Term Key Request */ + //hci->le_event_mask[0] |= 0x20; /* LE Remote Connection Parameter Request */ + + hci->le_mtu = 64; + hci->le_max_pkt = 1; + + memset(hci->le_features, 0, sizeof(hci->le_features)); + hci->le_features[0] |= 0x01; /* LE Encryption */ + //hci->le_features[0] |= 0x02; /* Connection Parameter Request Procedure */ + //hci->le_features[0] |= 0x04; /* Extended Reject Indication */ + //hci->le_features[0] |= 0x08; /* Slave-initiated Features Exchange */ + //hci->le_features[0] |= 0x10; /* LE Ping */ + + memset(hci->le_random_addr, 0, sizeof(hci->le_random_addr)); + + hci->le_adv_min_interval = 0x0800; + hci->le_adv_max_interval = 0x0800; + hci->le_adv_type = 0x00; + hci->le_adv_own_addr_type = 0x00; + hci->le_adv_direct_addr_type = 0x00; + memset(hci->le_adv_direct_addr, 0, 6); + hci->le_adv_channel_map = 0x07; + hci->le_adv_filter_policy = 0x00; + + hci->le_adv_tx_power = 0; + + memset(hci->le_adv_data, 0, sizeof(hci->le_adv_data)); + hci->le_adv_data_len = 0; + + memset(hci->le_scan_rsp_data, 0, sizeof(hci->le_scan_rsp_data)); + hci->le_scan_rsp_data_len = 0; + + hci->le_adv_enable = 0x00; + + hci->le_white_list_size = WHITE_LIST_SIZE; + + memset(hci->le_states, 0, sizeof(hci->le_states)); + hci->le_states[0] |= 0x01; /* Non-connectable Advertising */ + hci->le_states[0] |= 0x02; /* Scannable Advertising */ + hci->le_states[0] |= 0x04; /* Connectable Advertising */ + hci->le_states[0] |= 0x08; /* Directed Advertising */ + hci->le_states[0] |= 0x10; /* Passive Scanning */ + hci->le_states[0] |= 0x20; /* Active Scanning */ + hci->le_states[0] |= 0x40; /* Initiating */ + hci->le_states[0] |= 0x80; /* Connection */ +} + +static void send_event(struct bt_le *hci, uint8_t event, + void *data, uint8_t size) +{ + uint8_t type = BT_H4_EVT_PKT; + struct bt_hci_evt_hdr hdr; + struct iovec iov[3]; + int iovcnt; + + hdr.evt = event; + hdr.plen = size; + + iov[0].iov_base = &type; + iov[0].iov_len = 1; + iov[1].iov_base = &hdr; + iov[1].iov_len = sizeof(hdr); + + if (size > 0) { + iov[2].iov_base = data; + iov[2].iov_len = size; + iovcnt = 3; + } else + iovcnt = 2; + + if (writev(hci->vhci_fd, iov, iovcnt) < 0) + fprintf(stderr, "Write to /dev/vhci failed (%m)\n"); +} + +static void cmd_complete(struct bt_le *hci, uint16_t opcode, + const void *data, uint8_t len) +{ + struct bt_hci_evt_cmd_complete *cc; + void *pkt_data; + + pkt_data = alloca(sizeof(*cc) + len); + if (!pkt_data) + return; + + cc = pkt_data; + cc->ncmd = 0x01; + cc->opcode = cpu_to_le16(opcode); + + if (len > 0) + memcpy(pkt_data + sizeof(*cc), data, len); + + send_event(hci, BT_HCI_EVT_CMD_COMPLETE, pkt_data, sizeof(*cc) + len); +} + +static void cmd_status(struct bt_le *hci, uint8_t status, uint16_t opcode) +{ + struct bt_hci_evt_cmd_status cs; + + cs.status = status; + cs.ncmd = 0x01; + cs.opcode = cpu_to_le16(opcode); + + send_event(hci, BT_HCI_EVT_CMD_STATUS, &cs, sizeof(cs)); +} + +static void cmd_set_event_mask(struct bt_le *hci, + const void *data, uint8_t size) +{ + const struct bt_hci_cmd_set_event_mask *cmd = data; + uint8_t status; + + memcpy(hci->event_mask, cmd->mask, 8); + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_SET_EVENT_MASK, &status, sizeof(status)); +} + +static void cmd_reset(struct bt_le *hci, const void *data, uint8_t size) +{ + uint8_t status; + + reset_defaults(hci); + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_RESET, &status, sizeof(status)); +} + +static void cmd_read_local_version(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_read_local_version rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + rsp.hci_ver = 0x06; + rsp.hci_rev = cpu_to_le16(0x0000); + rsp.lmp_ver = 0x06; + rsp.manufacturer = cpu_to_le16(hci->manufacturer); + rsp.lmp_subver = cpu_to_le16(0x0000); + + cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_VERSION, &rsp, sizeof(rsp)); +} + +static void cmd_read_local_commands(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_read_local_commands rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(rsp.commands, hci->commands, 64); + + cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_COMMANDS, &rsp, sizeof(rsp)); +} + +static void cmd_read_local_features(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_read_local_features rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(rsp.features, hci->features, 8); + + cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_FEATURES, &rsp, sizeof(rsp)); +} + +static void cmd_read_buffer_size(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_read_buffer_size rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + rsp.acl_mtu = cpu_to_le16(0x0000); + rsp.sco_mtu = 0x00; + rsp.acl_max_pkt = cpu_to_le16(0x0000); + rsp.sco_max_pkt = cpu_to_le16(0x0000); + + cmd_complete(hci, BT_HCI_CMD_READ_BUFFER_SIZE, &rsp, sizeof(rsp)); +} + +static void cmd_read_bd_addr(struct bt_le *hci, const void *data, uint8_t size) +{ + struct bt_hci_rsp_read_bd_addr rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(rsp.bdaddr, hci->bdaddr, 6); + + cmd_complete(hci, BT_HCI_CMD_READ_BD_ADDR, &rsp, sizeof(rsp)); +} + +static void cmd_le_set_event_mask(struct bt_le *hci, + const void *data, uint8_t size) +{ + const struct bt_hci_cmd_le_set_event_mask *cmd = data; + uint8_t status; + + memcpy(hci->le_event_mask, cmd->mask, 8); + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_LE_SET_EVENT_MASK, + &status, sizeof(status)); +} + +static void cmd_le_read_buffer_size(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_le_read_buffer_size rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + rsp.le_mtu = cpu_to_le16(hci->le_mtu); + rsp.le_max_pkt = hci->le_max_pkt; + + cmd_complete(hci, BT_HCI_CMD_LE_READ_BUFFER_SIZE, &rsp, sizeof(rsp)); +} + +static void cmd_le_read_local_features(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_le_read_local_features rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(rsp.features, hci->le_features, 8); + + cmd_complete(hci, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, + &rsp, sizeof(rsp)); +} + +static void cmd_le_set_random_address(struct bt_le *hci, + const void *data, uint8_t size) +{ + const struct bt_hci_cmd_le_set_random_address *cmd = data; + uint8_t status; + + memcpy(hci->le_random_addr, cmd->addr, 6); + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, + &status, sizeof(status)); +} + +static void cmd_le_set_adv_parameters(struct bt_le *hci, + const void *data, uint8_t size) +{ + const struct bt_hci_cmd_le_set_adv_parameters *cmd = data; + uint16_t min_interval, max_interval; + uint8_t status; + + if (hci->le_adv_enable == 0x01) { + cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + + min_interval = le16_to_cpu(cmd->min_interval); + max_interval = le16_to_cpu(cmd->max_interval); + + /* Valid range for advertising type is 0x00 to 0x03 */ + switch (cmd->type) { + case 0x00: /* ADV_IND */ + /* Range for advertising interval min is 0x0020 to 0x4000 */ + if (min_interval < 0x0020 || min_interval > 0x4000) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + /* Range for advertising interval max is 0x0020 to 0x4000 */ + if (max_interval < 0x0020 || max_interval > 0x4000) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + /* Advertising interval max shall be less or equal */ + if (min_interval > max_interval) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + break; + + case 0x01: /* ADV_DIRECT_IND */ + /* Range for direct address type is 0x00 to 0x01 */ + if (cmd->direct_addr_type > 0x01) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + break; + + case 0x02: /* ADV_SCAN_IND */ + case 0x03: /* ADV_NONCONN_IND */ + /* Range for advertising interval min is 0x00a0 to 0x4000 */ + if (min_interval < 0x00a0 || min_interval > 0x4000) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + /* Range for advertising interval max is 0x00a0 to 0x4000 */ + if (max_interval < 0x00a0 || max_interval > 0x4000) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + /* Advertising interval min shall be less or equal */ + if (min_interval > max_interval) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + break; + + default: + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + + /* Valid range for own address type is 0x00 to 0x01 */ + if (cmd->own_addr_type > 0x01) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + + /* Valid range for advertising channel map is 0x01 to 0x07 */ + if (cmd->channel_map < 0x01 || cmd->channel_map > 0x07) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + + /* Valid range for advertising filter policy is 0x00 to 0x03 */ + if (cmd->filter_policy > 0x03) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_PARAMETERS); + return; + } + + hci->le_adv_min_interval = min_interval; + hci->le_adv_max_interval = max_interval; + hci->le_adv_type = cmd->type; + hci->le_adv_own_addr_type = cmd->own_addr_type; + hci->le_adv_direct_addr_type = cmd->direct_addr_type; + memcpy(hci->le_adv_direct_addr, cmd->direct_addr, 6); + hci->le_adv_channel_map = cmd->channel_map; + hci->le_adv_filter_policy = cmd->filter_policy; + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + &status, sizeof(status)); +} + +static void cmd_le_read_adv_tx_power(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_le_read_adv_tx_power rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + rsp.level = hci->le_adv_tx_power; + + cmd_complete(hci, BT_HCI_CMD_LE_READ_ADV_TX_POWER, &rsp, sizeof(rsp)); +} + +static void cmd_le_set_adv_data(struct bt_le *hci, + const void *data, uint8_t size) +{ + const struct bt_hci_cmd_le_set_adv_data *cmd = data; + uint8_t status; + + /* Valid range for advertising data length is 0x00 to 0x1f */ + if (cmd->len > 0x1f) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_DATA); + return; + } + + hci->le_adv_data_len = cmd->len; + memcpy(hci->le_adv_data, cmd->data, 31); + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_DATA, &status, sizeof(status)); +} + +static void cmd_le_set_scan_rsp_data(struct bt_le *hci, + const void *data, uint8_t size) +{ + const struct bt_hci_cmd_le_set_scan_rsp_data *cmd = data; + uint8_t status; + + /* Valid range for scan response data length is 0x00 to 0x1f */ + if (cmd->len > 0x1f) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_SCAN_RSP_DATA); + return; + } + + hci->le_scan_rsp_data_len = cmd->len; + memcpy(hci->le_scan_rsp_data, cmd->data, 31); + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_LE_SET_SCAN_RSP_DATA, + &status, sizeof(status)); +} + +static void cmd_le_set_adv_enable(struct bt_le *hci, + const void *data, uint8_t size) +{ + const struct bt_hci_cmd_le_set_adv_enable *cmd = data; + uint8_t status; + + /* Valid range for advertising enable is 0x00 to 0x01 */ + if (cmd->enable > 0x01) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, + BT_HCI_CMD_LE_SET_ADV_ENABLE); + return; + } + + if (cmd->enable == hci->le_adv_enable) { + cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, + BT_HCI_CMD_LE_SET_ADV_ENABLE); + return; + } + + hci->le_adv_enable = cmd->enable; + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_ENABLE, + &status, sizeof(status)); +} + +static void cmd_le_read_white_list_size(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_le_read_white_list_size rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + rsp.size = hci->le_white_list_size; + + cmd_complete(hci, BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE, + &rsp, sizeof(rsp)); +} + +static void cmd_le_clear_white_list(struct bt_le *hci, + const void *data, uint8_t size) +{ + uint8_t status; + + status = BT_HCI_ERR_SUCCESS; + cmd_complete(hci, BT_HCI_CMD_LE_CLEAR_WHITE_LIST, + &status, sizeof(status)); +} + +static void cmd_le_encrypt(struct bt_le *hci, const void *data, uint8_t size) +{ + const struct bt_hci_cmd_le_encrypt *cmd = data; + struct bt_hci_rsp_le_encrypt rsp; + + if (!bt_crypto_e(hci->crypto, cmd->key, cmd->plaintext, rsp.data)) { + cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, + BT_HCI_CMD_LE_ENCRYPT); + return; + } + + rsp.status = BT_HCI_ERR_SUCCESS; + + cmd_complete(hci, BT_HCI_CMD_LE_ENCRYPT, &rsp, sizeof(rsp)); +} + +static void cmd_le_rand(struct bt_le *hci, const void *data, uint8_t size) +{ + struct bt_hci_rsp_le_rand rsp; + uint8_t value[8]; + + if (!bt_crypto_random_bytes(hci->crypto, value, 8)) { + cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED, + BT_HCI_CMD_LE_RAND); + return; + } + + rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(&rsp.number, value, 8); + + cmd_complete(hci, BT_HCI_CMD_LE_RAND, &rsp, sizeof(rsp)); +} + +static void cmd_le_read_supported_states(struct bt_le *hci, + const void *data, uint8_t size) +{ + struct bt_hci_rsp_le_read_supported_states rsp; + + rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(rsp.states, hci->le_states, 8); + + cmd_complete(hci, BT_HCI_CMD_LE_READ_SUPPORTED_STATES, + &rsp, sizeof(rsp)); +} + +static const struct { + uint16_t opcode; + void (*func) (struct bt_le *hci, const void *data, uint8_t size); + uint8_t size; + bool fixed; +} cmd_table[] = { + { BT_HCI_CMD_SET_EVENT_MASK, cmd_set_event_mask, 8, true }, + { BT_HCI_CMD_RESET, cmd_reset, 0, true }, + { BT_HCI_CMD_READ_LOCAL_VERSION, cmd_read_local_version, 0, true }, + { BT_HCI_CMD_READ_LOCAL_COMMANDS, cmd_read_local_commands, 0, true }, + { BT_HCI_CMD_READ_LOCAL_FEATURES, cmd_read_local_features, 0, true }, + { BT_HCI_CMD_READ_BUFFER_SIZE, cmd_read_buffer_size, 0, true }, + { BT_HCI_CMD_READ_BD_ADDR, cmd_read_bd_addr, 0, true }, + + { BT_HCI_CMD_LE_SET_EVENT_MASK, + cmd_le_set_event_mask, 8, true }, + { BT_HCI_CMD_LE_READ_BUFFER_SIZE, + cmd_le_read_buffer_size, 0, true }, + { BT_HCI_CMD_LE_READ_LOCAL_FEATURES, + cmd_le_read_local_features, 0, true }, + { BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, + cmd_le_set_random_address, 6, true }, + { BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + cmd_le_set_adv_parameters, 15, true }, + { BT_HCI_CMD_LE_READ_ADV_TX_POWER, + cmd_le_read_adv_tx_power, 0, true }, + { BT_HCI_CMD_LE_SET_ADV_DATA, + cmd_le_set_adv_data, 32, true }, + { BT_HCI_CMD_LE_SET_SCAN_RSP_DATA, + cmd_le_set_scan_rsp_data, 32, true }, + { BT_HCI_CMD_LE_SET_ADV_ENABLE, + cmd_le_set_adv_enable, 1, true }, + + { BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE, + cmd_le_read_white_list_size, 0, true }, + { BT_HCI_CMD_LE_CLEAR_WHITE_LIST, + cmd_le_clear_white_list, 0, true }, + + { BT_HCI_CMD_LE_ENCRYPT, cmd_le_encrypt, 32, true }, + { BT_HCI_CMD_LE_RAND, cmd_le_rand, 0, true }, + + { BT_HCI_CMD_LE_READ_SUPPORTED_STATES, + cmd_le_read_supported_states, 0, true }, + + { } +}; + +static void process_command(struct bt_le *hci, const void *data, size_t size) +{ + const struct bt_hci_cmd_hdr *hdr = data; + uint16_t opcode; + unsigned int i; + + if (size < sizeof(*hdr)) + return; + + data += sizeof(*hdr); + size -= sizeof(*hdr); + + opcode = le16_to_cpu(hdr->opcode); + + if (hdr->plen != size) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, opcode); + return; + } + + for (i = 0; cmd_table[i].func; i++) { + if (cmd_table[i].opcode != opcode) + continue; + + if ((cmd_table[i].fixed && size != cmd_table[i].size) || + size < cmd_table[i].size) { + cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, opcode); + return; + } + + cmd_table[i].func(hci, data, size); + return; + } + + cmd_status(hci, BT_HCI_ERR_UNKNOWN_COMMAND, opcode); +} + +static void vhci_read_callback(int fd, uint32_t events, void *user_data) +{ + struct bt_le *hci = user_data; + unsigned char buf[4096]; + ssize_t len; + + if (events & (EPOLLERR | EPOLLHUP)) + return; + + len = read(hci->vhci_fd, buf, sizeof(buf)); + if (len < 1) + return; + + switch (buf[0]) { + case BT_H4_CMD_PKT: + process_command(hci, buf + 1, len - 1); + break; + } +} + +struct bt_le *bt_le_new(void) +{ + unsigned char setup_cmd[2]; + struct bt_le *hci; + + hci = calloc(1, sizeof(*hci)); + if (!hci) + return NULL; + + reset_defaults(hci); + + hci->vhci_fd = open("/dev/vhci", O_RDWR); + if (hci->vhci_fd < 0) { + free(hci); + return NULL; + } + + setup_cmd[0] = HCI_VENDOR_PKT; + setup_cmd[1] = HCI_BREDR; + + if (write(hci->vhci_fd, setup_cmd, sizeof(setup_cmd)) < 0) { + close(hci->vhci_fd); + free(hci); + return NULL; + } + + mainloop_add_fd(hci->vhci_fd, EPOLLIN, vhci_read_callback, hci, NULL); + + hci->crypto = bt_crypto_new(); + + return bt_le_ref(hci); +} + +struct bt_le *bt_le_ref(struct bt_le *hci) +{ + if (!hci) + return NULL; + + __sync_fetch_and_add(&hci->ref_count, 1); + + return hci; +} + +void bt_le_unref(struct bt_le *hci) +{ + if (!hci) + return; + + if (__sync_sub_and_fetch(&hci->ref_count, 1)) + return; + + bt_crypto_unref(hci->crypto); + + mainloop_remove_fd(hci->vhci_fd); + + close(hci->vhci_fd); + + free(hci); +} diff --git a/emulator/le.h b/emulator/le.h new file mode 100644 index 0000000..5e832e8 --- /dev/null +++ b/emulator/le.h @@ -0,0 +1,32 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * 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 + +struct bt_le; + +struct bt_le *bt_le_new(void); + +struct bt_le *bt_le_ref(struct bt_le *le); +void bt_le_unref(struct bt_le *le); diff --git a/emulator/main.c b/emulator/main.c index 85b10f1..bd2a29a 100644 --- a/emulator/main.c +++ b/emulator/main.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -35,6 +35,7 @@ #include "server.h" #include "vhci.h" #include "amp.h" +#include "le.h" static void signal_callback(int signum, void *user_data) { @@ -66,6 +67,7 @@ static const struct option main_options[] = { { "le", no_argument, NULL, 'L' }, { "bredr", no_argument, NULL, 'B' }, { "amp", no_argument, NULL, 'A' }, + { "letest", optional_argument, NULL, 'U' }, { "amptest", optional_argument, NULL, 'T' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, @@ -80,6 +82,7 @@ int main(int argc, char *argv[]) struct server *server4; struct server *server5; bool server_enabled = false; + int letest_count = 0; int amptest_count = 0; int vhci_count = 0; enum vhci_type vhci_type = VHCI_TYPE_BREDRLE; @@ -91,7 +94,8 @@ int main(int argc, char *argv[]) for (;;) { int opt; - opt = getopt_long(argc, argv, "sl::LBATvh", main_options, NULL); + opt = getopt_long(argc, argv, "sl::LBAUTvh", + main_options, NULL); if (opt < 0) break; @@ -114,6 +118,12 @@ int main(int argc, char *argv[]) case 'A': vhci_type = VHCI_TYPE_AMP; break; + case 'U': + if (optarg) + letest_count = atoi(optarg); + else + letest_count = 1; + break; case 'T': if (optarg) amptest_count = atoi(optarg); @@ -131,7 +141,8 @@ int main(int argc, char *argv[]) } } - if (amptest_count < 1 && vhci_count < 1 && !server_enabled) { + if (letest_count < 1 && amptest_count < 1 && + vhci_count < 1 && !server_enabled) { fprintf(stderr, "No emulator specified\n"); return EXIT_FAILURE; } @@ -144,6 +155,16 @@ int main(int argc, char *argv[]) printf("Bluetooth emulator ver %s\n", VERSION); + for (i = 0; i < letest_count; i++) { + struct bt_le *le; + + le = bt_le_new(); + if (!le) { + fprintf(stderr, "Failed to create LE controller\n"); + return EXIT_FAILURE; + } + } + for (i = 0; i < amptest_count; i++) { struct bt_amp *amp; diff --git a/emulator/server.c b/emulator/server.c index b2a4b02..f3c82d3 100644 --- a/emulator/server.c +++ b/emulator/server.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/emulator/server.h b/emulator/server.h index f0b3727..bf725e7 100644 --- a/emulator/server.h +++ b/emulator/server.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/emulator/smp.c b/emulator/smp.c new file mode 100644 index 0000000..32c82e5 --- /dev/null +++ b/emulator/smp.c @@ -0,0 +1,276 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation + * + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bluetooth/bluetooth.h" +#include "bluetooth/hci.h" + +#include "src/shared/crypto.h" +#include "monitor/bt.h" +#include "bthost.h" + +#define SMP_CID 0x0006 + +struct smp { + struct bthost *bthost; + struct smp_conn *conn; + struct bt_crypto *crypto; +}; + +struct smp_conn { + struct smp *smp; + uint16_t handle; + bool out; + uint8_t ia[6]; + uint8_t ia_type; + uint8_t ra[6]; + uint8_t ra_type; + uint8_t tk[16]; + uint8_t prnd[16]; + uint8_t rrnd[16]; + uint8_t pcnf[16]; + uint8_t preq[7]; + uint8_t prsp[7]; + uint8_t ltk[16]; +}; + +static bool verify_random(struct smp_conn *conn, const uint8_t rnd[16]) +{ + uint8_t confirm[16]; + + if (!bt_crypto_c1(conn->smp->crypto, conn->tk, conn->rrnd, conn->prsp, + conn->preq, conn->ia_type, conn->ia, + conn->ra_type, conn->ra, confirm)) + return false; + + if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf) != 0)) { + printf("Confirmation values don't match\n"); + return false; + } + + if (conn->out) { + bt_crypto_s1(conn->smp->crypto, conn->tk, conn->rrnd, + conn->prnd, conn->ltk); + bthost_le_start_encrypt(conn->smp->bthost, conn->handle, + conn->ltk); + } else { + bt_crypto_s1(conn->smp->crypto, conn->tk, conn->prnd, + conn->rrnd, conn->ltk); + } + + return true; +} + +static void pairing_req(struct smp_conn *conn, const void *data, uint16_t len) +{ + struct bthost *bthost = conn->smp->bthost; + static const uint8_t rsp[] = { 0x02, /* Pairing Response */ + 0x03, /* NoInputNoOutput */ + 0x00, /* OOB Flag */ + 0x01, /* Bonding - no MITM */ + 0x10, /* Max key size */ + 0x00, /* Init. key dist. */ + 0x01, /* Rsp. key dist. */ + }; + + memcpy(conn->preq, data, sizeof(conn->preq)); + memcpy(conn->prsp, rsp, sizeof(rsp)); + + bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp)); +} + +static void pairing_rsp(struct smp_conn *conn, const void *data, uint16_t len) +{ + memcpy(conn->prsp, data, sizeof(conn->prsp)); + + /*bthost_send_cid(bthost, handle, SMP_CID, pdu, req->send_len);*/ +} + +static void pairing_cfm(struct smp_conn *conn, const void *data, uint16_t len) +{ + struct bthost *bthost = conn->smp->bthost; + const uint8_t *cfm = data; + uint8_t rsp[17]; + + memcpy(conn->pcnf, data + 1, 16); + + rsp[0] = cfm[0]; + bt_crypto_c1(conn->smp->crypto, conn->tk, conn->prnd, conn->prsp, + conn->preq, conn->ia_type, conn->ia, + conn->ra_type, conn->ra, &rsp[1]); + + bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp)); +} + +static void pairing_rnd(struct smp_conn *conn, const void *data, uint16_t len) +{ + struct bthost *bthost = conn->smp->bthost; + const uint8_t *rnd = data; + uint8_t rsp[17]; + + memcpy(conn->rrnd, data + 1, 16); + + if (!verify_random(conn, data + 1)) + return; + + rsp[0] = rnd[0]; + memcpy(&rsp[1], conn->prnd, 16); + + bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp)); +} + +void smp_pair(void *conn_data) +{ + struct smp_conn *conn = conn_data; + struct bthost *bthost = conn->smp->bthost; + const uint8_t smp_pair_req[] = { 0x01, /* Pairing Request */ + 0x03, /* NoInputNoOutput */ + 0x00, /* OOB Flag */ + 0x01, /* Bonding - no MITM */ + 0x10, /* Max key size */ + 0x00, /* Init. key dist. */ + 0x01, /* Rsp. key dist. */ + }; + + memcpy(conn->preq, smp_pair_req, sizeof(smp_pair_req)); + + bthost_send_cid(bthost, conn->handle, SMP_CID, smp_pair_req, + sizeof(smp_pair_req)); +} + +void smp_data(void *conn_data, const void *data, uint16_t len) +{ + struct smp_conn *conn = conn_data; + uint8_t opcode; + + if (len < 1) { + printf("Received too small SMP PDU\n"); + return; + } + + opcode = *((const uint8_t *) data); + + switch (opcode) { + case 0x01: /* Pairing Request */ + pairing_req(conn, data, len); + break; + case 0x02: /* Pairing Response */ + pairing_rsp(conn, data, len); + break; + case 0x03: /* Pairing Confirm */ + pairing_cfm(conn, data, len); + break; + case 0x04: /* Pairing Random */ + pairing_rnd(conn, data, len); + break; + default: + break; + } +} + +int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk) +{ + struct smp_conn *conn = smp_data; + static const uint8_t no_ltk[16] = { 0 }; + + if (!memcmp(conn->ltk, no_ltk, 16)) + return -ENOENT; + + memcpy(ltk, conn->ltk, 16); + + return 0; +} + +void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia, + const uint8_t *ra, bool conn_init) +{ + struct smp *smp = smp_data; + struct smp_conn *conn; + + conn = malloc(sizeof(struct smp_conn)); + if (!conn) + return NULL; + + memset(conn, 0, sizeof(*conn)); + + conn->smp = smp; + conn->handle = handle; + conn->out = conn_init; + + conn->ia_type = LE_PUBLIC_ADDRESS; + conn->ra_type = LE_PUBLIC_ADDRESS; + memcpy(conn->ia, ia, 6); + memcpy(conn->ra, ra, 6); + + return conn; +} + +void smp_conn_del(void *conn_data) +{ + struct smp_conn *conn = conn_data; + + free(conn); +} + +void *smp_start(struct bthost *bthost) +{ + struct smp *smp; + + smp = malloc(sizeof(struct smp)); + if (!smp) + return NULL; + + memset(smp, 0, sizeof(*smp)); + + smp->crypto = bt_crypto_new(); + if (!smp->crypto) { + free(smp); + return NULL; + } + + smp->bthost = bthost; + + return smp; +} + +void smp_stop(void *smp_data) +{ + struct smp *smp = smp_data; + + bt_crypto_unref(smp->crypto); + + free(smp); +} diff --git a/emulator/vhci.c b/emulator/vhci.c index d32d5e5..00c6118 100644 --- a/emulator/vhci.c +++ b/emulator/vhci.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/emulator/vhci.h b/emulator/vhci.h index b9ae63f..1ec7191 100644 --- a/emulator/vhci.h +++ b/emulator/vhci.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/gdbus/client.c b/gdbus/client.c index be8cc29..eb68a0f 100644 --- a/gdbus/client.c +++ b/gdbus/client.c @@ -51,11 +51,14 @@ struct GDBusClient { GDBusWatchFunction connect_func; void *connect_data; GDBusWatchFunction disconn_func; + gboolean connected; void *disconn_data; GDBusMessageFunction signal_func; void *signal_data; GDBusProxyFunction proxy_added; GDBusProxyFunction proxy_removed; + GDBusClientFunction ready; + void *ready_data; GDBusPropertyFunction property_changed; void *user_data; GList *proxy_list; @@ -725,6 +728,93 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, return TRUE; } +gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy, + const char *name, int type, const void *value, + size_t size, GDBusResultFunction function, + void *user_data, GDBusDestroyFunction destroy) +{ + struct set_property_data *data; + GDBusClient *client; + DBusMessage *msg; + DBusMessageIter iter, variant, array; + DBusPendingCall *call; + char array_sig[3]; + char type_sig[2]; + + if (!proxy || !name || !value) + return FALSE; + + if (!dbus_type_is_basic(type)) + return FALSE; + + client = proxy->client; + if (!client) + return FALSE; + + data = g_try_new0(struct set_property_data, 1); + if (!data) + return FALSE; + + data->function = function; + data->user_data = user_data; + data->destroy = destroy; + + msg = dbus_message_new_method_call(client->service_name, + proxy->obj_path, + DBUS_INTERFACE_PROPERTIES, + "Set"); + if (!msg) { + g_free(data); + return FALSE; + } + + array_sig[0] = DBUS_TYPE_ARRAY; + array_sig[1] = (char) type; + array_sig[2] = '\0'; + + type_sig[0] = (char) type; + type_sig[1] = '\0'; + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + &proxy->interface); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); + + 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)) + dbus_message_iter_append_fixed_array(&array, type, &value, + size); + else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) { + const char **str = (const char **) value; + size_t i; + + for (i = 0; i < size; i++) + dbus_message_iter_append_basic(&array, type, &str[i]); + } + + dbus_message_iter_close_container(&variant, &array); + dbus_message_iter_close_container(&iter, &variant); + + if (g_dbus_send_message_with_reply(client->dbus_conn, msg, + &call, -1) == FALSE) { + dbus_message_unref(msg); + g_free(data); + return FALSE; + } + + dbus_pending_call_set_notify(call, set_property_reply, data, g_free); + dbus_pending_call_unref(call); + + dbus_message_unref(msg); + + return TRUE; +} + struct method_call_data { GDBusReturnFunction function; void *user_data; @@ -982,6 +1072,9 @@ static void parse_managed_objects(GDBusClient *client, DBusMessage *msg) dbus_message_iter_next(&dict); } + + if (client->ready) + client->ready(client, client->ready_data); } static void get_managed_objects_reply(DBusPendingCall *call, void *user_data) @@ -1054,6 +1147,8 @@ static void service_connect(DBusConnection *conn, void *user_data) get_managed_objects(client); + client->connected = TRUE; + g_dbus_client_unref(client); } @@ -1064,8 +1159,10 @@ static void service_disconnect(DBusConnection *conn, void *user_data) g_list_free_full(client->proxy_list, proxy_free); client->proxy_list = NULL; - if (client->disconn_func) + if (client->disconn_func) { client->disconn_func(conn, client->disconn_data); + client->connected = FALSE; + } } static DBusHandlerResult message_filter(DBusConnection *connection, @@ -1118,6 +1215,7 @@ GDBusClient *g_dbus_client_new(DBusConnection *connection, client->dbus_conn = dbus_connection_ref(connection); client->service_name = g_strdup(service); client->base_path = g_strdup(path); + client->connected = FALSE; client->match_rules = g_ptr_array_sized_new(1); g_ptr_array_set_free_func(client->match_rules, g_free); @@ -1192,7 +1290,11 @@ void g_dbus_client_unref(GDBusClient *client) g_list_free_full(client->proxy_list, proxy_free); - if (client->disconn_func) + /* + * Don't call disconn_func twice if disconnection + * was previously reported. + */ + if (client->disconn_func && client->connected) client->disconn_func(client->dbus_conn, client->disconn_data); g_dbus_remove_watch(client->dbus_conn, client->watch); @@ -1243,6 +1345,18 @@ gboolean g_dbus_client_set_signal_watch(GDBusClient *client, return TRUE; } +gboolean g_dbus_client_set_ready_watch(GDBusClient *client, + GDBusClientFunction ready, void *user_data) +{ + if (client == NULL) + return FALSE; + + client->ready = ready; + client->ready_data = user_data; + + return TRUE; +} + gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, GDBusProxyFunction proxy_added, GDBusProxyFunction proxy_removed, diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h index 9542109..551c306 100644 --- a/gdbus/gdbus.h +++ b/gdbus/gdbus.h @@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy); +gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy, + const char *name, int type, const void *value, + size_t size, GDBusResultFunction function, + void *user_data, GDBusDestroyFunction destroy); + typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data); typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data); @@ -337,6 +342,7 @@ gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method, GDBusReturnFunction function, void *user_data, GDBusDestroyFunction destroy); +typedef void (* GDBusClientFunction) (GDBusClient *client, void *user_data); typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data); typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data); @@ -359,7 +365,8 @@ gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data); gboolean g_dbus_client_set_signal_watch(GDBusClient *client, GDBusMessageFunction function, void *user_data); - +gboolean g_dbus_client_set_ready_watch(GDBusClient *client, + GDBusClientFunction ready, void *user_data); gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, GDBusProxyFunction proxy_added, GDBusProxyFunction proxy_removed, diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c index 099b67f..435fb93 100644 --- a/gdbus/mainloop.c +++ b/gdbus/mainloop.c @@ -30,8 +30,6 @@ #include "gdbus.h" -#define DISPATCH_TIMEOUT 0 - #define info(fmt...) #define error(fmt...) #define debug(fmt...) @@ -70,8 +68,6 @@ static gboolean message_dispatch(void *data) { DBusConnection *conn = data; - dbus_connection_ref(conn); - /* Dispatch messages */ while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS); @@ -84,7 +80,7 @@ static inline void queue_dispatch(DBusConnection *conn, DBusDispatchStatus status) { if (status == DBUS_DISPATCH_DATA_REMAINS) - g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn); + g_idle_add(message_dispatch, dbus_connection_ref(conn)); } static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) @@ -92,9 +88,6 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) struct watch_info *info = data; unsigned int flags = 0; DBusDispatchStatus status; - DBusConnection *conn; - - conn = dbus_connection_ref(info->conn); if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE; if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE; @@ -103,10 +96,8 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) dbus_watch_handle(info->watch, flags); - status = dbus_connection_get_dispatch_status(conn); - queue_dispatch(conn, status); - - dbus_connection_unref(conn); + status = dbus_connection_get_dispatch_status(info->conn); + queue_dispatch(info->conn, status); return TRUE; } diff --git a/gdbus/object.c b/gdbus/object.c index b248cbb..13cf9a9 100644 --- a/gdbus/object.c +++ b/gdbus/object.c @@ -1253,6 +1253,8 @@ static struct generic_data *object_path_ref(DBusConnection *connection, if (!dbus_connection_register_object_path(connection, path, &generic_table, data)) { + dbus_connection_unref(data->conn); + g_free(data->path); g_free(data->introspect); g_free(data); return NULL; diff --git a/gobex/gobex-header.c b/gobex/gobex-header.c index 281f8ea..fe70c8b 100644 --- a/gobex/gobex-header.c +++ b/gobex/gobex-header.c @@ -425,7 +425,7 @@ GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len) return header; } -GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam) +GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam) { guint8 buf[1024]; gssize len; @@ -434,7 +434,12 @@ GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam) if (len < 0) return NULL; - return g_obex_header_new_bytes(G_OBEX_HDR_APPARAM, buf, len); + return g_obex_header_new_bytes(id, buf, len); +} + +GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam) +{ + return g_obex_header_new_tag(G_OBEX_HDR_APPARAM, apparam); } GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val) diff --git a/gobex/gobex-header.h b/gobex/gobex-header.h index 42a2a0c..c2f975f 100644 --- a/gobex/gobex-header.h +++ b/gobex/gobex-header.h @@ -85,6 +85,7 @@ GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str); GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len); GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val); GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val); +GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam); GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam); GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args, diff --git a/gobex/gobex-transfer.c b/gobex/gobex-transfer.c index b815d60..8498177 100644 --- a/gobex/gobex-transfer.c +++ b/gobex/gobex-transfer.c @@ -195,9 +195,11 @@ static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp, struct transfer *transfer = user_data; GObexPacket *req; gboolean rspcode, final; + guint id; g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id); + id = transfer->req_id; transfer->req_id = 0; if (err != NULL) { @@ -230,8 +232,11 @@ static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp, } else if (!g_obex_srm_active(transfer->obex)) { req = g_obex_packet_new(transfer->opcode, TRUE, G_OBEX_HDR_INVALID); - } else + } else { + /* Keep id since request still outstanting */ + transfer->req_id = id; return; + } transfer->req_id = g_obex_send_req(obex, req, -1, transfer_response, transfer, &err); @@ -390,7 +395,7 @@ static void transfer_put_req(GObex *obex, GObexPacket *req, gpointer user_data) rspcode = put_get_bytes(transfer, req); - /* Don't send continue while in SRM */ + /* Don't send continue while SRM is active */ if (g_obex_srm_active(transfer->obex) && rspcode == G_OBEX_RSP_CONTINUE) goto done; diff --git a/gobex/gobex.c b/gobex/gobex.c index 8c08b1e..3848884 100644 --- a/gobex/gobex.c +++ b/gobex/gobex.c @@ -44,6 +44,13 @@ #define CONNID_INVALID 0xffffffff +/* Challenge request */ +#define NONCE_TAG 0x00 +#define NONCE_LEN 16 + +/* Challenge response */ +#define DIGEST_TAG 0x00 + guint gobex_debug = 0; struct srm_config { @@ -85,6 +92,7 @@ struct _GObex { guint16 tx_mtu; guint32 conn_id; + GObexApparam *authchal; GQueue *tx_queue; @@ -105,6 +113,8 @@ struct pending_pkt { GObexResponseFunc rsp_func; gpointer rsp_data; gboolean cancelled; + gboolean suspended; + gboolean authenticating; }; struct req_handler { @@ -253,6 +263,8 @@ static gboolean req_timeout(gpointer user_data) g_error_free(err); pending_pkt_free(p); + p->timeout_id = 0; + return FALSE; } @@ -355,9 +367,20 @@ done: obex->srm = NULL; } +static gboolean g_obex_srm_enabled(GObex *obex) +{ + if (!obex->use_srm) + return FALSE; + + if (obex->srm == NULL) + return FALSE; + + return obex->srm->enabled; +} + static void check_srm_final(GObex *obex, guint8 op) { - if (obex->srm == NULL || !obex->srm->enabled) + if (!g_obex_srm_enabled(obex)) return; switch (obex->srm->op) { @@ -388,7 +411,8 @@ static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing) g_obex_header_get_uint8(hdr, &srm); g_obex_debug(G_OBEX_DEBUG_COMMAND, "srm 0x%02x", srm); set_srm(obex, op, srm); - } + } else if (!g_obex_srm_enabled(obex)) + set_srm(obex, op, G_OBEX_SRM_DISABLE); hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP); if (hdr != NULL) { @@ -396,7 +420,9 @@ static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing) g_obex_header_get_uint8(hdr, &srmp); g_obex_debug(G_OBEX_DEBUG_COMMAND, "srmp 0x%02x", srmp); set_srmp(obex, srmp, outgoing); - } else + } else if (obex->pending_req && obex->pending_req->suspended) + g_obex_packet_add_uint8(pkt, G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT); + else set_srmp(obex, -1, outgoing); if (final) @@ -423,7 +449,7 @@ static gboolean write_data(GIOChannel *io, GIOCondition cond, setup_srm(obex, p->pkt, TRUE); - if (g_obex_srm_active(obex)) + if (g_obex_srm_enabled(obex)) goto encode; /* Can't send a request while there's a pending one */ @@ -531,26 +557,85 @@ static void init_connect_data(GObex *obex, struct connect_data *data) memcpy(&data->mtu, &u16, sizeof(u16)); } +static guint8 *digest_response(const guint8 *nonce) +{ + GChecksum *md5; + guint8 *result; + gsize size; + + result = g_new0(guint8, NONCE_LEN); + + md5 = g_checksum_new(G_CHECKSUM_MD5); + if (md5 == NULL) + return result; + + g_checksum_update(md5, nonce, NONCE_LEN); + g_checksum_update(md5, (guint8 *) ":BlueZ", 6); + + size = NONCE_LEN; + g_checksum_get_digest(md5, result, &size); + + g_checksum_free(md5); + + return result; +} + +static void prepare_auth_rsp(GObex *obex, GObexPacket *rsp) +{ + GObexHeader *hdr; + GObexApparam *authrsp; + const guint8 *nonce; + guint8 *result; + gsize len; + + /* Check if client is already responding to authentication challenge */ + hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_AUTHRESP); + if (hdr) + goto done; + + if (!g_obex_apparam_get_bytes(obex->authchal, NONCE_TAG, &nonce, &len)) + goto done; + + if (len != NONCE_LEN) + goto done; + + result = digest_response(nonce); + authrsp = g_obex_apparam_set_bytes(NULL, DIGEST_TAG, result, NONCE_LEN); + + hdr = g_obex_header_new_tag(G_OBEX_HDR_AUTHRESP, authrsp); + g_obex_packet_add_header(rsp, hdr); + + g_obex_apparam_free(authrsp); + g_free(result); + +done: + g_obex_apparam_free(obex->authchal); + obex->authchal = NULL; +} + static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp) { - GObexHeader *connid; + GObexHeader *hdr; struct connect_data data; static guint32 next_connid = 1; init_connect_data(obex, &data); g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY); - connid = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION); - if (connid != NULL) { - g_obex_header_get_uint32(connid, &obex->conn_id); - return; + hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION); + if (hdr) { + g_obex_header_get_uint32(hdr, &obex->conn_id); + goto done; } obex->conn_id = next_connid++; - connid = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, - obex->conn_id); - g_obex_packet_prepend_header(rsp, connid); + hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id); + g_obex_packet_prepend_header(rsp, hdr); + +done: + if (obex->authchal) + prepare_auth_rsp(obex, rsp); } static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt) @@ -646,7 +731,7 @@ guint g_obex_send_req(GObex *obex, GObexPacket *req, int timeout, if (obex->rx_last_op == G_OBEX_RSP_CONTINUE) goto create_pending; - if (g_obex_srm_active(obex) && obex->pending_req != NULL) + if (g_obex_srm_enabled(obex) && obex->pending_req != NULL) goto create_pending; hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION); @@ -695,7 +780,9 @@ static gboolean pending_req_abort(GObex *obex, GError **err) p->cancelled = TRUE; - g_source_remove(p->timeout_id); + if (p->timeout_id > 0) + g_source_remove(p->timeout_id); + p->timeout = G_OBEX_ABORT_TIMEOUT; p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex); @@ -834,24 +921,77 @@ gboolean g_obex_remove_request_function(GObex *obex, guint id) return TRUE; } +static void g_obex_srm_suspend(GObex *obex) +{ + struct pending_pkt *p = obex->pending_req; + GObexPacket *req; + + if (p->timeout_id > 0) { + g_source_remove(p->timeout_id); + p->timeout_id = 0; + } + + p->suspended = TRUE; + + req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, + G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT, + G_OBEX_HDR_INVALID); + + g_obex_send(obex, req, NULL); +} + void g_obex_suspend(GObex *obex) { + struct pending_pkt *req = obex->pending_req; + g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); + if (!g_obex_srm_active(obex) || !req) + goto done; + + /* Send SRMP wait in case of GET */ + if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET) { + g_obex_srm_suspend(obex); + return; + } + +done: + obex->suspended = TRUE; + if (obex->write_source > 0) { g_source_remove(obex->write_source); obex->write_source = 0; } +} - obex->suspended = TRUE; +static void g_obex_srm_resume(GObex *obex) +{ + struct pending_pkt *p = obex->pending_req; + GObexPacket *req; + + p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex); + p->suspended = FALSE; + + req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID); + + g_obex_send(obex, req, NULL); } void g_obex_resume(GObex *obex) { + struct pending_pkt *req = obex->pending_req; + g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id); obex->suspended = FALSE; + if (g_obex_srm_active(obex) || !req) + goto done; + + if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET) + g_obex_srm_resume(obex); + +done: if (g_queue_get_length(obex->tx_queue) > 0 || obex->tx_data > 0) enable_tx(obex); } @@ -860,10 +1000,7 @@ gboolean g_obex_srm_active(GObex *obex) { gboolean ret = FALSE; - if (!obex->use_srm) - return FALSE; - - if (obex->srm == NULL || !obex->srm->enabled) + if (!g_obex_srm_enabled(obex)) goto done; if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT) @@ -875,10 +1012,31 @@ done: return ret; } +static void auth_challenge(GObex *obex) +{ + struct pending_pkt *p = obex->pending_req; + + if (p->authenticating) + return; + + p->authenticating = TRUE; + + prepare_auth_rsp(obex, p->pkt); + + /* Remove it as pending and add it back to the queue so it gets sent + * again */ + if (p->timeout_id > 0) { + g_source_remove(p->timeout_id); + p->timeout_id = 0; + } + obex->pending_req = NULL; + g_obex_send_internal(obex, p, NULL); +} + static void parse_connect_data(GObex *obex, GObexPacket *pkt) { const struct connect_data *data; - GObexHeader *connid; + GObexHeader *hdr; guint16 u16; size_t data_len; @@ -893,9 +1051,13 @@ static void parse_connect_data(GObex *obex, GObexPacket *pkt) obex->tx_mtu = obex->io_tx_mtu; obex->tx_buf = g_realloc(obex->tx_buf, obex->tx_mtu); - connid = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION); - if (connid != NULL) - g_obex_header_get_uint32(connid, &obex->conn_id); + hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION); + if (hdr) + g_obex_header_get_uint32(hdr, &obex->conn_id); + + hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_AUTHCHAL); + if (hdr) + obex->authchal = g_obex_header_get_apparam(hdr); } static gboolean parse_response(GObex *obex, GObexPacket *rsp) @@ -907,12 +1069,15 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp) rspcode = g_obex_packet_get_operation(rsp, &final); opcode = g_obex_packet_get_operation(p->pkt, NULL); - if (opcode == G_OBEX_OP_CONNECT) + if (opcode == G_OBEX_OP_CONNECT) { parse_connect_data(obex, rsp); + if (rspcode == G_OBEX_RSP_UNAUTHORIZED && obex->authchal) + auth_challenge(obex); + } setup_srm(obex, rsp, FALSE); - if (!g_obex_srm_active(obex)) + if (!g_obex_srm_enabled(obex)) return final; /* @@ -921,7 +1086,9 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp) * continue sending responses until the transfer is finished */ if (opcode == G_OBEX_OP_GET && rspcode == G_OBEX_RSP_CONTINUE) { - g_source_remove(p->timeout_id); + if (p->timeout_id > 0) + g_source_remove(p->timeout_id); + p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex); return FALSE; @@ -932,12 +1099,21 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp) static void handle_response(GObex *obex, GError *err, GObexPacket *rsp) { - struct pending_pkt *p = obex->pending_req; + struct pending_pkt *p; gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE; if (rsp != NULL) final_rsp = parse_response(obex, rsp); + if (!obex->pending_req) + return; + + p = obex->pending_req; + + /* Reset if final so it can no longer be cancelled */ + if (final_rsp) + obex->pending_req = NULL; + if (p->cancelled) err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED, "The operation was cancelled"); @@ -949,17 +1125,15 @@ static void handle_response(GObex *obex, GError *err, GObexPacket *rsp) p->rsp_func(obex, err, rsp, p->rsp_data); /* Check if user callback removed the request */ - if (p != obex->pending_req) + if (!final_rsp && p != obex->pending_req) return; } if (p->cancelled) g_error_free(err); - if (final_rsp) { + if (final_rsp) pending_pkt_free(p); - obex->pending_req = NULL; - } if (!disconn && g_queue_get_length(obex->tx_queue) > 0) enable_tx(obex); @@ -1359,6 +1533,9 @@ void g_obex_unref(GObex *obex) if (obex->pending_req) pending_pkt_free(obex->pending_req); + if (obex->authchal) + g_obex_apparam_free(obex->authchal); + g_free(obex); } @@ -1384,6 +1561,18 @@ guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data, return g_obex_send_req(obex, req, -1, func, user_data, err); } +guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data, + GError **err) +{ + GObexPacket *req; + + g_obex_debug(G_OBEX_DEBUG_COMMAND, ""); + + req = g_obex_packet_new(G_OBEX_OP_DISCONNECT, TRUE, G_OBEX_HDR_INVALID); + + return g_obex_send_req(obex, req, -1, func, user_data, err); +} + guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func, gpointer user_data, GError **err) { diff --git a/gobex/gobex.h b/gobex/gobex.h index 76a224e..7c47590 100644 --- a/gobex/gobex.h +++ b/gobex/gobex.h @@ -75,6 +75,9 @@ void g_obex_unref(GObex *obex); guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data, GError **err, guint8 first_hdr_id, ...); +guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data, + GError **err); + guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func, gpointer user_data, GError **err); diff --git a/lib/bluetooth.c b/lib/bluetooth.c index 17ee2c1..406b508 100644 --- a/lib/bluetooth.c +++ b/lib/bluetooth.c @@ -542,7 +542,7 @@ const char *bt_compidtostr(int compid) case 139: return "Topcorn Positioning Systems, LLC"; case 140: - return "Qualcomm Labs, Inc."; + return "Qualcomm Retail Solutions, Inc. (formerly Qualcomm Labs, Inc.)"; case 141: return "Zscan Software"; case 142: @@ -677,6 +677,8 @@ const char *bt_compidtostr(int compid) return "Elgato Systems GmbH"; case 207: return "ARCHOS SA"; + case 208: + return "Dexcom, Inc."; case 209: return "Polar Electro Europe B.V."; case 210: @@ -744,7 +746,7 @@ const char *bt_compidtostr(int compid) case 241: return "Witron Technology Limited"; case 242: - return "Morse Project Inc."; + return "Aether Things Inc. (formerly Morse Project Inc.)"; case 243: return "Kent Displays Inc."; case 244: @@ -817,6 +819,120 @@ const char *bt_compidtostr(int compid) return "e.solutions"; case 278: return "1OAK Technologies"; + case 279: + return "Wimoto Technologies Inc"; + case 280: + return "Radius Networks, Inc."; + case 281: + return "Wize Technology Co., Ltd."; + case 282: + return "Qualcomm Labs, Inc."; + case 283: + return "Aruba Networks"; + case 284: + return "Baidu"; + case 285: + return "Arendi AG"; + case 286: + return "Skoda Auto a.s."; + case 287: + return "Volkswagon AG"; + case 288: + return "Porsche AG"; + case 289: + return "Sino Wealth Electronic Ltd."; + case 290: + return "AirTurn, Inc."; + case 291: + return "Kinsa, Inc."; + case 292: + return "HID Global"; + case 293: + return "SEAT es"; + case 294: + return "Promethean Ltd."; + case 295: + return "Salutica Allied Solutions"; + case 296: + return "GPSI Group Pty Ltd"; + case 297: + return "Nimble Devices Oy"; + case 298: + return "Changzhou Yongse Infotech Co., Ltd"; + case 299: + return "SportIQ"; + case 300: + return "TEMEC Instruments B.V."; + case 301: + return "Sony Corporation"; + case 302: + return "ASSA ABLOY"; + case 303: + return "Clarion Co., Ltd."; + case 304: + return "Warehouse Innovations"; + case 305: + return "Cypress Semiconductor Corporation"; + case 306: + return "MADS Inc"; + case 307: + return "Blue Maestro Limited"; + case 308: + return "Resolution Products, Inc."; + case 309: + return "Airewear LLC"; + case 310: + return "ETC sp. z.o.o."; + case 311: + return "Prestigio Plaza Ltd."; + case 312: + return "NTEO Inc."; + case 313: + return "Focus Systems Corporation"; + case 314: + return "Tencent Holdings Limited"; + case 315: + return "Allegion"; + case 316: + return "Murata Manufacuring Co., Ltd."; + case 317: + return "WirelessWERX"; + case 318: + return "Nod, Inc."; + case 319: + return "B&B Manufacturing Company"; + case 320: + return "Alpine Electronics (China) Co., Ltd"; + case 321: + return "FedEx Services"; + case 322: + return "Grape Systems Inc."; + case 323: + return "Bkon Connect"; + case 324: + return "Lintech GmbH"; + case 325: + return "Novatel Wireless"; + case 326: + return "Ciright"; + case 327: + return "Mighty Cast, Inc."; + case 328: + return "Ambimat Electronics"; + case 329: + return "Perytons Ltd."; + case 330: + return "Tivoli Audio, LLC"; + case 331: + return "Master Lock"; + case 332: + return "Mesh-Net Ltd"; + case 333: + return "HUIZHOU DESAY SV AUTOMOTIVE CO., LTD."; + case 334: + return "Tangerine, Inc."; + case 335: + return "B&W Group Ltd."; case 65535: return "internal use"; default: diff --git a/lib/mgmt.h b/lib/mgmt.h index a896e52..bab0f99 100644 --- a/lib/mgmt.h +++ b/lib/mgmt.h @@ -94,6 +94,9 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_HS 0x00000100 #define MGMT_SETTING_LE 0x00000200 #define MGMT_SETTING_ADVERTISING 0x00000400 +#define MGMT_SETTING_SECURE_CONN 0x00000800 +#define MGMT_SETTING_DEBUG_KEYS 0x00001000 +#define MGMT_SETTING_PRIVACY 0x00002000 #define MGMT_OP_READ_INFO 0x0004 struct mgmt_rp_read_info { @@ -176,11 +179,11 @@ struct mgmt_cp_load_link_keys { struct mgmt_ltk_info { struct mgmt_addr_info addr; - uint8_t authenticated; + uint8_t type; uint8_t master; uint8_t enc_size; uint16_t ediv; - uint8_t rand[8]; + uint64_t rand; uint8_t val[16]; } __packed; @@ -270,6 +273,12 @@ struct mgmt_rp_read_local_oob_data { uint8_t hash[16]; uint8_t randomizer[16]; } __packed; +struct mgmt_rp_read_local_oob_ext_data { + uint8_t hash192[16]; + uint8_t randomizer192[16]; + uint8_t hash256[16]; + uint8_t randomizer256[16]; +} __packed; #define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021 struct mgmt_cp_add_remote_oob_data { @@ -335,6 +344,38 @@ struct mgmt_cp_set_scan_params { uint16_t window; } __packed; +#define MGMT_OP_SET_SECURE_CONN 0x002D + +#define MGMT_OP_SET_DEBUG_KEYS 0x002E + +struct mgmt_irk_info { + struct mgmt_addr_info addr; + uint8_t val[16]; +} __packed; + +#define MGMT_OP_SET_PRIVACY 0x002F +struct mgmt_cp_set_privacy { + uint8_t privacy; + uint8_t irk[16]; +} __packed; + +#define MGMT_OP_LOAD_IRKS 0x0030 +struct mgmt_cp_load_irks { + uint16_t irk_count; + struct mgmt_irk_info irks[0]; +} __packed; + +#define MGMT_OP_GET_CONN_INFO 0x0031 +struct mgmt_cp_get_conn_info { + struct mgmt_addr_info addr; +} __packed; +struct mgmt_rp_get_conn_info { + struct mgmt_addr_info addr; + int8_t rssi; + int8_t tx_power; + int8_t max_tx_power; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { uint16_t opcode; @@ -471,6 +512,25 @@ struct mgmt_ev_passkey_notify { uint8_t entered; } __packed; +#define MGMT_EV_NEW_IRK 0x0018 +struct mgmt_ev_new_irk { + uint8_t store_hint; + bdaddr_t rpa; + struct mgmt_irk_info key; +} __packed; + +struct mgmt_csrk_info { + struct mgmt_addr_info addr; + uint8_t master; + uint8_t val[16]; +} __packed; + +#define MGMT_EV_NEW_CSRK 0x0019 +struct mgmt_ev_new_csrk { + uint8_t store_hint; + struct mgmt_csrk_info key; +} __packed; + static const char *mgmt_op[] = { "<0x0000>", "Read Version", @@ -517,6 +577,11 @@ static const char *mgmt_op[] = { "Set BR/EDR", "Set Static Address", "Set Scan Parameters", + "Set Secure Connections", + "Set Debug Keys", + "Set Privacy", + "Load Identity Resolving Keys", + "Get Connection Information", }; static const char *mgmt_ev[] = { @@ -544,6 +609,8 @@ static const char *mgmt_ev[] = { "Device Unblocked", "Device Unpaired", "Passkey Notify", + "New Identity Resolving Key", + "New Signature Resolving Key", }; static const char *mgmt_status[] = { diff --git a/lib/sdp.c b/lib/sdp.c index cbdf15e..e5e4622 100644 --- a/lib/sdp.c +++ b/lib/sdp.c @@ -67,6 +67,9 @@ static uint128_t bluetooth_base_uuid = { #define SDP_MAX_ATTR_LEN 65535 +/* match MTU used by RFCOMM */ +#define SDP_LARGE_L2CAP_MTU 1013 + 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); @@ -178,8 +181,9 @@ static struct tupla ServiceClass[] = { { HDP_SVCLASS_ID, "HDP" }, { HDP_SOURCE_SVCLASS_ID, "HDP Source" }, { HDP_SINK_SVCLASS_ID, "HDP Sink" }, - { APPLE_AGENT_SVCLASS_ID, "Apple Agent" }, + { GENERIC_ACCESS_SVCLASS_ID, "Generic Access" }, { GENERIC_ATTRIB_SVCLASS_ID, "Generic Attribute" }, + { APPLE_AGENT_SVCLASS_ID, "Apple Agent" }, { 0 } }; @@ -1601,13 +1605,13 @@ void sdp_record_print(const sdp_record_t *rec) { sdp_data_t *d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY); if (d && SDP_IS_TEXT_STR(d->dtd)) - printf("Service Name: %.*s", d->unitSize, d->val.str); + printf("Service Name: %.*s\n", d->unitSize, d->val.str); d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY); if (d && SDP_IS_TEXT_STR(d->dtd)) - printf("Service Description: %.*s", d->unitSize, d->val.str); + printf("Service Description: %.*s\n", d->unitSize, d->val.str); d = sdp_data_get(rec, SDP_ATTR_PROVNAME_PRIMARY); if (d && SDP_IS_TEXT_STR(d->dtd)) - printf("Service Provider: %.*s", d->unitSize, d->val.str); + printf("Service Provider: %.*s\n", d->unitSize, d->val.str); } #ifdef SDP_DEBUG @@ -4644,6 +4648,26 @@ static int sdp_connect_local(sdp_session_t *session) return connect(session->sock, (struct sockaddr *) &sa, sizeof(sa)); } +static int set_l2cap_mtu(int sk, uint16_t mtu) +{ + struct l2cap_options l2o; + socklen_t len; + + memset(&l2o, 0, sizeof(l2o)); + len = sizeof(l2o); + + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) + return -1; + + l2o.imtu = mtu; + l2o.omtu = mtu; + + if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) + return -1; + + return 0; +} + static int sdp_connect_l2cap(const bdaddr_t *src, const bdaddr_t *dst, sdp_session_t *session) { @@ -4678,6 +4702,10 @@ static int sdp_connect_l2cap(const bdaddr_t *src, setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); } + if ((flags & SDP_LARGE_MTU) && + set_l2cap_mtu(sk, SDP_LARGE_L2CAP_MTU) < 0) + return -1; + sa.l2_psm = htobs(SDP_PSM); sa.l2_bdaddr = *dst; @@ -4773,7 +4801,7 @@ int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf) free(dtds); goto fail; } - lengths = malloc(plen * sizeof(int *)); + lengths = malloc(plen * sizeof(int)); if (!lengths) { free(dtds); free(vals); diff --git a/lib/sdp.h b/lib/sdp.h index f2f2484..34adf9a 100644 --- a/lib/sdp.h +++ b/lib/sdp.h @@ -150,8 +150,9 @@ extern "C" { #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_ACCESS_SVCLASS_ID 0x1800 #define GENERIC_ATTRIB_SVCLASS_ID 0x1801 +#define APPLE_AGENT_SVCLASS_ID 0x2112 /* * Standard profile descriptor identifiers; note these @@ -207,6 +208,7 @@ extern "C" { #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 MAP_PROFILE_ID MAP_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 @@ -223,9 +225,9 @@ extern "C" { #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_ACCESS_PROFILE_ID GENERIC_ACCESS_SVCLASS_ID #define GENERIC_ATTRIB_PROFILE_ID GENERIC_ATTRIB_SVCLASS_ID +#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID /* * Compatibility macros for the old MDP acronym @@ -296,6 +298,8 @@ extern "C" { #define SDP_ATTR_SUPPORTED_REPOSITORIES 0x0314 #define SDP_ATTR_MAS_INSTANCE_ID 0x0315 #define SDP_ATTR_SUPPORTED_MESSAGE_TYPES 0x0316 +#define SDP_ATTR_PBAP_SUPPORTED_FEATURES 0x0317 +#define SDP_ATTR_MAP_SUPPORTED_FEATURES 0x0317 #define SDP_ATTR_SPECIFICATION_ID 0x0200 #define SDP_ATTR_VENDOR_ID 0x0201 diff --git a/lib/sdp_lib.h b/lib/sdp_lib.h index 6e1eb91..3ded393 100644 --- a/lib/sdp_lib.h +++ b/lib/sdp_lib.h @@ -81,6 +81,7 @@ static inline void sdp_list_foreach(sdp_list_t *list, sdp_list_func_t f, void *u #define SDP_RETRY_IF_BUSY 0x01 #define SDP_WAIT_ON_CLOSE 0x02 #define SDP_NON_BLOCKING 0x04 +#define SDP_LARGE_MTU 0x08 /* * a session with an SDP server diff --git a/lib/uuid.c b/lib/uuid.c index 4363aee..5c3f986 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -32,7 +32,6 @@ #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 } @@ -41,33 +40,36 @@ static uint128_t bluetooth_base_uuid = { #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) { + uint16_t be16; + 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)); + /* + * No matter the system: 128-bit UUIDs should be stored + * as big-endian. 16-bit UUIDs are stored on host order. + */ + + be16 = htons(src->value.u16); + memcpy(&dst->value.u128.data[BASE_UUID16_OFFSET], &be16, sizeof(be16)); } static void bt_uuid32_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst) { + uint32_t be32; + 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)); + /* + * No matter the system: 128-bit UUIDs should be stored + * as big-endian. 32-bit UUIDs are stored on host order. + */ + + be32 = htonl(src->value.u32); + memcpy(&dst->value.u128.data[BASE_UUID32_OFFSET], &be32, sizeof(be32)); } void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst) @@ -154,10 +156,7 @@ int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n) unsigned int data4; unsigned short data5; - uint128_t nvalue; - const uint8_t *data = (uint8_t *) &nvalue; - - hton128(&uuid->value.u128, &nvalue); + const uint8_t *data = (uint8_t *) &uuid->value.u128; memcpy(&data0, &data[0], 4); memcpy(&data1, &data[4], 2); @@ -231,8 +230,8 @@ 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; + uint128_t u128; + uint8_t *val = (uint8_t *) &u128; if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx", &data0, &data1, &data2, @@ -253,8 +252,6 @@ static int bt_string_to_uuid128(bt_uuid_t *uuid, const char *string) memcpy(&val[10], &data4, 4); memcpy(&val[14], &data5, 2); - ntoh128(&n128, &u128); - bt_uuid128_create(uuid, u128); return 0; diff --git a/lib/uuid.h b/lib/uuid.h index 95e5a9a..814fd92 100644 --- a/lib/uuid.h +++ b/lib/uuid.h @@ -105,6 +105,38 @@ extern "C" { #define OBEX_MNS_UUID "00001133-0000-1000-8000-00805f9b34fb" #define OBEX_MAP_UUID "00001134-0000-1000-8000-00805f9b34fb" +/* GATT UUIDs section */ +#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 +#define GATT_CHARAC_SYSTEM_ID 0x2A23 +#define GATT_CHARAC_MODEL_NUMBER_STRING 0x2A24 +#define GATT_CHARAC_SERIAL_NUMBER_STRING 0x2A25 +#define GATT_CHARAC_FIRMWARE_REVISION_STRING 0x2A26 +#define GATT_CHARAC_HARDWARE_REVISION_STRING 0x2A27 +#define GATT_CHARAC_SOFTWARE_REVISION_STRING 0x2A28 +#define GATT_CHARAC_MANUFACTURER_NAME_STRING 0x2A29 + +/* 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 +#define GATT_CHARAC_VALID_RANGE_UUID 0x2906 +#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907 +#define GATT_REPORT_REFERENCE 0x2908 + typedef struct { enum { BT_UUID_UNSPEC = 0, @@ -133,6 +165,11 @@ void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst); 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); +static inline int bt_uuid_len(const bt_uuid_t *uuid) +{ + return uuid->type / 8; +} + #ifdef __cplusplus } #endif diff --git a/monitor/analyze.c b/monitor/analyze.c new file mode 100644 index 0000000..a747938 --- /dev/null +++ b/monitor/analyze.c @@ -0,0 +1,325 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include + +#include "src/shared/util.h" +#include "src/shared/queue.h" +#include "src/shared/btsnoop.h" +#include "monitor/bt.h" +#include "analyze.h" + +#define MAX_PACKET_SIZE (1486 + 4) + +struct hci_dev { + uint16_t index; + uint8_t type; + uint8_t bdaddr[6]; + struct timeval time_added; + struct timeval time_removed; + unsigned long num_cmd; + unsigned long num_evt; + unsigned long num_acl; + unsigned long num_sco; +}; + +static struct queue *dev_list; + +static void dev_destroy(void *data) +{ + struct hci_dev *dev = data; + const char *str; + + switch (dev->type) { + case 0x00: + str = "BR/EDR"; + break; + case 0x01: + str = "AMP"; + break; + default: + str = "unknown"; + break; + } + + printf("Found %s controller with index %u\n", str, dev->index); + printf(" BD_ADDR %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", + dev->bdaddr[5], dev->bdaddr[4], dev->bdaddr[3], + dev->bdaddr[2], dev->bdaddr[1], dev->bdaddr[0]); + printf(" %lu commands\n", dev->num_cmd); + printf(" %lu events\n", dev->num_evt); + printf(" %lu ACL packets\n", dev->num_acl); + printf(" %lu SCO packets\n", dev->num_sco); + printf("\n"); + + free(dev); +} + +static struct hci_dev *dev_alloc(uint16_t index) +{ + struct hci_dev *dev; + + dev = new0(struct hci_dev, 1); + if (!dev) { + fprintf(stderr, "Failed to allocate new device entry\n"); + return NULL; + } + + dev->index = index; + + return dev; +} + +static bool dev_match_index(const void *a, const void *b) +{ + const struct hci_dev *dev = a; + uint16_t index = PTR_TO_UINT(b); + + return dev->index == index; +} + +static struct hci_dev *dev_lookup(uint16_t index) +{ + struct hci_dev *dev; + + dev = queue_find(dev_list, dev_match_index, UINT_TO_PTR(index)); + if (!dev) { + fprintf(stderr, "Creating new device for unknown index\n"); + + dev = dev_alloc(index); + if (!dev) + return NULL; + + queue_push_tail(dev_list, dev); + } + + return dev; +} + +static void new_index(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + const struct btsnoop_opcode_new_index *ni = data; + struct hci_dev *dev; + + dev = dev_alloc(index); + if (!dev) + return; + + dev->type = ni->type; + memcpy(dev->bdaddr, ni->bdaddr, 6); + + queue_push_tail(dev_list, dev); +} + +static void del_index(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + struct hci_dev *dev; + + dev = queue_remove_if(dev_list, dev_match_index, UINT_TO_PTR(index)); + if (!dev) { + fprintf(stderr, "Remove for an unexisting device\n"); + return; + } + + dev_destroy(dev); +} + +static void command_pkt(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + const struct bt_hci_cmd_hdr *hdr = data; + struct hci_dev *dev; + + data += sizeof(*hdr); + size -= sizeof(*hdr); + + dev = dev_lookup(index); + if (!dev) + return; + + dev->num_cmd++; +} + +static void rsp_read_bd_addr(struct hci_dev *dev, struct timeval *tv, + const void *data, uint16_t size) +{ + const struct bt_hci_rsp_read_bd_addr *rsp = data; + + printf("Read BD Addr event with status 0x%2.2x\n", rsp->status); + + if (rsp->status) + return; + + memcpy(dev->bdaddr, rsp->bdaddr, 6); +} + +static void evt_cmd_complete(struct hci_dev *dev, struct timeval *tv, + const void *data, uint16_t size) +{ + const struct bt_hci_evt_cmd_complete *evt = data; + uint16_t opcode; + + data += sizeof(*evt); + size -= sizeof(*evt); + + opcode = le16_to_cpu(evt->opcode); + + switch (opcode) { + case BT_HCI_CMD_READ_BD_ADDR: + rsp_read_bd_addr(dev, tv, data, size); + break; + } +} + +static void event_pkt(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + const struct bt_hci_evt_hdr *hdr = data; + struct hci_dev *dev; + + data += sizeof(*hdr); + size -= sizeof(*hdr); + + dev = dev_lookup(index); + if (!dev) + return; + + dev->num_evt++; + + switch (hdr->evt) { + case BT_HCI_EVT_CMD_COMPLETE: + evt_cmd_complete(dev, tv, data, size); + break; + } +} + +static void acl_pkt(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + const struct bt_hci_acl_hdr *hdr = data; + struct hci_dev *dev; + + data += sizeof(*hdr); + size -= sizeof(*hdr); + + dev = dev_lookup(index); + if (!dev) + return; + + dev->num_acl++; +} + +static void sco_pkt(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + const struct bt_hci_sco_hdr *hdr = data; + struct hci_dev *dev; + + data += sizeof(*hdr); + size -= sizeof(*hdr); + + dev = dev_lookup(index); + if (!dev) + return; + + dev->num_sco++; +} + +void analyze_trace(const char *path) +{ + struct btsnoop *btsnoop_file; + unsigned long num_packets = 0; + uint32_t type; + + btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT); + if (!btsnoop_file) + return; + + type = btsnoop_get_type(btsnoop_file); + + switch (type) { + case BTSNOOP_TYPE_HCI: + case BTSNOOP_TYPE_UART: + case BTSNOOP_TYPE_MONITOR: + break; + default: + fprintf(stderr, "Unsupported packet format\n"); + return; + } + + dev_list = queue_new(); + if (!dev_list) { + fprintf(stderr, "Failed to allocate device list\n"); + goto done; + } + + while (1) { + unsigned char buf[MAX_PACKET_SIZE]; + struct timeval tv; + uint16_t index, opcode, pktlen; + + if (btsnoop_read_hci(btsnoop_file, &tv, &index, &opcode, + buf, &pktlen) < 0) + break; + + switch (opcode) { + case BTSNOOP_OPCODE_NEW_INDEX: + new_index(&tv, index, buf, pktlen); + break; + case BTSNOOP_OPCODE_DEL_INDEX: + del_index(&tv, index, buf, pktlen); + break; + case BTSNOOP_OPCODE_COMMAND_PKT: + command_pkt(&tv, index, buf, pktlen); + break; + case BTSNOOP_OPCODE_EVENT_PKT: + event_pkt(&tv, index, buf, pktlen); + break; + case BTSNOOP_OPCODE_ACL_TX_PKT: + case BTSNOOP_OPCODE_ACL_RX_PKT: + acl_pkt(&tv, index, buf, pktlen); + break; + case BTSNOOP_OPCODE_SCO_TX_PKT: + case BTSNOOP_OPCODE_SCO_RX_PKT: + sco_pkt(&tv, index, buf, pktlen); + break; + } + + num_packets++; + } + + printf("Trace contains %lu packets\n\n", num_packets); + + queue_destroy(dev_list, dev_destroy); + +done: + btsnoop_unref(btsnoop_file); +} diff --git a/monitor/analyze.h b/monitor/analyze.h new file mode 100644 index 0000000..c643d35 --- /dev/null +++ b/monitor/analyze.h @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 + * + */ + +void analyze_trace(const char *path); diff --git a/monitor/bt.h b/monitor/bt.h index 849ed86..1a41d9e 100644 --- a/monitor/bt.h +++ b/monitor/bt.h @@ -2,8 +2,8 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * * This library is free software; you can redistribute it and/or @@ -99,19 +99,255 @@ struct bt_ll_reject_ind { uint8_t error; } __attribute__ ((packed)); +#define LMP_ESC4(x) ((127 << 8) | (x)) + +#define BT_LMP_ACCEPTED 3 +struct bt_lmp_accepted { + uint8_t opcode; +} __attribute__ ((packed)); + +#define BT_LMP_NOT_ACCEPTED 4 +struct bt_lmp_not_accepted { + uint8_t opcode; + uint8_t error; +} __attribute__ ((packed)); + +#define BT_LMP_CLKOFFSET_REQ 5 + +#define BT_LMP_DETACH 7 +struct bt_lmp_detach { + uint8_t error; +} __attribute__ ((packed)); + +#define BT_LMP_AU_RAND 11 +struct bt_lmp_au_rand { + uint8_t number[16]; +} __attribute__ ((packed)); + +#define BT_LMP_SRES 12 +struct bt_lmp_sres { + uint8_t response[4]; +} __attribute__ ((packed)); + +#define BT_LMP_ENCRYPTION_MODE_REQ 15 +struct bt_lmp_encryption_mode_req { + uint8_t mode; +} __attribute__ ((packed)); + +#define BT_LMP_ENCRYPTION_KEY_SIZE_REQ 16 +struct bt_lmp_encryption_key_size_req { + uint8_t key_size; +} __attribute__ ((packed)); + +#define BT_LMP_START_ENCRYPTION_REQ 17 +struct bt_lmp_start_encryption_req { + uint8_t number[16]; +} __attribute__ ((packed)); + +#define BT_LMP_STOP_ENCRYPTION_REQ 18 + +#define BT_LMP_UNSNIFF_REQ 24 + +#define BT_LMP_MAX_POWER 33 + +#define BT_LMP_MIN_POWER 34 + +#define BT_LMP_AUTO_RATE 35 + +#define BT_LMP_VERSION_REQ 37 +struct bt_lmp_version_req { + uint8_t version; + uint16_t company; + uint16_t subversion; +} __attribute__ ((packed)); + +#define BT_LMP_VERSION_RES 38 +struct bt_lmp_version_res { + uint8_t version; + uint16_t company; + uint16_t subversion; +} __attribute__ ((packed)); + +#define BT_LMP_FEATURES_REQ 39 +struct bt_lmp_features_req { + uint8_t features[8]; +} __attribute__ ((packed)); + +#define BT_LMP_FEATURES_RES 40 +struct bt_lmp_features_res { + uint8_t features[8]; +} __attribute__ ((packed)); + +#define BT_LMP_MAX_SLOT 45 +struct bt_lmp_max_slot { + uint8_t slots; +} __attribute__ ((packed)); + +#define BT_LMP_MAX_SLOT_REQ 46 +struct bt_lmp_max_slot_req { + uint8_t slots; +} __attribute__ ((packed)); + +#define BT_LMP_TIMING_ACCURACY_REQ 47 + +#define BT_LMP_TIMING_ACCURACY_RES 48 +struct bt_lmp_timing_accuracy_res { + uint8_t drift; + uint8_t jitter; +} __attribute__ ((packed)); + +#define BT_LMP_SETUP_COMPLETE 49 + +#define BT_LMP_USE_SEMI_PERMANENT_KEY 50 + +#define BT_LMP_HOST_CONNECTION_REQ 51 + +#define BT_LMP_PAGE_SCAN_MODE_REQ 54 +struct bt_lmp_page_scan_mode_req { + uint8_t scheme; + uint8_t settings; +} __attribute__ ((packed)); + +#define BT_LMP_TEST_ACTIVATE 56 + +#define BT_LMP_ENCRYPTION_KEY_SIZE_MASK_REQ 58 + +#define BT_LMP_SET_AFH 60 +struct bt_lmp_set_afh { + uint32_t instant; + uint8_t mode; + uint8_t map[10]; +} __attribute__ ((packed)); + +#define BT_LMP_ENCAPSULATED_HEADER 61 +struct bt_lmp_encapsulated_header { + uint8_t major; + uint8_t minor; + uint8_t length; +} __attribute__ ((packed)); + +#define BT_LMP_ENCAPSULATED_PAYLOAD 62 +struct bt_lmp_encapsulated_payload { + uint8_t data[16]; +} __attribute__ ((packed)); + +#define BT_LMP_SIMPLE_PAIRING_CONFIRM 63 +struct bt_lmp_simple_pairing_confirm { + uint8_t value[16]; +} __attribute__ ((packed)); + +#define BT_LMP_SIMPLE_PAIRING_NUMBER 64 +struct bt_lmp_simple_pairing_number { + uint8_t value[16]; +} __attribute__ ((packed)); + +#define BT_LMP_DHKEY_CHECK 65 +struct bt_lmp_dhkey_check { + uint8_t value[16]; +} __attribute__ ((packed)); + +#define BT_LMP_PAUSE_ENCRYPTION_AES_REQ 66 + +#define BT_LMP_ACCEPTED_EXT LMP_ESC4(1) +struct bt_lmp_accepted_ext { + uint8_t escape; + uint8_t opcode; +} __attribute__ ((packed)); + +#define BT_LMP_NOT_ACCEPTED_EXT LMP_ESC4(2) +struct bt_lmp_not_accepted_ext { + uint8_t escape; + uint8_t opcode; + uint8_t error; +} __attribute__ ((packed)); + +#define BT_LMP_FEATURES_REQ_EXT LMP_ESC4(3) +struct bt_lmp_features_req_ext { + uint8_t page; + uint8_t max_page; + uint8_t features[8]; +} __attribute__ ((packed)); + +#define BT_LMP_FEATURES_RES_EXT LMP_ESC4(4) +struct bt_lmp_features_res_ext { + uint8_t page; + uint8_t max_page; + uint8_t features[8]; +} __attribute__ ((packed)); + +#define BT_LMP_PACKET_TYPE_TABLE_REQ LMP_ESC4(11) +struct bt_lmp_packet_type_table_req { + uint8_t table; +} __attribute__ ((packed)); + +#define BT_LMP_CHANNEL_CLASSIFICATION_REQ LMP_ESC4(16) +struct bt_lmp_channel_classification_req { + uint8_t mode; + uint16_t min_interval; + uint16_t max_interval; +} __attribute__ ((packed)); + +#define BT_LMP_CHANNEL_CLASSIFICATION LMP_ESC4(17) +struct bt_lmp_channel_classification { + uint8_t classification[10]; +} __attribute__ ((packed)); + +#define BT_LMP_PAUSE_ENCRYPTION_REQ LMP_ESC4(23) + +#define BT_LMP_RESUME_ENCRYPTION_REQ LMP_ESC4(24) + +#define BT_LMP_IO_CAPABILITY_REQ LMP_ESC4(25) +struct bt_lmp_io_capability_req { + uint8_t capability; + uint8_t oob_data; + uint8_t authentication; +} __attribute__ ((packed)); + +#define BT_LMP_IO_CAPABILITY_RES LMP_ESC4(26) +struct bt_lmp_io_capability_res { + uint8_t capability; + uint8_t oob_data; + uint8_t authentication; +} __attribute__ ((packed)); + +#define BT_LMP_NUMERIC_COMPARISON_FAILED LMP_ESC(27) + +#define BT_LMP_PASSKEY_FAILED LMP_ESC4(28) + +#define BT_LMP_OOB_FAILED LMP_ESC(29) + +#define BT_LMP_POWER_CONTROL_REQ LMP_ESC4(31) +struct bt_lmp_power_control_req { + uint8_t request; +} __attribute__ ((packed)); + +#define BT_LMP_POWER_CONTROL_RES LMP_ESC4(32) +struct bt_lmp_power_control_res { + uint8_t response; +} __attribute__ ((packed)); + +#define BT_LMP_PING_REQ LMP_ESC4(33) + +#define BT_LMP_PING_RES LMP_ESC4(34) + #define BT_H4_CMD_PKT 0x01 #define BT_H4_ACL_PKT 0x02 #define BT_H4_SCO_PKT 0x03 #define BT_H4_EVT_PKT 0x04 +struct bt_hci_cmd_hdr { + uint16_t opcode; + uint8_t plen; +} __attribute__ ((packed)); + struct bt_hci_acl_hdr { uint16_t handle; uint16_t dlen; } __attribute__ ((packed)); -struct bt_hci_cmd_hdr { - uint16_t opcode; - uint8_t plen; +struct bt_hci_sco_hdr { + uint16_t handle; + uint8_t dlen; } __attribute__ ((packed)); struct bt_hci_evt_hdr { @@ -185,11 +421,19 @@ struct bt_hci_cmd_link_key_request_reply { uint8_t bdaddr[6]; uint8_t link_key[16]; } __attribute__ ((packed)); +struct bt_hci_rsp_link_key_request_reply { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); #define BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY 0x040c struct bt_hci_cmd_link_key_request_neg_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); +struct bt_hci_rsp_link_key_request_neg_reply { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); #define BT_HCI_CMD_PIN_CODE_REQUEST_REPLY 0x040d struct bt_hci_cmd_pin_code_request_reply { @@ -202,6 +446,10 @@ struct bt_hci_cmd_pin_code_request_reply { struct bt_hci_cmd_pin_code_request_neg_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); +struct bt_hci_rsp_pin_code_request_neg_reply { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); #define BT_HCI_CMD_CHANGE_CONN_PKT_TYPE 0x040f struct bt_hci_cmd_change_conn_pkt_type { @@ -314,16 +562,28 @@ struct bt_hci_cmd_io_capability_request_reply { uint8_t oob_data; uint8_t authentication; } __attribute__ ((packed)); +struct bt_hci_rsp_io_capability_request_reply { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); #define BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY 0x042c struct bt_hci_cmd_user_confirm_request_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); +struct bt_hci_rsp_user_confirm_request_reply { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); #define BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY 0x042d struct bt_hci_cmd_user_confirm_request_neg_reply { uint8_t bdaddr[6]; } __attribute__ ((packed)); +struct bt_hci_rsp_user_confirm_request_neg_reply { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); #define BT_HCI_CMD_USER_PASSKEY_REQUEST_REPLY 0x042e struct bt_hci_cmd_user_passkey_request_reply { @@ -353,6 +613,10 @@ struct bt_hci_cmd_io_capability_request_neg_reply { uint8_t bdaddr[6]; uint8_t reason; } __attribute__ ((packed)); +struct bt_hci_rsp_io_capability_request_neg_reply { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); #define BT_HCI_CMD_CREATE_PHY_LINK 0x0435 struct bt_hci_cmd_create_phy_link { @@ -918,8 +1182,8 @@ struct bt_hci_cmd_set_afh_host_classification { #define BT_HCI_CMD_READ_INQUIRY_SCAN_TYPE 0x0c42 struct bt_hci_rsp_read_inquiry_scan_type { - uint8_t status; - uint8_t type; + uint8_t status; + uint8_t type; } __attribute__ ((packed)); #define BT_HCI_CMD_WRITE_INQUIRY_SCAN_TYPE 0x0c43 @@ -1220,6 +1484,8 @@ struct bt_hci_rsp_read_data_block_size { uint16_t num_blocks; } __attribute__ ((packed)); +#define BT_HCI_CMD_READ_LOCAL_CODECS 0x100b + #define BT_HCI_CMD_READ_FAILED_CONTACT_COUNTER 0x1401 struct bt_hci_cmd_read_failed_contact_counter { uint16_t handle; @@ -1332,6 +1598,15 @@ struct bt_hci_rsp_write_remote_amp_assoc { uint8_t phy_handle; } __attribute__ ((packed)); +#define BT_HCI_CMD_SET_TRIGGERED_CLOCK_CAPTURE 0x140d +struct bt_hci_cmd_set_triggered_clock_capture { + uint16_t handle; + uint8_t enable; + uint8_t type; + uint8_t lpo_allowed; + uint8_t num_filter; +} __attribute__ ((packed)); + #define BT_HCI_CMD_ENABLE_DUT_MODE 0x1803 #define BT_HCI_CMD_WRITE_SSP_DEBUG_MODE 0x1804 @@ -1494,14 +1769,14 @@ struct bt_hci_rsp_le_encrypt { #define BT_HCI_CMD_LE_RAND 0x2018 struct bt_hci_rsp_le_rand { uint8_t status; - uint8_t number[8]; + uint64_t number; } __attribute__ ((packed)); #define BT_HCI_CMD_LE_START_ENCRYPT 0x2019 struct bt_hci_cmd_le_start_encrypt { uint16_t handle; - uint8_t number[8]; - uint16_t diversifier; + uint64_t rand; + uint16_t ediv; uint8_t ltk[16]; } __attribute__ ((packed)); @@ -2025,6 +2300,12 @@ struct bt_hci_evt_slave_broadcast_channel_map_change { uint8_t map[10]; } __attribute__ ((packed)); +#define BT_HCI_EVT_INQUIRY_RESPONSE_NOTIFY 0x56 +struct bt_hci_evt_inquiry_response_notify { + uint8_t lap[3]; + int8_t rssi; +} __attribute__ ((packed)); + #define BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXPIRED 0x57 struct bt_hci_evt_auth_payload_timeout_expired { uint16_t handle; @@ -2072,8 +2353,17 @@ struct bt_hci_evt_le_remote_features_complete { #define BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST 0x05 struct bt_hci_evt_le_long_term_key_request { uint16_t handle; - uint8_t number[8]; - uint16_t diversifier; + uint64_t rand; + uint16_t ediv; +} __attribute__ ((packed)); + +#define BT_HCI_EVT_LE_REMOTE_CONN_PARAM_REQUEST 0x06 +struct bt_hci_evt_le_remote_conn_param_request { + uint16_t handle; + uint16_t min_interval; + uint16_t max_interval; + uint16_t latency; + uint16_t supv_timeout; } __attribute__ ((packed)); #define BT_HCI_ERR_SUCCESS 0x00 @@ -2081,6 +2371,8 @@ struct bt_hci_evt_le_long_term_key_request { #define BT_HCI_ERR_UNKNOWN_CONN_ID 0x02 #define BT_HCI_ERR_HARDWARE_FAILURE 0x03 #define BT_HCI_ERR_PAGE_TIMEOUT 0x04 +#define BT_HCI_ERR_AUTH_FAILURE 0x05 +#define BT_HCI_ERR_PIN_OR_KEY_MISSING 0x06 #define BT_HCI_ERR_MEM_CAPACITY_EXCEEDED 0x07 #define BT_HCI_ERR_COMMAND_DISALLOWED 0x0c #define BT_HCI_ERR_UNSUPPORTED_FEATURE 0x11 diff --git a/monitor/btsnoop.c b/monitor/btsnoop.c deleted file mode 100644 index b92fb74..0000000 --- a/monitor/btsnoop.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have 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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "btsnoop.h" - -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 const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, - 0x6f, 0x6f, 0x70, 0x00 }; - -static const uint32_t btsnoop_version = 1; -static uint32_t btsnoop_type = 0; - -static int btsnoop_fd = -1; -static uint16_t btsnoop_index = 0xffff; - -void btsnoop_create(const char *path, uint32_t type) -{ - struct btsnoop_hdr hdr; - ssize_t written; - - if (btsnoop_fd >= 0) - return; - - btsnoop_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (btsnoop_fd < 0) - return; - - btsnoop_type = type; - - memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id)); - hdr.version = htonl(btsnoop_version); - hdr.type = htonl(btsnoop_type); - - written = write(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE); - if (written < 0) { - close(btsnoop_fd); - btsnoop_fd = -1; - return; - } -} - -void btsnoop_write(struct timeval *tv, uint32_t flags, - const void *data, uint16_t size) -{ - struct btsnoop_pkt pkt; - uint64_t ts; - ssize_t written; - - ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec; - - pkt.size = htonl(size); - pkt.len = htonl(size); - pkt.flags = htonl(flags); - pkt.drops = htonl(0); - pkt.ts = hton64(ts + 0x00E03AB44A676000ll); - - written = write(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE); - if (written < 0) - return; - - if (data && size > 0) { - written = write(btsnoop_fd, data, size); - if (written < 0) - return; - } -} - -static uint32_t get_flags_from_opcode(uint16_t opcode) -{ - switch (opcode) { - case BTSNOOP_OPCODE_NEW_INDEX: - case BTSNOOP_OPCODE_DEL_INDEX: - break; - case BTSNOOP_OPCODE_COMMAND_PKT: - return 0x02; - case BTSNOOP_OPCODE_EVENT_PKT: - return 0x03; - case BTSNOOP_OPCODE_ACL_TX_PKT: - return 0x00; - case BTSNOOP_OPCODE_ACL_RX_PKT: - return 0x01; - case BTSNOOP_OPCODE_SCO_TX_PKT: - case BTSNOOP_OPCODE_SCO_RX_PKT: - break; - } - - return 0xff; -} - -void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode, - const void *data, uint16_t size) -{ - uint32_t flags; - - if (!tv) - return; - - if (btsnoop_fd < 0) - return; - - switch (btsnoop_type) { - case BTSNOOP_TYPE_HCI: - if (btsnoop_index == 0xffff) - btsnoop_index = index; - - if (index != btsnoop_index) - return; - - flags = get_flags_from_opcode(opcode); - if (flags == 0xff) - return; - break; - - case BTSNOOP_TYPE_EXTENDED_HCI: - flags = (index << 16) | opcode; - break; - - default: - return; - } - - btsnoop_write(tv, flags, data, size); -} - -void btsnoop_write_phy(struct timeval *tv, uint16_t frequency, - const void *data, uint16_t size) -{ - uint32_t flags; - - if (!tv) - return; - - if (btsnoop_fd < 0) - return; - - switch (btsnoop_type) { - case BTSNOOP_TYPE_EXTENDED_PHY: - flags = (1 << 16) | frequency; - break; - - default: - return; - } - - btsnoop_write(tv, flags, data, size); -} - -int btsnoop_open(const char *path, uint32_t *type) -{ - struct btsnoop_hdr hdr; - ssize_t len; - - if (btsnoop_fd >= 0) { - fprintf(stderr, "Too many open files\n"); - return -1; - } - - btsnoop_fd = open(path, O_RDONLY | O_CLOEXEC); - if (btsnoop_fd < 0) { - perror("Failed to open file"); - return -1; - } - - len = read(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE); - if (len < 0 || len != BTSNOOP_HDR_SIZE) { - perror("Failed to read header"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) { - fprintf(stderr, "Invalid btsnoop header\n"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - if (ntohl(hdr.version) != btsnoop_version) { - fprintf(stderr, "Invalid btsnoop version\n"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - btsnoop_type = ntohl(hdr.type); - - if (type) - *type = btsnoop_type; - - return 0; -} - -static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags) -{ - switch (type) { - case HCI_COMMAND_PKT: - return BTSNOOP_OPCODE_COMMAND_PKT; - case HCI_ACLDATA_PKT: - if (flags & 0x01) - return BTSNOOP_OPCODE_ACL_RX_PKT; - else - return BTSNOOP_OPCODE_ACL_TX_PKT; - case HCI_SCODATA_PKT: - if (flags & 0x01) - return BTSNOOP_OPCODE_SCO_RX_PKT; - else - return BTSNOOP_OPCODE_SCO_TX_PKT; - case HCI_EVENT_PKT: - return BTSNOOP_OPCODE_EVENT_PKT; - case 0xff: - if (flags & 0x02) { - if (flags & 0x01) - return BTSNOOP_OPCODE_EVENT_PKT; - else - return BTSNOOP_OPCODE_COMMAND_PKT; - } else { - if (flags & 0x01) - return BTSNOOP_OPCODE_ACL_RX_PKT; - else - return BTSNOOP_OPCODE_ACL_TX_PKT; - } - break; - } - - return 0xff; -} - -int btsnoop_read_hci(struct timeval *tv, uint16_t *index, uint16_t *opcode, - void *data, uint16_t *size) -{ - struct btsnoop_pkt pkt; - uint32_t toread, flags; - uint64_t ts; - uint8_t pkt_type; - ssize_t len; - - if (btsnoop_fd < 0) - return -1; - - len = read(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE); - if (len == 0) - return -1; - - if (len < 0 || len != BTSNOOP_PKT_SIZE) { - perror("Failed to read packet"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - toread = ntohl(pkt.size); - flags = ntohl(pkt.flags); - - ts = ntoh64(pkt.ts) - 0x00E03AB44A676000ll; - tv->tv_sec = (ts / 1000000ll) + 946684800ll; - tv->tv_usec = ts % 1000000ll; - - switch (btsnoop_type) { - case BTSNOOP_TYPE_HCI: - *index = 0; - *opcode = get_opcode_from_flags(0xff, flags); - break; - - case BTSNOOP_TYPE_UART: - len = read(btsnoop_fd, &pkt_type, 1); - if (len < 0) { - perror("Failed to read packet type"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - toread--; - - *index = 0; - *opcode = get_opcode_from_flags(pkt_type, flags); - break; - - case BTSNOOP_TYPE_EXTENDED_HCI: - *index = flags >> 16; - *opcode = flags & 0xffff; - break; - - default: - fprintf(stderr, "Unknown packet type\n"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - len = read(btsnoop_fd, data, toread); - if (len < 0) { - perror("Failed to read data"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - *size = toread; - - return 0; -} - -int btsnoop_read_phy(struct timeval *tv, uint16_t *frequency, - void *data, uint16_t *size) -{ - struct btsnoop_pkt pkt; - uint32_t toread, flags; - uint64_t ts; - ssize_t len; - - if (btsnoop_fd < 0) - return -1; - - len = read(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE); - if (len == 0) - return -1; - - if (len < 0 || len != BTSNOOP_PKT_SIZE) { - perror("Failed to read packet"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - toread = ntohl(pkt.size); - flags = ntohl(pkt.flags); - - ts = ntoh64(pkt.ts) - 0x00E03AB44A676000ll; - tv->tv_sec = (ts / 1000000ll) + 946684800ll; - tv->tv_usec = ts % 1000000ll; - - switch (btsnoop_type) { - case BTSNOOP_TYPE_EXTENDED_PHY: - if ((flags >> 16) != 1) - break; - *frequency = flags & 0xffff; - break; - - default: - fprintf(stderr, "Unknown packet type\n"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - len = read(btsnoop_fd, data, toread); - if (len < 0) { - perror("Failed to read data"); - close(btsnoop_fd); - btsnoop_fd = -1; - return -1; - } - - *size = toread; - - return 0; -} - -void btsnoop_close(void) -{ - if (btsnoop_fd < 0) - return; - - close(btsnoop_fd); - btsnoop_fd = -1; - - btsnoop_index = 0xffff; -} diff --git a/monitor/btsnoop.h b/monitor/btsnoop.h deleted file mode 100644 index cdefb8c..0000000 --- a/monitor/btsnoop.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have 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 -#include - -#define BTSNOOP_TYPE_HCI 1001 -#define BTSNOOP_TYPE_UART 1002 -#define BTSNOOP_TYPE_BCSP 1003 -#define BTSNOOP_TYPE_3WIRE 1004 - -#define BTSNOOP_TYPE_EXTENDED_HCI 2001 -#define BTSNOOP_TYPE_EXTENDED_PHY 2002 - -#define BTSNOOP_OPCODE_NEW_INDEX 0 -#define BTSNOOP_OPCODE_DEL_INDEX 1 -#define BTSNOOP_OPCODE_COMMAND_PKT 2 -#define BTSNOOP_OPCODE_EVENT_PKT 3 -#define BTSNOOP_OPCODE_ACL_TX_PKT 4 -#define BTSNOOP_OPCODE_ACL_RX_PKT 5 -#define BTSNOOP_OPCODE_SCO_TX_PKT 6 -#define BTSNOOP_OPCODE_SCO_RX_PKT 7 - -void btsnoop_create(const char *path, uint32_t type); -void btsnoop_write(struct timeval *tv, uint32_t flags, - const void *data, uint16_t size); -void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode, - const void *data, uint16_t size); -void btsnoop_write_phy(struct timeval *tv, uint16_t frequency, - const void *data, uint16_t size); -int btsnoop_open(const char *path, uint32_t *type); -int btsnoop_read_hci(struct timeval *tv, uint16_t *index, uint16_t *opcode, - void *data, uint16_t *size); -int btsnoop_read_phy(struct timeval *tv, uint16_t *frequency, - void *data, uint16_t *size); -void btsnoop_close(void); diff --git a/monitor/control.c b/monitor/control.c index 4edefde..8197813 100644 --- a/monitor/control.c +++ b/monitor/control.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -40,13 +40,16 @@ #include "lib/hci.h" #include "lib/mgmt.h" +#include "src/shared/util.h" +#include "src/shared/btsnoop.h" #include "mainloop.h" #include "display.h" #include "packet.h" -#include "btsnoop.h" #include "hcidump.h" +#include "ellisys.h" #include "control.h" +static struct btsnoop *btsnoop_file = NULL; static bool hcidump_fallback = false; #define MAX_PACKET_SIZE (1486 + 4) @@ -105,7 +108,7 @@ static void mgmt_controller_error(uint16_t len, const void *buf) static const char *settings_str[] = { "powered", "connectable", "fast-connectable", "discoverable", "pairable", "link-security", "ssp", "br/edr", "hs", "le", - "advertising", + "advertising", "secure-conn", "debug-keys", "privacy", }; static void mgmt_new_settings(uint16_t len, const void *buf) @@ -118,7 +121,7 @@ static void mgmt_new_settings(uint16_t len, const void *buf) return; } - settings = bt_get_le32(buf); + settings = get_le32(buf); printf("@ New Settings: 0x%4.4x\n", settings); @@ -204,7 +207,8 @@ static void mgmt_new_long_term_key(uint16_t len, const void *buf) ba2str(&ev->key.addr.bdaddr, str); - printf("@ New Long Term Key: %s (%d)\n", str, ev->key.addr.type); + printf("@ New Long Term Key: %s (%d) %s\n", str, ev->key.addr.type, + ev->key.master ? "Master" : "Slave"); buf += sizeof(*ev); len -= sizeof(*ev); @@ -223,7 +227,7 @@ static void mgmt_device_connected(uint16_t len, const void *buf) return; } - flags = btohl(ev->flags); + flags = le32_to_cpu(ev->flags); ba2str(&ev->addr.bdaddr, str); printf("@ Device Connected: %s (%d) flags 0x%4.4x\n", @@ -381,7 +385,7 @@ static void mgmt_device_found(uint16_t len, const void *buf) return; } - flags = btohl(ev->flags); + flags = le32_to_cpu(ev->flags); ba2str(&ev->addr.bdaddr, str); printf("@ Device Found: %s (%d) rssi %d flags 0x%4.4x\n", @@ -483,7 +487,7 @@ static void mgmt_passkey_notify(uint16_t len, const void *buf) ba2str(&ev->addr.bdaddr, str); - passkey = btohl(ev->passkey); + passkey = le32_to_cpu(ev->passkey); printf("@ Passkey Notify: %s (%d) passkey %06u entered %u\n", str, ev->addr.type, passkey, ev->entered); @@ -494,6 +498,48 @@ static void mgmt_passkey_notify(uint16_t len, const void *buf) packet_hexdump(buf, len); } +static void mgmt_new_irk(uint16_t len, const void *buf) +{ + const struct mgmt_ev_new_irk *ev = buf; + char addr[18], rpa[18]; + + if (len < sizeof(*ev)) { + printf("* Malformed New IRK control\n"); + return; + } + + ba2str(&ev->rpa, rpa); + ba2str(&ev->key.addr.bdaddr, addr); + + printf("@ New IRK: %s (%d) %s\n", addr, ev->key.addr.type, rpa); + + buf += sizeof(*ev); + len -= sizeof(*ev); + + packet_hexdump(buf, len); +} + +static void mgmt_new_csrk(uint16_t len, const void *buf) +{ + const struct mgmt_ev_new_csrk *ev = buf; + char addr[18]; + + if (len < sizeof(*ev)) { + printf("* Malformed New CSRK control\n"); + return; + } + + ba2str(&ev->key.addr.bdaddr, addr); + + printf("@ New CSRK: %s (%d) %s\n", addr, ev->key.addr.type, + ev->key.master ? "Master" : "Slave"); + + buf += sizeof(*ev); + len -= sizeof(*ev); + + packet_hexdump(buf, len); +} + void control_message(uint16_t opcode, const void *data, uint16_t size) { switch (opcode) { @@ -560,6 +606,12 @@ void control_message(uint16_t opcode, const void *data, uint16_t size) case MGMT_EV_PASSKEY_NOTIFY: mgmt_passkey_notify(size, data); break; + case MGMT_EV_NEW_IRK: + mgmt_new_irk(size, data); + break; + case MGMT_EV_NEW_CSRK: + mgmt_new_csrk(size, data); + break; default: printf("* Unknown control (code %d len %d)\n", opcode, size); packet_hexdump(data, size); @@ -616,9 +668,9 @@ static void data_callback(int fd, uint32_t events, void *user_data) } } - opcode = btohs(hdr.opcode); - index = btohs(hdr.index); - pktlen = btohs(hdr.len); + opcode = le16_to_cpu(hdr.opcode); + index = le16_to_cpu(hdr.index); + pktlen = le16_to_cpu(hdr.len); switch (data->channel) { case HCI_CHANNEL_CONTROL: @@ -626,7 +678,10 @@ static void data_callback(int fd, uint32_t events, void *user_data) break; case HCI_CHANNEL_MONITOR: packet_monitor(tv, index, opcode, data->buf, pktlen); - btsnoop_write_hci(tv, index, opcode, data->buf, pktlen); + btsnoop_write_hci(btsnoop_file, tv, index, opcode, + data->buf, pktlen); + ellisys_inject_hci(tv, index, opcode, + data->buf, pktlen); break; } } @@ -710,11 +765,11 @@ static void client_callback(int fd, uint32_t events, void *user_data) if (data->offset > MGMT_HDR_SIZE) { struct mgmt_hdr *hdr = (struct mgmt_hdr *) data->buf; - uint16_t pktlen = btohs(hdr->len); + uint16_t pktlen = le16_to_cpu(hdr->len); if (data->offset > pktlen + MGMT_HDR_SIZE) { - uint16_t opcode = btohs(hdr->opcode); - uint16_t index = btohs(hdr->index); + uint16_t opcode = le16_to_cpu(hdr->opcode); + uint16_t index = le16_to_cpu(hdr->index); packet_monitor(NULL, index, opcode, data->buf + MGMT_HDR_SIZE, pktlen); @@ -809,7 +864,7 @@ void control_server(const char *path) void control_writer(const char *path) { - btsnoop_create(path, BTSNOOP_TYPE_EXTENDED_HCI); + btsnoop_file = btsnoop_create(path, BTSNOOP_TYPE_MONITOR); } void control_reader(const char *path) @@ -819,17 +874,20 @@ void control_reader(const char *path) uint32_t type; struct timeval tv; - if (btsnoop_open(path, &type) < 0) + btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT); + if (!btsnoop_file) return; + type = btsnoop_get_type(btsnoop_file); + switch (type) { case BTSNOOP_TYPE_HCI: case BTSNOOP_TYPE_UART: - case BTSNOOP_TYPE_EXTENDED_PHY: + case BTSNOOP_TYPE_SIMULATOR: packet_del_filter(PACKET_FILTER_SHOW_INDEX); break; - case BTSNOOP_TYPE_EXTENDED_HCI: + case BTSNOOP_TYPE_MONITOR: packet_add_filter(PACKET_FILTER_SHOW_INDEX); break; } @@ -839,24 +897,28 @@ void control_reader(const char *path) switch (type) { case BTSNOOP_TYPE_HCI: case BTSNOOP_TYPE_UART: - case BTSNOOP_TYPE_EXTENDED_HCI: + case BTSNOOP_TYPE_MONITOR: while (1) { uint16_t index, opcode; - if (btsnoop_read_hci(&tv, &index, &opcode, - buf, &pktlen) < 0) + if (!btsnoop_read_hci(btsnoop_file, &tv, &index, + &opcode, buf, &pktlen)) break; + if (opcode == 0xffff) + continue; + packet_monitor(&tv, index, opcode, buf, pktlen); + ellisys_inject_hci(&tv, index, opcode, buf, pktlen); } break; - case BTSNOOP_TYPE_EXTENDED_PHY: + case BTSNOOP_TYPE_SIMULATOR: while (1) { uint16_t frequency; - if (btsnoop_read_phy(&tv, &frequency, - buf, &pktlen) < 0) + if (!btsnoop_read_phy(btsnoop_file, &tv, &frequency, + buf, &pktlen)) break; packet_simulator(&tv, frequency, buf, pktlen); @@ -866,7 +928,7 @@ void control_reader(const char *path) close_pager(); - btsnoop_close(); + btsnoop_unref(btsnoop_file); } int control_tracing(void) diff --git a/monitor/control.h b/monitor/control.h index 8781bd0..8de4c6c 100644 --- a/monitor/control.h +++ b/monitor/control.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/crc.c b/monitor/crc.c index 9ea11fb..912b37e 100644 --- a/monitor/crc.c +++ b/monitor/crc.c @@ -2,8 +2,8 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/monitor/crc.h b/monitor/crc.h index 84f5d7f..772388b 100644 --- a/monitor/crc.h +++ b/monitor/crc.h @@ -2,8 +2,8 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/monitor/display.c b/monitor/display.c index af4171f..411af94 100644 --- a/monitor/display.c +++ b/monitor/display.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/display.h b/monitor/display.h index 885eb34..e627401 100644 --- a/monitor/display.h +++ b/monitor/display.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/ellisys.c b/monitor/ellisys.c new file mode 100644 index 0000000..bafbb5d --- /dev/null +++ b/monitor/ellisys.c @@ -0,0 +1,162 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "src/shared/btsnoop.h" +#include "ellisys.h" + +static int ellisys_fd = -1; +static uint16_t ellisys_index = 0xffff; + +void ellisys_enable(const char *server, uint16_t port) +{ + struct sockaddr_in addr; + int fd; + + if (ellisys_fd >= 0) { + fprintf(stderr, "Ellisys injection already enabled\n"); + return; + } + + fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("Failed to open UDP injection socket"); + return; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(server); + addr.sin_port = htons(port); + + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to connect UDP injection socket"); + close(fd); + return; + } + + ellisys_fd = fd; +} + +void ellisys_inject_hci(struct timeval *tv, uint16_t index, uint16_t opcode, + const void *data, uint16_t size) +{ + uint8_t msg[] = { + /* HCI Injection Service, Version 1 */ + 0x02, 0x00, 0x01, + /* DateTimeNs Object */ + 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Bitrate Object, 12000000 bps */ + 0x80, 0x00, 0x1b, 0x37, 0x4b, + /* HCI Packet Type Object */ + 0x81, 0x00, + /* HCI Packet Data Object */ + 0x82 + }; + long nsec; + time_t t; + struct tm tm; + struct iovec iov[2]; + int iovcnt; + + if (!tv) + return; + + if (ellisys_fd < 0) + return; + + if (ellisys_index == 0xffff) + ellisys_index = index; + + if (index != ellisys_index) + return; + + t = tv->tv_sec; + localtime_r(&t, &tm); + + nsec = ((tm.tm_sec + (tm.tm_min * 60) + + (tm.tm_hour * 3600)) * 1000000l + tv->tv_usec) * 1000l; + + msg[4] = (1900 + tm.tm_year) & 0xff; + msg[5] = (1900 + tm.tm_year) >> 8; + msg[6] = (tm.tm_mon + 1) & 0xff; + msg[7] = tm.tm_mday & 0xff; + msg[8] = (nsec & 0x0000000000ffl); + msg[9] = (nsec & 0x00000000ff00l) >> 8; + msg[10] = (nsec & 0x000000ff0000l) >> 16; + msg[11] = (nsec & 0x0000ff000000l) >> 24; + msg[12] = (nsec & 0x00ff00000000l) >> 32; + msg[13] = (nsec & 0xff0000000000l) >> 40; + + switch (opcode) { + case BTSNOOP_OPCODE_COMMAND_PKT: + msg[20] = 0x01; + break; + case BTSNOOP_OPCODE_EVENT_PKT: + msg[20] = 0x84; + break; + case BTSNOOP_OPCODE_ACL_TX_PKT: + msg[20] = 0x02; + break; + case BTSNOOP_OPCODE_ACL_RX_PKT: + msg[20] = 0x82; + break; + case BTSNOOP_OPCODE_SCO_TX_PKT: + msg[20] = 0x03; + break; + case BTSNOOP_OPCODE_SCO_RX_PKT: + msg[20] = 0x83; + break; + default: + return; + } + + iov[0].iov_base = msg; + iov[0].iov_len = sizeof(msg); + + if (size > 0) { + iov[1].iov_base = (void *) data; + iov[1].iov_len = size; + iovcnt = 2; + } else + iovcnt = 1; + + if (writev(ellisys_fd, iov, iovcnt) < 0) + perror("Failed to send Ellisys injection packet"); +} diff --git a/monitor/ellisys.h b/monitor/ellisys.h new file mode 100644 index 0000000..8be888d --- /dev/null +++ b/monitor/ellisys.h @@ -0,0 +1,30 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 + +void ellisys_enable(const char *server, uint16_t port); + +void ellisys_inject_hci(struct timeval *tv, uint16_t index, uint16_t opcode, + const void *data, uint16_t size); diff --git a/monitor/hcidump.c b/monitor/hcidump.c index 9881bb3..1515f93 100644 --- a/monitor/hcidump.c +++ b/monitor/hcidump.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/hcidump.h b/monitor/hcidump.h index 3801c98..c908650 100644 --- a/monitor/hcidump.h +++ b/monitor/hcidump.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/hwdb.c b/monitor/hwdb.c new file mode 100644 index 0000000..6931660 --- /dev/null +++ b/monitor/hwdb.c @@ -0,0 +1,137 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include + +#include "hwdb.h" + +#ifdef HAVE_UDEV_HWDB_NEW +#include + +bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model) +{ + struct udev *udev; + struct udev_hwdb *hwdb; + struct udev_list_entry *head, *entry; + bool result; + + udev = udev_new(); + if (!udev) + return false; + + hwdb = udev_hwdb_new(udev); + if (!hwdb) { + result = false; + goto done; + } + + *vendor = NULL; + *model = NULL; + + head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0); + + udev_list_entry_foreach(entry, head) { + const char *name = udev_list_entry_get_name(entry); + + if (!name) + continue; + + if (!*vendor && !strcmp(name, "ID_VENDOR_FROM_DATABASE")) + *vendor = strdup(udev_list_entry_get_value(entry)); + else if (!*model && !strcmp(name, "ID_MODEL_FROM_DATABASE")) + *model = strdup(udev_list_entry_get_value(entry)); + } + + hwdb = udev_hwdb_unref(hwdb); + + result = true; + +done: + udev = udev_unref(udev); + + return result; +} + +bool hwdb_get_company(const uint8_t *bdaddr, char **company) +{ + struct udev *udev; + struct udev_hwdb *hwdb; + struct udev_list_entry *head, *entry; + char modalias[11]; + bool result; + + if (!bdaddr[2] && !bdaddr[1] && !bdaddr[0]) + return false; + + sprintf(modalias, "OUI:%2.2X%2.2X%2.2X", + bdaddr[5], bdaddr[4], bdaddr[3]); + + udev = udev_new(); + if (!udev) + return false; + + hwdb = udev_hwdb_new(udev); + if (!hwdb) { + result = false; + goto done; + } + + *company = NULL; + + head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0); + + udev_list_entry_foreach(entry, head) { + const char *name = udev_list_entry_get_name(entry); + + if (name && !strcmp(name, "ID_OUI_FROM_DATABASE")) { + *company = strdup(udev_list_entry_get_value(entry)); + break; + } + } + + hwdb = udev_hwdb_unref(hwdb); + + result = true; + +done: + udev = udev_unref(udev); + + return result; +} +#else +bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model) +{ + return false; +} + +bool hwdb_get_company(const uint8_t *bdaddr, char **company) +{ + return false; +} +#endif diff --git a/monitor/hwdb.h b/monitor/hwdb.h new file mode 100644 index 0000000..79f505a --- /dev/null +++ b/monitor/hwdb.h @@ -0,0 +1,29 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 +#include + +bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model); +bool hwdb_get_company(const uint8_t *bdaddr, char **company); diff --git a/monitor/keys.c b/monitor/keys.c new file mode 100644 index 0000000..4ccef22 --- /dev/null +++ b/monitor/keys.c @@ -0,0 +1,144 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 +#endif + +#include + +#include "src/shared/util.h" +#include "src/shared/queue.h" +#include "src/shared/crypto.h" + +#include "keys.h" + +static const uint8_t empty_key[16] = { 0x00, }; +static const uint8_t empty_addr[6] = { 0x00, }; + +static struct bt_crypto *crypto; + +struct irk_data { + uint8_t key[16]; + uint8_t addr[6]; + uint8_t addr_type; +}; + +static struct queue *irk_list; + +void keys_setup(void) +{ + crypto = bt_crypto_new(); + + irk_list = queue_new(); +} + +void keys_cleanup(void) +{ + bt_crypto_unref(crypto); + + queue_destroy(irk_list, free); +} + +void keys_update_identity_key(const uint8_t key[16]) +{ + struct irk_data *irk; + + irk = queue_peek_tail(irk_list); + if (irk && !memcmp(irk->key, empty_key, 16)) { + memcpy(irk->key, key, 16); + return; + } + + irk = new0(struct irk_data, 1); + if (irk) { + memcpy(irk->key, key, 16); + if (!queue_push_tail(irk_list, irk)) + free(irk); + } +} + +void keys_update_identity_addr(const uint8_t addr[6], uint8_t addr_type) +{ + struct irk_data *irk; + + irk = queue_peek_tail(irk_list); + if (irk && !memcmp(irk->addr, empty_addr, 6)) { + memcpy(irk->addr, addr, 6); + irk->addr_type = addr_type; + return; + } + + irk = new0(struct irk_data, 1); + if (irk) { + memcpy(irk->addr, addr, 6); + irk->addr_type = addr_type; + if (!queue_push_tail(irk_list, irk)) + free(irk); + } +} + +struct resolve_data { + bool found; + uint8_t addr[6]; + uint8_t ident[6]; + uint8_t ident_type; +}; + +static void try_resolve_irk(void *data, void *user_data) +{ + struct irk_data *irk = data; + struct resolve_data *result = user_data; + uint8_t local_hash[3]; + + if (result->found) + return; + + bt_crypto_ah(crypto, irk->key, result->addr + 3, local_hash); + + if (!memcmp(result->addr, local_hash, 3)) { + result->found = true; + memcpy(result->ident, irk->addr, 6); + result->ident_type = irk->addr_type; + } +} + +bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6], + uint8_t *ident_type) +{ + struct resolve_data result; + + result.found = false; + memcpy(result.addr, addr, 6); + + queue_foreach(irk_list, try_resolve_irk, &result); + + if (result.found) { + memcpy(ident, result.ident, 6); + *ident_type = result.ident_type; + return true; + } + + return false; +} diff --git a/monitor/keys.h b/monitor/keys.h new file mode 100644 index 0000000..61ec50a --- /dev/null +++ b/monitor/keys.h @@ -0,0 +1,35 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 +#include + +void keys_setup(void); +void keys_cleanup(void); + +void keys_update_identity_key(const uint8_t key[16]); +void keys_update_identity_addr(const uint8_t addr[6], uint8_t addr_type); + +bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6], + uint8_t *ident_type); diff --git a/monitor/l2cap.c b/monitor/l2cap.c index c215882..993aa8b 100644 --- a/monitor/l2cap.c +++ b/monitor/l2cap.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -28,15 +28,18 @@ #include #include +#include #include #include +#include "src/shared/util.h" #include "bt.h" #include "packet.h" #include "display.h" #include "l2cap.h" #include "uuid.h" +#include "keys.h" #include "sdp.h" #define MAX_CHAN 64 @@ -271,19 +274,19 @@ static void clear_fragment_buffer(uint16_t index) static void print_psm(uint16_t psm) { - print_field("PSM: %d (0x%4.4x)", btohs(psm), btohs(psm)); + print_field("PSM: %d (0x%4.4x)", le16_to_cpu(psm), le16_to_cpu(psm)); } static void print_cid(const char *type, uint16_t cid) { - print_field("%s CID: %d", type, btohs(cid)); + print_field("%s CID: %d", type, le16_to_cpu(cid)); } static void print_reject_reason(uint16_t reason) { const char *str; - switch (btohs(reason)) { + switch (le16_to_cpu(reason)) { case 0x0000: str = "Command not understood"; break; @@ -298,14 +301,14 @@ static void print_reject_reason(uint16_t reason) break; } - print_field("Reason: %s (0x%4.4x)", str, btohs(reason)); + print_field("Reason: %s (0x%4.4x)", str, le16_to_cpu(reason)); } static void print_conn_result(uint16_t result) { const char *str; - switch (btohs(result)) { + switch (le16_to_cpu(result)) { case 0x0000: str = "Connection successful"; break; @@ -332,14 +335,14 @@ static void print_conn_result(uint16_t result) break; } - print_field("Result: %s (0x%4.4x)", str, btohs(result)); + print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void print_conn_status(uint16_t status) { const char *str; - switch (btohs(status)) { + switch (le16_to_cpu(status)) { case 0x0000: str = "No further information available"; break; @@ -354,26 +357,26 @@ static void print_conn_status(uint16_t status) break; } - print_field("Status: %s (0x%4.4x)", str, btohs(status)); + print_field("Status: %s (0x%4.4x)", str, le16_to_cpu(status)); } static void print_config_flags(uint16_t flags) { const char *str; - if (btohs(flags) & 0x0001) + if (le16_to_cpu(flags) & 0x0001) str = " (continuation)"; else str = ""; - print_field("Flags: 0x%4.4x%s", btohs(flags), str); + print_field("Flags: 0x%4.4x%s", le16_to_cpu(flags), str); } static void print_config_result(uint16_t result) { const char *str; - switch (btohs(result)) { + switch (le16_to_cpu(result)) { case 0x0000: str = "Success"; break; @@ -397,7 +400,7 @@ static void print_config_result(uint16_t result) break; } - print_field("Result: %s (0x%4.4x)", str, btohs(result)); + print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static struct { @@ -454,11 +457,11 @@ static void print_config_options(const struct l2cap_frame *frame, switch (type) { case 0x01: print_field(" MTU: %d", - bt_get_le16(data + consumed + 2)); + get_le16(data + consumed + 2)); break; case 0x02: print_field(" Flush timeout: %d", - bt_get_le16(data + consumed + 2)); + get_le16(data + consumed + 2)); break; case 0x03: switch (data[consumed + 3]) { @@ -479,15 +482,15 @@ static void print_config_options(const struct l2cap_frame *frame, print_field(" Service type: %s (0x%2.2x)", str, data[consumed + 3]); print_field(" Token rate: 0x%8.8x", - bt_get_le32(data + consumed + 4)); + get_le32(data + consumed + 4)); print_field(" Token bucket size: 0x%8.8x", - bt_get_le32(data + consumed + 8)); + get_le32(data + consumed + 8)); print_field(" Peak bandwidth: 0x%8.8x", - bt_get_le32(data + consumed + 12)); + get_le32(data + consumed + 12)); print_field(" Latency: 0x%8.8x", - bt_get_le32(data + consumed + 16)); + get_le32(data + consumed + 16)); print_field(" Delay variation: 0x%8.8x", - bt_get_le32(data + consumed + 20)); + get_le32(data + consumed + 20)); break; case 0x04: if (response) @@ -518,11 +521,11 @@ static void print_config_options(const struct l2cap_frame *frame, print_field(" TX window size: %d", data[consumed + 3]); print_field(" Max transmit: %d", data[consumed + 4]); print_field(" Retransmission timeout: %d", - bt_get_le16(data + consumed + 5)); + get_le16(data + consumed + 5)); print_field(" Monitor timeout: %d", - bt_get_le16(data + consumed + 7)); + get_le16(data + consumed + 7)); print_field(" Maximum PDU size: %d", - bt_get_le16(data + consumed + 9)); + get_le16(data + consumed + 9)); break; case 0x05: switch (data[consumed + 2]) { @@ -559,17 +562,17 @@ static void print_config_options(const struct l2cap_frame *frame, print_field(" Service type: %s (0x%2.2x)", str, data[consumed + 3]); print_field(" Maximum SDU size: 0x%4.4x", - bt_get_le16(data + consumed + 4)); + get_le16(data + consumed + 4)); print_field(" SDU inter-arrival time: 0x%8.8x", - bt_get_le32(data + consumed + 6)); + get_le32(data + consumed + 6)); print_field(" Access latency: 0x%8.8x", - bt_get_le32(data + consumed + 10)); + get_le32(data + consumed + 10)); print_field(" Flush timeout: 0x%8.8x", - bt_get_le32(data + consumed + 14)); + get_le32(data + consumed + 14)); break; case 0x07: print_field(" Max window size: %d", - bt_get_le16(data + consumed + 2)); + get_le16(data + consumed + 2)); break; default: packet_hexdump(data + consumed + 2, len); @@ -587,7 +590,7 @@ static void print_info_type(uint16_t type) { const char *str; - switch (btohs(type)) { + switch (le16_to_cpu(type)) { case 0x0001: str = "Connectionless MTU"; break; @@ -602,14 +605,14 @@ static void print_info_type(uint16_t type) break; } - print_field("Type: %s (0x%4.4x)", str, btohs(type)); + print_field("Type: %s (0x%4.4x)", str, le16_to_cpu(type)); } static void print_info_result(uint16_t result) { const char *str; - switch (btohs(result)) { + switch (le16_to_cpu(result)) { case 0x0000: str = "Success"; break; @@ -621,7 +624,7 @@ static void print_info_result(uint16_t result) break; } - print_field("Result: %s (0x%4.4x)", str, btohs(result)); + print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static struct { @@ -697,7 +700,7 @@ static void print_move_result(uint16_t result) { const char *str; - switch (btohs(result)) { + switch (le16_to_cpu(result)) { case 0x0000: str = "Move success"; break; @@ -724,14 +727,14 @@ static void print_move_result(uint16_t result) break; } - print_field("Result: %s (0x%4.4x)", str, btohs(result)); + print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void print_move_cfm_result(uint16_t result) { const char *str; - switch (btohs(result)) { + switch (le16_to_cpu(result)) { case 0x0000: str = "Move success - both sides succeed"; break; @@ -743,14 +746,14 @@ static void print_move_cfm_result(uint16_t result) break; } - print_field("Result: %s (0x%4.4x)", str, btohs(result)); + print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void print_conn_param_result(uint16_t result) { const char *str; - switch (btohs(result)) { + switch (le16_to_cpu(result)) { case 0x0000: str = "Connection Parameters accepted"; break; @@ -762,7 +765,7 @@ static void print_conn_param_result(uint16_t result) break; } - print_field("Result: %s (0x%4.4x)", str, btohs(result)); + print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result)); } static void sig_cmd_reject(const struct l2cap_frame *frame) @@ -777,7 +780,7 @@ static void sig_cmd_reject(const struct l2cap_frame *frame) data += sizeof(*pdu); size -= sizeof(*pdu); - switch (btohs(pdu->reason)) { + switch (le16_to_cpu(pdu->reason)) { case 0x0000: if (size != 0) { print_text(COLOR_ERROR, "invalid data size"); @@ -791,7 +794,7 @@ static void sig_cmd_reject(const struct l2cap_frame *frame) packet_hexdump(data, size); break; } - print_field("MTU: %d", bt_get_le16(data)); + print_field("MTU: %d", get_le16(data)); break; case 0x0002: if (size != 4) { @@ -799,10 +802,10 @@ static void sig_cmd_reject(const struct l2cap_frame *frame) packet_hexdump(data, size); break; } - dcid = bt_get_le16(data); - scid = bt_get_le16(data + 2); - print_cid("Destination", htobs(dcid)); - print_cid("Source", htobs(scid)); + dcid = get_le16(data); + scid = get_le16(data + 2); + print_cid("Destination", cpu_to_le16(dcid)); + print_cid("Source", cpu_to_le16(scid)); break; default: packet_hexdump(data, size); @@ -817,7 +820,7 @@ static void sig_conn_req(const struct l2cap_frame *frame) print_psm(pdu->psm); print_cid("Source", pdu->scid); - assign_scid(frame, btohs(pdu->scid), btohs(pdu->psm), 0); + assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), 0); } static void sig_conn_rsp(const struct l2cap_frame *frame) @@ -829,7 +832,7 @@ static void sig_conn_rsp(const struct l2cap_frame *frame) print_conn_result(pdu->result); print_conn_status(pdu->status); - assign_dcid(frame, btohs(pdu->dcid), btohs(pdu->scid)); + assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid)); } static void sig_config_req(const struct l2cap_frame *frame) @@ -838,7 +841,7 @@ static void sig_config_req(const struct l2cap_frame *frame) print_cid("Destination", pdu->dcid); print_config_flags(pdu->flags); - print_config_options(frame, 4, btohs(pdu->dcid), false); + print_config_options(frame, 4, le16_to_cpu(pdu->dcid), false); } static void sig_config_rsp(const struct l2cap_frame *frame) @@ -848,7 +851,7 @@ static void sig_config_rsp(const struct l2cap_frame *frame) print_cid("Source", pdu->scid); print_config_flags(pdu->flags); print_config_result(pdu->result); - print_config_options(frame, 6, btohs(pdu->scid), true); + print_config_options(frame, 6, le16_to_cpu(pdu->scid), true); } static void sig_disconn_req(const struct l2cap_frame *frame) @@ -866,7 +869,7 @@ static void sig_disconn_rsp(const struct l2cap_frame *frame) print_cid("Destination", pdu->dcid); print_cid("Source", pdu->scid); - release_scid(frame, btohs(pdu->scid)); + release_scid(frame, le16_to_cpu(pdu->scid)); } static void sig_echo_req(const struct l2cap_frame *frame) @@ -898,7 +901,7 @@ static void sig_info_rsp(const struct l2cap_frame *frame) data += sizeof(*pdu); size -= sizeof(*pdu); - if (btohs(pdu->result) != 0x0000) { + if (le16_to_cpu(pdu->result) != 0x0000) { if (size > 0) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); @@ -906,14 +909,14 @@ static void sig_info_rsp(const struct l2cap_frame *frame) return; } - switch (btohs(pdu->type)) { + switch (le16_to_cpu(pdu->type)) { case 0x0001: if (size != 2) { print_text(COLOR_ERROR, "invalid data size"); packet_hexdump(data, size); break; } - print_field("MTU: %d", bt_get_le16(data)); + print_field("MTU: %d", get_le16(data)); break; case 0x0002: if (size != 4) { @@ -921,7 +924,7 @@ static void sig_info_rsp(const struct l2cap_frame *frame) packet_hexdump(data, size); break; } - print_features(bt_get_le32(data)); + print_features(get_le32(data)); break; case 0x0003: if (size != 8) { @@ -929,7 +932,7 @@ static void sig_info_rsp(const struct l2cap_frame *frame) packet_hexdump(data, size); break; } - print_channels(bt_get_le64(data)); + print_channels(get_le64(data)); break; default: packet_hexdump(data, size); @@ -945,7 +948,8 @@ static void sig_create_chan_req(const struct l2cap_frame *frame) print_cid("Source", pdu->scid); print_field("Controller ID: %d", pdu->ctrlid); - assign_scid(frame, btohs(pdu->scid), btohs(pdu->psm), pdu->ctrlid); + assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), + pdu->ctrlid); } static void sig_create_chan_rsp(const struct l2cap_frame *frame) @@ -957,7 +961,7 @@ static void sig_create_chan_rsp(const struct l2cap_frame *frame) print_conn_result(pdu->result); print_conn_status(pdu->status); - assign_dcid(frame, btohs(pdu->dcid), btohs(pdu->scid)); + assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid)); } static void sig_move_chan_req(const struct l2cap_frame *frame) @@ -995,10 +999,10 @@ static void sig_conn_param_req(const struct l2cap_frame *frame) { const struct bt_l2cap_pdu_conn_param_req *pdu = frame->data; - print_field("Min interval: %d", btohs(pdu->min_interval)); - print_field("Max interval: %d", btohs(pdu->max_interval)); - print_field("Slave latency: %d", btohs(pdu->latency)); - print_field("Timeout multiplier: %d", btohs(pdu->timeout)); + print_field("Min interval: %d", le16_to_cpu(pdu->min_interval)); + print_field("Max interval: %d", le16_to_cpu(pdu->max_interval)); + print_field("Slave latency: %d", le16_to_cpu(pdu->latency)); + print_field("Timeout multiplier: %d", le16_to_cpu(pdu->timeout)); } static void sig_conn_param_rsp(const struct l2cap_frame *frame) @@ -1014,11 +1018,11 @@ static void sig_le_conn_req(const struct l2cap_frame *frame) print_psm(pdu->psm); print_cid("Source", pdu->scid); - print_field("MTU: %u", btohs(pdu->mtu)); - print_field("MPS: %u", btohs(pdu->mps)); - print_field("Credits: %u", btohs(pdu->credits)); + print_field("MTU: %u", le16_to_cpu(pdu->mtu)); + print_field("MPS: %u", le16_to_cpu(pdu->mps)); + print_field("Credits: %u", le16_to_cpu(pdu->credits)); - assign_scid(frame, btohs(pdu->scid), btohs(pdu->psm), 0); + assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), 0); } static void sig_le_conn_rsp(const struct l2cap_frame *frame) @@ -1026,12 +1030,12 @@ static void sig_le_conn_rsp(const struct l2cap_frame *frame) const struct bt_l2cap_pdu_le_conn_rsp *pdu = frame->data; print_cid("Destination", pdu->dcid); - print_field("MTU: %u", btohs(pdu->mtu)); - print_field("MPS: %u", btohs(pdu->mps)); - print_field("Credits: %u", btohs(pdu->credits)); + print_field("MTU: %u", le16_to_cpu(pdu->mtu)); + print_field("MPS: %u", le16_to_cpu(pdu->mps)); + print_field("Credits: %u", le16_to_cpu(pdu->credits)); print_conn_result(pdu->result); - /*assign_dcid(frame, btohs(pdu->dcid), btohs(pdu->scid));*/ + /*assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid));*/ } static void sig_le_flowctl_creds(const struct l2cap_frame *frame) @@ -1039,7 +1043,7 @@ static void sig_le_flowctl_creds(const struct l2cap_frame *frame) const struct bt_l2cap_pdu_le_flowctl_creds *pdu = frame->data; print_cid("Source", pdu->cid); - print_field("Credits: %u", btohs(pdu->credits)); + print_field("Credits: %u", le16_to_cpu(pdu->credits)); } struct sig_opcode_data { @@ -1126,7 +1130,7 @@ static void bredr_sig_packet(uint16_t index, bool in, uint16_t handle, return; } - len = btohs(hdr->len); + len = le16_to_cpu(hdr->len); data += 4; size -= 4; @@ -1214,7 +1218,7 @@ static void le_sig_packet(uint16_t index, bool in, uint16_t handle, return; } - len = btohs(hdr->len); + len = le16_to_cpu(hdr->len); data += 4; size -= 4; @@ -1286,7 +1290,7 @@ static void connless_packet(uint16_t index, bool in, uint16_t handle, return; } - psm = btohs(hdr->psm); + psm = le16_to_cpu(hdr->psm); data += 2; size -= 2; @@ -1364,23 +1368,25 @@ static void amp_cmd_reject(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_cmd_reject *pdu = frame->data; - print_field("Reason: 0x%4.4x", btohs(pdu->reason)); + print_field("Reason: 0x%4.4x", le16_to_cpu(pdu->reason)); } static void amp_discover_req(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_discover_req *pdu = frame->data; - print_field("MTU/MPS size: %d", btohs(pdu->size)); - print_field("Extended feature mask: 0x%4.4x", btohs(pdu->features)); + print_field("MTU/MPS size: %d", le16_to_cpu(pdu->size)); + print_field("Extended feature mask: 0x%4.4x", + le16_to_cpu(pdu->features)); } static void amp_discover_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_amp_discover_rsp *pdu = frame->data; - print_field("MTU/MPS size: %d", btohs(pdu->size)); - print_field("Extended feature mask: 0x%4.4x", btohs(pdu->features)); + print_field("MTU/MPS size: %d", le16_to_cpu(pdu->size)); + print_field("Extended feature mask: 0x%4.4x", + le16_to_cpu(pdu->features)); print_controller_list(frame->data + 4, frame->size - 4); } @@ -1422,12 +1428,13 @@ static void amp_get_info_rsp(const struct l2cap_frame *frame) print_field("Status: %s (0x%2.2x)", str, pdu->status); - print_field("Total bandwidth: %d kbps", btohl(pdu->total_bw)); - print_field("Max guaranteed bandwidth: %d kbps", btohl(pdu->max_bw)); - print_field("Min latency: %d", btohl(pdu->min_latency)); + print_field("Total bandwidth: %d kbps", le32_to_cpu(pdu->total_bw)); + print_field("Max guaranteed bandwidth: %d kbps", + le32_to_cpu(pdu->max_bw)); + print_field("Min latency: %d", le32_to_cpu(pdu->min_latency)); - print_field("PAL capabilities: 0x%4.4x", btohs(pdu->pal_cap)); - print_field("Max ASSOC length: %d", btohs(pdu->max_assoc_len)); + print_field("PAL capabilities: 0x%4.4x", le16_to_cpu(pdu->pal_cap)); + print_field("Max ASSOC length: %d", le16_to_cpu(pdu->max_assoc_len)); } static void amp_get_assoc_req(const struct l2cap_frame *frame) @@ -1597,8 +1604,8 @@ static void amp_packet(uint16_t index, bool in, uint16_t handle, return; } - control = bt_get_le16(data); - fcs = bt_get_le16(data + size - 2); + control = get_le16(data); + fcs = get_le16(data + size - 2); print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF, " %d dlen %d control 0x%4.4x fcs 0x%4.4x", @@ -1615,7 +1622,7 @@ static void amp_packet(uint16_t index, bool in, uint16_t handle, opcode = *((const uint8_t *) (data + 2)); ident = *((const uint8_t *) (data + 3)); - len = bt_get_le16(data + 4); + len = get_le16(data + 4); if (len != size - 8) { print_text(COLOR_ERROR, "invalid manager packet size"); @@ -1690,20 +1697,20 @@ static void print_uuid(const char *label, const void *data, uint16_t size) switch (size) { case 2: - str = uuid16_to_str(bt_get_le16(data)); - print_field("%s: %s (0x%4.4x)", label, str, bt_get_le16(data)); + str = uuid16_to_str(get_le16(data)); + print_field("%s: %s (0x%4.4x)", label, str, get_le16(data)); break; case 4: - str = uuid32_to_str(bt_get_le32(data)); - print_field("%s: %s (0x%8.8x)", label, str, bt_get_le32(data)); + str = uuid32_to_str(get_le32(data)); + print_field("%s: %s (0x%8.8x)", label, str, get_le32(data)); break; case 16: str = uuid128_to_str(data); print_field("%s: %s (%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x)", label, str, - bt_get_le32(data + 12), bt_get_le16(data + 10), - bt_get_le16(data + 8), bt_get_le16(data + 6), - bt_get_le32(data + 2), bt_get_le16(data + 0)); + get_le32(data + 12), get_le16(data + 10), + get_le16(data + 8), get_le16(data + 6), + get_le32(data + 2), get_le16(data + 0)); break; default: packet_hexdump(data, size); @@ -1714,7 +1721,7 @@ static void print_uuid(const char *label, const void *data, uint16_t size) static void print_handle_range(const char *label, const void *data) { print_field("%s: 0x%4.4x-0x%4.4x", label, - bt_get_le16(data), bt_get_le16(data + 2)); + get_le16(data), get_le16(data + 2)); } static void print_data_list(const char *label, uint8_t length, @@ -1730,7 +1737,7 @@ static void print_data_list(const char *label, uint8_t length, print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies"); while (size >= length) { - print_field("Handle: 0x%4.4x", bt_get_le16(data)); + print_field("Handle: 0x%4.4x", get_le16(data)); print_hex_field("Value", data + 2, length - 2); data += length; @@ -1765,7 +1772,7 @@ static void print_attribute_info(uint16_t type, const void *data, uint16_t len) break; } print_field(" Properties: 0x%2.2x", *((uint8_t *) data)); - print_field(" Handle: 0x%2.2x", bt_get_le16(data + 1)); + print_field(" Handle: 0x%2.2x", get_le16(data + 1)); print_uuid(" UUID", data + 3, len - 3); break; default: @@ -1840,7 +1847,7 @@ static void att_error_response(const struct l2cap_frame *frame) print_field("%s (0x%2.2x)", att_opcode_to_str(pdu->request), pdu->request); - print_field("Handle: 0x%4.4x", btohs(pdu->handle)); + print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle)); print_field("Error: %s (0x%2.2x)", str, pdu->error); } @@ -1848,14 +1855,14 @@ static void att_exchange_mtu_req(const struct l2cap_frame *frame) { const struct bt_l2cap_att_exchange_mtu_req *pdu = frame->data; - print_field("Client RX MTU: %d", btohs(pdu->mtu)); + print_field("Client RX MTU: %d", le16_to_cpu(pdu->mtu)); } static void att_exchange_mtu_rsp(const struct l2cap_frame *frame) { const struct bt_l2cap_att_exchange_mtu_rsp *pdu = frame->data; - print_field("Server RX MTU: %d", btohs(pdu->mtu)); + print_field("Server RX MTU: %d", le16_to_cpu(pdu->mtu)); } static void att_find_info_req(const struct l2cap_frame *frame) @@ -1878,7 +1885,7 @@ static const char *att_format_str(uint8_t format) static uint16_t print_info_data_16(const uint16_t *data, uint16_t len) { while (len >= 4) { - print_field("Handle: 0x%4.4x", bt_get_le16(data)); + print_field("Handle: 0x%4.4x", get_le16(data)); print_uuid("UUID", data + 2, 2); data += 4; len -= 4; @@ -1890,7 +1897,7 @@ static uint16_t print_info_data_16(const uint16_t *data, uint16_t len) static uint16_t print_info_data_128(const uint16_t *data, uint16_t len) { while (len >= 18) { - print_field("Handle: 0x%4.4x", bt_get_le16(data)); + print_field("Handle: 0x%4.4x", get_le16(data)); print_uuid("UUID", data + 2, 16); data += 18; len -= 18; @@ -1922,7 +1929,7 @@ static void att_find_by_type_val_req(const struct l2cap_frame *frame) print_handle_range("Handle range", frame->data); - type = bt_get_le16(frame->data + 4); + type = get_le16(frame->data + 4); print_attribute_info(type, frame->data + 6, frame->size - 6); } @@ -1959,7 +1966,7 @@ static void att_read_req(const struct l2cap_frame *frame) { const struct bt_l2cap_att_read_req *pdu = frame->data; - print_field("Handle: 0x%4.4x", btohs(pdu->handle)); + print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle)); } static void att_read_rsp(const struct l2cap_frame *frame) @@ -1969,8 +1976,8 @@ static void att_read_rsp(const struct l2cap_frame *frame) static void att_read_blob_req(const struct l2cap_frame *frame) { - print_field("Handle: 0x%4.4x", bt_get_le16(frame->data)); - print_field("Offset: 0x%4.4x", bt_get_le16(frame->data + 2)); + print_field("Handle: 0x%4.4x", get_le16(frame->data)); + print_field("Offset: 0x%4.4x", get_le16(frame->data + 2)); } static void att_read_blob_rsp(const struct l2cap_frame *frame) @@ -1986,7 +1993,7 @@ static void att_read_multiple_req(const struct l2cap_frame *frame) for (i = 0; i < count; i++) print_field("Handle: 0x%4.4x", - bt_get_le16(frame->data + (i * 2))); + get_le16(frame->data + (i * 2))); } static void att_read_group_type_req(const struct l2cap_frame *frame) @@ -2006,7 +2013,7 @@ static void att_read_group_type_rsp(const struct l2cap_frame *frame) static void att_write_req(const struct l2cap_frame *frame) { - print_field("Handle: 0x%4.4x", bt_get_le16(frame->data)); + print_field("Handle: 0x%4.4x", get_le16(frame->data)); print_hex_field(" Data", frame->data + 2, frame->size - 2); } @@ -2016,15 +2023,15 @@ static void att_write_rsp(const struct l2cap_frame *frame) static void att_prepare_write_req(const struct l2cap_frame *frame) { - print_field("Handle: 0x%4.4x", bt_get_le16(frame->data)); - print_field("Offset: 0x%4.4x", bt_get_le16(frame->data + 2)); + print_field("Handle: 0x%4.4x", get_le16(frame->data)); + print_field("Offset: 0x%4.4x", get_le16(frame->data + 2)); print_hex_field(" Data", frame->data + 4, frame->size - 4); } static void att_prepare_write_rsp(const struct l2cap_frame *frame) { - print_field("Handle: 0x%4.4x", bt_get_le16(frame->data)); - print_field("Offset: 0x%4.4x", bt_get_le16(frame->data + 2)); + print_field("Handle: 0x%4.4x", get_le16(frame->data)); + print_field("Offset: 0x%4.4x", get_le16(frame->data + 2)); print_hex_field(" Data", frame->data + 4, frame->size - 4); } @@ -2052,7 +2059,7 @@ static void att_handle_value_notify(const struct l2cap_frame *frame) { const struct bt_l2cap_att_handle_value_notify *pdu = frame->data; - print_field("Handle: 0x%4.4x", btohs(pdu->handle)); + print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle)); print_hex_field(" Data", frame->data + 2, frame->size - 2); } @@ -2060,7 +2067,7 @@ static void att_handle_value_ind(const struct l2cap_frame *frame) { const struct bt_l2cap_att_handle_value_ind *pdu = frame->data; - print_field("Handle: 0x%4.4x", btohs(pdu->handle)); + print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle)); print_hex_field(" Data", frame->data + 2, frame->size - 2); } @@ -2070,7 +2077,7 @@ static void att_handle_value_conf(const struct l2cap_frame *frame) static void att_write_command(const struct l2cap_frame *frame) { - print_field("Handle: 0x%4.4x", bt_get_le16(frame->data)); + print_field("Handle: 0x%4.4x", get_le16(frame->data)); print_hex_field(" Data", frame->data + 2, frame->size - 2); } @@ -2337,6 +2344,25 @@ static void print_smp_auth_req(uint8_t auth_req) str, (auth_req & 0x04) ? "MITM" : "No MITM", auth_req); } +static void print_smp_key_dist(const char *label, uint8_t dist) +{ + char str[19]; + + if (!(dist & 0x07)) { + strcpy(str, " "); + } else { + str[0] = '\0'; + if (dist & 0x01) + strcat(str, "EncKey "); + if (dist & 0x02) + strcat(str, "IdKey "); + if (dist & 0x04) + strcat(str, "Sign "); + } + + print_field("%s: %s(0x%2.2x)", label, str, dist); +} + static void smp_pairing_request(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_pairing_request *pdu = frame->data; @@ -2346,8 +2372,8 @@ static void smp_pairing_request(const struct l2cap_frame *frame) print_smp_auth_req(pdu->auth_req); print_field("Max encryption key size: %d", pdu->max_key_size); - print_field("Initiator key distribution: 0x%2.2x", pdu->init_key_dist); - print_field("Responder key distribution: 0x%2.2x", pdu->resp_key_dist); + print_smp_key_dist("Initiator key distribution", pdu->init_key_dist); + print_smp_key_dist("Responder key distribution", pdu->resp_key_dist); } static void smp_pairing_response(const struct l2cap_frame *frame) @@ -2359,8 +2385,8 @@ static void smp_pairing_response(const struct l2cap_frame *frame) print_smp_auth_req(pdu->auth_req); print_field("Max encryption key size: %d", pdu->max_key_size); - print_field("Initiator key distribution: 0x%2.2x", pdu->init_key_dist); - print_field("Responder key distribution: 0x%2.2x", pdu->resp_key_dist); + print_smp_key_dist("Initiator key distribution", pdu->init_key_dist); + print_smp_key_dist("Responder key distribution", pdu->resp_key_dist); } static void smp_pairing_confirm(const struct l2cap_frame *frame) @@ -2432,8 +2458,8 @@ static void smp_master_ident(const struct l2cap_frame *frame) { const struct bt_l2cap_smp_master_ident *pdu = frame->data; - print_field("EDIV: 0x%4.4x", btohs(pdu->ediv)); - print_field("Rand: 0x%16.16" PRIx64, btohll(pdu->rand)); + print_field("EDIV: 0x%4.4x", le16_to_cpu(pdu->ediv)); + print_field("Rand: 0x%16.16" PRIx64, le64_to_cpu(pdu->rand)); } static void smp_ident_info(const struct l2cap_frame *frame) @@ -2441,6 +2467,8 @@ static void smp_ident_info(const struct l2cap_frame *frame) const struct bt_l2cap_smp_ident_info *pdu = frame->data; print_hex_field("Identity resolving key", pdu->irk, 16); + + keys_update_identity_key(pdu->irk); } static void smp_ident_addr_info(const struct l2cap_frame *frame) @@ -2449,6 +2477,8 @@ static void smp_ident_addr_info(const struct l2cap_frame *frame) print_addr_type(pdu->addr_type); print_addr(pdu->addr, pdu->addr_type); + + keys_update_identity_addr(pdu->addr, pdu->addr_type); } static void smp_signing_info(const struct l2cap_frame *frame) @@ -2640,8 +2670,8 @@ void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags, return; } - len = btohs(hdr->len); - cid = btohs(hdr->cid); + len = le16_to_cpu(hdr->len); + cid = le16_to_cpu(hdr->cid); data += sizeof(*hdr); size -= sizeof(*hdr); @@ -2715,8 +2745,8 @@ void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags, return; } - len = btohs(hdr->len); - cid = btohs(hdr->cid); + len = le16_to_cpu(hdr->len); + cid = le16_to_cpu(hdr->cid); data += sizeof(*hdr); size -= sizeof(*hdr); diff --git a/monitor/l2cap.h b/monitor/l2cap.h index 30bcb6a..0d18478 100644 --- a/monitor/l2cap.h +++ b/monitor/l2cap.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/ll.c b/monitor/ll.c index 1a66572..e9d0cf6 100644 --- a/monitor/ll.c +++ b/monitor/ll.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -26,10 +26,10 @@ #include #endif +#include #include -#include - +#include "src/shared/util.h" #include "display.h" #include "packet.h" #include "crc.h" @@ -328,7 +328,7 @@ void ll_packet(uint16_t frequency, const void *data, uint8_t size) return; } - access_addr = btohl(hdr->access_addr); + access_addr = le32_to_cpu(hdr->access_addr); pdu_data = data + sizeof(*hdr); pdu_len = size - sizeof(*hdr) - 3; @@ -380,11 +380,11 @@ static void conn_update_req(const void *data, uint8_t size) const struct bt_ll_conn_update_req *pdu = data; print_field("Transmit window size: %u", pdu->win_size); - print_field("Transmit window offset: %u", btohs(pdu->win_offset)); - print_field("Connection interval: %u", btohs(pdu->interval)); - print_field("Connection slave latency: %u", btohs(pdu->latency)); - print_field("Connection supervision timeout: %u", btohs(pdu->timeout));; - print_field("Connection instant: %u", btohs(pdu->instant)); + print_field("Transmit window offset: %u", le16_to_cpu(pdu->win_offset)); + print_field("Connection interval: %u", le16_to_cpu(pdu->interval)); + print_field("Connection slave latency: %u", le16_to_cpu(pdu->latency)); + print_field("Connection supervision timeout: %u", le16_to_cpu(pdu->timeout)); + print_field("Connection instant: %u", le16_to_cpu(pdu->instant)); } static void channel_map_req(const void *data, uint8_t size) @@ -392,32 +392,32 @@ static void channel_map_req(const void *data, uint8_t size) const struct bt_ll_channel_map_req *pdu = data; packet_print_channel_map_ll(pdu->map); - print_field("Connection instant: %u", btohs(pdu->instant)); + print_field("Connection instant: %u", le16_to_cpu(pdu->instant)); } static void terminate_ind(const void *data, uint8_t size) { const struct bt_ll_terminate_ind *pdu = data; - print_field("Error code: 0x%2.2x", pdu->error); + packet_print_error("Error code", pdu->error); } static void enc_req(const void *data, uint8_t size) { const struct bt_ll_enc_req *pdu = data; - print_field("Rand: 0x%16.16" PRIx64, btohll(pdu->rand)); - print_field("EDIV: 0x%4.4x", btohs(pdu->ediv)); - print_field("SKD (master): 0x%16.16" PRIx64, btohll(pdu->skd)); - print_field("IV (master): 0x%8.8x", btohl(pdu->iv)); + print_field("Rand: 0x%16.16" PRIx64, le64_to_cpu(pdu->rand)); + print_field("EDIV: 0x%4.4x", le16_to_cpu(pdu->ediv)); + print_field("SKD (master): 0x%16.16" PRIx64, le64_to_cpu(pdu->skd)); + print_field("IV (master): 0x%8.8x", le32_to_cpu(pdu->iv)); } static void enc_rsp(const void *data, uint8_t size) { const struct bt_ll_enc_rsp *pdu = data; - print_field("SKD (slave): 0x%16.16" PRIx64, btohll(pdu->skd)); - print_field("IV (slave): 0x%8.8x", btohl(pdu->iv)); + print_field("SKD (slave): 0x%16.16" PRIx64, le64_to_cpu(pdu->skd)); + print_field("IV (slave): 0x%8.8x", le32_to_cpu(pdu->iv)); } static const char *opcode_to_string(uint8_t opcode); @@ -449,15 +449,15 @@ static void version_ind(const void *data, uint8_t size) const struct bt_ll_version_ind *pdu = data; packet_print_version("Version", pdu->version, - "Subversion", btohs(pdu->subversion)); - packet_print_company("Company", btohs(pdu->company)); + "Subversion", le16_to_cpu(pdu->subversion)); + packet_print_company("Company", le16_to_cpu(pdu->company)); } static void reject_ind(const void *data, uint8_t size) { const struct bt_ll_reject_ind *pdu = data; - print_field("Error code: 0x%2.2x", pdu->error); + packet_print_error("Error code", pdu->error); } struct llcp_data { diff --git a/monitor/ll.h b/monitor/ll.h index 17d934b..ee28f40 100644 --- a/monitor/ll.h +++ b/monitor/ll.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/lmp.c b/monitor/lmp.c index e6db107..4a05973 100644 --- a/monitor/lmp.c +++ b/monitor/lmp.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -28,14 +28,593 @@ #include +#include "src/shared/util.h" #include "display.h" #include "packet.h" +#include "bt.h" #include "lmp.h" #define COLOR_OPCODE COLOR_MAGENTA #define COLOR_OPCODE_UNKNOWN COLOR_WHITE_BG -#define ESC4(x) ((127 << 8) | (x)) +static const char *get_opcode_str(uint16_t opcode); + +static void print_opcode(uint16_t opcode) +{ + const char *str; + + str = get_opcode_str(opcode); + if (!str) + str = "Unknown"; + + if (opcode & 0xff00) + print_field("Operation: %s (%u/%u)", str, + opcode >> 8, opcode & 0xff); + else + print_field("Operation: %s (%u)", str, opcode); +} + +static void accepted(const void *data, uint8_t size) +{ + const struct bt_lmp_accepted *pdu = data; + + print_opcode(pdu->opcode); +} + +static void not_accepted(const void *data, uint8_t size) +{ + const struct bt_lmp_not_accepted *pdu = data; + + print_opcode(pdu->opcode); + packet_print_error("Error code", pdu->error); +} + +static void clkoffset_req(const void *data, uint8_t size) +{ +} + +static void detach(const void *data, uint8_t size) +{ + const struct bt_lmp_detach *pdu = data; + + packet_print_error("Error code", pdu->error); +} + +static void au_rand(const void *data, uint8_t size) +{ + const struct bt_lmp_au_rand *pdu = data; + + packet_hexdump(pdu->number, 16); +} + +static void sres(const void *data, uint8_t size) +{ + const struct bt_lmp_sres *pdu = data; + + packet_hexdump(pdu->response, 4); +} + +static void encryption_mode_req(const void *data, uint8_t size) +{ + const struct bt_lmp_encryption_mode_req *pdu = data; + const char *str; + + switch (pdu->mode) { + case 0x00: + str = "No encryption"; + break; + case 0x01: + str = "Encryption"; + break; + case 0x02: + str = "Encryption"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Mode: %s (%u)", str, pdu->mode); +} + +static void encryption_key_size_req(const void *data, uint8_t size) +{ + const struct bt_lmp_encryption_key_size_req *pdu = data; + + print_field("Key size: %u", pdu->key_size); +} + +static void start_encryption_req(const void *data, uint8_t size) +{ + const struct bt_lmp_start_encryption_req *pdu = data; + + packet_hexdump(pdu->number, 16); +} + +static void stop_encryption_req(const void *data, uint8_t size) +{ +} + +static void unsniff_req(const void *data, uint8_t size) +{ +} + +static void max_power(const void *data, uint8_t size) +{ +} + +static void min_power(const void *data, uint8_t size) +{ +} + +static void auto_rate(const void *data, uint8_t size) +{ +} + +static void version_req(const void *data, uint8_t size) +{ + const struct bt_lmp_version_req *pdu = data; + + packet_print_version("Version", pdu->version, + "Subversion", le16_to_cpu(pdu->subversion)); + packet_print_company("Company", le16_to_cpu(pdu->company)); +} + +static void version_res(const void *data, uint8_t size) +{ + const struct bt_lmp_version_res *pdu = data; + + packet_print_version("Version", pdu->version, + "Subversion", le16_to_cpu(pdu->subversion)); + packet_print_company("Company", le16_to_cpu(pdu->company)); +} + +static void features_req(const void *data, uint8_t size) +{ + const struct bt_lmp_features_req *pdu = data; + + packet_print_features_lmp(pdu->features, 0x00); +} + +static void features_res(const void *data, uint8_t size) +{ + const struct bt_lmp_features_res *pdu = data; + + packet_print_features_lmp(pdu->features, 0x00); +} + +static void max_slot(const void *data, uint8_t size) +{ + const struct bt_lmp_max_slot *pdu = data; + + print_field("Slots: 0x%4.4x", pdu->slots); +} + +static void max_slot_req(const void *data, uint8_t size) +{ + const struct bt_lmp_max_slot_req *pdu = data; + + print_field("Slots: 0x%4.4x", pdu->slots); +} + +static void timing_accuracy_req(const void *data, uint8_t size) +{ +} + +static void timing_accuracy_res(const void *data, uint8_t size) +{ + const struct bt_lmp_timing_accuracy_res *pdu = data; + + print_field("Drift: %u ppm", pdu->drift); + print_field("Jitter: %u usec", pdu->jitter); +} + +static void setup_complete(const void *data, uint8_t size) +{ +} + +static void use_semi_permanent_key(const void *data, uint8_t size) +{ +} + +static void host_connection_req(const void *data, uint8_t size) +{ +} + +static void page_scan_mode_req(const void *data, uint8_t size) +{ + const struct bt_lmp_page_scan_mode_req *pdu = data; + const char *str; + + switch (pdu->scheme) { + case 0x00: + str = "Mandatory"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Paging scheme: %s (%u)", str, pdu->scheme); + + if (pdu->scheme == 0x00) { + switch (pdu->settings) { + case 0x00: + str = "R0"; + break; + case 0x01: + str = "R1"; + break; + case 0x02: + str = "R2"; + break; + default: + str = "Reserved"; + break; + } + } else + str = "Reserved"; + + print_field("Paging scheme settings: %s (%u)", str, pdu->settings); +} + +static void test_activate(const void *data, uint8_t size) +{ +} + +static void encryption_key_size_mask_req(const void *data, uint8_t size) +{ +} + +static void set_afh(const void *data, uint8_t size) +{ + const struct bt_lmp_set_afh *pdu = data; + const char *str; + + print_field("Instant: %u", le32_to_cpu(pdu->instant)); + + switch (pdu->mode) { + case 0x00: + str = "Disabled"; + break; + case 0x01: + str = "Enabled"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Mode: %s (0x%2.2x)", str, pdu->mode); + packet_print_channel_map_lmp(pdu->map); +} + +static void encapsulated_header(const void *data, uint8_t size) +{ + const struct bt_lmp_encapsulated_header *pdu = data; + const char *str; + + print_field("Major type: %u", pdu->major); + print_field("Minor type: %u", pdu->minor); + + if (pdu->major == 0x01) { + switch (pdu->minor) { + case 0x01: + str = "P-192 Public Key"; + break; + case 0x02: + str = "P-256 Public Key"; + break; + default: + str = "Reserved"; + break; + } + + print_field(" %s", str); + } + + print_field("Length: %u", pdu->length); +} + +static void encapsulated_payload(const void *data, uint8_t size) +{ + const struct bt_lmp_encapsulated_payload *pdu = data; + + packet_hexdump(pdu->data, 16); +} + +static void simple_pairing_confirm(const void *data, uint8_t size) +{ + const struct bt_lmp_simple_pairing_confirm *pdu = data; + + packet_hexdump(pdu->value, 16); +} + +static void simple_pairing_number(const void *data, uint8_t size) +{ + const struct bt_lmp_simple_pairing_number *pdu = data; + + packet_hexdump(pdu->value, 16); +} + +static void dhkey_check(const void *data, uint8_t size) +{ + const struct bt_lmp_dhkey_check *pdu = data; + + packet_hexdump(pdu->value, 16); +} + +static void accepted_ext(const void *data, uint8_t size) +{ + const struct bt_lmp_accepted_ext *pdu = data; + uint16_t opcode; + + switch (pdu->escape) { + case 127: + opcode = LMP_ESC4(pdu->opcode); + break; + default: + return; + } + + print_opcode(opcode); +} + +static void not_accepted_ext(const void *data, uint8_t size) +{ + const struct bt_lmp_not_accepted_ext *pdu = data; + uint16_t opcode; + + switch (pdu->escape) { + case 127: + opcode = LMP_ESC4(pdu->opcode); + break; + default: + return; + } + + print_opcode(opcode); + print_field("Error code: %u", pdu->error); +} + +static void features_req_ext(const void *data, uint8_t size) +{ + const struct bt_lmp_features_req_ext *pdu = data; + + print_field("Features page: %u", pdu->page); + print_field("Max supported page: %u", pdu->max_page); + packet_print_features_lmp(pdu->features, pdu->page); +} + +static void features_res_ext(const void *data, uint8_t size) +{ + const struct bt_lmp_features_res_ext *pdu = data; + + print_field("Features page: %u", pdu->page); + print_field("Max supported page: %u", pdu->max_page); + packet_print_features_lmp(pdu->features, pdu->page); +} + +static void packet_type_table_req(const void *data, uint8_t size) +{ + const struct bt_lmp_packet_type_table_req *pdu = data; + const char *str; + + switch (pdu->table) { + case 0x00: + str = "1 Mbps only"; + break; + case 0x01: + str = "2/3 Mbps"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Table: %s (0x%2.2x)", str, pdu->table); +} + +static void channel_classification_req(const void *data, uint8_t size) +{ + const struct bt_lmp_channel_classification_req *pdu = data; + const char *str; + + switch (pdu->mode) { + case 0x00: + str = "Disabled"; + break; + case 0x01: + str = "Enabled"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Reporting mode: %s (0x%2.2x)", str, pdu->mode); + print_field("Min interval: 0x%2.2x", pdu->min_interval); + print_field("Max interval: 0x%2.2x", pdu->max_interval); +} + +static void channel_classification(const void *data, uint8_t size) +{ + const struct bt_lmp_channel_classification *pdu = data; + char str[21]; + int i; + + for (i = 0; i < 10; i++) + sprintf(str + (i * 2), "%2.2x", pdu->classification[i]); + + print_field("Features: 0x%s", str); +} + +static void pause_encryption_req(const void *data, uint8_t size) +{ +} + +static void resume_encryption_req(const void *data, uint8_t size) +{ +} + +static void io_capability_req(const void *data, uint8_t size) +{ + const struct bt_lmp_io_capability_req *pdu = data; + const char *str; + + packet_print_io_capability(pdu->capability); + + switch (pdu->oob_data) { + case 0x00: + str = "No authentication data received"; + break; + case 0x01: + str = "Authentication data received"; + break; + default: + str = "Reserved"; + break; + } + + print_field("OOB data: %s (0x%2.2x)", str, pdu->oob_data); + + packet_print_io_authentication(pdu->authentication); +} + +static void io_capability_res(const void *data, uint8_t size) +{ + const struct bt_lmp_io_capability_res *pdu = data; + const char *str; + + packet_print_io_capability(pdu->capability); + + switch (pdu->oob_data) { + case 0x00: + str = "No authentication data received"; + break; + case 0x01: + str = "Authentication data received"; + break; + default: + str = "Reserved"; + break; + } + + print_field("OOB data: %s (0x%2.2x)", str, pdu->oob_data); + + packet_print_io_authentication(pdu->authentication); +} + +static void numeric_comparison_failed(const void *data, uint8_t size) +{ +} + +static void passkey_failed(const void *data, uint8_t size) +{ +} + +static void oob_failed(const void *data, uint8_t size) +{ +} + +static void power_control_req(const void *data, uint8_t size) +{ + const struct bt_lmp_power_control_req *pdu = data; + const char *str; + + switch (pdu->request) { + case 0x00: + str = "Decrement power one step"; + break; + case 0x01: + str = "Increment power one step"; + break; + case 0x02: + str = "Increase to maximum power"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Request: %s (0x%2.2x)", str, pdu->request); +} + +static void power_control_res(const void *data, uint8_t size) +{ + const struct bt_lmp_power_control_res *pdu = data; + const char *str; + + print_field("Response: 0x%2.2x", pdu->response); + + switch (pdu->response & 0x03) { + case 0x00: + str = "Not supported"; + break; + case 0x01: + str = "Changed one step"; + break; + case 0x02: + str = "Max power"; + break; + case 0x03: + str = "Min power"; + break; + default: + str = "Reserved"; + break; + } + + print_field(" GFSK: %s", str); + + switch ((pdu->response & 0x0c) >> 2) { + case 0x00: + str = "Not supported"; + break; + case 0x01: + str = "Changed one step"; + break; + case 0x02: + str = "Max power"; + break; + case 0x03: + str = "Min power"; + break; + default: + str = "Reserved"; + break; + } + + print_field(" DQPSK: %s", str); + + switch ((pdu->response & 0x30) >> 4) { + case 0x00: + str = "Not supported"; + break; + case 0x01: + str = "Changed one step"; + break; + case 0x02: + str = "Max power"; + break; + case 0x03: + str = "Min power"; + break; + default: + str = "Reserved"; + break; + } + + print_field(" 8DPSK: %s", str); +} + +static void ping_req(const void *data, uint8_t size) +{ +} + +static void ping_res(const void *data, uint8_t size) +{ +} struct lmp_data { uint16_t opcode; @@ -48,28 +627,28 @@ struct lmp_data { static const struct lmp_data lmp_table[] = { { 1, "LMP_name_req" }, { 2, "LMP_name_res" }, - { 3, "LMP_accepted" }, - { 4, "LMP_not_accepted" }, - { 5, "LMP_clkoffset_req" }, + { 3, "LMP_accepted", accepted, 1, true }, + { 4, "LMP_not_accepted", not_accepted, 2, true }, + { 5, "LMP_clkoffset_req", clkoffset_req, 0, true }, { 6, "LMP_clkoffset_res" }, - { 7, "LMP_detach" }, + { 7, "LMP_detach", detach, 1, true }, { 8, "LMP_in_rand" }, { 9, "LMP_comb_key" }, { 10, "LMP_unit_key" }, - { 11, "LMP_au_rand" }, - { 12, "LMP_sres" }, + { 11, "LMP_au_rand", au_rand, 16, true }, + { 12, "LMP_sres", sres, 4, true }, { 13, "LMP_temp_rand" }, { 14, "LMP_temp_key" }, - { 15, "LMP_encryption_mode_req" }, - { 16, "LMP_encryption_key_size_req" }, - { 17, "LMP_start_encryption_req" }, - { 18, "LMP_stop_encryption_req" }, + { 15, "LMP_encryption_mode_req", encryption_mode_req, 1, true }, + { 16, "LMP_encryption_key_size_req", encryption_key_size_req, 1, true }, + { 17, "LMP_start_encryption_req", start_encryption_req, 16, true }, + { 18, "LMP_stop_encryption_req", stop_encryption_req, 0, true }, { 19, "LMP_switch_req" }, { 20, "LMP_hold" }, { 21, "LMP_hold_req" }, { 22, "LMP_sniff" }, { 23, "LMP_sniff_req" }, - { 24, "LMP_unsniff_req" }, + { 24, "LMP_unsniff_req", unsniff_req, 0, true }, { 25, "LMP_park_req" }, { 26, "LMP_park" }, { 27, "LMP_set_broadcast_scan_window" }, @@ -78,69 +657,80 @@ static const struct lmp_data lmp_table[] = { { 30, "LMP_unpark_PM_ADDR_req" }, { 31, "LMP_incr_power_req" }, { 32, "LMP_decr_power_req" }, - { 33, "LMP_max_power" }, - { 34, "LMP_min_power" }, - { 35, "LMP_auto_rate" }, + { 33, "LMP_max_power", max_power, 0, true }, + { 34, "LMP_min_power", min_power, 0, true }, + { 35, "LMP_auto_rate", auto_rate, 0, true }, { 36, "LMP_preferred_rate" }, - { 37, "LMP_version_req" }, - { 38, "LMP_version_res" }, - { 39, "LMP_features_req" }, - { 40, "LMP_features_res" }, + { 37, "LMP_version_req", version_req, 5, true }, + { 38, "LMP_version_res", version_res, 5, true }, + { 39, "LMP_features_req", features_req, 8, true }, + { 40, "LMP_features_res", features_res, 8, true }, { 41, "LMP_quality_of_service" }, { 42, "LMP_quality_of_service_req" }, { 43, "LMP_SCO_link_req" }, { 44, "LMP_remove_SCO_link_req" }, - { 45, "LMP_max_slot" }, - { 46, "LMP_max_slot_req" }, - { 47, "LMP_timing_accuracy_req" }, - { 48, "LMP_timing_accuracy_res" }, - { 49, "LMP_setup_complete" }, - { 50, "LMP_use_semi_permanent_key" }, - { 51, "LMP_host_connection_req" }, + { 45, "LMP_max_slot", max_slot, 1, true }, + { 46, "LMP_max_slot_req", max_slot_req, 1, true }, + { 47, "LMP_timing_accuracy_req", timing_accuracy_req, 0, true }, + { 48, "LMP_timing_accuracy_res", timing_accuracy_res, 2, true }, + { 49, "LMP_setup_complete", setup_complete, 0, true }, + { 50, "LMP_use_semi_permanent_key", use_semi_permanent_key, 0, true }, + { 51, "LMP_host_connection_req", host_connection_req, 0, true }, { 52, "LMP_slot_offset" }, { 53, "LMP_page_mode_req" }, - { 54, "LMP_Page_scan_mode_req" }, + { 54, "LMP_page_scan_mode_req", page_scan_mode_req, 2, true }, { 55, "LMP_supervision_timeout" }, - { 56, "LMP_test_activate" }, + { 56, "LMP_test_activate", test_activate, 0, true }, { 57, "LMP_test_control" }, - { 58, "LMP_encryption_key_size_mask_req" }, + { 58, "LMP_encryption_key_size_mask_req", encryption_key_size_mask_req, 0, true }, { 59, "LMP_encryption_key_size_mask_res" }, - { 60, "LMP_set_AFH" }, - { 61, "LMP_encapsulated_header" }, - { 62, "LMP_encapsulated_payload" }, - { 63, "LMP_simple_pairing_confirm" }, - { 64, "LMP_simple_pairing_number" }, - { 65, "LMP_DHkey_check" }, + { 60, "LMP_set_AFH", set_afh, 15, true }, + { 61, "LMP_encapsulated_header", encapsulated_header, 3, true }, + { 62, "LMP_encapsulated_payload", encapsulated_payload, 16, true }, + { 63, "LMP_simple_pairing_confirm", simple_pairing_confirm, 16, true }, + { 64, "LMP_simple_pairing_number", simple_pairing_number, 16, true }, + { 65, "LMP_DHkey_check", dhkey_check, 16, true }, { 66, "LMP_pause_encryption_aes_req" }, - { ESC4(1), "LMP_accepted_ext" }, - { ESC4(2), "LMP_not_accepted_ext" }, - { ESC4(3), "LMP_features_req_ext" }, - { ESC4(4), "LMP_features_res_ext" }, - { ESC4(5), "LMP_clk_adj" }, - { ESC4(6), "LMP_clk_adj_ack" }, - { ESC4(7), "LMP_clk_adj_req" }, - { ESC4(11), "LMP_packet_type_table" }, - { ESC4(12), "LMP_eSCO_link_req" }, - { ESC4(13), "LMP_remove_eSCO_link_req" }, - { ESC4(16), "LMP_channel_classification_req" }, - { ESC4(17), "LMP_channel_classification" }, - { ESC4(21), "LMP_sniff_subrating_req" }, - { ESC4(22), "LMP_sniff_subrating_res" }, - { ESC4(23), "LMP_pause_encryption_req" }, - { ESC4(24), "LMP_resume_encryption_req" }, - { ESC4(25), "LMP_IO_capability_req" }, - { ESC4(26), "LMP_IO_capability_res" }, - { ESC4(27), "LMP_numeric_comparision_failed" }, - { ESC4(28), "LMP_passkey_failed" }, - { ESC4(29), "LMP_oob_failed" }, - { ESC4(30), "LMP_keypress_notification" }, - { ESC4(31), "LMP_power_control_req" }, - { ESC4(32), "LMP_power_control_res" }, - { ESC4(33), "LMP_ping_req" }, - { ESC4(34), "LMP_ping_res" }, + { LMP_ESC4(1), "LMP_accepted_ext", accepted_ext, 2, true }, + { LMP_ESC4(2), "LMP_not_accepted_ext", not_accepted_ext, 3, true }, + { LMP_ESC4(3), "LMP_features_req_ext", features_req_ext, 10, true }, + { LMP_ESC4(4), "LMP_features_res_ext", features_res_ext, 10, true }, + { LMP_ESC4(5), "LMP_clk_adj" }, + { LMP_ESC4(6), "LMP_clk_adj_ack" }, + { LMP_ESC4(7), "LMP_clk_adj_req" }, + { LMP_ESC4(11), "LMP_packet_type_table_req", packet_type_table_req, 1, true }, + { LMP_ESC4(12), "LMP_eSCO_link_req" }, + { LMP_ESC4(13), "LMP_remove_eSCO_link_req" }, + { LMP_ESC4(16), "LMP_channel_classification_req", channel_classification_req, 5, true }, + { LMP_ESC4(17), "LMP_channel_classification", channel_classification, 10, true }, + { LMP_ESC4(21), "LMP_sniff_subrating_req" }, + { LMP_ESC4(22), "LMP_sniff_subrating_res" }, + { LMP_ESC4(23), "LMP_pause_encryption_req", pause_encryption_req, 0, true }, + { LMP_ESC4(24), "LMP_resume_encryption_req", resume_encryption_req, 0, true }, + { LMP_ESC4(25), "LMP_IO_capability_req", io_capability_req, 3, true }, + { LMP_ESC4(26), "LMP_IO_capability_res", io_capability_res, 3, true }, + { LMP_ESC4(27), "LMP_numeric_comparison_failed", numeric_comparison_failed, 0, true }, + { LMP_ESC4(28), "LMP_passkey_failed", passkey_failed, 0, true }, + { LMP_ESC4(29), "LMP_oob_failed", oob_failed, 0, true }, + { LMP_ESC4(30), "LMP_keypress_notification" }, + { LMP_ESC4(31), "LMP_power_control_req", power_control_req, 1, true }, + { LMP_ESC4(32), "LMP_power_control_res", power_control_res, 1, true }, + { LMP_ESC4(33), "LMP_ping_req", ping_req, 0, true }, + { LMP_ESC4(34), "LMP_ping_res", ping_res, 0, true }, { } }; +static const char *get_opcode_str(uint16_t opcode) +{ + int i; + + for (i = 0; lmp_table[i].str; i++) { + if (lmp_table[i].opcode == opcode) + return lmp_table[i].str; + } + + return NULL; +} void lmp_packet(const void *data, uint8_t size) { @@ -155,7 +745,7 @@ void lmp_packet(const void *data, uint8_t size) switch (opcode) { case 127: - opcode = ESC4(((const uint8_t *) data)[1]); + opcode = LMP_ESC4(((const uint8_t *) data)[1]); off = 2; break; case 126: @@ -187,10 +777,10 @@ void lmp_packet(const void *data, uint8_t size) if (opcode & 0xff00) print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, - " (%d/%d) TID %d", opcode >> 8, opcode & 0xff, tid); + " (%u/%u) TID %u", opcode >> 8, opcode & 0xff, tid); else print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, - " (%d) TID %d", opcode, tid); + " (%u) TID %d", opcode, tid); if (!lmp_data || !lmp_data->func) { packet_hexdump(data + off, size - off); @@ -198,13 +788,13 @@ void lmp_packet(const void *data, uint8_t size) } if (lmp_data->fixed) { - if (size - 1 != lmp_data->size) { + if (size - off != lmp_data->size) { print_text(COLOR_ERROR, "invalid packet size"); packet_hexdump(data + off, size - off); return; } } else { - if (size - 1 < lmp_data->size) { + if (size - off < lmp_data->size) { print_text(COLOR_ERROR, "too short packet"); packet_hexdump(data + off, size - off); return; @@ -213,3 +803,17 @@ void lmp_packet(const void *data, uint8_t size) lmp_data->func(data + off, size - off); } + +void lmp_todo(void) +{ + int i; + + printf("LMP operations with missing decodings:\n"); + + for (i = 0; lmp_table[i].str; i++) { + if (lmp_table[i].func) + continue; + + printf("\t%s\n", lmp_table[i].str); + } +} diff --git a/monitor/lmp.h b/monitor/lmp.h index b2beda4..9b5393b 100644 --- a/monitor/lmp.h +++ b/monitor/lmp.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -25,3 +25,5 @@ #include void lmp_packet(const void *data, uint8_t size); + +void lmp_todo(void); diff --git a/monitor/main.c b/monitor/main.c index aed9b03..d4e8e6d 100644 --- a/monitor/main.c +++ b/monitor/main.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -34,6 +34,10 @@ #include "mainloop.h" #include "packet.h" +#include "lmp.h" +#include "keys.h" +#include "analyze.h" +#include "ellisys.h" #include "control.h" static void signal_callback(int signum, void *user_data) @@ -54,22 +58,26 @@ static void usage(void) printf("options:\n" "\t-r, --read Read traces in btsnoop format\n" "\t-w, --write Save traces in btsnoop format\n" + "\t-a, --analyze Analyze traces in btsnoop format\n" "\t-s, --server Start monitor server socket\n" "\t-i, --index Show only specified controller\n" "\t-t, --time Show time instead of time offset\n" "\t-T, --date Show time and date information\n" "\t-S, --sco Dump SCO traffic\n" + "\t-E, --ellisys [ip] Send Ellisys HCI Injection\n" "\t-h, --help Show help options\n"); } static const struct option main_options[] = { { "read", required_argument, NULL, 'r' }, { "write", required_argument, NULL, 'w' }, + { "analyze", required_argument, NULL, 'a' }, { "server", required_argument, NULL, 's' }, { "index", required_argument, NULL, 'i' }, { "time", no_argument, NULL, 't' }, { "date", no_argument, NULL, 'T' }, { "sco", no_argument, NULL, 'S' }, + { "ellisys", required_argument, NULL, 'E' }, { "todo", no_argument, NULL, '#' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, @@ -79,7 +87,13 @@ static const struct option main_options[] = { int main(int argc, char *argv[]) { unsigned long filter_mask = 0; - const char *str, *reader_path = NULL, *writer_path = NULL; + const char *reader_path = NULL; + const char *writer_path = NULL; + const char *analyze_path = NULL; + const char *ellisys_server = NULL; + unsigned short ellisys_port = 0; + const char *str; + int exit_status; sigset_t mask; mainloop_init(); @@ -89,7 +103,7 @@ int main(int argc, char *argv[]) for (;;) { int opt; - opt = getopt_long(argc, argv, "r:w:s:i:tTSvh", + opt = getopt_long(argc, argv, "r:w:a:s:i:tTSE:vh", main_options, NULL); if (opt < 0) break; @@ -101,6 +115,9 @@ int main(int argc, char *argv[]) case 'w': writer_path = optarg; break; + case 'a': + analyze_path = optarg; + break; case 's': control_server(optarg); break; @@ -127,8 +144,13 @@ int main(int argc, char *argv[]) case 'S': filter_mask |= PACKET_FILTER_SHOW_SCO_DATA; break; + case 'E': + ellisys_server = optarg; + ellisys_port = 24352; + break; case '#': packet_todo(); + lmp_todo(); return EXIT_SUCCESS; case 'v': printf("%s\n", VERSION); @@ -146,6 +168,11 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } + if (reader_path && analyze_path) { + fprintf(stderr, "Display and analyze can't be combined\n"); + return EXIT_FAILURE; + } + sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); @@ -154,9 +181,19 @@ int main(int argc, char *argv[]) printf("Bluetooth monitor ver %s\n", VERSION); + keys_setup(); + packet_set_filter(filter_mask); + if (analyze_path) { + analyze_trace(analyze_path); + return EXIT_SUCCESS; + } + if (reader_path) { + if (ellisys_server) + ellisys_enable(ellisys_server, ellisys_port); + control_reader(reader_path); return EXIT_SUCCESS; } @@ -164,8 +201,15 @@ int main(int argc, char *argv[]) if (writer_path) control_writer(writer_path); + if (ellisys_server) + ellisys_enable(ellisys_server, ellisys_port); + if (control_tracing() < 0) return EXIT_FAILURE; - return mainloop_run(); + exit_status = mainloop_run(); + + keys_cleanup(); + + return exit_status; } diff --git a/monitor/mainloop.c b/monitor/mainloop.c index 1cc787e..8d4b391 100644 --- a/monitor/mainloop.c +++ b/monitor/mainloop.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -222,9 +222,6 @@ int mainloop_modify_fd(int fd, uint32_t events) if (!data) return -ENXIO; - if (data->events == events) - return 0; - memset(&ev, 0, sizeof(ev)); ev.events = events; ev.data.ptr = data; @@ -290,20 +287,21 @@ static void timeout_callback(int fd, uint32_t events, void *user_data) data->callback(data->fd, data->user_data); } -static inline int timeout_set(int fd, unsigned int seconds) +static inline int timeout_set(int fd, unsigned int msec) { struct itimerspec itimer; + unsigned int sec = msec / 1000; memset(&itimer, 0, sizeof(itimer)); itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_nsec = 0; - itimer.it_value.tv_sec = seconds; - itimer.it_value.tv_nsec = 0; + itimer.it_value.tv_sec = sec; + itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000; return timerfd_settime(fd, 0, &itimer, NULL); } -int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback, +int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback, void *user_data, mainloop_destroy_func destroy) { struct timeout_data *data; @@ -326,8 +324,8 @@ int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback, return -EIO; } - if (seconds > 0) { - if (timeout_set(data->fd, seconds) < 0) { + if (msec > 0) { + if (timeout_set(data->fd, msec) < 0) { close(data->fd); free(data); return -EIO; @@ -344,10 +342,10 @@ int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback, return data->fd; } -int mainloop_modify_timeout(int id, unsigned int seconds) +int mainloop_modify_timeout(int id, unsigned int msec) { - if (seconds > 0) { - if (timeout_set(id, seconds) < 0) + if (msec > 0) { + if (timeout_set(id, msec) < 0) return -EIO; } diff --git a/monitor/mainloop.h b/monitor/mainloop.h index 04745d2..dafec8b 100644 --- a/monitor/mainloop.h +++ b/monitor/mainloop.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2011-2014 Intel Corporation * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -40,9 +40,9 @@ int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback, int mainloop_modify_fd(int fd, uint32_t events); int mainloop_remove_fd(int fd); -int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback, +int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback, void *user_data, mainloop_destroy_func destroy); -int mainloop_modify_timeout(int fd, unsigned int seconds); +int mainloop_modify_timeout(int fd, unsigned int msec); int mainloop_remove_timeout(int id); int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback, diff --git a/monitor/packet.c b/monitor/packet.c index 6ec7d9b..d8ff2da 100644 --- a/monitor/packet.c +++ b/monitor/packet.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -41,13 +41,16 @@ #include #include +#include "src/shared/util.h" +#include "src/shared/btsnoop.h" #include "display.h" #include "bt.h" #include "ll.h" +#include "hwdb.h" +#include "keys.h" #include "uuid.h" #include "l2cap.h" #include "control.h" -#include "btsnoop.h" #include "vendor.h" #include "packet.h" @@ -68,9 +71,11 @@ #define COLOR_UNKNOWN_ERROR COLOR_WHITE_BG #define COLOR_UNKNOWN_FEATURE_BIT COLOR_WHITE_BG +#define COLOR_UNKNOWN_COMMAND_BIT COLOR_WHITE_BG #define COLOR_UNKNOWN_EVENT_MASK COLOR_WHITE_BG #define COLOR_UNKNOWN_LE_STATES COLOR_WHITE_BG #define COLOR_UNKNOWN_SERVICE_CLASS COLOR_WHITE_BG +#define COLOR_UNKNOWN_PKT_TYPE_BIT COLOR_WHITE_BG #define COLOR_PHY_PACKET COLOR_BLUE @@ -394,27 +399,55 @@ static void print_reason(uint8_t reason) print_error("Reason", reason); } -static void print_bdaddr(const uint8_t *bdaddr) +void packet_print_error(const char *label, uint8_t error) { - print_field("Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X" - " (OUI %2.2X-%2.2X-%2.2X)", - bdaddr[5], bdaddr[4], bdaddr[3], - bdaddr[2], bdaddr[1], bdaddr[0], - bdaddr[5], bdaddr[4], bdaddr[3]); + print_error(label, error); } -static void print_addr(const char *label, const uint8_t *addr, - uint8_t addr_type) +static void print_addr_type(const char *label, uint8_t addr_type) +{ + const char *str; + + switch (addr_type) { + case 0x00: + str = "Public"; + break; + case 0x01: + str = "Random"; + break; + default: + str = "Reserved"; + break; + } + + print_field("%s: %s (0x%2.2x)", label, str, addr_type); +} + +static void print_addr_resolve(const char *label, const uint8_t *addr, + uint8_t addr_type, bool resolve) { const char *str; + char *company; switch (addr_type) { case 0x00: - print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X" - " (OUI %2.2X-%2.2X-%2.2X)", label, - addr[5], addr[4], addr[3], - addr[2], addr[1], addr[0], - addr[5], addr[4], addr[3]); + if (!hwdb_get_company(addr, &company)) + company = NULL; + + if (company) { + print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X" + " (%s)", label, addr[5], addr[4], + addr[3], addr[2], + addr[1], addr[0], + company); + free(company); + } else { + print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X" + " (OUI %2.2X-%2.2X-%2.2X)", label, + addr[5], addr[4], addr[3], + addr[2], addr[1], addr[0], + addr[5], addr[4], addr[3]); + } break; case 0x01: switch ((addr[5] & 0xc0) >> 6) { @@ -435,6 +468,16 @@ static void print_addr(const char *label, const uint8_t *addr, print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X (%s)", label, addr[5], addr[4], addr[3], addr[2], addr[1], addr[0], str); + + if (resolve && (addr[5] & 0xc0) == 0x40) { + uint8_t ident[6], ident_type; + + if (keys_resolve_identity(addr, ident, &ident_type)) { + print_addr_type(" Identity type", ident_type); + print_addr_resolve(" Identity", ident, + ident_type, false); + } + } break; default: print_field("%s: %2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2X", @@ -444,23 +487,15 @@ static void print_addr(const char *label, const uint8_t *addr, } } -static void print_addr_type(const char *label, uint8_t addr_type) +static void print_addr(const char *label, const uint8_t *addr, + uint8_t addr_type) { - const char *str; - - switch (addr_type) { - case 0x00: - str = "Public"; - break; - case 0x01: - str = "Random"; - break; - default: - str = "Reserved"; - break; - } + print_addr_resolve(label, addr, addr_type, true); +} - print_field("%s: %s (0x%2.2x)", label, str, addr_type); +static void print_bdaddr(const uint8_t *bdaddr) +{ + print_addr("Address", bdaddr, 0x00); } static void print_lt_addr(uint8_t lt_addr) @@ -470,7 +505,7 @@ static void print_lt_addr(uint8_t lt_addr) static void print_handle(uint16_t handle) { - print_field("Handle: %d", btohs(handle)); + print_field("Handle: %d", le16_to_cpu(handle)); } static void print_phy_handle(uint8_t phy_handle) @@ -478,9 +513,82 @@ static void print_phy_handle(uint8_t phy_handle) print_field("Physical handle: %d", phy_handle); } +static const struct { + uint8_t bit; + const char *str; +} pkt_type_table[] = { + { 1, "2-DH1 may not be used" }, + { 2, "3-DH1 may not be used" }, + { 3, "DM1 may be used" }, + { 4, "DH1 may be used" }, + { 8, "2-DH3 may not be used" }, + { 9, "3-DH3 may not be used" }, + { 10, "DM3 may be used" }, + { 11, "DH3 may be used" }, + { 12, "3-DH5 may not be used" }, + { 13, "3-DH5 may not be used" }, + { 14, "DM5 may be used" }, + { 15, "DH5 may be used" }, + { } +}; + static void print_pkt_type(uint16_t pkt_type) { - print_field("Packet type: 0x%4.4x", btohs(pkt_type)); + uint16_t mask; + int i; + + print_field("Packet type: 0x%4.4x", le16_to_cpu(pkt_type)); + + mask = le16_to_cpu(pkt_type); + + for (i = 0; pkt_type_table[i].str; i++) { + if (le16_to_cpu(pkt_type) & (1 << pkt_type_table[i].bit)) { + print_field(" %s", pkt_type_table[i].str); + mask &= ~(1 << pkt_type_table[i].bit); + } + } + + if (mask) + print_text(COLOR_UNKNOWN_PKT_TYPE_BIT, + " Unknown packet types (0x%4.4x)", mask); +} + +static const struct { + uint8_t bit; + const char *str; +} pkt_type_sco_table[] = { + { 0, "HV1 may be used" }, + { 1, "HV2 may be used" }, + { 2, "HV3 may be used" }, + { 3, "EV3 may be used" }, + { 4, "EV4 may be used" }, + { 5, "EV5 may be used" }, + { 6, "2-EV3 may not be used" }, + { 7, "3-EV3 may not be used" }, + { 8, "2-EV5 may not be used" }, + { 9, "3-EV5 may not be used" }, + { } +}; + +static void print_pkt_type_sco(uint16_t pkt_type) +{ + uint16_t mask; + int i; + + print_field("Packet type: 0x%4.4x", le16_to_cpu(pkt_type)); + + mask = le16_to_cpu(pkt_type); + + for (i = 0; pkt_type_sco_table[i].str; i++) { + if (le16_to_cpu(pkt_type) & (1 << pkt_type_sco_table[i].bit)) { + print_field(" %s", pkt_type_sco_table[i].str); + mask &= ~(1 << pkt_type_sco_table[i].bit); + } + } + + if (mask) + print_text(COLOR_UNKNOWN_PKT_TYPE_BIT, + " Unknown packet types (0x%4.4x)", mask); } static void print_iac(const uint8_t *lap) @@ -753,6 +861,77 @@ static void print_dev_class(const uint8_t *dev_class) " Unknown service class (0x%2.2x)", mask); } +static const struct { + uint16_t val; + bool generic; + const char *str; +} appearance_table[] = { + { 0, true, "Unknown" }, + { 64, true, "Phone" }, + { 128, true, "Computer" }, + { 192, true, "Watch" }, + { 193, false, "Sports Watch" }, + { 256, true, "Clock" }, + { 320, true, "Display" }, + { 384, true, "Remote Control" }, + { 448, true, "Eye-glasses" }, + { 512, true, "Tag" }, + { 576, true, "Keyring" }, + { 640, true, "Media Player" }, + { 704, true, "Barcode Scanner" }, + { 768, true, "Thermometer" }, + { 769, false, "Thermometer: Ear" }, + { 832, true, "Heart Rate Sensor" }, + { 833, false, "Heart Rate Belt" }, + { 896, true, "Blood Pressure" }, + { 897, false, "Blood Pressure: Arm" }, + { 898, false, "Blood Pressure: Wrist" }, + { 960, true, "Human Interface Device" }, + { 961, false, "Keyboard" }, + { 962, false, "Mouse" }, + { 963, false, "Joystick" }, + { 964, false, "Gamepad" }, + { 965, false, "Digitizer Tablet" }, + { 966, false, "Card Reader" }, + { 967, false, "Digital Pen" }, + { 968, false, "Barcode Scanner" }, + { 1024, true, "Glucose Meter" }, + { 1088, true, "Running Walking Sensor" }, + { 1152, true, "Cycling" }, + { 1216, true, "Undefined" }, + + { 3136, true, "Pulse Oximeter" }, + { 3200, true, "Undefined" }, + + { 5184, true, "Outdoor Sports Activity"}, + { 5248, true, "Undefined" }, + { } +}; + +static void print_appearance(uint16_t appearance) +{ + const char *str = NULL; + int i, type = 0; + + for (i = 0; appearance_table[i].str; i++) { + if (appearance_table[i].generic) { + if (appearance < appearance_table[i].val) + break; + type = i; + } + + if (appearance_table[i].val == appearance) { + str = appearance_table[i].str; + break; + } + } + + if (!str) + str = appearance_table[type].str; + + print_field("Appearance: %s (0x%4.4x)", str, appearance); +} + static void print_num_broadcast_retrans(uint8_t num_retrans) { print_field("Number of broadcast retransmissions: %u", num_retrans); @@ -845,7 +1024,76 @@ static void print_host_flow_control(uint8_t enable) static void print_voice_setting(uint16_t setting) { - print_field("Setting: 0x%4.4x", btohs(setting)); + uint8_t input_coding = (le16_to_cpu(setting) & 0x0300) >> 8; + uint8_t input_data_format = (le16_to_cpu(setting) & 0xc0) >> 6; + uint8_t air_coding_format = le16_to_cpu(setting) & 0x0003; + const char *str; + + print_field("Setting: 0x%4.4x", le16_to_cpu(setting)); + + switch (input_coding) { + case 0x00: + str = "Linear"; + break; + case 0x01: + str ="u-law"; + break; + case 0x02: + str = "A-law"; + break; + default: + str = "Reserved"; + break; + } + + print_field(" Input Coding: %s", str); + + switch (input_data_format) { + case 0x00: + str = "1's complement"; + break; + case 0x01: + str = "2's complement"; + break; + case 0x02: + str = "Sign-Magnitude"; + break; + case 0x03: + str = "Unsigned"; + break; + default: + str = "Reserved"; + break; + } + + print_field(" Input Data Format: %s", str); + + if (input_coding == 0x00) { + print_field(" Input Sample Size: %s", + le16_to_cpu(setting) & 0x20 ? "16-bit" : "8-bit"); + print_field(" # of bits padding at MSB: %d", + (le16_to_cpu(setting) & 0x1c) >> 2); + } + + switch (air_coding_format) { + case 0x00: + str = "CVSD"; + break; + case 0x01: + str ="u-law"; + break; + case 0x02: + str = "A-law"; + break; + case 0x03: + str = "Transparent Data"; + break; + default: + str = "Reserved"; + break; + } + + print_field(" Air Coding Format: %s", str); } static void print_retransmission_effort(uint8_t effort) @@ -900,7 +1148,7 @@ static void print_scan_enable(uint8_t scan_enable) static void print_link_policy(uint16_t link_policy) { - uint16_t policy = btohs(link_policy); + uint16_t policy = le16_to_cpu(link_policy); print_field("Link policy: 0x%4.4x", policy); @@ -1083,7 +1331,7 @@ static void print_secure_conn_support(uint8_t support) static void print_auth_payload_timeout(uint16_t timeout) { print_field("Timeout: %d msec (0x%4.4x)", - btohs(timeout) * 10, btohs(timeout)); + le16_to_cpu(timeout) * 10, le16_to_cpu(timeout)); } static void print_pscan_rep_mode(uint8_t pscan_rep_mode) @@ -1158,21 +1406,47 @@ static void print_pscan_mode(uint8_t pscan_mode) static void print_clock_offset(uint16_t clock_offset) { - print_field("Clock offset: 0x%4.4x", btohs(clock_offset)); + print_field("Clock offset: 0x%4.4x", le16_to_cpu(clock_offset)); } static void print_clock(uint32_t clock) { - print_field("Clock: 0x%8.8x", btohl(clock)); + print_field("Clock: 0x%8.8x", le32_to_cpu(clock)); +} + +static void print_clock_type(uint8_t type) +{ + const char *str; + + switch (type) { + case 0x00: + str = "Local clock"; + break; + case 0x01: + str = "Piconet clock"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Type: %s (0x%2.2x)", str, type); } static void print_clock_accuracy(uint16_t accuracy) { - if (btohs(accuracy) == 0xffff) - print_field("Accuracy: Unknown (0x%4.4x)", btohs(accuracy)); + if (le16_to_cpu(accuracy) == 0xffff) + print_field("Accuracy: Unknown (0x%4.4x)", + le16_to_cpu(accuracy)); else print_field("Accuracy: %.4f msec (0x%4.4x)", - btohs(accuracy) * 0.3125, btohs(accuracy)); + le16_to_cpu(accuracy) * 0.3125, + le16_to_cpu(accuracy)); +} + +static void print_lpo_allowed(uint8_t lpo_allowed) +{ + print_field("LPO allowed: 0x%2.2x", lpo_allowed); } static void print_broadcast_fragment(uint8_t fragment) @@ -1246,7 +1520,7 @@ static void print_encr_mode_change(uint8_t encr_mode, uint16_t handle) const char *str; uint8_t conn_type; - conn_type = get_type(btohs(handle)); + conn_type = get_type(le16_to_cpu(handle)); switch (encr_mode) { case 0x00: @@ -1410,19 +1684,29 @@ static void print_pin_code(const uint8_t *pin_code, uint8_t pin_len) print_field("PIN code: %s", str); } -static void print_hash(const char *label, const uint8_t *hash) +static void print_hash_p192(const uint8_t *hash) +{ + print_key("Hash C from P-192", hash); +} + +static void print_hash_p256(const uint8_t *hash) { - print_key("Hash C from %s", hash); + print_key("Hash C from P-256", hash); } -static void print_randomizer(const char *label, const uint8_t *randomizer) +static void print_randomizer_p192(const uint8_t *randomizer) { - print_key("Randomizer R with %s", randomizer); + print_key("Randomizer R with P-192", randomizer); +} + +static void print_randomizer_p256(const uint8_t *randomizer) +{ + print_key("Randomizer R with P-256", randomizer); } static void print_passkey(uint32_t passkey) { - print_field("Passkey: %06d", btohl(passkey)); + print_field("Passkey: %06d", le32_to_cpu(passkey)); } static void print_io_capability(uint8_t capability) @@ -1506,6 +1790,16 @@ static void print_authentication(uint8_t authentication) print_field("Authentication: %s (0x%2.2x)", str, authentication); } +void packet_print_io_capability(uint8_t capability) +{ + print_io_capability(capability); +} + +void packet_print_io_authentication(uint8_t authentication) +{ + print_authentication(authentication); +} + static void print_location_domain_aware(uint8_t aware) { const char *str; @@ -1622,10 +1916,10 @@ static void print_flow_spec(const char *label, const uint8_t *data) print_field("%s flow spec: 0x%2.2x", label, data[0]); print_field(" Service type: %s (0x%2.2x)", str, data[1]); - print_field(" Maximum SDU size: 0x%4.4x", bt_get_le16(data + 2)); - print_field(" SDU inter-arrival time: 0x%8.8x", bt_get_le32(data + 4)); - print_field(" Access latency: 0x%8.8x", bt_get_le32(data + 8)); - print_field(" Flush timeout: 0x%8.8x", bt_get_le32(data + 12)); + print_field(" Maximum SDU size: 0x%4.4x", get_le16(data + 2)); + print_field(" SDU inter-arrival time: 0x%8.8x", get_le32(data + 4)); + print_field(" Access latency: 0x%8.8x", get_le32(data + 8)); + print_field(" Flush timeout: 0x%8.8x", get_le32(data + 12)); } static void print_short_range_mode(uint8_t mode) @@ -1702,13 +1996,13 @@ static void print_rssi(int8_t rssi) static void print_slot_625(const char *label, uint16_t value) { print_field("%s: %.3f msec (0x%4.4x)", label, - btohs(value) * 0.625, btohs(value)); + le16_to_cpu(value) * 0.625, le16_to_cpu(value)); } static void print_slot_125(const char *label, uint16_t value) { print_field("%s: %.2f msec (0x%4.4x)", label, - btohs(value) * 1.25, btohs(value)); + le16_to_cpu(value) * 1.25, le16_to_cpu(value)); } static void print_timeout(uint16_t timeout) @@ -1782,13 +2076,39 @@ static void print_name(const uint8_t *name) static void print_channel_map(const uint8_t *map) { + unsigned int count = 0, start = 0; char str[21]; - int i; + int i, n; for (i = 0; i < 10; i++) sprintf(str + (i * 2), "%2.2x", map[i]); print_field("Channel map: 0x%s", str); + + for (i = 0; i < 10; i++) { + for (n = 0; n < 8; n++) { + if (map[i] & (1 << n)) { + if (count == 0) + start = (i * 8) + n; + count++; + continue; + } + + if (count > 1) { + print_field(" Channel %u-%u", + start, start + count - 1 ); + count = 0; + } else if (count > 0) { + print_field(" Channel %u", start); + count = 0; + } + } + } +} + +void packet_print_channel_map_lmp(const uint8_t *map) +{ + print_channel_map(map); } static void print_flush_timeout(uint16_t timeout) @@ -1841,13 +2161,13 @@ void packet_print_version(const char *label, uint8_t version, static void print_hci_version(uint8_t version, uint16_t revision) { packet_print_version("HCI version", version, - "Revision", btohs(revision)); + "Revision", le16_to_cpu(revision)); } static void print_lmp_version(uint8_t version, uint16_t subversion) { packet_print_version("LMP version", version, - "Subversion", btohs(subversion)); + "Subversion", le16_to_cpu(subversion)); } static void print_pal_version(uint8_t version, uint16_t subversion) @@ -1864,7 +2184,9 @@ static void print_pal_version(uint8_t version, uint16_t subversion) } print_field("PAL version: %s (0x%2.2x) - Subversion %d (0x%4.4x)", - str, version, btohs(subversion), btohs(subversion)); + str, version, + le16_to_cpu(subversion), + le16_to_cpu(subversion)); } void packet_print_company(const char *label, uint16_t company) @@ -1874,7 +2196,7 @@ void packet_print_company(const char *label, uint16_t company) static void print_manufacturer(uint16_t manufacturer) { - packet_print_company("Manufacturer", btohs(manufacturer)); + packet_print_company("Manufacturer", le16_to_cpu(manufacturer)); } static const char *get_supported_command(int bit); @@ -1901,7 +2223,12 @@ static void print_commands(const uint8_t *commands) continue; cmd = get_supported_command((i * 8) + n); - print_field(" %s (Octet %d - Bit %d)", cmd, i, n); + if (cmd) + print_field(" %s (Octet %d - Bit %d)", + cmd, i, n); + else + print_text(COLOR_UNKNOWN_COMMAND_BIT, + " Octet %d - Bit %d ", i, n); } } } @@ -2057,6 +2384,11 @@ static void print_features(uint8_t page, const uint8_t *features_array, "(0x%16.16" PRIx64 ")", mask); } +void packet_print_features_lmp(const uint8_t *features, uint8_t page) +{ + print_features(page, features, 0x00); +} + void packet_print_features_ll(const uint8_t *features) { print_features(0, features, 0x01); @@ -2138,13 +2470,34 @@ static void print_le_states(const uint8_t *states_array) static void print_le_channel_map(const uint8_t *map) { + unsigned int count = 0, start = 0; char str[11]; - int i; + int i, n; for (i = 0; i < 5; i++) sprintf(str + (i * 2), "%2.2x", map[i]); print_field("Channel map: 0x%s", str); + + for (i = 0; i < 5; i++) { + for (n = 0; n < 8; n++) { + if (map[i] & (1 << n)) { + if (count == 0) + start = (i * 8) + n; + count++; + continue; + } + + if (count > 1) { + print_field(" Channel %u-%u", + start, start + count - 1 ); + count = 0; + } else if (count > 0) { + print_field(" Channel %u", start); + count = 0; + } + } + } } void packet_print_channel_map_ll(const uint8_t *map) @@ -2152,9 +2505,14 @@ void packet_print_channel_map_ll(const uint8_t *map) print_le_channel_map(map); } -static void print_random_number(const uint8_t *number) +static void print_random_number(uint64_t rand) +{ + print_field("Random number: 0x%16.16" PRIx64, le64_to_cpu(rand)); +} + +static void print_encrypted_diversifier(uint16_t ediv) { - print_hex_field("Random number", number, 8); + print_field("Encrypted diversifier: 0x%4.4x", le16_to_cpu(ediv)); } static const struct { @@ -2385,50 +2743,91 @@ static void print_fec(uint8_t fec) static void print_manufacturer_apple(const void *data, uint8_t data_len) { uint8_t type = *((uint8_t *) data); - uint8_t len; - const uint8_t *uuid; - uint16_t minor, major; - int8_t tx_power; - char identifier[100]; if (data_len < 1) return; - switch (type) { - case 0x01: + if (type == 0x01) { + char identifier[100]; + snprintf(identifier, sizeof(identifier) - 1, "%s", (const char *) (data + 1)); + print_field(" Identifier: %s", identifier); - break; - case 0x02: - len = *((uint8_t *) (data + 1)); - if (len != 0x15 || len != data_len - 2) { - print_hex_field(" Data", data, data_len); + return; + } + + while (data_len > 0) { + uint8_t len; + const char *str; + + type = *((uint8_t *) data); + data++; + data_len--; + + if (type == 0x00) + continue; + + if (data_len < 1) + break; + + switch (type) { + case 0x02: + str = "iBeacon"; + break; + case 0x05: + str = "AirDrop"; + break; + case 0x09: + str = "Apple TV"; + break; + default: + str = "Unknown"; break; } - uuid = data + 2; - print_field(" iBeacon: %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", - bt_get_le32(&uuid[12]), bt_get_le16(&uuid[10]), - bt_get_le16(&uuid[8]), bt_get_le16(&uuid[6]), - bt_get_le32(&uuid[2]), bt_get_le16(&uuid[0])); + print_field(" Type: %s (%u)", str, type); - major = bt_get_le16(data + 18); - minor = bt_get_le16(data + 20); - print_field(" Version: %u.%u", major, minor); + len = *((uint8_t *) data); + data++; + data_len--; - tx_power = *(int8_t *) (data + 22); - print_field(" TX power: %d dB", tx_power); - break; - default: - print_hex_field(" Data", data, data_len); - break; + if (len < 1) + continue; + + if (len > data_len) + break; + + if (type == 0x02 && len == 0x15) { + const uint8_t *uuid; + uint16_t minor, major; + int8_t tx_power; + + uuid = data; + print_field(" UUID: %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", + get_le32(&uuid[12]), get_le16(&uuid[10]), + get_le16(&uuid[8]), get_le16(&uuid[6]), + get_le32(&uuid[2]), get_le16(&uuid[0])); + + major = get_le16(data + 16); + minor = get_le16(data + 18); + print_field(" Version: %u.%u", major, minor); + + tx_power = *(int8_t *) (data + 20); + print_field(" TX power: %d dB", tx_power); + } else + print_hex_field(" Data", data, len); + + data += len; + data_len -= len; } + + packet_hexdump(data, data_len); } static void print_manufacturer_data(const void *data, uint8_t data_len) { - uint16_t company = bt_get_le16(data); + uint16_t company = get_le16(data); packet_print_company("Company", company); @@ -2445,35 +2844,63 @@ static void print_manufacturer_data(const void *data, uint8_t data_len) static void print_device_id(const void *data, uint8_t data_len) { - uint16_t source; + uint16_t source, vendor, product, version; + char modalias[26], *vendor_str, *product_str; const char *str; if (data_len < 8) return; - source = bt_get_le16(data); + source = get_le16(data); + vendor = get_le16(data + 2); + product = get_le16(data + 4); + version = get_le16(data + 6); switch (source) { case 0x0001: str = "Bluetooth SIG assigned"; + sprintf(modalias, "bluetooth:v%04Xp%04Xd%04X", + vendor, product, version); break; case 0x0002: str = "USB Implementer's Forum assigned"; + sprintf(modalias, "usb:v%04Xp%04Xd%04X", + vendor, product, version); break; default: str = "Reserved"; + modalias[0] = '\0'; break; } print_field("Device ID: %s (0x%4.4x)", str, source); - if (source == 0x0001) - packet_print_company(" Vendor", bt_get_le16(data + 2)); + if (!hwdb_get_vendor_model(modalias, &vendor_str, &product_str)) { + vendor_str = NULL; + product_str = NULL; + } + + if (source != 0x0001) { + if (vendor_str) + print_field(" Vendor: %s (0x%4.4x)", + vendor_str, vendor); + else + print_field(" Vendor: 0x%4.4x", vendor); + } else + packet_print_company(" Vendor", vendor); + + if (product_str) + print_field(" Product: %s (0x%4.4x)", product_str, product); else - print_field(" Vendor: 0x%4.4x", bt_get_le16(data + 2)); + print_field(" Product: 0x%4.4x", product); + + print_field(" Version: %u.%u.%u (0x%4.4x)", + (version & 0xff00) >> 8, + (version & 0x00f0) >> 4, + (version & 0x000f), version); - print_field(" Product: 0x%4.4x", bt_get_le16(data + 4)); - print_field(" Version: 0x%4.4x", bt_get_le16(data + 6)); + free(vendor_str); + free(product_str); } static void print_uuid16_list(const char *label, const void *data, @@ -2485,7 +2912,7 @@ static void print_uuid16_list(const char *label, const void *data, print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies"); for (i = 0; i < count; i++) { - uint16_t uuid = bt_get_le16(data + (i * 2)); + uint16_t uuid = get_le16(data + (i * 2)); print_field(" %s (0x%4.4x)", uuid16_to_str(uuid), uuid); } } @@ -2499,7 +2926,7 @@ static void print_uuid32_list(const char *label, const void *data, print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies"); for (i = 0; i < count; i++) { - uint32_t uuid = bt_get_le32(data + (i * 4)); + uint32_t uuid = get_le32(data + (i * 4)); print_field(" %s (0x%8.8x)", uuid32_to_str(uuid), uuid); } } @@ -2516,9 +2943,9 @@ static void print_uuid128_list(const char *label, const void *data, const uint8_t *uuid = data + (i * 16); print_field(" %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", - bt_get_le32(&uuid[12]), bt_get_le16(&uuid[10]), - bt_get_le16(&uuid[8]), bt_get_le16(&uuid[6]), - bt_get_le32(&uuid[2]), bt_get_le16(&uuid[0])); + get_le32(&uuid[12]), get_le16(&uuid[10]), + get_le16(&uuid[8]), get_le16(&uuid[6]), + get_le32(&uuid[2]), get_le16(&uuid[0])); } } @@ -2663,13 +3090,13 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le) case BT_EIR_SSP_HASH_P192: if (data_len < 16) break; - print_hash("P-192", data); + print_hash_p192(data); break; case BT_EIR_SSP_RANDOMIZER_P192: if (data_len < 16) break; - print_randomizer("P-192", data); + print_randomizer_p192(data); break; case BT_EIR_DEVICE_ID: @@ -2688,8 +3115,8 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le) if (data_len < 4) break; print_field("Slave Conn. Interval: 0x%4.4x - 0x%4.4x", - bt_get_le16(&data[0]), - bt_get_le16(&data[2])); + get_le16(&data[0]), + get_le16(&data[2])); break; case BT_EIR_SERVICE_UUID16: @@ -2710,7 +3137,7 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le) if (data_len < 2) break; sprintf(label, "Service Data (UUID 0x%4.4x)", - bt_get_le16(&data[0])); + get_le16(&data[0])); print_hex_field(label, &data[2], data_len - 2); break; @@ -2729,19 +3156,19 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le) case BT_EIR_GAP_APPEARANCE: if (data_len < 2) break; - print_field("Appearance: 0x%4.4x", bt_get_le16(data)); + print_appearance(get_le16(data)); break; case BT_EIR_SSP_HASH_P256: if (data_len < 16) break; - print_hash("P-256", data); + print_hash_p256(data); break; case BT_EIR_SSP_RANDOMIZER_P256: if (data_len < 16) break; - print_randomizer("P-256", data); + print_randomizer_p256(data); break; case BT_EIR_3D_INFO_DATA: @@ -2846,22 +3273,17 @@ void packet_control(struct timeval *tv, uint16_t index, uint16_t opcode, control_message(opcode, data, size); } -struct monitor_new_index { - uint8_t type; - uint8_t bus; - bdaddr_t bdaddr; - char name[8]; -} __attribute__((packed)); - -#define MONITOR_NEW_INDEX_SIZE 16 - -#define MONITOR_DEL_INDEX_SIZE 0 +static int addr2str(const uint8_t *addr, char *str) +{ + return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); +} #define MAX_INDEX 16 struct index_data { - uint8_t type; - bdaddr_t bdaddr; + uint8_t type; + uint8_t bdaddr[6]; }; static struct index_data index_list[MAX_INDEX]; @@ -2869,7 +3291,7 @@ static struct index_data index_list[MAX_INDEX]; void packet_monitor(struct timeval *tv, uint16_t index, uint16_t opcode, const void *data, uint16_t size) { - const struct monitor_new_index *ni; + const struct btsnoop_opcode_new_index *ni; char str[18], extra_str[24]; if (index_filter && index_number != index) @@ -2886,17 +3308,17 @@ void packet_monitor(struct timeval *tv, uint16_t index, uint16_t opcode, if (index < MAX_INDEX) { index_list[index].type = ni->type; - bacpy(&index_list[index].bdaddr, &ni->bdaddr); + memcpy(index_list[index].bdaddr, ni->bdaddr, 6); } - ba2str(&ni->bdaddr, str); + addr2str(ni->bdaddr, str); packet_new_index(tv, index, str, ni->type, ni->bus, ni->name); break; case BTSNOOP_OPCODE_DEL_INDEX: if (index < MAX_INDEX) - ba2str(&index_list[index].bdaddr, str); + addr2str(index_list[index].bdaddr, str); else - ba2str(BDADDR_ANY, str); + sprintf(str, "00:00:00:00:00:00"); packet_del_index(tv, index, str); break; @@ -3025,7 +3447,7 @@ static void add_sco_conn_cmd(const void *data, uint8_t size) const struct bt_hci_cmd_add_sco_conn *cmd = data; print_handle(cmd->handle); - print_pkt_type(cmd->pkt_type); + print_pkt_type_sco(cmd->pkt_type); } static void create_conn_cancel_cmd(const void *data, uint8_t size) @@ -3179,7 +3601,7 @@ static void read_lmp_handle_rsp(const void *data, uint8_t size) print_status(rsp->status); print_handle(rsp->handle); print_field("LMP handle: %d", rsp->lmp_handle); - print_field("Reserved: %d", btohl(rsp->reserved)); + print_field("Reserved: %d", le32_to_cpu(rsp->reserved)); } static void setup_sync_conn_cmd(const void *data, uint8_t size) @@ -3187,12 +3609,12 @@ static void setup_sync_conn_cmd(const void *data, uint8_t size) const struct bt_hci_cmd_setup_sync_conn *cmd = data; print_handle(cmd->handle); - print_field("Transmit bandwidth: %d", btohl(cmd->tx_bandwidth)); - print_field("Receive bandwidth: %d", btohl(cmd->rx_bandwidth)); - print_field("Max latency: %d", btohs(cmd->max_latency)); + print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth)); + print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth)); + print_field("Max latency: %d", le16_to_cpu(cmd->max_latency)); print_voice_setting(cmd->voice_setting); print_retransmission_effort(cmd->retrans_effort); - print_pkt_type(cmd->pkt_type); + print_pkt_type_sco(cmd->pkt_type); } static void accept_sync_conn_request_cmd(const void *data, uint8_t size) @@ -3200,12 +3622,12 @@ static void accept_sync_conn_request_cmd(const void *data, uint8_t size) const struct bt_hci_cmd_accept_sync_conn_request *cmd = data; print_bdaddr(cmd->bdaddr); - print_field("Transmit bandwidth: %d", btohl(cmd->tx_bandwidth)); - print_field("Receive bandwidth: %d", btohl(cmd->rx_bandwidth)); - print_field("Max latency: %d", btohs(cmd->max_latency)); + print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth)); + print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth)); + print_field("Max latency: %d", le16_to_cpu(cmd->max_latency)); print_voice_setting(cmd->voice_setting); print_retransmission_effort(cmd->retrans_effort); - print_pkt_type(cmd->pkt_type); + print_pkt_type_sco(cmd->pkt_type); } static void reject_sync_conn_request_cmd(const void *data, uint8_t size) @@ -3260,8 +3682,8 @@ static void remote_oob_data_request_reply_cmd(const void *data, uint8_t size) const struct bt_hci_cmd_remote_oob_data_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); - print_hash("P-192", cmd->hash); - print_randomizer("P-192", cmd->randomizer); + print_hash_p192(cmd->hash); + print_randomizer_p192(cmd->randomizer); } static void remote_oob_data_request_neg_reply_cmd(const void *data, uint8_t size) @@ -3382,7 +3804,7 @@ static void set_slave_broadcast_cmd(const void *data, uint8_t size) print_field("Enable: 0x%2.2x", cmd->enable); print_lt_addr(cmd->lt_addr); - print_field("LPO allowed: 0x%2.2x", cmd->lpo_allowed); + print_lpo_allowed(cmd->lpo_allowed); print_pkt_type(cmd->pkt_type); print_slot_625("Min interval", cmd->min_interval); print_slot_625("Max interval", cmd->max_interval); @@ -3406,8 +3828,9 @@ static void set_slave_broadcast_receive_cmd(const void *data, uint8_t size) print_bdaddr(cmd->bdaddr); print_lt_addr(cmd->lt_addr); print_interval(cmd->interval); - print_field("Offset: 0x%8.8x", btohl(cmd->offset)); - print_field("Next broadcast instant: 0x%4.4x", btohs(cmd->instant)); + print_field("Offset: 0x%8.8x", le32_to_cpu(cmd->offset)); + print_field("Next broadcast instant: 0x%4.4x", + le16_to_cpu(cmd->instant)); print_slot_625("Supervision timeout", cmd->timeout); print_field("Remote timing accuracy: %d ppm", cmd->accuracy); print_field("Skip: 0x%2.2x", cmd->skip); @@ -3439,10 +3862,10 @@ static void remote_oob_ext_data_request_reply_cmd(const void *data, uint8_t size const struct bt_hci_cmd_remote_oob_ext_data_request_reply *cmd = data; print_bdaddr(cmd->bdaddr); - print_hash("P-192", cmd->hash192); - print_randomizer("P-192", cmd->randomizer192); - print_hash("P-256", cmd->hash256); - print_randomizer("P-256", cmd->randomizer256); + print_hash_p192(cmd->hash192); + print_randomizer_p192(cmd->randomizer192); + print_hash_p256(cmd->hash256); + print_randomizer_p256(cmd->randomizer256); } static void hold_mode_cmd(const void *data, uint8_t size) @@ -3497,10 +3920,10 @@ static void qos_setup_cmd(const void *data, uint8_t size) print_service_type(cmd->service_type); - print_field("Token rate: %d", btohl(cmd->token_rate)); - print_field("Peak bandwidth: %d", btohl(cmd->peak_bandwidth)); - print_field("Latency: %d", btohl(cmd->latency)); - print_field("Delay variation: %d", btohl(cmd->delay_variation)); + print_field("Token rate: %d", le32_to_cpu(cmd->token_rate)); + print_field("Peak bandwidth: %d", le32_to_cpu(cmd->peak_bandwidth)); + print_field("Latency: %d", le32_to_cpu(cmd->latency)); + print_field("Delay variation: %d", le32_to_cpu(cmd->delay_variation)); } static void role_discovery_cmd(const void *data, uint8_t size) @@ -3584,10 +4007,11 @@ static void flow_spec_cmd(const void *data, uint8_t size) print_flow_direction(cmd->direction); print_service_type(cmd->service_type); - print_field("Token rate: %d", btohl(cmd->token_rate)); - print_field("Token bucket size: %d", btohl(cmd->token_bucket_size)); - print_field("Peak bandwidth: %d", btohl(cmd->peak_bandwidth)); - print_field("Access latency: %d", btohl(cmd->access_latency)); + print_field("Token rate: %d", le32_to_cpu(cmd->token_rate)); + print_field("Token bucket size: %d", + le32_to_cpu(cmd->token_bucket_size)); + print_field("Peak bandwidth: %d", le32_to_cpu(cmd->peak_bandwidth)); + print_field("Access latency: %d", le32_to_cpu(cmd->access_latency)); } static void sniff_subrating_cmd(const void *data, uint8_t size) @@ -3742,8 +4166,8 @@ static void read_stored_link_key_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_read_stored_link_key *rsp = data; print_status(rsp->status); - print_field("Max num keys: %d", btohs(rsp->max_num_keys)); - print_field("Num keys: %d", btohs(rsp->num_keys)); + print_field("Max num keys: %d", le16_to_cpu(rsp->max_num_keys)); + print_field("Num keys: %d", le16_to_cpu(rsp->num_keys)); } static void write_stored_link_key_cmd(const void *data, uint8_t size) @@ -3776,7 +4200,7 @@ static void delete_stored_link_key_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_delete_stored_link_key *rsp = data; print_status(rsp->status); - print_field("Num keys: %d", btohs(rsp->num_keys)); + print_field("Num keys: %d", le16_to_cpu(rsp->num_keys)); } static void write_local_name_cmd(const void *data, uint8_t size) @@ -4039,9 +4463,11 @@ static void host_buffer_size_cmd(const void *data, uint8_t size) const struct bt_hci_cmd_host_buffer_size *cmd = data; print_field("ACL MTU: %-4d ACL max packet: %d", - btohs(cmd->acl_mtu), btohs(cmd->acl_max_pkt)); + le16_to_cpu(cmd->acl_mtu), + le16_to_cpu(cmd->acl_max_pkt)); print_field("SCO MTU: %-4d SCO max packet: %d", - cmd->sco_mtu, btohs(cmd->sco_max_pkt)); + cmd->sco_mtu, + le16_to_cpu(cmd->sco_max_pkt)); } static void read_link_supv_timeout_cmd(const void *data, uint8_t size) @@ -4248,8 +4674,8 @@ static void read_local_oob_data_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_read_local_oob_data *rsp = data; print_status(rsp->status); - print_hash("P-192", rsp->hash); - print_randomizer("P-192", rsp->randomizer); + print_hash_p192(rsp->hash); + print_randomizer_p192(rsp->randomizer); } static void read_inquiry_resp_tx_power_rsp(const void *data, uint8_t size) @@ -4445,7 +4871,8 @@ static void read_sync_train_params_rsp(const void *data, uint8_t size) print_status(rsp->status); print_interval(rsp->interval); print_field("Timeout: %.3f msec (0x%8.8x)", - btohl(rsp->timeout) * 0.625, btohl(rsp->timeout)); + le32_to_cpu(rsp->timeout) * 0.625, + le32_to_cpu(rsp->timeout)); print_field("Service Data: 0x%2.2x", rsp->service_data); } @@ -4456,7 +4883,8 @@ static void write_sync_train_params_cmd(const void *data, uint8_t size) print_slot_625("Min interval", cmd->min_interval); print_slot_625("Max interval", cmd->max_interval); print_field("Timeout: %.3f msec (0x%8.8x)", - btohl(cmd->timeout) * 0.625, btohl(cmd->timeout)); + le32_to_cpu(cmd->timeout) * 0.625, + le32_to_cpu(cmd->timeout)); print_field("Service Data: 0x%2.2x", cmd->service_data); } @@ -4520,10 +4948,10 @@ static void read_local_oob_ext_data_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_read_local_oob_ext_data *rsp = data; print_status(rsp->status); - print_hash("P-192", rsp->hash192); - print_randomizer("P-192", rsp->randomizer192); - print_hash("P-256", rsp->hash256); - print_randomizer("P-256", rsp->randomizer256); + print_hash_p192(rsp->hash192); + print_randomizer_p192(rsp->randomizer192); + print_hash_p256(rsp->hash256); + print_randomizer_p256(rsp->randomizer256); } static void read_local_version_rsp(const void *data, uint8_t size) @@ -4583,9 +5011,11 @@ static void read_buffer_size_rsp(const void *data, uint8_t size) print_status(rsp->status); print_field("ACL MTU: %-4d ACL max packet: %d", - btohs(rsp->acl_mtu), btohs(rsp->acl_max_pkt)); + le16_to_cpu(rsp->acl_mtu), + le16_to_cpu(rsp->acl_max_pkt)); print_field("SCO MTU: %-4d SCO max packet: %d", - rsp->sco_mtu, btohs(rsp->sco_max_pkt)); + rsp->sco_mtu, + le16_to_cpu(rsp->sco_max_pkt)); } static void read_country_code_rsp(const void *data, uint8_t size) @@ -4623,9 +5053,9 @@ static void read_data_block_size_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_read_data_block_size *rsp = data; print_status(rsp->status); - print_field("Max ACL length: %d", btohs(rsp->max_acl_len)); - print_field("Block length: %d", btohs(rsp->block_len)); - print_field("Num blocks: %d", btohs(rsp->num_blocks)); + print_field("Max ACL length: %d", le16_to_cpu(rsp->max_acl_len)); + print_field("Block length: %d", le16_to_cpu(rsp->block_len)); + print_field("Num blocks: %d", le16_to_cpu(rsp->num_blocks)); } static void read_failed_contact_counter_cmd(const void *data, uint8_t size) @@ -4641,7 +5071,7 @@ static void read_failed_contact_counter_rsp(const void *data, uint8_t size) print_status(rsp->status); print_handle(rsp->handle); - print_field("Counter: %u", htobs(rsp->counter)); + print_field("Counter: %u", le16_to_cpu(rsp->counter)); } static void reset_failed_contact_counter_cmd(const void *data, uint8_t size) @@ -4711,23 +5141,9 @@ static void read_afh_channel_map_rsp(const void *data, uint8_t size) static void read_clock_cmd(const void *data, uint8_t size) { const struct bt_hci_cmd_read_clock *cmd = data; - const char *str; print_handle(cmd->handle); - - switch (cmd->type) { - case 0x00: - str = "Local clock"; - break; - case 0x01: - str = "Piconet clock"; - break; - default: - str = "Reserved"; - break; - } - - print_field("Type: %s (0x%2.2x)", str, cmd->type); + print_clock_type(cmd->type); } static void read_clock_rsp(const void *data, uint8_t size) @@ -4764,10 +5180,11 @@ static void read_local_amp_info_rsp(const void *data, uint8_t size) print_status(rsp->status); print_amp_status(rsp->amp_status); - print_field("Total bandwidth: %d kbps", btohl(rsp->total_bw)); - print_field("Max guaranteed bandwidth: %d kbps", btohl(rsp->max_bw)); - print_field("Min latency: %d", btohl(rsp->min_latency)); - print_field("Max PDU size: %d", btohl(rsp->max_pdu)); + print_field("Total bandwidth: %d kbps", le32_to_cpu(rsp->total_bw)); + print_field("Max guaranteed bandwidth: %d kbps", + le32_to_cpu(rsp->max_bw)); + print_field("Min latency: %d", le32_to_cpu(rsp->min_latency)); + print_field("Max PDU size: %d", le32_to_cpu(rsp->max_pdu)); switch (rsp->amp_type) { case 0x00: @@ -4783,10 +5200,11 @@ static void read_local_amp_info_rsp(const void *data, uint8_t size) print_field("Controller type: %s (0x%2.2x)", str, rsp->amp_type); - print_field("PAL capabilities: 0x%4.4x", btohs(rsp->pal_cap)); - print_field("Max ASSOC length: %d", btohs(rsp->max_assoc_len)); - print_field("Max flush timeout: %d", btohl(rsp->max_flush_to)); - print_field("Best effort flush timeout: %d", btohl(rsp->be_flush_to)); + print_field("PAL capabilities: 0x%4.4x", le16_to_cpu(rsp->pal_cap)); + print_field("Max ASSOC length: %d", le16_to_cpu(rsp->max_assoc_len)); + print_field("Max flush timeout: %d", le32_to_cpu(rsp->max_flush_to)); + print_field("Best effort flush timeout: %d", + le32_to_cpu(rsp->be_flush_to)); } static void read_local_amp_assoc_cmd(const void *data, uint8_t size) @@ -4794,8 +5212,8 @@ static void read_local_amp_assoc_cmd(const void *data, uint8_t size) const struct bt_hci_cmd_read_local_amp_assoc *cmd = data; print_phy_handle(cmd->phy_handle); - print_field("Length so far: %d", btohs(cmd->len_so_far)); - print_field("Max ASSOC length: %d", btohs(cmd->max_assoc_len)); + print_field("Length so far: %d", le16_to_cpu(cmd->len_so_far)); + print_field("Max ASSOC length: %d", le16_to_cpu(cmd->max_assoc_len)); } static void read_local_amp_assoc_rsp(const void *data, uint8_t size) @@ -4804,7 +5222,8 @@ static void read_local_amp_assoc_rsp(const void *data, uint8_t size) print_status(rsp->status); print_phy_handle(rsp->phy_handle); - print_field("Remaining ASSOC length: %d", btohs(rsp->remain_assoc_len)); + print_field("Remaining ASSOC length: %d", + le16_to_cpu(rsp->remain_assoc_len)); packet_hexdump(data + 4, size - 4); } @@ -4814,8 +5233,9 @@ static void write_remote_amp_assoc_cmd(const void *data, uint8_t size) const struct bt_hci_cmd_write_remote_amp_assoc *cmd = data; print_phy_handle(cmd->phy_handle); - print_field("Length so far: %d", btohs(cmd->len_so_far)); - print_field("Remaining ASSOC length: %d", btohs(cmd->remain_assoc_len)); + print_field("Length so far: %d", le16_to_cpu(cmd->len_so_far)); + print_field("Remaining ASSOC length: %d", + le16_to_cpu(cmd->remain_assoc_len)); packet_hexdump(data + 5, size - 5); } @@ -4828,6 +5248,32 @@ static void write_remote_amp_assoc_rsp(const void *data, uint8_t size) print_phy_handle(rsp->phy_handle); } +static void set_triggered_clock_capture_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_set_triggered_clock_capture *cmd = data; + const char *str; + + print_handle(cmd->handle); + + switch (cmd->enable) { + case 0x00: + str = "Disabled"; + break; + case 0x01: + str = "Enabled"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Capture: %s (0x%2.2x)", str, cmd->enable); + + print_clock_type(cmd->type); + print_lpo_allowed(cmd->lpo_allowed); + print_field("Clock captures to filter: %u", cmd->num_filter); +} + static void write_ssp_debug_mode_cmd(const void *data, uint8_t size) { const struct bt_hci_cmd_write_ssp_debug_mode *cmd = data; @@ -4847,7 +5293,7 @@ static void le_read_buffer_size_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_le_read_buffer_size *rsp = data; print_status(rsp->status); - print_field("Data packet length: %d", btohs(rsp->le_mtu)); + print_field("Data packet length: %d", le16_to_cpu(rsp->le_mtu)); print_field("Num data packets: %d", rsp->le_max_pkt); } @@ -5099,9 +5545,10 @@ static void le_create_conn_cmd(const void *data, uint8_t size) print_slot_125("Min connection interval", cmd->min_interval); print_slot_125("Max connection interval", cmd->max_interval); - print_field("Connection latency: 0x%4.4x", btohs(cmd->latency)); + print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency)); print_field("Supervision timeout: %d msec (0x%4.4x)", - btohs(cmd->supv_timeout) * 10, btohs(cmd->supv_timeout)); + le16_to_cpu(cmd->supv_timeout) * 10, + le16_to_cpu(cmd->supv_timeout)); print_slot_625("Min connection length", cmd->min_length); print_slot_625("Max connection length", cmd->max_length); } @@ -5137,9 +5584,10 @@ static void le_conn_update_cmd(const void *data, uint8_t size) print_handle(cmd->handle); print_slot_125("Min connection interval", cmd->min_interval); print_slot_125("Max connection interval", cmd->max_interval); - print_field("Connection latency: 0x%4.4x", btohs(cmd->latency)); + print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency)); print_field("Supervision timeout: %d msec (0x%4.4x)", - btohs(cmd->supv_timeout) * 10, btohs(cmd->supv_timeout)); + le16_to_cpu(cmd->supv_timeout) * 10, + le16_to_cpu(cmd->supv_timeout)); print_slot_625("Min connection length", cmd->min_length); print_slot_625("Max connection length", cmd->max_length); } @@ -5203,9 +5651,8 @@ static void le_start_encrypt_cmd(const void *data, uint8_t size) const struct bt_hci_cmd_le_start_encrypt *cmd = data; print_handle(cmd->handle); - print_random_number(cmd->number); - print_field("Encryption diversifier: 0x%4.4x", - btohs(cmd->diversifier)); + print_random_number(cmd->rand); + print_encrypted_diversifier(cmd->ediv); print_key("Long term key", cmd->ltk); } @@ -5271,7 +5718,7 @@ static void le_test_end_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_le_test_end *rsp = data; print_status(rsp->status); - print_field("Number of packets: %d", btohs(rsp->num_packets)); + print_field("Number of packets: %d", le16_to_cpu(rsp->num_packets)); } struct opcode_data { @@ -5792,7 +6239,9 @@ static const struct opcode_data opcode_table[] = { write_remote_amp_assoc_cmd, 6, false, write_remote_amp_assoc_rsp, 2, true }, { 0x140c, 243, "Get MWS Transport Layer Configuration" }, - { 0x140d, 245, "Set Triggered Clock Capture" }, + { 0x140d, 245, "Set Triggered Clock Capture", + set_triggered_clock_capture_cmd, 6, true, + status_rsp, 1, true }, /* OGF 6 - Testing */ { 0x1801, 128, "Read Loopback Mode" }, @@ -5946,7 +6395,7 @@ static void conn_complete_evt(const void *data, uint8_t size) print_encr_mode(evt->encr_mode); if (evt->status == 0x00) - assign_handle(btohs(evt->handle), 0x00); + assign_handle(le16_to_cpu(evt->handle), 0x00); } static void conn_request_evt(const void *data, uint8_t size) @@ -5967,7 +6416,7 @@ static void disconnect_complete_evt(const void *data, uint8_t size) print_reason(evt->reason); if (evt->status == 0x00) - release_handle(btohs(evt->handle)); + release_handle(le16_to_cpu(evt->handle)); } static void auth_complete_evt(const void *data, uint8_t size) @@ -6042,16 +6491,16 @@ static void qos_setup_complete_evt(const void *data, uint8_t size) print_service_type(evt->service_type); - print_field("Token rate: %d", btohl(evt->token_rate)); - print_field("Peak bandwidth: %d", btohl(evt->peak_bandwidth)); - print_field("Latency: %d", btohl(evt->latency)); - print_field("Delay variation: %d", btohl(evt->delay_variation)); + print_field("Token rate: %d", le32_to_cpu(evt->token_rate)); + print_field("Peak bandwidth: %d", le32_to_cpu(evt->peak_bandwidth)); + print_field("Latency: %d", le32_to_cpu(evt->latency)); + print_field("Delay variation: %d", le32_to_cpu(evt->delay_variation)); } static void cmd_complete_evt(const void *data, uint8_t size) { const struct bt_hci_evt_cmd_complete *evt = data; - uint16_t opcode = btohs(evt->opcode); + uint16_t opcode = le16_to_cpu(evt->opcode); uint16_t ogf = cmd_opcode_ogf(opcode); uint16_t ocf = cmd_opcode_ocf(opcode); const struct opcode_data *opcode_data = NULL; @@ -6072,15 +6521,32 @@ static void cmd_complete_evt(const void *data, uint8_t size) opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = opcode_data->str; } else { - opcode_color = COLOR_HCI_COMMAND_UNKNOWN; - opcode_str = "Unknown"; + if (ogf == 0x3f) { + opcode_color = COLOR_HCI_COMMAND; + opcode_str = "Vendor"; + } else { + opcode_color = COLOR_HCI_COMMAND_UNKNOWN; + opcode_str = "Unknown"; + } } print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, " (0x%2.2x|0x%4.4x) ncmd %d", ogf, ocf, evt->ncmd); if (!opcode_data || !opcode_data->rsp_func) { - packet_hexdump(data + 3, size - 3); + if (size > 3) { + uint8_t status = *((uint8_t *) (data + 3)); + + print_status(status); + packet_hexdump(data + 4, size - 4); + } + return; + } + + if (opcode_data->rsp_size > 1 && size - 3 == 1) { + uint8_t status = *((uint8_t *) (data + 3)); + + print_status(status); return; } @@ -6104,7 +6570,7 @@ static void cmd_complete_evt(const void *data, uint8_t size) static void cmd_status_evt(const void *data, uint8_t size) { const struct bt_hci_evt_cmd_status *evt = data; - uint16_t opcode = btohs(evt->opcode); + uint16_t opcode = le16_to_cpu(evt->opcode); uint16_t ogf = cmd_opcode_ogf(opcode); uint16_t ocf = cmd_opcode_ocf(opcode); const struct opcode_data *opcode_data = NULL; @@ -6122,8 +6588,13 @@ static void cmd_status_evt(const void *data, uint8_t size) opcode_color = COLOR_HCI_COMMAND; opcode_str = opcode_data->str; } else { - opcode_color = COLOR_HCI_COMMAND_UNKNOWN; - opcode_str = "Unknown"; + if (ogf == 0x3f) { + opcode_color = COLOR_HCI_COMMAND; + opcode_str = "Vendor"; + } else { + opcode_color = COLOR_HCI_COMMAND_UNKNOWN; + opcode_str = "Unknown"; + } } print_indent(6, opcode_color, "", opcode_str, COLOR_OFF, @@ -6161,7 +6632,7 @@ static void num_completed_packets_evt(const void *data, uint8_t size) print_field("Num handles: %d", evt->num_handles); print_handle(evt->handle); - print_field("Count: %d", btohs(evt->count)); + print_field("Count: %d", le16_to_cpu(evt->count)); if (size > sizeof(*evt)) packet_hexdump(data + sizeof(*evt), size - sizeof(*evt)); @@ -6281,10 +6752,11 @@ static void flow_spec_complete_evt(const void *data, uint8_t size) print_flow_direction(evt->direction); print_service_type(evt->service_type); - print_field("Token rate: %d", btohl(evt->token_rate)); - print_field("Token bucket size: %d", btohl(evt->token_bucket_size)); - print_field("Peak bandwidth: %d", btohl(evt->peak_bandwidth)); - print_field("Access latency: %d", btohl(evt->access_latency)); + print_field("Token rate: %d", le32_to_cpu(evt->token_rate)); + print_field("Token bucket size: %d", + le32_to_cpu(evt->token_bucket_size)); + print_field("Peak bandwidth: %d", le32_to_cpu(evt->peak_bandwidth)); + print_field("Access latency: %d", le32_to_cpu(evt->access_latency)); } static void inquiry_result_with_rssi_evt(const void *data, uint8_t size) @@ -6323,8 +6795,8 @@ static void sync_conn_complete_evt(const void *data, uint8_t size) print_link_type(evt->link_type); print_field("Transmission interval: 0x%2.2x", evt->tx_interval); print_field("Retransmission window: 0x%2.2x", evt->retrans_window); - print_field("RX packet length: %d", btohs(evt->rx_pkt_len)); - print_field("TX packet length: %d", btohs(evt->tx_pkt_len)); + print_field("RX packet length: %d", le16_to_cpu(evt->rx_pkt_len)); + print_field("TX packet length: %d", le16_to_cpu(evt->tx_pkt_len)); print_air_mode(evt->air_mode); } @@ -6336,8 +6808,8 @@ static void sync_conn_changed_evt(const void *data, uint8_t size) print_handle(evt->handle); print_field("Transmission interval: 0x%2.2x", evt->tx_interval); print_field("Retransmission window: 0x%2.2x", evt->retrans_window); - print_field("RX packet length: %d", btohs(evt->rx_pkt_len)); - print_field("TX packet length: %d", btohs(evt->tx_pkt_len)); + print_field("RX packet length: %d", le16_to_cpu(evt->rx_pkt_len)); + print_field("TX packet length: %d", le16_to_cpu(evt->tx_pkt_len)); } static void sniff_subrating_evt(const void *data, uint8_t size) @@ -6576,7 +7048,8 @@ static void num_completed_data_blocks_evt(const void *data, uint8_t size) { const struct bt_hci_evt_num_completed_data_blocks *evt = data; - print_field("Total num data blocks: %d", btohs(evt->total_num_blocks)); + print_field("Total num data blocks: %d", + le16_to_cpu(evt->total_num_blocks)); print_field("Num handles: %d", evt->num_handles); print_handle(evt->handle); print_field("Num packets: %d", evt->num_packets); @@ -6616,10 +7089,11 @@ static void sync_train_received_evt(const void *data, uint8_t size) print_status(evt->status); print_bdaddr(evt->bdaddr); - print_field("Offset: 0x%8.8x", btohl(evt->offset)); + print_field("Offset: 0x%8.8x", le32_to_cpu(evt->offset)); print_channel_map(evt->map); print_lt_addr(evt->lt_addr); - print_field("Next broadcast instant: 0x%4.4x", btohs(evt->instant)); + print_field("Next broadcast instant: 0x%4.4x", + le16_to_cpu(evt->instant)); print_interval(evt->interval); print_field("Service Data: 0x%2.2x", evt->service_data); } @@ -6630,8 +7104,8 @@ static void slave_broadcast_receive_evt(const void *data, uint8_t size) print_bdaddr(evt->bdaddr); print_lt_addr(evt->lt_addr); - print_field("Clock: 0x%8.8x", btohl(evt->clock)); - print_field("Offset: 0x%8.8x", btohl(evt->offset)); + print_field("Clock: 0x%8.8x", le32_to_cpu(evt->clock)); + print_field("Offset: 0x%8.8x", le32_to_cpu(evt->offset)); print_field("Receive status: 0x%2.2x", evt->status); print_broadcast_fragment(evt->fragment); print_field("Length: %d", evt->length); @@ -6670,6 +7144,14 @@ static void slave_broadcast_channel_map_change_evt(const void *data, uint8_t siz print_channel_map(evt->map); } +static void inquiry_response_notify_evt(const void *data, uint8_t size) +{ + const struct bt_hci_evt_inquiry_response_notify *evt = data; + + print_iac(evt->lap); + print_rssi(evt->rssi); +} + static void auth_payload_timeout_expired_evt(const void *data, uint8_t size) { const struct bt_hci_evt_auth_payload_timeout_expired *evt = data; @@ -6689,11 +7171,12 @@ static void le_conn_complete_evt(const void *data, uint8_t size) print_slot_125("Connection interval", evt->interval); print_slot_125("Connection latency", evt->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", - btohs(evt->supv_timeout) * 10, btohs(evt->supv_timeout)); + le16_to_cpu(evt->supv_timeout) * 10, + le16_to_cpu(evt->supv_timeout)); print_field("Master clock accuracy: 0x%2.2x", evt->clock_accuracy); if (evt->status == 0x00) - assign_handle(btohs(evt->handle), 0x01); + assign_handle(le16_to_cpu(evt->handle), 0x01); } static void le_adv_report_evt(const void *data, uint8_t size) @@ -6705,6 +7188,7 @@ static void le_adv_report_evt(const void *data, uint8_t size) print_num_reports(evt->num_reports); +report: switch (evt->event_type) { case 0x00: str = "Connectable undirected - ADV_IND"; @@ -6737,8 +7221,12 @@ static void le_adv_report_evt(const void *data, uint8_t size) evt_len = sizeof(*evt) + evt->data_len + 1; - if (size > evt_len) - packet_hexdump(data + evt_len, size - evt_len); + if (size > evt_len) { + data += evt_len - 1; + size -= evt_len - 1; + evt = data; + goto report; + } } static void le_conn_update_complete_evt(const void *data, uint8_t size) @@ -6750,7 +7238,8 @@ static void le_conn_update_complete_evt(const void *data, uint8_t size) print_slot_125("Connection interval", evt->interval); print_slot_125("Connection latency", evt->latency); print_field("Supervision timeout: %d msec (0x%4.4x)", - btohs(evt->supv_timeout) * 10, btohs(evt->supv_timeout)); + le16_to_cpu(evt->supv_timeout) * 10, + le16_to_cpu(evt->supv_timeout)); } static void le_remote_features_complete_evt(const void *data, uint8_t size) @@ -6767,9 +7256,8 @@ static void le_long_term_key_request_evt(const void *data, uint8_t size) const struct bt_hci_evt_le_long_term_key_request *evt = data; print_handle(evt->handle); - print_random_number(evt->number); - print_field("Encryption diversifier: 0x%4.4x", - btohs(evt->diversifier)); + print_random_number(evt->rand); + print_encrypted_diversifier(evt->ediv); } struct subevent_data { @@ -7003,7 +7491,8 @@ static const struct event_data event_table[] = { slave_page_response_timeout_evt, 0, true }, { 0x55, "Connectionless Slave Broadcast Channel Map Change", slave_broadcast_channel_map_change_evt, 10, true }, - { 0x56, "Inquiry Response Notification" }, + { 0x56, "Inquiry Response Notification", + inquiry_response_notify_evt, 4, true }, { 0x57, "Authenticated Payload Timeout Expired", auth_payload_timeout_expired_evt, 2, true }, { 0xfe, "Testing" }, @@ -7033,7 +7522,7 @@ void packet_hci_command(struct timeval *tv, uint16_t index, const void *data, uint16_t size) { const hci_command_hdr *hdr = data; - uint16_t opcode = btohs(hdr->opcode); + uint16_t opcode = le16_to_cpu(hdr->opcode); uint16_t ogf = cmd_opcode_ogf(opcode); uint16_t ocf = cmd_opcode_ocf(opcode); const struct opcode_data *opcode_data = NULL; @@ -7066,8 +7555,13 @@ void packet_hci_command(struct timeval *tv, uint16_t index, opcode_color = COLOR_HCI_COMMAND_UNKNOWN; opcode_str = opcode_data->str; } else { - opcode_color = COLOR_HCI_COMMAND_UNKNOWN; - opcode_str = "Unknown"; + if (ogf == 0x3f) { + opcode_color = COLOR_HCI_COMMAND; + opcode_str = "Vendor"; + } else { + opcode_color = COLOR_HCI_COMMAND_UNKNOWN; + opcode_str = "Unknown"; + } } sprintf(extra_str, "(0x%2.2x|0x%4.4x) plen %d", ogf, ocf, hdr->plen); @@ -7080,6 +7574,13 @@ void packet_hci_command(struct timeval *tv, uint16_t index, return; } + if (size != hdr->plen) { + print_text(COLOR_ERROR, "invalid packet size (%u != %u)", size, + hdr->plen); + packet_hexdump(data, size); + return; + } + if (opcode_data->cmd_fixed) { if (hdr->plen != opcode_data->cmd_size) { print_text(COLOR_ERROR, "invalid packet size"); @@ -7145,6 +7646,13 @@ void packet_hci_event(struct timeval *tv, uint16_t index, return; } + if (size != hdr->plen) { + print_text(COLOR_ERROR, "invalid packet size (%u != %u)", size, + hdr->plen); + packet_hexdump(data, size); + return; + } + if (event_data->fixed) { if (hdr->plen != event_data->size) { print_text(COLOR_ERROR, "invalid packet size"); @@ -7166,8 +7674,8 @@ void packet_hci_acldata(struct timeval *tv, uint16_t index, bool in, const void *data, uint16_t size) { const struct bt_hci_acl_hdr *hdr = data; - uint16_t handle = btohs(hdr->handle); - uint16_t dlen = btohs(hdr->dlen); + uint16_t handle = le16_to_cpu(hdr->handle); + uint16_t dlen = le16_to_cpu(hdr->dlen); uint8_t flags = acl_flags(handle); char handle_str[16], extra_str[32]; @@ -7209,7 +7717,7 @@ void packet_hci_scodata(struct timeval *tv, uint16_t index, bool in, const void *data, uint16_t size) { const hci_sco_hdr *hdr = data; - uint16_t handle = btohs(hdr->handle); + uint16_t handle = le16_to_cpu(hdr->handle); uint8_t flags = acl_flags(handle); char handle_str[16], extra_str[32]; diff --git a/monitor/packet.h b/monitor/packet.h index d44849c..c39816b 100644 --- a/monitor/packet.h +++ b/monitor/packet.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -40,13 +40,18 @@ void packet_del_filter(unsigned long filter); void packet_select_index(uint16_t index); void packet_hexdump(const unsigned char *buf, uint16_t len); +void packet_print_error(const char *label, uint8_t error); void packet_print_version(const char *label, uint8_t version, const char *sublabel, uint16_t subversion); void packet_print_company(const char *label, uint16_t company); void packet_print_addr(const char *label, const void *data, bool random); void packet_print_ad(const void *data, uint8_t size); +void packet_print_features_lmp(const uint8_t *features, uint8_t page); void packet_print_features_ll(const uint8_t *features); +void packet_print_channel_map_lmp(const uint8_t *map); void packet_print_channel_map_ll(const uint8_t *map); +void packet_print_io_capability(uint8_t capability); +void packet_print_io_authentication(uint8_t authentication); void packet_control(struct timeval *tv, uint16_t index, uint16_t opcode, const void *data, uint16_t size); diff --git a/monitor/rfcomm.h b/monitor/rfcomm.h new file mode 100644 index 0000000..c157352 --- /dev/null +++ b/monitor/rfcomm.h @@ -0,0 +1,80 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * 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 RFCOMM_SABM 0x2f +#define RFCOMM_DISC 0x43 +#define RFCOMM_UA 0x63 +#define RFCOMM_DM 0x0f +#define RFCOMM_UIH 0xef + +#define RFCOMM_GET_TYPE(control) ((control) & 0xef) +#define RFCOMM_GET_DLCI(address) ((address & 0xfc) >> 2) +#define RFCOMM_GET_CHANNEL(address) ((address & 0xf8) >> 3) +#define RFCOMM_GET_DIR(address) ((address & 0x04) >> 2) +#define RFCOMM_TEST_EA(length) ((length & 0x01)) + +struct rfcomm_hdr { + uint8_t address; + uint8_t control; + uint8_t length; +} __attribute__((packed)); + +struct rfcomm_cmd { + uint8_t address; + uint8_t control; + uint8_t length; + uint8_t fcs; +} __attribute__((packed)); + +#define RFCOMM_TEST 0x08 +#define RFCOMM_FCON 0x28 +#define RFCOMM_FCOFF 0x18 +#define RFCOMM_MSC 0x38 +#define RFCOMM_RPN 0x24 +#define RFCOMM_RLS 0x14 +#define RFCOMM_PN 0x20 +#define RFCOMM_NSC 0x04 + +#define RFCOMM_TEST_CR(type) ((type & 0x02)) +#define RFCOMM_GET_MCC_TYPE(type) ((type & 0xfc) >> 2) + +struct rfcomm_mcc { + uint8_t type; + uint8_t length; +} __attribute__((packed)); + +struct rfcomm_msc { + uint8_t dlci; + uint8_t v24_sig; +} __attribute__((packed)); + +struct rfcomm_pn { + uint8_t dlci; + uint8_t flow_ctrl; + uint8_t priority; + uint8_t ack_timer; + uint16_t mtu; + uint8_t max_retrans; + uint8_t credits; +} __attribute__((packed)); diff --git a/monitor/sdp.c b/monitor/sdp.c index 4eb398b..a0ab314 100644 --- a/monitor/sdp.c +++ b/monitor/sdp.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ @@ -33,6 +33,8 @@ #include +#include "src/shared/util.h" + #include "bt.h" #include "packet.h" #include "display.h" @@ -89,14 +91,13 @@ static void print_uint(uint8_t indent, const uint8_t *data, uint32_t size) print_field("%*c0x%2.2x", indent, ' ', data[0]); break; case 2: - print_field("%*c0x%4.4x", indent, ' ', bt_get_be16(data)); + print_field("%*c0x%4.4x", indent, ' ', get_be16(data)); break; case 4: - print_field("%*c0x%8.8x", indent, ' ', bt_get_be32(data)); + print_field("%*c0x%8.8x", indent, ' ', get_be32(data)); break; case 8: - print_field("%*c0x%16.16" PRIx64, indent, ' ', - bt_get_be64(data)); + print_field("%*c0x%16.16" PRIx64, indent, ' ', get_be64(data)); break; default: packet_hexdump(data, size); @@ -114,26 +115,26 @@ static void print_uuid(uint8_t indent, const uint8_t *data, uint32_t size) switch (size) { case 2: print_field("%*c%s (0x%4.4x)", indent, ' ', - uuid16_to_str(bt_get_be16(data)), bt_get_be16(data)); + uuid16_to_str(get_be16(data)), get_be16(data)); break; case 4: print_field("%*c%s (0x%8.8x)", indent, ' ', - uuid32_to_str(bt_get_be32(data)), bt_get_be32(data)); + uuid32_to_str(get_be32(data)), get_be32(data)); break; case 16: /* BASE_UUID = 00000000-0000-1000-8000-00805F9B34FB */ print_field("%*c%8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.4x", indent, ' ', - bt_get_be32(data), bt_get_be16(data + 4), - bt_get_be16(data + 6), bt_get_be16(data + 8), - bt_get_be16(data + 10), bt_get_be32(data + 12)); - if (bt_get_be16(data + 4) == 0x0000 && - bt_get_be16(data + 6) == 0x1000 && - bt_get_be16(data + 8) == 0x8000 && - bt_get_be16(data + 10) == 0x0080 && - bt_get_be32(data + 12) == 0x5F9B34FB) + get_be32(data), get_be16(data + 4), + get_be16(data + 6), get_be16(data + 8), + get_be16(data + 10), get_be32(data + 12)); + if (get_be16(data + 4) == 0x0000 && + get_be16(data + 6) == 0x1000 && + get_be16(data + 8) == 0x8000 && + get_be16(data + 10) == 0x0080 && + get_be32(data + 12) == 0x5F9B34FB) print_field("%*c%s", indent, ' ', - uuid32_to_str(bt_get_be32(data))); + uuid32_to_str(get_be32(data))); break; default: packet_hexdump(data, size); @@ -233,9 +234,9 @@ static uint32_t get_size(const uint8_t *data, uint32_t size) case 8: return data[1]; case 16: - return bt_get_be16(data + 1); + return get_be16(data + 1); case 32: - return bt_get_be32(data + 1); + return get_be32(data + 1); default: return 0; } @@ -319,9 +320,9 @@ static uint32_t get_bytes(const uint8_t *data, uint32_t size) case 5: return 2 + data[1]; case 6: - return 3 + bt_get_be16(data + 1); + return 3 + get_be16(data + 1); case 7: - return 5 + bt_get_be32(data + 1); + return 5 + get_be32(data + 1); } return 0; @@ -354,7 +355,7 @@ static void print_attr(uint32_t position, uint8_t indent, uint8_t type, int i; if ((position % 2) == 0) { - uint16_t id = bt_get_be16(data); + uint16_t id = get_be16(data); const char *str = "Unknown"; for (i = 0; attribute_table[i].str; i++) { @@ -509,7 +510,7 @@ static uint16_t common_rsp(const struct l2cap_frame *frame, return 0; } - bytes = bt_get_be16(frame->data); + bytes = get_be16(frame->data); print_field("Attribute bytes: %d", bytes); if (bytes > frame->size - 2) { @@ -533,7 +534,7 @@ static void error_rsp(const struct l2cap_frame *frame, struct tid_data *tid) return; } - error = bt_get_be16(frame->data); + error = get_be16(frame->data); print_field("Error code: 0x%2.2x", error); } @@ -554,7 +555,7 @@ static void service_req(const struct l2cap_frame *frame, struct tid_data *tid) decode_data_elements(0, 2, frame->data, search_bytes, NULL); print_field("Max record count: %d", - bt_get_be16(frame->data + search_bytes)); + get_be16(frame->data + search_bytes)); print_continuation(frame->data + search_bytes + 2, frame->size - search_bytes - 2); @@ -573,14 +574,14 @@ static void service_rsp(const struct l2cap_frame *frame, struct tid_data *tid) return; } - count = bt_get_be16(frame->data + 2); + count = get_be16(frame->data + 2); - print_field("Total record count: %d", bt_get_be16(frame->data)); + print_field("Total record count: %d", get_be16(frame->data)); print_field("Current record count: %d", count); for (i = 0; i < count; i++) print_field("Record handle: 0x%4.4x", - bt_get_be32(frame->data + 4 + (i * 4))); + get_be32(frame->data + 4 + (i * 4))); print_continuation(frame->data + 4 + (count * 4), frame->size - 4 - (count * 4)); @@ -596,8 +597,8 @@ static void attr_req(const struct l2cap_frame *frame, struct tid_data *tid) return; } - print_field("Record handle: 0x%4.4x", bt_get_be32(frame->data)); - print_field("Max attribute bytes: %d", bt_get_be16(frame->data + 4)); + print_field("Record handle: 0x%4.4x", get_be32(frame->data)); + print_field("Max attribute bytes: %d", get_be16(frame->data + 4)); attr_bytes = get_bytes(frame->data + 6, frame->size - 6); print_field("Attribute list: [len %d]", attr_bytes); @@ -643,7 +644,7 @@ static void search_attr_req(const struct l2cap_frame *frame, decode_data_elements(0, 2, frame->data, search_bytes, NULL); print_field("Max record count: %d", - bt_get_be16(frame->data + search_bytes)); + get_be16(frame->data + search_bytes)); attr_bytes = get_bytes(frame->data + search_bytes + 2, frame->size - search_bytes - 2); @@ -703,8 +704,8 @@ void sdp_packet(const struct l2cap_frame *frame, uint16_t channel) } pdu = *((uint8_t *) frame->data); - tid = bt_get_be16(frame->data + 1); - plen = bt_get_be16(frame->data + 3); + tid = get_be16(frame->data + 1); + plen = get_be16(frame->data + 3); if (frame->size != plen + 5) { print_text(COLOR_ERROR, "invalid frame size"); diff --git a/monitor/sdp.h b/monitor/sdp.h index c339772..ca2cd45 100644 --- a/monitor/sdp.h +++ b/monitor/sdp.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/uuid.c b/monitor/uuid.c index 3d39a47..4b7448e 100644 --- a/monitor/uuid.c +++ b/monitor/uuid.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/uuid.h b/monitor/uuid.h index 2a33c27..f467f51 100644 --- a/monitor/uuid.h +++ b/monitor/uuid.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/vendor.c b/monitor/vendor.c index 31d48ee..8d3b614 100644 --- a/monitor/vendor.c +++ b/monitor/vendor.c @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/monitor/vendor.h b/monitor/vendor.h index 7dbd284..9ac9a4b 100644 --- a/monitor/vendor.h +++ b/monitor/vendor.h @@ -2,22 +2,22 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2011-2012 Intel Corporation - * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011-2014 Intel Corporation + * Copyright (C) 2002-2010 Marcel Holtmann * * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 program is distributed in the hope that it will be useful, + * 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 General Public License for more details. + * 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 General Public License - * along with this program; if not, write to the Free Software + * 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 * */ diff --git a/obexd/client/bluetooth.c b/obexd/client/bluetooth.c index 75deea0..e89a92b 100644 --- a/obexd/client/bluetooth.c +++ b/obexd/client/bluetooth.c @@ -30,13 +30,14 @@ #include #include -#include #include #include #include #include +#include "btio/btio.h" + #include "log.h" #include "transport.h" #include "bluetooth.h" diff --git a/obexd/client/session.c b/obexd/client/session.c index 8138b1e..cb176e4 100644 --- a/obexd/client/session.c +++ b/obexd/client/session.c @@ -240,8 +240,6 @@ static void session_free(struct obc_session *session) if (session->p) pending_request_free(session->p); - sessions = g_slist_remove(sessions, session); - g_free(session->path); g_free(session->owner); g_free(session->source); @@ -250,6 +248,25 @@ static void session_free(struct obc_session *session) g_free(session); } +static void disconnect_complete(GObex *obex, GError *err, GObexPacket *rsp, + void *user_data) +{ + struct obc_session *session = user_data; + + DBG(""); + + if (err) + error("%s", err->message); + + /* Disconnect transport */ + if (session->id > 0 && session->transport != NULL) { + session->transport->disconnect(session->id); + session->id = 0; + } + + session_free(session); +} + void obc_session_unref(struct obc_session *session) { int refs; @@ -261,6 +278,19 @@ void obc_session_unref(struct obc_session *session) if (refs > 0) return; + sessions = g_slist_remove(sessions, session); + + if (!session->obex) + goto disconnect; + + /* Wait OBEX Disconnect to complete if command succeed otherwise + * proceed with transport disconnection since there is nothing else to + * be done */ + if (g_obex_disconnect(session->obex, disconnect_complete, session, + NULL)) + return; + +disconnect: /* Disconnect transport */ if (session->id > 0 && session->transport != NULL) { session->transport->disconnect(session->id); diff --git a/obexd/client/transfer.c b/obexd/client/transfer.c index 5a8d4f2..bd2b1ab 100644 --- a/obexd/client/transfer.c +++ b/obexd/client/transfer.c @@ -60,6 +60,7 @@ struct transfer_callback { enum { TRANSFER_STATUS_QUEUED = 0, TRANSFER_STATUS_ACTIVE, + TRANSFER_STATUS_SUSPENDED, TRANSFER_STATUS_COMPLETE, TRANSFER_STATUS_ERROR }; @@ -79,6 +80,7 @@ struct obc_transfer { char *name; /* Transfer object name */ char *type; /* Transfer object type */ int fd; + guint req; guint xfer; gint64 size; gint64 transferred; @@ -157,6 +159,14 @@ static DBusMessage *obc_transfer_cancel(DBusConnection *connection, ERROR_INTERFACE ".InProgress", "Cancellation already in progress"); + if (transfer->req > 0) { + if (!g_obex_cancel_req(transfer->obex, transfer->req, TRUE)) + return g_dbus_create_error(message, + ERROR_INTERFACE ".Failed", + "Failed"); + transfer->req = 0; + } + if (transfer->xfer == 0) { struct transfer_callback *callback = transfer->callback; @@ -187,6 +197,65 @@ static DBusMessage *obc_transfer_cancel(DBusConnection *connection, return NULL; } +static void transfer_set_status(struct obc_transfer *transfer, uint8_t status) +{ + if (transfer->status == status) + return; + + transfer->status = status; + + g_dbus_emit_property_changed(transfer->conn, transfer->path, + TRANSFER_INTERFACE, "Status"); +} + +static DBusMessage *obc_transfer_suspend(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct obc_transfer *transfer = user_data; + const char *sender; + + sender = dbus_message_get_sender(message); + if (g_strcmp0(transfer->owner, sender) != 0) + return g_dbus_create_error(message, + ERROR_INTERFACE ".NotAuthorized", + "Not Authorized"); + + if (transfer->xfer == 0) + return g_dbus_create_error(message, + ERROR_INTERFACE ".NotInProgress", + "Not in progress"); + + g_obex_suspend(transfer->obex); + + transfer_set_status(transfer, TRANSFER_STATUS_SUSPENDED); + + return g_dbus_create_reply(message, DBUS_TYPE_INVALID); +} + +static DBusMessage *obc_transfer_resume(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct obc_transfer *transfer = user_data; + const char *sender; + + sender = dbus_message_get_sender(message); + if (g_strcmp0(transfer->owner, sender) != 0) + return g_dbus_create_error(message, + ERROR_INTERFACE ".NotAuthorized", + "Not Authorized"); + + if (transfer->xfer == 0) + return g_dbus_create_error(message, + ERROR_INTERFACE ".NotInProgress", + "Not in progress"); + + g_obex_resume(transfer->obex); + + transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE); + + return g_dbus_create_reply(message, DBUS_TYPE_INVALID); +} + static gboolean name_exists(const GDBusPropertyTable *property, void *data) { struct obc_transfer *transfer = data; @@ -269,6 +338,8 @@ static const char *status2str(uint8_t status) return "queued"; case TRANSFER_STATUS_ACTIVE: return "active"; + case TRANSFER_STATUS_SUSPENDED: + return "suspended"; case TRANSFER_STATUS_COMPLETE: return "complete"; case TRANSFER_STATUS_ERROR: @@ -300,6 +371,8 @@ static gboolean get_session(const GDBusPropertyTable *property, } static const GDBusMethodTable obc_transfer_methods[] = { + { GDBUS_METHOD("Suspend", NULL, NULL, obc_transfer_suspend) }, + { GDBUS_METHOD("Resume", NULL, NULL, obc_transfer_resume) }, { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, obc_transfer_cancel) }, { } @@ -319,6 +392,9 @@ static void obc_transfer_free(struct obc_transfer *transfer) { DBG("%p", transfer); + if (transfer->req > 0) + g_obex_cancel_req(transfer->obex, transfer->req, TRUE); + if (transfer->xfer) g_obex_cancel_transfer(transfer->xfer, NULL, NULL); @@ -549,14 +625,6 @@ static gboolean get_xfer_progress(const void *buf, gsize len, return TRUE; } -static void transfer_set_status(struct obc_transfer *transfer, uint8_t status) -{ - transfer->status = status; - - g_dbus_emit_property_changed(transfer->conn, transfer->path, - TRANSFER_INTERFACE, "Status"); -} - static void xfer_complete(GObex *obex, GError *err, gpointer user_data) { struct obc_transfer *transfer = user_data; @@ -630,17 +698,22 @@ static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp, } if (rspcode == G_OBEX_RSP_SUCCESS) { + transfer->req = 0; xfer_complete(obex, err, transfer); return; } - if (!g_obex_srm_active(obex)) { - req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID); + if (g_obex_srm_active(obex) || + transfer->status == TRANSFER_STATUS_SUSPENDED) + return; + + transfer->req = 0; - transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress, + req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID); + + transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress, xfer_complete, transfer, &err); - } } static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data) @@ -689,7 +762,8 @@ static gboolean report_progress(gpointer data) return FALSE; } - if (transfer->status != TRANSFER_STATUS_ACTIVE) + if (transfer->status != TRANSFER_STATUS_ACTIVE && + transfer->status != TRANSFER_STATUS_SUSPENDED) transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE); g_dbus_emit_property_changed(transfer->conn, transfer->path, @@ -724,11 +798,11 @@ static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err) g_obex_packet_add_header(req, hdr); } - transfer->xfer = g_obex_send_req(transfer->obex, req, + transfer->req = g_obex_send_req(transfer->obex, req, FIRST_PACKET_TIMEOUT, get_xfer_progress_first, transfer, err); - if (transfer->xfer == 0) + if (transfer->req == 0) return FALSE; if (transfer->path == NULL) diff --git a/obexd/plugins/bluetooth.c b/obexd/plugins/bluetooth.c index 143d0c2..cf326cc 100644 --- a/obexd/plugins/bluetooth.c +++ b/obexd/plugins/bluetooth.c @@ -35,8 +35,8 @@ #include #include -#include +#include "btio/btio.h" #include "lib/uuid.h" #include "obexd.h" diff --git a/obexd/plugins/pbap.c b/obexd/plugins/pbap.c index 4740188..acac3aa 100644 --- a/obexd/plugins/pbap.c +++ b/obexd/plugins/pbap.c @@ -188,7 +188,7 @@ static void phonebook_size_result(const char *buffer, size_t bufsize, DBG("vcards %d", vcards); - phonebooksize = htons(vcards); + phonebooksize = vcards; pbap->obj->apparam = g_obex_apparam_set_uint16(NULL, PHONEBOOKSIZE_TAG, phonebooksize); @@ -365,7 +365,7 @@ static int generate_response(void *user_data) if (max == 0) { /* Ignore all other parameter and return PhoneBookSize */ - uint16_t size = htons(g_slist_length(pbap->cache.entries)); + uint16_t size = g_slist_length(pbap->cache.entries); pbap->obj->apparam = g_obex_apparam_set_uint16( pbap->obj->apparam, diff --git a/obexd/plugins/phonebook.h b/obexd/plugins/phonebook.h index 441cff2..fff33c1 100644 --- a/obexd/plugins/phonebook.h +++ b/obexd/plugins/phonebook.h @@ -37,7 +37,7 @@ #define PB_CALLS_INCOMING_FOLDER "/telecom/ich" #define PB_CALLS_MISSED_FOLDER "/telecom/mch" #define PB_CALLS_OUTGOING_FOLDER "/telecom/och" -#define PB_LUID_FOLDER "/telecom/luid" +#define PB_LUID_FOLDER "/telecom/pb/luid" #define PB_CONTACTS "/telecom/pb.vcf" #define PB_CALLS_COMBINED "/telecom/cch.vcf" diff --git a/obexd/src/manager.c b/obexd/src/manager.c index cec8a39..326e56f 100644 --- a/obexd/src/manager.c +++ b/obexd/src/manager.c @@ -33,9 +33,9 @@ #include #include -#include #include +#include "btio/btio.h" #include "obexd.h" #include "obex.h" #include "obex-priv.h" diff --git a/obexd/src/obex.c b/obexd/src/obex.c index 8a7a8a3..7b4634e 100644 --- a/obexd/src/obex.c +++ b/obexd/src/obex.c @@ -40,9 +40,9 @@ #include #include -#include #include +#include "btio/btio.h" #include "obexd.h" #include "log.h" #include "obex.h" @@ -53,18 +53,6 @@ #include "service.h" #include "transport.h" -/* Challenge request */ -#define NONCE_TAG 0x00 -#define OPTIONS_TAG 0x01 /* Optional */ -#define REALM_TAG 0x02 /* Optional */ - -#define NONCE_LEN 16 - -/* Challenge response */ -#define DIGEST_TAG 0x00 -#define USER_ID_TAG 0x01 /* Optional */ -#define DIGEST_NONCE_TAG 0x02 /* Optional */ - static GSList *sessions = NULL; typedef struct { @@ -305,53 +293,6 @@ static time_t parse_iso8610(const char *val, int size) return time; } -static uint8_t *extract_nonce(const uint8_t *buffer, unsigned int hlen) -{ - struct auth_header *hdr; - uint8_t *nonce = NULL; - uint32_t len = 0; - - while (len < hlen) { - hdr = (void *) buffer + len; - - switch (hdr->tag) { - case NONCE_TAG: - if (hdr->len != NONCE_LEN) - return NULL; - - nonce = hdr->val; - break; - } - - len += hdr->len + sizeof(struct auth_header); - } - - return nonce; -} - -static uint8_t *challenge_response(const uint8_t *nonce) -{ - GChecksum *md5; - uint8_t *result; - size_t size; - - result = g_new0(uint8_t, NONCE_LEN); - - md5 = g_checksum_new(G_CHECKSUM_MD5); - if (md5 == NULL) - return result; - - g_checksum_update(md5, nonce, NONCE_LEN); - g_checksum_update(md5, (uint8_t *) ":BlueZ", 6); - - size = NONCE_LEN; - g_checksum_get_digest(md5, result, &size); - - g_checksum_free(md5); - - return result; -} - static void parse_service(struct obex_session *os, GObexPacket *req) { GObexHeader *hdr; @@ -377,36 +318,6 @@ probe: who, who_size); } -static void parse_authchal(struct obex_session *session, GObexPacket *req, - GObexPacket *rsp) -{ - GObexHeader *hdr; - const guint8 *data, *nonce = NULL; - gsize len; - uint8_t challenge[18]; - struct auth_header *auth = (struct auth_header *) challenge; - uint8_t *response; - - hdr = g_obex_packet_get_header(req, G_OBEX_HDR_AUTHCHAL); - if (hdr == NULL) - return; - - if (!g_obex_header_get_bytes(hdr, &data, &len)) - return; - - nonce = extract_nonce(data, len); - DBG("AUTH CHALLENGE REQUEST"); - - response = challenge_response(nonce); - auth->tag = DIGEST_TAG; - auth->len = NONCE_LEN; - memcpy(auth->val, response, NONCE_LEN); - - hdr = g_obex_header_new_bytes(G_OBEX_HDR_AUTHRESP, challenge, - sizeof(challenge)); - g_obex_packet_add_header(rsp, hdr); -} - static void cmd_connect(GObex *obex, GObexPacket *req, void *user_data) { struct obex_session *os = user_data; @@ -438,8 +349,6 @@ static void cmd_connect(GObex *obex, GObexPacket *req, void *user_data) rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID); - parse_authchal(os, req, rsp); - if (os->service->target) { hdr = g_obex_header_new_bytes(G_OBEX_HDR_WHO, os->service->target, @@ -964,6 +873,10 @@ static void cmd_put(GObex *obex, GObexPacket *req, gpointer user_data) os->cmd = G_OBEX_OP_PUT; + /* Set size to unknown if a body header exists */ + if (g_obex_packet_get_body(req)) + os->size = OBJECT_SIZE_UNKNOWN; + parse_name(os, req); parse_length(os, req); parse_time(os, req); diff --git a/plugins/autopair.c b/plugins/autopair.c index 8c98c12..d63da93 100644 --- a/plugins/autopair.c +++ b/plugins/autopair.c @@ -29,15 +29,16 @@ #include #include #include +#include #include #include -#include "plugin.h" -#include "adapter.h" -#include "device.h" -#include "log.h" -#include "storage.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/log.h" +#include "src/storage.h" /* * Plugin to handle automatic pairing of devices with reduced user @@ -157,19 +158,27 @@ static int autopair_init(void) { /* Initialize the random seed from /dev/urandom */ unsigned int seed; - int fd; + int fd, err; + ssize_t n; fd = open("/dev/urandom", O_RDONLY); - if (fd >= 0) { - ssize_t n; - - n = read(fd, &seed, sizeof(seed)); - if (n < (ssize_t) sizeof(seed)) - seed = time(NULL); + if (fd < 0) { + err = -errno; + error("Failed to open /dev/urandom: %s (%d)", strerror(-err), + -err); + return err; + } + n = read(fd, &seed, sizeof(seed)); + if (n < (ssize_t) sizeof(seed)) { + err = (n == -1) ? -errno : -EIO; + error("Failed to read %zu bytes from /dev/urandom", + sizeof(seed)); close(fd); - } else - seed = time(NULL); + return err; + } + + close(fd); srand(seed); diff --git a/plugins/external-dummy.c b/plugins/external-dummy.c index ff31290..536ad06 100644 --- a/plugins/external-dummy.c +++ b/plugins/external-dummy.c @@ -22,8 +22,8 @@ #include #endif -#include "plugin.h" -#include "log.h" +#include "src/plugin.h" +#include "src/log.h" static int dummy_init(void) { diff --git a/plugins/gatt-example.c b/plugins/gatt-example.c index 9b4187a..6e20b1f 100644 --- a/plugins/gatt-example.c +++ b/plugins/gatt-example.c @@ -28,18 +28,18 @@ #include #include -#include #include "lib/uuid.h" -#include "plugin.h" -#include "hcid.h" -#include "log.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/shared/util.h" +#include "src/log.h" #include "attrib/gattrib.h" #include "attrib/gatt-service.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "attrib/att-database.h" -#include "attrib-server.h" +#include "src/attrib-server.h" /* FIXME: Not defined by SIG? UUID128? */ #define OPCODES_SUPPORTED_UUID 0xA001 @@ -115,8 +115,8 @@ static gboolean register_battery_service(struct btd_adapter *adapter) return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid, /* battery state characteristic */ GATT_OPT_CHR_UUID16, BATTERY_STATE_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ | - ATT_CHAR_PROPER_NOTIFY, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ | + GATT_CHR_PROP_NOTIFY, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, battery_state_read, adapter, @@ -149,7 +149,7 @@ static void register_termometer_service(struct gatt_example_adapter *adapter, /* Thermometer: primary service definition */ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); - att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]); + put_le16(THERM_HUMIDITY_SVC_UUID, &atval[0]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); @@ -157,27 +157,27 @@ static void register_termometer_service(struct gatt_example_adapter *adapter, /* Thermometer: Include */ if (manuf1[0] && manuf1[1]) { - att_put_u16(manuf1[0], &atval[0]); - att_put_u16(manuf1[1], &atval[2]); - att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]); + put_le16(manuf1[0], &atval[0]); + put_le16(manuf1[1], &atval[2]); + put_le16(MANUFACTURER_SVC_UUID, &atval[4]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6); } /* Thermometer: Include */ if (manuf2[0] && manuf2[1]) { - att_put_u16(manuf2[0], &atval[0]); - att_put_u16(manuf2[1], &atval[2]); - att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]); + put_le16(manuf2[0], &atval[0]); + put_le16(manuf2[1], &atval[2]); + put_le16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]); attrib_db_add(adapter->adapter, h++, &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(h + 1, &atval[1]); - att_put_u16(TEMPERATURE_UUID, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(h + 1, &atval[1]); + put_le16(TEMPERATURE_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); @@ -192,9 +192,9 @@ static void register_termometer_service(struct gatt_example_adapter *adapter, bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID); atval[0] = 0x0E; atval[1] = 0xFE; - att_put_u16(FMT_CELSIUS_UUID, &atval[2]); + put_le16(FMT_CELSIUS_UUID, &atval[2]); atval[4] = 0x01; - att_put_u16(FMT_OUTSIDE_UUID, &atval[5]); + put_le16(FMT_OUTSIDE_UUID, &atval[5]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 7); @@ -207,9 +207,9 @@ static void register_termometer_service(struct gatt_example_adapter *adapter, /* Thermometer: relative humidity characteristic */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); - atval[0] = ATT_CHAR_PROPER_READ; - att_put_u16(h + 1, &atval[1]); - att_put_u16(RELATIVE_HUMIDITY_UUID, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(h + 1, &atval[1]); + put_le16(RELATIVE_HUMIDITY_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); @@ -223,9 +223,9 @@ static void register_termometer_service(struct gatt_example_adapter *adapter, 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]); + put_le16(FMT_PERCENT_UUID, &atval[2]); + put_le16(BLUETOOTH_SIG_UUID, &atval[4]); + put_le16(FMT_OUTSIDE_UUID, &atval[6]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8); @@ -270,15 +270,15 @@ static void register_manuf1_service(struct gatt_example_adapter *adapter, /* Secondary Service: Manufacturer Service */ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID); - att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]); + put_le16(MANUFACTURER_SVC_UUID, &atval[0]); attrib_db_add(adapter->adapter, h++, &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(h + 1, &atval[1]); - att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(h + 1, &atval[1]); + put_le16(MANUFACTURER_NAME_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); @@ -291,9 +291,9 @@ static void register_manuf1_service(struct gatt_example_adapter *adapter, /* Manufacturer serial number characteristic */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); - atval[0] = ATT_CHAR_PROPER_READ; - att_put_u16(h + 1, &atval[1]); - att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(h + 1, &atval[1]); + put_le16(MANUFACTURER_SERIAL_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); @@ -334,15 +334,15 @@ static void register_manuf2_service(struct gatt_example_adapter *adapter, /* Secondary Service: Manufacturer Service */ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID); - att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]); + put_le16(MANUFACTURER_SVC_UUID, &atval[0]); attrib_db_add(adapter->adapter, h++, &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(h + 1, &atval[1]); - att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(h + 1, &atval[1]); + put_le16(MANUFACTURER_NAME_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); @@ -355,9 +355,9 @@ static void register_manuf2_service(struct gatt_example_adapter *adapter, /* Characteristic: serial number */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); - atval[0] = ATT_CHAR_PROPER_READ; - att_put_u16(h + 1, &atval[1]); - att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(h + 1, &atval[1]); + put_le16(MANUFACTURER_SERIAL_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); @@ -395,15 +395,15 @@ static void register_vendor_service(struct gatt_example_adapter *adapter, /* Secondary Service: Vendor Specific Service */ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID); - att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]); + put_le16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]); attrib_db_add(adapter->adapter, h++, &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(h + 1, &atval[1]); - att_put_u16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(h + 1, &atval[1]); + put_le16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); @@ -465,17 +465,17 @@ static void register_weight_service(struct gatt_example_adapter *adapter, if (vendor[0] && vendor[1]) { /* Weight: include */ bt_uuid16_create(&uuid, GATT_INCLUDE_UUID); - att_put_u16(vendor[0], &atval[0]); - att_put_u16(vendor[1], &atval[2]); - att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]); + put_le16(vendor[0], &atval[0]); + put_le16(vendor[1], &atval[2]); + put_le16(MANUFACTURER_SVC_UUID, &atval[4]); attrib_db_add(adapter->adapter, h++, &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(h + 1, &atval[1]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(h + 1, &atval[1]); memcpy(&atval[3], &char_weight_uuid_btorder, 16); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 19); @@ -493,9 +493,9 @@ static void register_weight_service(struct gatt_example_adapter *adapter, 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]); + put_le16(FMT_KILOGRAM_UUID, &atval[2]); + put_le16(BLUETOOTH_SIG_UUID, &atval[4]); + put_le16(FMT_HANGING_UUID, &atval[6]); attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8); diff --git a/plugins/hostname.c b/plugins/hostname.c index 92a71e0..d4d72d3 100644 --- a/plugins/hostname.c +++ b/plugins/hostname.c @@ -30,10 +30,10 @@ #include #include -#include "dbus-common.h" -#include "plugin.h" -#include "adapter.h" -#include "log.h" +#include "src/dbus-common.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/log.h" /* http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm */ diff --git a/plugins/neard.c b/plugins/neard.c index 35fdaeb..137d601 100644 --- a/plugins/neard.c +++ b/plugins/neard.c @@ -25,6 +25,7 @@ #include #endif +#include #include #include @@ -32,14 +33,14 @@ #include #include -#include "plugin.h" -#include "log.h" -#include "dbus-common.h" -#include "adapter.h" -#include "device.h" -#include "eir.h" -#include "agent.h" -#include "hcid.h" +#include "src/plugin.h" +#include "src/log.h" +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/eir.h" +#include "src/agent.h" +#include "src/hcid.h" #define NEARD_NAME "org.neard" #define NEARD_PATH "/" @@ -77,7 +78,7 @@ struct oob_params { static void free_oob_params(struct oob_params *params) { - g_slist_free_full(params->services, g_free); + g_slist_free_full(params->services, free); g_free(params->name); g_free(params->hash); g_free(params->randomizer); @@ -326,7 +327,7 @@ static int check_device(struct btd_device *device) return -ENOENT; /* If already paired */ - if (device_is_paired(device)) { + if (device_is_paired(device, BDADDR_BREDR)) { DBG("already paired"); return -EALREADY; } @@ -643,7 +644,8 @@ static void store_params(struct btd_adapter *adapter, struct btd_device *device, btd_device_device_set_name(device, params->name); } - /* TODO handle UUIDs? */ + if (params->services) + device_add_eir_uuids(device, params->services); if (params->hash) { btd_adapter_add_remote_oob_data(adapter, ¶ms->address, @@ -767,42 +769,39 @@ static DBusMessage *request_oob(DBusConnection *conn, DBusMessage *msg, if (err < 0) return error_reply(msg, -err); - if (bacmp(&remote.address, BDADDR_ANY) == 0) - goto read_local; - - device = btd_adapter_get_device(adapter, &remote.address, - BDADDR_BREDR); + if (bacmp(&remote.address, BDADDR_ANY) == 0) { + if (btd_adapter_get_powered(adapter)) + goto read_local; - err = check_device(device); - if (err < 0) { - free_oob_params(&remote); + goto done; + } - if (err == -EALREADY) - return create_request_oob_reply(adapter, NULL, NULL, - msg); + device = btd_adapter_get_device(adapter, &remote.address, BDADDR_BREDR); - return error_reply(msg, -err); - } + err = check_device(device); + if (err < 0) + goto done; if (!btd_adapter_get_pairable(adapter)) { - free_oob_params(&remote); - - return error_reply(msg, ENONET); + err = -ENONET; + goto done; } store_params(adapter, device, &remote); - if (!remote.hash || !btd_adapter_get_powered(adapter)) { - free_oob_params(&remote); - return create_request_oob_reply(adapter, NULL, NULL, msg); - } + if (remote.hash && btd_adapter_get_powered(adapter)) + goto read_local; +done: + free_oob_params(&remote); + + if (err < 0 && err != -EALREADY) + return error_reply(msg, -err); + + return create_request_oob_reply(adapter, NULL, NULL, msg); read_local: free_oob_params(&remote); - if (!btd_adapter_get_powered(adapter)) - return create_request_oob_reply(adapter, NULL, NULL, msg); - err = btd_adapter_read_local_oob_data(adapter); if (err < 0) return error_reply(msg, -err); diff --git a/plugins/policy.c b/plugins/policy.c index 0292482..6fb0729 100644 --- a/plugins/policy.c +++ b/plugins/policy.c @@ -28,16 +28,19 @@ #include #include #include +#include #include #include "lib/uuid.h" +#include "lib/mgmt.h" #include "src/log.h" #include "src/plugin.h" #include "src/adapter.h" #include "src/device.h" #include "src/service.h" #include "src/profile.h" +#include "src/hcid.h" #define CONTROL_CONNECT_TIMEOUT 2 #define SOURCE_RETRY_TIMEOUT 2 @@ -45,6 +48,26 @@ #define SOURCE_RETRIES 1 #define SINK_RETRIES SOURCE_RETRIES +/* Tracking of remote services to be auto-reconnected upon link loss */ + +#define RECONNECT_GIVE_UP (3 * 60) +#define RECONNECT_TIMEOUT 1 + +struct reconnect_data { + struct btd_device *dev; + bool reconnect; + GSList *services; + guint timer; + bool active; + time_t start; + int timeout; +}; + +static const char *default_reconnect[] = { + HSP_AG_UUID, HFP_AG_UUID, A2DP_SOURCE_UUID, NULL }; +static char **reconnect_uuids = NULL; +static GSList *reconnects = NULL; + static unsigned int service_id = 0; static GSList *devices = NULL; @@ -402,12 +425,114 @@ static void target_cb(struct btd_service *service, } } +static void reconnect_reset(struct reconnect_data *reconnect) +{ + reconnect->start = 0; + reconnect->timeout = 1; + + if (reconnect->timer > 0) { + g_source_remove(reconnect->timer); + reconnect->timer = 0; + } +} + +static bool reconnect_match(const char *uuid) +{ + char **str; + + if (!reconnect_uuids) + return false; + + for (str = reconnect_uuids; *str; str++) { + if (!bt_uuid_strcmp(uuid, *str)) + return true; + } + + return false; +} + +static struct reconnect_data *reconnect_find(struct btd_device *dev) +{ + GSList *l; + + for (l = reconnects; l; l = g_slist_next(l)) { + struct reconnect_data *reconnect = l->data; + + if (reconnect->dev == dev) + return reconnect; + } + + return NULL; +} + +static struct reconnect_data *reconnect_add(struct btd_service *service) +{ + struct btd_device *dev = btd_service_get_device(service); + struct reconnect_data *reconnect; + + reconnect = reconnect_find(dev); + if (!reconnect) { + reconnect = g_new0(struct reconnect_data, 1); + reconnect->dev = dev; + reconnects = g_slist_append(reconnects, reconnect); + } + + if (g_slist_find(reconnect->services, service)) + return reconnect; + + reconnect->services = g_slist_append(reconnect->services, + btd_service_ref(service)); + + return reconnect; +} + +static void reconnect_destroy(gpointer data) +{ + struct reconnect_data *reconnect = data; + + if (reconnect->timer > 0) + g_source_remove(reconnect->timer); + + g_slist_free_full(reconnect->services, + (GDestroyNotify) btd_service_unref); + g_free(reconnect); +} + +static void reconnect_remove(struct btd_service *service) +{ + struct btd_device *dev = btd_service_get_device(service); + struct reconnect_data *reconnect; + GSList *l; + + reconnect = reconnect_find(dev); + if (!reconnect) + return; + + l = g_slist_find(reconnect->services, service); + if (!l) + return; + + reconnect->services = g_slist_delete_link(reconnect->services, l); + btd_service_unref(service); + + if (reconnect->services) + return; + + reconnects = g_slist_remove(reconnects, reconnect); + + if (reconnect->timer > 0) + g_source_remove(reconnect->timer); + + g_free(reconnect); +} + static void service_cb(struct btd_service *service, btd_service_state_t old_state, btd_service_state_t new_state, void *user_data) { struct btd_profile *profile = btd_service_get_profile(service); + struct reconnect_data *reconnect; if (g_str_equal(profile->remote_uuid, A2DP_SINK_UUID)) sink_cb(service, old_state, new_state); @@ -417,17 +542,178 @@ static void service_cb(struct btd_service *service, controller_cb(service, old_state, new_state); else if (g_str_equal(profile->remote_uuid, AVRCP_TARGET_UUID)) target_cb(service, old_state, new_state); + + /* + * Return if the reconnection feature is not enabled (all + * subsequent code in this function is about that). + */ + if (!reconnect_uuids || !reconnect_uuids[0]) + return; + + /* + * We're only interested in reconnecting profiles which have set + * auto_connect to true. + */ + if (!profile->auto_connect) + return; + + /* + * If the service went away remove it from the reconnection + * tracking. The function will remove the entire tracking data + * if this was the last service for the device. + */ + if (new_state == BTD_SERVICE_STATE_UNAVAILABLE) { + reconnect_remove(service); + return; + } + + if (new_state != BTD_SERVICE_STATE_CONNECTED) + return; + + /* + * Add an entry to track reconnections. The function will return + * an existing entry if there is one. + */ + reconnect = reconnect_add(service); + + reconnect->active = false; + reconnect_reset(reconnect); + + /* + * Should this device be reconnected? A matching UUID might not + * be the first profile that's connected so we might have an + * entry but with the reconnect flag set to false. + */ + if (!reconnect->reconnect) + reconnect->reconnect = reconnect_match(profile->remote_uuid); + + DBG("Added %s reconnect %u", profile->name, reconnect->reconnect); +} + +static gboolean reconnect_timeout(gpointer data) +{ + struct reconnect_data *reconnect = data; + int err; + + DBG("Reconnecting profiles"); + + /* Mark the GSource as invalid */ + reconnect->timer = 0; + + /* Increase timeout for the next attempt if this * one fails. */ + reconnect->timeout *= 2; + + err = btd_device_connect_services(reconnect->dev, reconnect->services); + if (err < 0) { + error("Reconnecting services failed: %s (%d)", + strerror(-err), -err); + reconnect_reset(reconnect); + return FALSE; + } + + reconnect->active = true; + + if (!reconnect->start) + reconnect->start = time(NULL); + + return FALSE; +} + +static void disconnect_cb(struct btd_device *dev, uint8_t reason) +{ + struct reconnect_data *reconnect; + + DBG("reason %u", reason); + + if (reason != MGMT_DEV_DISCONN_TIMEOUT) + return; + + reconnect = reconnect_find(dev); + if (!reconnect || !reconnect->reconnect) + return; + + DBG("Device %s identified for auto-reconnection", + device_get_path(dev)); + + reconnect->timer = g_timeout_add_seconds(reconnect->timeout, + reconnect_timeout, + reconnect); +} + +static void conn_fail_cb(struct btd_device *dev, uint8_t status) +{ + struct reconnect_data *reconnect; + time_t duration; + + DBG("status %u", status); + + reconnect = reconnect_find(dev); + if (!reconnect || !reconnect->reconnect) + return; + + if (!reconnect->active) + return; + + reconnect->active = false; + + /* Give up if we were powered off */ + if (status == MGMT_STATUS_NOT_POWERED) { + reconnect_reset(reconnect); + return; + } + + /* Give up if we've tried for too long */ + duration = time(NULL) - reconnect->start; + if (duration + reconnect->timeout >= RECONNECT_GIVE_UP) { + reconnect_reset(reconnect); + return; + } + + reconnect->timer = g_timeout_add_seconds(reconnect->timeout, + reconnect_timeout, + reconnect); } static int policy_init(void) { + GError *gerr = NULL; + GKeyFile *conf; + service_id = btd_service_add_state_cb(service_cb, NULL); + conf = btd_get_main_conf(); + if (!conf) { + reconnect_uuids = g_strdupv((char **) default_reconnect); + goto add_cb; + } + + reconnect_uuids = g_key_file_get_string_list(conf, "Policy", + "ReconnectUUIDs", + NULL, &gerr); + if (gerr) { + g_error_free(gerr); + reconnect_uuids = g_strdupv((char **) default_reconnect); + goto add_cb; + } +add_cb: + if (reconnect_uuids && reconnect_uuids[0]) { + btd_add_disconnect_cb(disconnect_cb); + btd_add_conn_fail_cb(conn_fail_cb); + } + return 0; } static void policy_exit(void) { + btd_remove_disconnect_cb(disconnect_cb); + btd_remove_conn_fail_cb(conn_fail_cb); + + if (reconnect_uuids) + g_strfreev(reconnect_uuids); + + g_slist_free_full(reconnects, reconnect_destroy); + g_slist_free_full(devices, policy_remove); btd_service_remove_state_cb(service_id); diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c index 45fa170..1b7bb30 100644 --- a/plugins/sixaxis.c +++ b/plugins/sixaxis.c @@ -125,8 +125,25 @@ static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr) return ret; } -static gboolean setup_leds(GIOChannel *channel, GIOCondition cond, - gpointer user_data) +static uint8_t calc_leds_bitmap(int number) +{ + uint8_t bitmap = 0; + + /* TODO we could support up to 10 (1 + 2 + 3 + 4) */ + if (number > 7) + return bitmap; + + if (number > 4) { + bitmap |= 0x10; + number -= 4; + } + + bitmap |= 0x01 << number; + + return bitmap; +} + +static void set_leds_hidraw(int fd, uint8_t leds_bitmap) { /* * the total time the led is active (0xff means forever) @@ -147,36 +164,37 @@ static gboolean setup_leds(GIOChannel *channel, GIOCondition cond, 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, }; - int number = GPOINTER_TO_INT(user_data); int ret; - int fd; - - if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) - return FALSE; - - DBG("number %d", number); - - /* TODO we could support up to 10 (1 + 2 + 3 + 4) */ - if (number > 7) - return FALSE; - - if (number > 4) { - leds_report[10] |= 0x10; - number -= 4; - } - - leds_report[10] |= 0x01 << number; - fd = g_io_channel_unix_get_fd(channel); + leds_report[10] = leds_bitmap; ret = write(fd, leds_report, sizeof(leds_report)); if (ret == sizeof(leds_report)) - return FALSE; + return; if (ret < 0) error("sixaxis: failed to set LEDS (%s)", strerror(errno)); else error("sixaxis: failed to set LEDS (%d bytes written)", ret); +} + +static gboolean setup_leds(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + int number = GPOINTER_TO_INT(user_data); + uint8_t bitmap; + int fd; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) + return FALSE; + + DBG("number %d", number); + + fd = g_io_channel_unix_get_fd(channel); + + bitmap = calc_leds_bitmap(number); + if (bitmap != 0) + set_leds_hidraw(fd, bitmap); return FALSE; } @@ -197,7 +215,8 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter) /* This can happen if controller was plugged while already connected * eg. to charge up battery. * Don't set LEDs in that case, hence return false */ - device = btd_adapter_find_device(adapter, &device_bdaddr); + device = btd_adapter_find_device(adapter, &device_bdaddr, + BDADDR_BREDR); if (device && btd_device_is_connected(device)) return false; @@ -228,7 +247,6 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter) btd_device_set_pnpid(device, devices[index].source, devices[index].vid, devices[index].pid, devices[index].version); btd_device_set_temporary(device, FALSE); - btd_device_set_trusted(device, TRUE); return true; } @@ -301,7 +319,7 @@ static int get_supported_device(struct udev_device *udevice, uint16_t *bus) struct udev_device *hid_parent; uint16_t vid, pid; const char *hid_id; - int i; + guint i; hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice, "hid", NULL); @@ -313,7 +331,7 @@ static int get_supported_device(struct udev_device *udevice, uint16_t *bus) if (sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3) return -1; - for (i = 0; G_N_ELEMENTS(devices); i++) { + for (i = 0; i < G_N_ELEMENTS(devices); i++) { if (devices[i].vid == vid && devices[i].pid == pid) return i; } diff --git a/plugins/wiimote.c b/plugins/wiimote.c index 96a6569..bd8820e 100644 --- a/plugins/wiimote.c +++ b/plugins/wiimote.c @@ -30,11 +30,11 @@ #include #include -#include "plugin.h" -#include "adapter.h" -#include "device.h" -#include "log.h" -#include "storage.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/log.h" +#include "src/storage.h" /* * Nintendo Wii Remote devices require the bdaddr of the host as pin input for @@ -90,7 +90,6 @@ static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device, product = btd_device_get_product(device); device_get_name(device, name, sizeof(name)); - name[sizeof(name) - 1] = 0; for (i = 0; i < G_N_ELEMENTS(wii_ids); ++i) { if (vendor == wii_ids[i][0] && product == wii_ids[i][1]) diff --git a/profiles/alert/server.c b/profiles/alert/server.c index 4565470..1612d6c 100644 --- a/profiles/alert/server.c +++ b/profiles/alert/server.c @@ -33,21 +33,21 @@ #include #include "lib/uuid.h" -#include "plugin.h" -#include "dbus-common.h" +#include "src/plugin.h" +#include "src/dbus-common.h" #include "attrib/att.h" -#include "adapter.h" -#include "device.h" +#include "src/adapter.h" +#include "src/device.h" #include "attrib/att-database.h" -#include "log.h" +#include "src/log.h" #include "attrib/gatt-service.h" #include "attrib/gattrib.h" -#include "attrib-server.h" +#include "src/attrib-server.h" #include "attrib/gatt.h" -#include "profile.h" -#include "error.h" -#include "textfile.h" -#include "attio.h" +#include "src/profile.h" +#include "src/error.h" +#include "src/textfile.h" +#include "src/attio.h" #define PHONE_ALERT_STATUS_SVC_UUID 0x180E #define ALERT_NOTIF_SVC_UUID 0x1811 @@ -363,6 +363,20 @@ end: return result; } +static void destroy_notify_callback(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct notify_callback *cb = user_data; + + DBG("status=%#x", status); + + btd_device_remove_attio_callback(cb->device, cb->id); + btd_device_unref(cb->device); + g_free(cb->notify_data->value); + g_free(cb->notify_data); + g_free(cb); +} + static void attio_connected_cb(GAttrib *attrib, gpointer user_data) { struct notify_callback *cb = user_data; @@ -398,7 +412,9 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data) al_adapter->hnd_value[type], al_adapter->hnd_ccc[type]); - g_attrib_send(attrib, 0, pdu, len, NULL, NULL, NULL); + g_attrib_send(attrib, 0, pdu, len, destroy_notify_callback, cb, NULL); + + return; end: btd_device_remove_attio_callback(cb->device, cb->id); @@ -790,8 +806,8 @@ static void register_phone_alert_service(struct alert_adapter *al_adapter) gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid, /* Alert Status characteristic */ GATT_OPT_CHR_UUID16, ALERT_STATUS_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ | - ATT_CHAR_PROPER_NOTIFY, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ | + GATT_CHR_PROP_NOTIFY, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, alert_status_read, al_adapter->adapter, GATT_OPT_CCC_GET_HANDLE, @@ -800,13 +816,13 @@ static void register_phone_alert_service(struct alert_adapter *al_adapter) &al_adapter->hnd_value[NOTIFY_ALERT_STATUS], /* Ringer Control Point characteristic */ GATT_OPT_CHR_UUID16, RINGER_CP_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_WRITE_WITHOUT_RESP, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE_WITHOUT_RESP, GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, ringer_cp_write, NULL, /* Ringer Setting characteristic */ GATT_OPT_CHR_UUID16, RINGER_SETTING_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ | - ATT_CHAR_PROPER_NOTIFY, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ | + GATT_CHR_PROP_NOTIFY, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, ringer_setting_read, al_adapter->adapter, GATT_OPT_CCC_GET_HANDLE, @@ -893,35 +909,35 @@ static void register_alert_notif_service(struct alert_adapter *al_adapter) gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid, /* Supported New Alert Category */ GATT_OPT_CHR_UUID16, SUPP_NEW_ALERT_CAT_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, supp_new_alert_cat_read, al_adapter->adapter, GATT_OPT_CHR_VALUE_GET_HANDLE, &al_adapter->supp_new_alert_cat_handle, /* New Alert */ GATT_OPT_CHR_UUID16, NEW_ALERT_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_NOTIFY, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY, GATT_OPT_CCC_GET_HANDLE, &al_adapter->hnd_ccc[NOTIFY_NEW_ALERT], GATT_OPT_CHR_VALUE_GET_HANDLE, &al_adapter->hnd_value[NOTIFY_NEW_ALERT], /* Supported Unread Alert Category */ GATT_OPT_CHR_UUID16, SUPP_UNREAD_ALERT_CAT_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, supp_unread_alert_cat_read, al_adapter->adapter, GATT_OPT_CHR_VALUE_GET_HANDLE, &al_adapter->supp_unread_alert_cat_handle, /* Unread Alert Status */ GATT_OPT_CHR_UUID16, UNREAD_ALERT_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_NOTIFY, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY, GATT_OPT_CCC_GET_HANDLE, &al_adapter->hnd_ccc[NOTIFY_UNREAD_ALERT], GATT_OPT_CHR_VALUE_GET_HANDLE, &al_adapter->hnd_value[NOTIFY_UNREAD_ALERT], /* Alert Notification Control Point */ GATT_OPT_CHR_UUID16, ALERT_NOTIF_CP_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_WRITE, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE, GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, alert_notif_cp_write, NULL, GATT_OPT_INVALID); diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c index 29a1593..cabdd66 100644 --- a/profiles/audio/a2dp.c +++ b/profiles/audio/a2dp.c @@ -43,14 +43,14 @@ #include "src/device.h" #include "src/profile.h" #include "src/service.h" +#include "src/log.h" +#include "src/sdpd.h" -#include "log.h" #include "avdtp.h" #include "sink.h" #include "source.h" #include "a2dp.h" #include "a2dp-codecs.h" -#include "sdpd.h" #include "media.h" /* The duration that streams without users are allowed to stay in diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c index 6669ddc..74d3512 100644 --- a/profiles/audio/avctp.c +++ b/profiles/audio/avctp.c @@ -44,15 +44,16 @@ #include #include -#include +#include "btio/btio.h" #include "lib/uuid.h" #include "src/adapter.h" #include "src/device.h" -#include "log.h" -#include "error.h" -#include "uinput.h" +#include "src/log.h" +#include "src/error.h" +#include "src/uinput.h" + #include "avctp.h" #include "avrcp.h" @@ -237,25 +238,53 @@ static struct { { "ROOT MENU", AVC_ROOT_MENU, KEY_MENU }, { "CONTENTS MENU", AVC_CONTENTS_MENU, KEY_PROGRAM }, { "FAVORITE MENU", AVC_FAVORITE_MENU, KEY_FAVORITES }, + { "EXIT", AVC_EXIT, KEY_EXIT }, + { "ON DEMAND MENU", AVC_ON_DEMAND_MENU, KEY_MENU }, + { "APPS MENU", AVC_APPS_MENU, KEY_MENU }, + { "0", AVC_0, KEY_0 }, + { "1", AVC_1, KEY_1 }, + { "2", AVC_2, KEY_2 }, + { "3", AVC_3, KEY_3 }, + { "4", AVC_4, KEY_4 }, + { "5", AVC_5, KEY_5 }, + { "6", AVC_6, KEY_6 }, + { "7", AVC_7, KEY_7 }, + { "8", AVC_8, KEY_8 }, + { "9", AVC_9, KEY_9 }, + { "DOT", AVC_DOT, KEY_DOT }, { "ENTER", AVC_ENTER, KEY_ENTER }, { "CHANNEL UP", AVC_CHANNEL_UP, KEY_CHANNELUP }, { "CHANNEL DOWN", AVC_CHANNEL_DOWN, KEY_CHANNELDOWN }, + { "CHANNEL PREVIOUS", AVC_CHANNEL_PREVIOUS, KEY_LAST }, { "INPUT SELECT", AVC_INPUT_SELECT, KEY_CONFIG }, + { "INFO", AVC_INFO, KEY_INFO }, { "HELP", AVC_HELP, KEY_HELP }, { "POWER", AVC_POWER, KEY_POWER2 }, { "VOLUME UP", AVC_VOLUME_UP, KEY_VOLUMEUP }, { "VOLUME DOWN", AVC_VOLUME_DOWN, KEY_VOLUMEDOWN }, + { "MUTE", AVC_MUTE, KEY_MUTE }, { "PLAY", AVC_PLAY, KEY_PLAYCD }, { "STOP", AVC_STOP, KEY_STOPCD }, { "PAUSE", AVC_PAUSE, KEY_PAUSECD }, { "FORWARD", AVC_FORWARD, KEY_NEXTSONG }, { "BACKWARD", AVC_BACKWARD, KEY_PREVIOUSSONG }, + { "RECORD", AVC_RECORD, KEY_RECORD }, { "REWIND", AVC_REWIND, KEY_REWIND }, { "FAST FORWARD", AVC_FAST_FORWARD, KEY_FASTFORWARD }, + { "LIST", AVC_LIST, KEY_LIST }, { "F1", AVC_F1, KEY_F1 }, { "F2", AVC_F2, KEY_F2 }, { "F3", AVC_F3, KEY_F3 }, { "F4", AVC_F4, KEY_F4 }, + { "F5", AVC_F5, KEY_F5 }, + { "F6", AVC_F6, KEY_F6 }, + { "F7", AVC_F7, KEY_F7 }, + { "F8", AVC_F8, KEY_F8 }, + { "F9", AVC_F9, KEY_F9 }, + { "RED", AVC_RED, KEY_RED }, + { "GREEN", AVC_GREEN, KEY_GREEN }, + { "BLUE", AVC_BLUE, KEY_BLUE }, + { "YELLOW", AVC_YELLOW, KEY_YELLOW }, { NULL } }; @@ -313,7 +342,7 @@ static size_t handle_panel_passthrough(struct avctp *session, if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) { *code = AVC_CTYPE_REJECTED; - return 0; + return operand_count; } if (operand_count == 0) @@ -379,7 +408,7 @@ static size_t handle_panel_passthrough(struct avctp *session, DBG("AV/C: unknown button 0x%02X %s", operands[0] & 0x7F, status); *code = AVC_CTYPE_NOT_IMPLEMENTED; - return 0; + return operand_count; } done: @@ -903,8 +932,7 @@ failed: return FALSE; } -static gboolean session_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) +static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; struct avctp_channel *control = session->control; @@ -924,24 +952,24 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, if (ret <= 0) goto failed; - if ((unsigned int) ret < sizeof(struct avctp_header)) { + if (ret < AVCTP_HEADER_LENGTH) { error("Too small AVCTP packet"); goto failed; } avctp = (struct avctp_header *) buf; - ret -= sizeof(struct avctp_header); - if ((unsigned int) ret < sizeof(struct avc_header)) { - error("Too small AVCTP packet"); + ret -= AVCTP_HEADER_LENGTH; + if (ret < AVC_HEADER_LENGTH) { + error("Too small AVC packet"); goto failed; } - avc = (struct avc_header *) (buf + sizeof(struct avctp_header)); + avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH); - ret -= sizeof(struct avc_header); + ret -= AVC_HEADER_LENGTH; - operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header); + operands = (uint8_t *) avc + AVC_HEADER_LENGTH; operand_count = ret; if (avctp->cr == AVCTP_RESPONSE) { @@ -1371,7 +1399,8 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data) DBG("AVCTP: incoming connect from %s", address); - device = btd_adapter_find_device(adapter_find(&src), &dst); + device = btd_adapter_find_device(adapter_find(&src), &dst, + BDADDR_BREDR); if (!device) return; @@ -1582,36 +1611,16 @@ int avctp_send_browsing_req(struct avctp *session, return 0; } -static char *op2str(uint8_t op) -{ - switch (op & 0x7f) { - case AVC_VOLUME_UP: - return "VOLUME UP"; - case AVC_VOLUME_DOWN: - return "VOLUME DOWN"; - case AVC_MUTE: - return "MUTE"; - case AVC_PLAY: - return "PLAY"; - case AVC_STOP: - return "STOP"; - case AVC_PAUSE: - return "PAUSE"; - case AVC_RECORD: - return "RECORD"; - case AVC_REWIND: - return "REWIND"; - case AVC_FAST_FORWARD: - return "FAST FORWARD"; - case AVC_EJECT: - return "EJECT"; - case AVC_FORWARD: - return "FORWARD"; - case AVC_BACKWARD: - return "BACKWARD"; - default: - return "UNKNOWN"; +static const char *op2str(uint8_t op) +{ + int i; + + for (i = 0; key_map[i].name != NULL; i++) { + if ((op & 0x7F) == key_map[i].avc) + return key_map[i].name; } + + return "UNKNOWN"; } static int avctp_passthrough_press(struct avctp *session, uint8_t op) diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h index f9c665e..05fceb4 100644 --- a/profiles/audio/avctp.h +++ b/profiles/audio/avctp.h @@ -57,11 +57,30 @@ #define AVC_ROOT_MENU 0x09 #define AVC_CONTENTS_MENU 0x0b #define AVC_FAVORITE_MENU 0x0c +#define AVC_EXIT 0x0d +#define AVC_ON_DEMAND_MENU 0x0e +#define AVC_APPS_MENU 0x0f +#define AVC_0 0x20 +#define AVC_1 0x21 +#define AVC_2 0x22 +#define AVC_3 0x23 +#define AVC_4 0x24 +#define AVC_5 0x25 +#define AVC_6 0x26 +#define AVC_7 0x27 +#define AVC_8 0x28 +#define AVC_9 0x29 +#define AVC_DOT 0x2a #define AVC_ENTER 0x2b #define AVC_CHANNEL_UP 0x30 #define AVC_CHANNEL_DOWN 0x31 +#define AVC_CHANNEL_PREVIOUS 0x32 #define AVC_INPUT_SELECT 0x34 +#define AVC_INFO 0x35 #define AVC_HELP 0x36 +#define AVC_PAGE_UP 0x37 +#define AVC_PAGE_DOWN 0x38 +#define AVC_LOCK 0x3a #define AVC_POWER 0x40 #define AVC_VOLUME_UP 0x41 #define AVC_VOLUME_DOWN 0x42 @@ -75,10 +94,20 @@ #define AVC_EJECT 0x4a #define AVC_FORWARD 0x4b #define AVC_BACKWARD 0x4c +#define AVC_LIST 0x4d #define AVC_F1 0x71 #define AVC_F2 0x72 #define AVC_F3 0x73 #define AVC_F4 0x74 +#define AVC_F5 0x75 +#define AVC_F6 0x76 +#define AVC_F7 0x77 +#define AVC_F8 0x78 +#define AVC_F9 0x79 +#define AVC_RED 0x7a +#define AVC_GREEN 0x7b +#define AVC_BLUE 0x7c +#define AVC_YELLOW 0x7c struct avctp; diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c index f866b39..8a7d1c0 100644 --- a/profiles/audio/avdtp.c +++ b/profiles/audio/avdtp.c @@ -39,10 +39,10 @@ #include #include -#include -#include "log.h" +#include "src/log.h" +#include "btio/btio.h" #include "lib/uuid.h" #include "src/adapter.h" #include "src/device.h" @@ -2467,7 +2467,8 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data) DBG("AVDTP: incoming connect from %s", address); - device = btd_adapter_find_device(adapter_find(&src), &dst); + device = btd_adapter_find_device(adapter_find(&src), &dst, + BDADDR_BREDR); if (!device) goto drop; diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index cd027c6..da2a746 100644 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -53,12 +53,14 @@ #include "src/profile.h" #include "src/service.h" -#include "log.h" -#include "error.h" +#include "src/log.h" +#include "src/error.h" +#include "src/sdpd.h" +#include "src/dbus-common.h" +#include "src/shared/util.h" + #include "avctp.h" #include "avrcp.h" -#include "sdpd.h" -#include "dbus-common.h" #include "control.h" #include "player.h" #include "transport.h" @@ -281,72 +283,72 @@ static sdp_record_t *avrcp_ct_record(void) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); + root = sdp_list_append(NULL, &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); + svclass_id = sdp_list_append(NULL, &avrct); sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &avrctr); sdp_set_service_classes(record, svclass_id); /* Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap); + proto[0] = sdp_list_append(NULL, &l2cap); psm[0] = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm[0]); - apseq = sdp_list_append(0, proto[0]); + apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&avctp, AVCTP_UUID); - proto[1] = sdp_list_append(0, &avctp); + proto[1] = sdp_list_append(NULL, &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); + aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); /* Additional Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); - proto1[0] = sdp_list_append(0, &l2cap); + proto1[0] = sdp_list_append(NULL, &l2cap); psm[1] = sdp_data_alloc(SDP_UINT16, &ap); proto1[0] = sdp_list_append(proto1[0], psm[1]); - apseq1 = sdp_list_append(0, proto1[0]); + apseq1 = sdp_list_append(NULL, proto1[0]); sdp_uuid16_create(&avctp, AVCTP_UUID); - proto1[1] = sdp_list_append(0, &avctp); + proto1[1] = sdp_list_append(NULL, &avctp); proto1[1] = sdp_list_append(proto1[1], version); apseq1 = sdp_list_append(apseq1, proto1[1]); - aproto1 = sdp_list_append(0, apseq1); + aproto1 = sdp_list_append(NULL, apseq1); sdp_set_add_access_protos(record, aproto1); /* 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]); + pfseq = sdp_list_append(NULL, &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); + sdp_set_info_attr(record, "AVRCP CT", NULL, NULL); free(psm[0]); free(psm[1]); free(version); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(proto1[0], 0); - sdp_list_free(proto1[1], 0); - sdp_list_free(aproto1, 0); - sdp_list_free(apseq1, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(proto1[0], NULL); + sdp_list_free(proto1[1], NULL); + sdp_list_free(aproto1, NULL); + sdp_list_free(apseq1, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); return record; } @@ -375,67 +377,67 @@ static sdp_record_t *avrcp_tg_record(void) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); + root = sdp_list_append(NULL, &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); + svclass_id = sdp_list_append(NULL, &avrtg); sdp_set_service_classes(record, svclass_id); /* Protocol Descriptor List */ sdp_uuid16_create(&l2cap, L2CAP_UUID); - proto_control[0] = sdp_list_append(0, &l2cap); + proto_control[0] = sdp_list_append(NULL, &l2cap); psm_control = sdp_data_alloc(SDP_UINT16, &lp); proto_control[0] = sdp_list_append(proto_control[0], psm_control); - apseq = sdp_list_append(0, proto_control[0]); + apseq = sdp_list_append(NULL, proto_control[0]); sdp_uuid16_create(&avctp, AVCTP_UUID); - proto_control[1] = sdp_list_append(0, &avctp); + proto_control[1] = sdp_list_append(NULL, &avctp); version = sdp_data_alloc(SDP_UINT16, &avctp_ver); proto_control[1] = sdp_list_append(proto_control[1], version); apseq = sdp_list_append(apseq, proto_control[1]); - aproto_control = sdp_list_append(0, apseq); + aproto_control = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto_control); - proto_browsing[0] = sdp_list_append(0, &l2cap); + proto_browsing[0] = sdp_list_append(NULL, &l2cap); psm_browsing = sdp_data_alloc(SDP_UINT16, &lp_browsing); proto_browsing[0] = sdp_list_append(proto_browsing[0], psm_browsing); - apseq_browsing = sdp_list_append(0, proto_browsing[0]); + apseq_browsing = sdp_list_append(NULL, proto_browsing[0]); - proto_browsing[1] = sdp_list_append(0, &avctp); + proto_browsing[1] = sdp_list_append(NULL, &avctp); proto_browsing[1] = sdp_list_append(proto_browsing[1], version); apseq_browsing = sdp_list_append(apseq_browsing, proto_browsing[1]); - aproto_browsing = sdp_list_append(0, apseq_browsing); + aproto_browsing = sdp_list_append(NULL, apseq_browsing); sdp_set_add_access_protos(record, aproto_browsing); /* 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]); + pfseq = sdp_list_append(NULL, &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); + sdp_set_info_attr(record, "AVRCP TG", NULL, NULL); free(psm_browsing); - sdp_list_free(proto_browsing[0], 0); - sdp_list_free(proto_browsing[1], 0); - sdp_list_free(apseq_browsing, 0); - sdp_list_free(aproto_browsing, 0); + sdp_list_free(proto_browsing[0], NULL); + sdp_list_free(proto_browsing[1], NULL); + sdp_list_free(apseq_browsing, NULL); + sdp_list_free(aproto_browsing, NULL); free(psm_control); free(version); - sdp_list_free(proto_control[0], 0); - sdp_list_free(proto_control[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(aproto_control, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); + sdp_list_free(proto_control[0], NULL); + sdp_list_free(proto_control[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(aproto_control, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); return record; } @@ -489,11 +491,11 @@ static uint32_t get_company_id(const uint8_t cid[3]) * * Set three-byte Company_ID into outgoing AVRCP message */ -static void set_company_id(uint8_t cid[3], const uint32_t cid_in) +static void set_company_id(uint8_t cid[3], uint32_t cid_in) { - cid[0] = cid_in >> 16; - cid[1] = cid_in >> 8; - cid[2] = cid_in; + cid[0] = (cid_in & 0xff0000) >> 16; + cid[1] = (cid_in & 0x00ff00) >> 8; + cid[2] = (cid_in & 0x0000ff); } static const char *attr_to_str(uint8_t attr) @@ -920,6 +922,7 @@ static uint8_t avrcp_handle_get_capabilities(struct avrcp *session, return AVC_CTYPE_STABLE; case CAP_EVENTS_SUPPORTED: + pdu->params[1] = 0; for (i = 1; i <= AVRCP_EVENT_LAST; i++) { if (session->supported_events & (1 << i)) { pdu->params[1]++; @@ -938,11 +941,19 @@ err: return AVC_CTYPE_REJECTED; } +static struct avrcp_player *target_get_player(struct avrcp *session) +{ + if (!session->target) + return NULL; + + return session->target->player; +} + static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); uint16_t len = ntohs(pdu->params_len); unsigned int i; @@ -974,7 +985,7 @@ static uint8_t avrcp_handle_list_player_values(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); uint16_t len = ntohs(pdu->params_len); unsigned int i; @@ -1043,9 +1054,9 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); uint16_t len = ntohs(pdu->params_len); - uint64_t identifier = bt_get_le64(&pdu->params[0]); + uint64_t identifier = get_le64(&pdu->params[0]); uint16_t pos; uint8_t nattr; GList *attr_ids; @@ -1071,7 +1082,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session, for (i = 0, len = 0, attr_ids = NULL; i < nattr; i++) { uint32_t id; - id = bt_get_be32(&pdu->params[9] + (i * sizeof(id))); + id = get_be32(&pdu->params[9] + (i * sizeof(id))); /* Don't add invalid attributes */ if (id == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || @@ -1115,7 +1126,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); uint16_t len = ntohs(pdu->params_len); uint8_t *settings; unsigned int i; @@ -1174,7 +1185,7 @@ static uint8_t avrcp_handle_set_player_value(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); uint16_t len = ntohs(pdu->params_len); unsigned int i; uint8_t *param; @@ -1293,7 +1304,7 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); uint16_t len = ntohs(pdu->params_len); uint32_t position; uint32_t duration; @@ -1337,7 +1348,7 @@ static GList *player_list_settings(struct avrcp_player *player) static bool avrcp_handle_play(struct avrcp *session) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; @@ -1347,7 +1358,7 @@ static bool avrcp_handle_play(struct avrcp *session) static bool avrcp_handle_stop(struct avrcp *session) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; @@ -1357,7 +1368,7 @@ static bool avrcp_handle_stop(struct avrcp *session) static bool avrcp_handle_pause(struct avrcp *session) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; @@ -1367,7 +1378,7 @@ static bool avrcp_handle_pause(struct avrcp *session) static bool avrcp_handle_next(struct avrcp *session) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; @@ -1377,7 +1388,7 @@ static bool avrcp_handle_next(struct avrcp *session) static bool avrcp_handle_previous(struct avrcp *session) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); if (player == NULL) return false; @@ -1420,7 +1431,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); struct btd_device *dev = session->dev; uint16_t len = ntohs(pdu->params_len); uint64_t uid; @@ -1446,7 +1457,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session, break; case AVRCP_EVENT_TRACK_CHANGED: len = 9; - uid = player_get_uid(session->target->player); + uid = player_get_uid(player); memcpy(&pdu->params[1], &uid, sizeof(uint64_t)); break; @@ -1508,7 +1519,7 @@ static uint8_t avrcp_handle_request_continuing(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); uint16_t len = ntohs(pdu->params_len); struct pending_pdu *pending; @@ -1582,14 +1593,12 @@ static uint8_t avrcp_handle_set_absolute_volume(struct avrcp *session, if (len != 1) goto err; - volume = pdu->params[0] & 0x7F; - if (volume > 127) - goto err; - if (!player) goto err; - media_transport_update_device_volume(session->dev, pdu->params[0]); + volume = pdu->params[0] & 0x7F; + + media_transport_update_device_volume(session->dev, volume); return AVC_CTYPE_ACCEPTED; @@ -1650,7 +1659,7 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction, } DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X", - pdu->pdu_id, company_id, pdu->params_len); + pdu->pdu_id, company_id, ntohs(pdu->params_len)); pdu->packet_type = 0; pdu->rsvd = 0; @@ -1665,7 +1674,7 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction, break; } - if (!handler || handler->code != *code) { + if (handler->pdu_id != pdu->pdu_id || handler->code != *code) { pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; goto err_metadata; } @@ -1722,16 +1731,16 @@ static size_t handle_browsing_pdu(struct avctp *conn, struct avrcp_browsing_header *pdu = (void *) operands; DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id, - pdu->param_len); + ntohs(pdu->param_len)); for (handler = browsing_handlers; handler->pdu_id; handler++) { if (handler->pdu_id == pdu->pdu_id) - break; + goto done; } - if (handler == NULL || handler->func == NULL) - return avrcp_browsing_general_reject(operands); + return avrcp_browsing_general_reject(operands); +done: session->transaction = transaction; handler->func(session, pdu, transaction); return AVRCP_BROWSING_HEADER_LENGTH + ntohs(pdu->param_len); @@ -1747,7 +1756,7 @@ size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands) pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR; DBG("rejecting AVRCP PDU 0x%02X, company 0x%06X len 0x%04X", - pdu->pdu_id, company_id, pdu->params_len); + pdu->pdu_id, company_id, ntohs(pdu->params_len)); return AVRCP_HEADER_LENGTH + 1; } @@ -1897,9 +1906,9 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn, static void avrcp_get_current_player_value(struct avrcp *session, uint8_t *attrs, uint8_t count) { - uint8_t buf[AVRCP_HEADER_LENGTH + 5]; + uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_ATTRIBUTE_LAST + 1]; struct avrcp_header *pdu = (void *) buf; - int i; + uint16_t length = AVRCP_HEADER_LENGTH + count + 1; memset(buf, 0, sizeof(buf)); @@ -1909,11 +1918,10 @@ static void avrcp_get_current_player_value(struct avrcp *session, pdu->params_len = htons(count + 1); pdu->params[0] = count; - for (i = 0; count > 0; count--, i++) - pdu->params[i + 1] = attrs[i]; + memcpy(pdu->params + 1, attrs, count); avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS, - AVC_SUBUNIT_PANEL, buf, sizeof(buf), + AVC_SUBUNIT_PANEL, buf, length, avrcp_player_value_rsp, session); } @@ -1922,22 +1930,32 @@ static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn, uint8_t *operands, size_t operand_count, void *user_data) { + uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; struct avrcp *session = user_data; struct avrcp_header *pdu = (void *) operands; - uint8_t count; + uint8_t len, count = 0; + int i; if (code == AVC_CTYPE_REJECTED) return FALSE; - count = pdu->params[0]; + len = pdu->params[0]; if (ntohs(pdu->params_len) < count) { error("Invalid parameters"); return FALSE; } - avrcp_get_current_player_value(session, &pdu->params[1], - pdu->params[0]); + for (i = 0; len > 0; len--, i++) { + /* Don't query invalid attributes */ + if (pdu->params[i + 1] == AVRCP_ATTRIBUTE_ILEGAL || + pdu->params[i + 1] > AVRCP_ATTRIBUTE_LAST) + continue; + + attrs[count++] = pdu->params[i + 1]; + } + + avrcp_get_current_player_value(session, attrs, count); return FALSE; } @@ -1972,13 +1990,13 @@ static void avrcp_parse_attribute_list(struct avrcp_player *player, uint32_t id; uint16_t charset, len; - id = bt_get_be32(&operands[i]); + id = get_be32(&operands[i]); i += sizeof(uint32_t); - charset = bt_get_be16(&operands[i]); + charset = get_be16(&operands[i]); i += sizeof(uint16_t); - len = bt_get_be16(&operands[i]); + len = get_be16(&operands[i]); i += sizeof(uint16_t); if (charset == 106) { @@ -2108,9 +2126,9 @@ static struct media_item *parse_media_element(struct avrcp *session, if (len < 13) return NULL; - uid = bt_get_be64(&operands[0]); + uid = get_be64(&operands[0]); - namelen = MIN(bt_get_be16(&operands[11]), sizeof(name) - 1); + namelen = MIN(get_be16(&operands[11]), sizeof(name) - 1); if (namelen > 0) { memcpy(name, &operands[13], namelen); name[namelen] = '\0'; @@ -2133,24 +2151,33 @@ static struct media_item *parse_media_folder(struct avrcp *session, { struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; + struct media_item *item; uint16_t namelen; char name[255]; uint64_t uid; uint8_t type; + uint8_t playable; if (len < 12) return NULL; - uid = bt_get_be64(&operands[0]); - type = operands[9]; + uid = get_be64(&operands[0]); + type = operands[8]; + playable = operands[9]; - namelen = MIN(bt_get_be16(&operands[12]), sizeof(name) - 1); + namelen = MIN(get_be16(&operands[12]), sizeof(name) - 1); if (namelen > 0) { memcpy(name, &operands[14], namelen); name[namelen] = '\0'; } - return media_player_create_folder(mp, name, type, uid); + item = media_player_create_folder(mp, name, type, uid); + if (!item) + return NULL; + + media_item_set_playable(item, playable & 0x01); + + return item; } static void avrcp_list_items(struct avrcp *session, uint32_t start, @@ -2185,7 +2212,7 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands, goto done; } - count = bt_get_be16(&operands[6]); + count = get_be16(&operands[6]); if (count == 0) goto done; @@ -2195,7 +2222,7 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands, uint16_t len; type = operands[i++]; - len = bt_get_be16(&operands[i]); + len = get_be16(&operands[i]); i += 2; if (type != 0x03 && type != 0x02) { @@ -2210,7 +2237,7 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands, if (type == 0x03) item = parse_media_element(session, &operands[i], len); - else if (type == 0x02) + else item = parse_media_folder(session, &operands[i], len); if (item) { @@ -2256,8 +2283,8 @@ static void avrcp_list_items(struct avrcp *session, uint32_t start, pdu->params[0] = player->scope; - bt_put_be32(start, &pdu->params[1]); - bt_put_be32(end, &pdu->params[5]); + put_be32(start, &pdu->params[1]); + put_be32(end, &pdu->params[5]); pdu->params[9] = 1; @@ -2292,7 +2319,7 @@ static gboolean avrcp_change_path_rsp(struct avctp *conn, goto done; } - ret = bt_get_be32(&pdu->params[1]); + ret = get_be32(&pdu->params[1]); done: if (ret < 0) { @@ -2327,10 +2354,10 @@ static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn, operand_count < 13) return FALSE; - player->uid_counter = bt_get_be16(&pdu->params[1]); + player->uid_counter = get_be16(&pdu->params[1]); player->browsed = true; - items = bt_get_be32(&pdu->params[3]); + items = get_be32(&pdu->params[3]); depth = pdu->params[9]; @@ -2422,8 +2449,8 @@ static void avrcp_get_item_attributes(struct avrcp *session, uint64_t uid) pdu->pdu_id = AVRCP_GET_ITEM_ATTRIBUTES; pdu->params[0] = 0x03; - bt_put_be64(uid, &pdu->params[1]); - bt_put_be16(player->uid_counter, &pdu->params[9]); + put_be64(uid, &pdu->params[1]); + put_be16(player->uid_counter, &pdu->params[9]); pdu->param_len = htons(12); avctp_send_browsing_req(session->conn, buf, sizeof(buf), @@ -2607,9 +2634,9 @@ static void avrcp_change_path(struct avrcp *session, uint8_t direction, struct avrcp_browsing_header *pdu = (void *) buf; memset(buf, 0, sizeof(buf)); - bt_put_be16(player->uid_counter, &pdu->params[0]); + put_be16(player->uid_counter, &pdu->params[0]); pdu->params[2] = direction; - bt_put_be64(uid, &pdu->params[3]); + put_be64(uid, &pdu->params[3]); pdu->pdu_id = AVRCP_CHANGE_PATH; pdu->param_len = htons(11); @@ -2653,8 +2680,8 @@ static gboolean avrcp_search_rsp(struct avctp *conn, uint8_t *operands, goto done; } - player->uid_counter = bt_get_be16(&pdu->params[1]); - ret = bt_get_be32(&pdu->params[3]); + player->uid_counter = get_be16(&pdu->params[1]); + ret = get_be32(&pdu->params[3]); done: media_player_search_complete(mp, ret); @@ -2673,8 +2700,8 @@ static void avrcp_search(struct avrcp *session, const char *string) stringlen = strnlen(string, sizeof(buf) - len); len += stringlen; - bt_put_be16(AVRCP_CHARSET_UTF8, &pdu->params[0]); - bt_put_be16(stringlen, &pdu->params[2]); + put_be16(AVRCP_CHARSET_UTF8, &pdu->params[0]); + put_be16(stringlen, &pdu->params[2]); memcpy(&pdu->params[4], string, stringlen); pdu->pdu_id = AVRCP_SEARCH; pdu->param_len = htons(len - AVRCP_BROWSING_HEADER_LENGTH); @@ -2711,8 +2738,8 @@ static void avrcp_play_item(struct avrcp *session, uint64_t uid) pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; pdu->params[0] = player->scope; - bt_put_be64(uid, &pdu->params[1]); - bt_put_be16(player->uid_counter, &pdu->params[9]); + put_be64(uid, &pdu->params[1]); + put_be16(player->uid_counter, &pdu->params[9]); length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len); @@ -2757,8 +2784,8 @@ static void avrcp_add_to_nowplaying(struct avrcp *session, uint64_t uid) pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; pdu->params[0] = player->scope; - bt_put_be64(uid, &pdu->params[1]); - bt_put_be16(player->uid_counter, &pdu->params[9]); + put_be64(uid, &pdu->params[1]); + put_be16(player->uid_counter, &pdu->params[9]); length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len); @@ -2867,7 +2894,7 @@ avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands, if (len < 28) return NULL; - id = bt_get_be16(&operands[0]); + id = get_be16(&operands[0]); player = find_ct_player(session, id); if (player == NULL) { @@ -2881,7 +2908,7 @@ avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands, media_player_set_type(mp, type_to_string(operands[2])); - subtype = bt_get_be32(&operands[3]); + subtype = get_be32(&operands[3]); media_player_set_subtype(mp, subtype_to_string(subtype)); @@ -2895,7 +2922,7 @@ avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands, avrcp_player_parse_features(player, &operands[8]); - namelen = bt_get_be16(&operands[26]); + namelen = get_be16(&operands[26]); if (namelen > 0 && namelen + 28 == len) { namelen = MIN(namelen, sizeof(name) - 1); memcpy(name, &operands[28], namelen); @@ -2955,7 +2982,7 @@ static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn, return FALSE; removed = g_slist_copy(session->controller->players); - count = bt_get_be16(&operands[6]); + count = get_be16(&operands[6]); for (i = 8; count && i < operand_count; count--) { struct avrcp_player *player; @@ -2963,7 +2990,7 @@ static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn, uint16_t len; type = operands[i++]; - len = bt_get_be16(&operands[i]); + len = get_be16(&operands[i]); i += 2; if (type != 0x01) { @@ -3009,10 +3036,10 @@ static void avrcp_get_media_player_list(struct avrcp *session) static void avrcp_volume_changed(struct avrcp *session, struct avrcp_header *pdu) { - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); uint8_t volume; - if (player == NULL) + if (!player) return; volume = pdu->params[1] & 0x7F; @@ -3045,7 +3072,7 @@ static void avrcp_track_changed(struct avrcp *session, { if (session->browsing_id) { struct avrcp_player *player = session->controller->player; - player->uid = bt_get_be64(&pdu->params[1]); + player->uid = get_be64(&pdu->params[1]); avrcp_get_item_attributes(session, player->uid); } else avrcp_get_element_attributes(session); @@ -3085,7 +3112,7 @@ static void avrcp_addressed_player_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = session->controller->player; - uint16_t id = bt_get_be16(&pdu->params[1]); + uint16_t id = get_be16(&pdu->params[1]); if (player != NULL && player->id == id) return; @@ -3097,7 +3124,7 @@ static void avrcp_addressed_player_changed(struct avrcp *session, return; } - player->uid_counter = bt_get_be16(&pdu->params[3]); + player->uid_counter = get_be16(&pdu->params[3]); session->controller->player = player; if (player->features != NULL) @@ -3110,7 +3137,7 @@ static void avrcp_uids_changed(struct avrcp *session, struct avrcp_header *pdu) { struct avrcp_player *player = session->controller->player; - player->uid_counter = bt_get_be16(&pdu->params[1]); + player->uid_counter = get_be16(&pdu->params[1]); } static gboolean avrcp_handle_event(struct avctp *conn, @@ -3497,6 +3524,8 @@ static void session_destroy(struct avrcp *session) server->sessions = g_slist_remove(server->sessions, session); + session_abort_pending_pdu(session); + if (session->browsing_timer > 0) g_source_remove(session->browsing_timer); @@ -3684,7 +3713,7 @@ static gboolean avrcp_handle_set_volume(struct avctp *conn, void *user_data) { struct avrcp *session = user_data; - struct avrcp_player *player = session->target->player; + struct avrcp_player *player = target_get_player(session); struct avrcp_header *pdu = (void *) operands; uint8_t volume; @@ -3704,7 +3733,7 @@ int avrcp_set_volume(struct btd_device *dev, uint8_t volume) { struct avrcp_server *server; struct avrcp *session; - uint8_t buf[AVRCP_HEADER_LENGTH + 2]; + uint8_t buf[AVRCP_HEADER_LENGTH + 1]; struct avrcp_header *pdu = (void *) buf; server = find_server(servers, device_get_adapter(dev)); @@ -3724,30 +3753,14 @@ int avrcp_set_volume(struct btd_device *dev, uint8_t volume) DBG("volume=%u", volume); - if (session->target) { - pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME; - pdu->params[0] = volume; - pdu->params_len = htons(1); + pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME; + pdu->params[0] = volume; + pdu->params_len = htons(1); - return avctp_send_vendordep_req(session->conn, + return avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, buf, sizeof(buf), avrcp_handle_set_volume, session); - } else if (session->registered_events & - (1 << AVRCP_EVENT_VOLUME_CHANGED)) { - uint8_t id = AVRCP_EVENT_VOLUME_CHANGED; - pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION; - pdu->params[0] = AVRCP_EVENT_VOLUME_CHANGED; - pdu->params[1] = volume; - pdu->params_len = htons(2); - - return avctp_send_vendordep(session->conn, - session->transaction_events[id], - AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL, - buf, sizeof(buf)); - } - - return 0; } static int avrcp_connect(struct btd_service *service) diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h index 4c520ca..6ac5294 100644 --- a/profiles/audio/avrcp.h +++ b/profiles/audio/avrcp.h @@ -28,6 +28,7 @@ #define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02 #define AVRCP_ATTRIBUTE_SHUFFLE 0x03 #define AVRCP_ATTRIBUTE_SCAN 0x04 +#define AVRCP_ATTRIBUTE_LAST AVRCP_ATTRIBUTE_SCAN /* equalizer values */ #define AVRCP_EQUALIZER_OFF 0x01 diff --git a/profiles/audio/control.c b/profiles/audio/control.c index 5f88a94..ab94a57 100644 --- a/profiles/audio/control.c +++ b/profiles/audio/control.c @@ -51,13 +51,14 @@ #include "src/profile.h" #include "src/service.h" -#include "log.h" -#include "error.h" +#include "src/log.h" +#include "src/error.h" +#include "src/sdpd.h" +#include "src/uuid-helper.h" +#include "src/dbus-common.h" + #include "avctp.h" #include "control.h" -#include "sdpd.h" -#include "glib-helper.h" -#include "dbus-common.h" static GSList *devices = NULL; diff --git a/profiles/audio/media.c b/profiles/audio/media.c index 5edd3de..b71196a 100644 --- a/profiles/audio/media.c +++ b/profiles/audio/media.c @@ -40,9 +40,10 @@ #include "src/dbus-common.h" #include "src/profile.h" -#include "glib-helper.h" -#include "log.h" -#include "error.h" +#include "src/uuid-helper.h" +#include "src/log.h" +#include "src/error.h" + #include "avdtp.h" #include "media.h" #include "transport.h" diff --git a/profiles/audio/player.c b/profiles/audio/player.c index 6150c8a..de608a3 100644 --- a/profiles/audio/player.c +++ b/profiles/audio/player.c @@ -38,10 +38,11 @@ #include #include -#include "log.h" +#include "src/log.h" +#include "src/dbus-common.h" +#include "src/error.h" + #include "player.h" -#include "dbus-common.h" -#include "error.h" #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1" #define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1" @@ -392,7 +393,7 @@ static gboolean browsable_exists(const GDBusPropertyTable *property, void *data) { struct media_player *mp = data; - return mp->scope != NULL; + return mp->folder != NULL; } static gboolean get_browsable(const GDBusPropertyTable *property, @@ -401,7 +402,7 @@ static gboolean get_browsable(const GDBusPropertyTable *property, struct media_player *mp = data; dbus_bool_t value; - if (mp->scope == NULL) + if (mp->folder == NULL) return FALSE; DBG("%s", mp->browsable ? "true" : "false"); @@ -418,7 +419,7 @@ static gboolean searchable_exists(const GDBusPropertyTable *property, { struct media_player *mp = data; - return mp->scope != NULL; + return mp->folder != NULL; } static gboolean get_searchable(const GDBusPropertyTable *property, @@ -777,19 +778,16 @@ static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg, dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - return btd_error_failed(msg, strerror(EINVAL)); + return btd_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &string); - if (!mp->searchable || folder != mp->folder) - return btd_error_failed(msg, strerror(ENOTSUP)); + if (!mp->searchable || folder != mp->folder || !cb->cbs->search) + return btd_error_not_supported(msg); if (folder->msg != NULL) return btd_error_failed(msg, strerror(EINVAL)); - if (cb->cbs->search == NULL) - return btd_error_failed(msg, strerror(ENOTSUP)); - err = cb->cbs->search(mp, string, cb->user_data); if (err < 0) return btd_error_failed(msg, strerror(-err)); @@ -807,7 +805,8 @@ static int parse_filters(struct media_player *player, DBusMessageIter *iter, int ctype; *start = 0; - *end = folder->number_of_items ? folder->number_of_items : UINT32_MAX; + *end = folder->number_of_items ? folder->number_of_items - 1 : + UINT32_MAX; ctype = dbus_message_iter_get_arg_type(iter); if (ctype != DBUS_TYPE_ARRAY) @@ -996,14 +995,14 @@ static DBusMessage *media_folder_change_folder(DBusConnection *conn, if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) - return btd_error_failed(msg, strerror(EINVAL)); + return btd_error_invalid_args(msg); if (folder->msg != NULL) return btd_error_failed(msg, strerror(EBUSY)); folder = media_player_find_folder(mp, path); if (folder == NULL) - return btd_error_failed(msg, strerror(EINVAL)); + return btd_error_invalid_args(msg); if (mp->scope == folder) return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); @@ -1014,8 +1013,16 @@ static DBusMessage *media_folder_change_folder(DBusConnection *conn, return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } + /* + * ChangePath can only navigate one level up/down so check if folder + * is direct child or parent of the current folder otherwise fail. + */ + if (!g_slist_find(mp->folder->subfolders, folder) && + !g_slist_find(folder->subfolders, mp->folder)) + return btd_error_invalid_args(msg); + if (cb->cbs->change_folder == NULL) - return btd_error_failed(msg, strerror(ENOTSUP)); + return btd_error_not_supported(msg); err = cb->cbs->change_folder(mp, folder->item->name, folder->item->uid, cb->user_data); @@ -1471,11 +1478,8 @@ static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg, struct player_callback *cb = mp->cb; int err; - if (!item->playable) - return btd_error_failed(msg, strerror(ENOTSUP)); - - if (cb->cbs->play_item == NULL) - return btd_error_failed(msg, strerror(ENOTSUP)); + if (!item->playable || !cb->cbs->play_item) + return btd_error_not_supported(msg); err = cb->cbs->play_item(mp, item->path, item->uid, cb->user_data); if (err < 0) @@ -1492,11 +1496,8 @@ static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn, struct player_callback *cb = mp->cb; int err; - if (!item->playable) - return btd_error_failed(msg, strerror(ENOTSUP)); - - if (cb->cbs->play_item == NULL) - return btd_error_failed(msg, strerror(ENOTSUP)); + if (!item->playable || !cb->cbs->play_item) + return btd_error_not_supported(msg); err = cb->cbs->add_to_nowplaying(mp, item->path, item->uid, cb->user_data); diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c index 4f39622..d16af23 100644 --- a/profiles/audio/sink.c +++ b/profiles/audio/sink.c @@ -37,18 +37,18 @@ #include #include -#include "log.h" +#include "src/log.h" #include "src/adapter.h" #include "src/device.h" #include "src/service.h" +#include "src/error.h" +#include "src/dbus-common.h" #include "avdtp.h" #include "media.h" #include "a2dp.h" -#include "error.h" #include "sink.h" -#include "dbus-common.h" #define STREAM_SETUP_RETRY_TIMER 2 diff --git a/profiles/audio/source.c b/profiles/audio/source.c index 7b129b7..843b3e8 100644 --- a/profiles/audio/source.c +++ b/profiles/audio/source.c @@ -38,18 +38,18 @@ #include #include -#include "log.h" +#include "src/log.h" #include "src/adapter.h" #include "src/device.h" #include "src/service.h" +#include "src/error.h" +#include "src/dbus-common.h" #include "avdtp.h" #include "media.h" #include "a2dp.h" -#include "error.h" #include "source.h" -#include "dbus-common.h" struct source { struct btd_service *service; diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c index 087c0ee..c82fbb5 100644 --- a/profiles/audio/transport.c +++ b/profiles/audio/transport.c @@ -36,8 +36,9 @@ #include "src/device.h" #include "src/dbus-common.h" -#include "log.h" -#include "error.h" +#include "src/log.h" +#include "src/error.h" + #include "avdtp.h" #include "media.h" #include "transport.h" diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c index 6ecc985..6b00da7 100644 --- a/profiles/cyclingspeed/cyclingspeed.c +++ b/profiles/cyclingspeed/cyclingspeed.c @@ -30,18 +30,19 @@ #include #include "lib/uuid.h" -#include "plugin.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" -#include "dbus-common.h" -#include "error.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/dbus-common.h" +#include "src/shared/util.h" +#include "src/error.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" -#include "attio.h" -#include "log.h" +#include "src/attio.h" +#include "src/log.h" /* min length for ATT indication or notification: opcode (1b) + handle (2b) */ #define ATT_HDR_LEN 3 @@ -380,7 +381,7 @@ static void read_feature_cb(guint8 status, const guint8 *pdu, guint16 len, return; } - csc->feature = att_get_u16(value); + csc->feature = get_le16(value); if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT) && (csc->locations == NULL)) @@ -418,13 +419,12 @@ static void read_location_cb(guint8 status, const guint8 *pdu, CYCLINGSPEED_INTERFACE, "Location"); } -static void discover_desc_cb(guint8 status, const guint8 *pdu, - guint16 len, gpointer user_data) +static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data) { struct characteristic *ch = user_data; - struct att_data_list *list = NULL; - uint8_t format; - int i; + struct gatt_desc *desc; + uint8_t attr_val[2]; + char *msg = NULL; if (status != 0) { error("Discover %s descriptors failed: %s", ch->uuid, @@ -432,55 +432,31 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu, goto done; } - list = dec_find_info_resp(pdu, len, &format); - if (list == NULL) - goto done; + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; - if (format != ATT_FIND_INFO_RESP_FMT_16BIT) - goto done; + if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) { + ch->csc->measurement_ccc_handle = desc->handle; - for (i = 0; i < list->num; i++) { - uint8_t *value; - uint16_t handle, uuid; - uint8_t attr_val[2]; - char *msg; - - value = list->data[i]; - handle = att_get_u16(value); - uuid = att_get_u16(value + 2); - - if (uuid != GATT_CLIENT_CHARAC_CFG_UUID) - continue; - - if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) { - ch->csc->measurement_ccc_handle = handle; - - if (g_slist_length(ch->csc->cadapter->watchers) == 0) { - att_put_u16(0x0000, attr_val); - msg = g_strdup("Disable measurement"); - } else { - att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, - attr_val); - msg = g_strdup("Enable measurement"); - } - - } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) { - att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val); - msg = g_strdup("Enable SC Control Point indications"); + if (g_slist_length(ch->csc->cadapter->watchers) == 0) { + put_le16(0x0000, attr_val); + msg = g_strdup("Disable measurement"); } else { - break; + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, + attr_val); + msg = g_strdup("Enable measurement"); } + } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) { + put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val); + msg = g_strdup("Enable SC Control Point indications"); + } else { + goto done; + } - gatt_write_char(ch->csc->attrib, handle, attr_val, + gatt_write_char(ch->csc->attrib, desc->handle, attr_val, sizeof(attr_val), char_write_cb, msg); - /* We only want CCC, can break here */ - break; - } - done: - if (list) - att_data_list_free(list); g_free(ch); } @@ -489,6 +465,7 @@ static void discover_desc(struct csc *csc, struct gatt_char *c, { struct characteristic *ch; uint16_t start, end; + bt_uuid_t uuid; start = c->value_handle + 1; @@ -506,7 +483,10 @@ static void discover_desc(struct csc *csc, struct gatt_char *c, ch->csc = csc; memcpy(ch->uuid, c->uuid, sizeof(c->uuid)); - gatt_discover_char_desc(csc->attrib, start, end, discover_desc_cb, ch); + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb, + ch); } static void update_watcher(gpointer data, gpointer user_data) @@ -573,8 +553,8 @@ static void process_measurement(struct csc *csc, const uint8_t *pdu, } m.has_wheel_rev = true; - m.wheel_rev = att_get_u32(pdu); - m.last_wheel_time = att_get_u16(pdu + 4); + m.wheel_rev = get_le32(pdu); + m.last_wheel_time = get_le16(pdu + 4); pdu += 6; len -= 6; } @@ -586,8 +566,8 @@ static void process_measurement(struct csc *csc, const uint8_t *pdu, } m.has_crank_rev = true; - m.crank_rev = att_get_u16(pdu); - m.last_crank_time = att_get_u16(pdu + 2); + m.crank_rev = get_le16(pdu); + m.last_crank_time = get_le16(pdu + 2); pdu += 4; len -= 4; } @@ -759,7 +739,7 @@ done: g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL); } -static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data) +static void discover_char_cb(uint8_t status, GSList *chars, void *user_data) { struct csc *csc = user_data; uint16_t feature_val_handle = 0; @@ -816,7 +796,7 @@ static void enable_measurement(gpointer data, gpointer user_data) if (csc->attrib == NULL || !handle) return; - att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); msg = g_strdup("Enable measurement"); gatt_write_char(csc->attrib, handle, value, sizeof(value), @@ -833,7 +813,7 @@ static void disable_measurement(gpointer data, gpointer user_data) if (csc->attrib == NULL || !handle) return; - att_put_u16(0x0000, value); + put_le16(0x0000, value); msg = g_strdup("Disable measurement"); gatt_write_char(csc->attrib, handle, value, sizeof(value), @@ -1166,7 +1146,7 @@ static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn, csc->pending_req = req; att_val[0] = SET_CUMULATIVE_VALUE; - att_put_u32(value, att_val + 1); + put_le32(value, att_val + 1); gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val, sizeof(att_val), controlpoint_write_cb, req); diff --git a/profiles/deviceinfo/deviceinfo.c b/profiles/deviceinfo/deviceinfo.c index 8343bb5..208598a 100644 --- a/profiles/deviceinfo/deviceinfo.c +++ b/profiles/deviceinfo/deviceinfo.c @@ -30,16 +30,17 @@ #include #include "lib/uuid.h" -#include "plugin.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/shared/util.h" #include "attrib/gattrib.h" -#include "attio.h" +#include "src/attio.h" #include "attrib/att.h" #include "attrib/gatt.h" -#include "log.h" +#include "src/log.h" #define PNP_ID_SIZE 7 @@ -96,8 +97,8 @@ static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len, return; } - btd_device_set_pnpid(ch->d->dev, value[0], att_get_u16(&value[1]), - att_get_u16(&value[3]), att_get_u16(&value[5])); + btd_device_set_pnpid(ch->d->dev, value[0], get_le16(&value[1]), + get_le16(&value[3]), get_le16(&value[5])); } static void process_deviceinfo_char(struct characteristic *ch) @@ -107,8 +108,8 @@ static void process_deviceinfo_char(struct characteristic *ch) read_pnpid_cb, ch); } -static void configure_deviceinfo_cb(GSList *characteristics, guint8 status, - gpointer user_data) +static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics, + void *user_data) { struct deviceinfo *d = user_data; GSList *l; diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c index 5d6880f..d240450 100644 --- a/profiles/gatt/gas.c +++ b/profiles/gatt/gas.c @@ -32,20 +32,21 @@ #include #include -#include +#include "btio/btio.h" #include "lib/uuid.h" -#include "plugin.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/shared/util.h" #include "attrib/att.h" #include "attrib/gattrib.h" -#include "attio.h" +#include "src/attio.h" #include "attrib/gatt.h" -#include "log.h" -#include "textfile.h" +#include "src/log.h" +#include "src/textfile.h" /* Generic Attribute/Access Service */ struct gas { @@ -165,7 +166,7 @@ static void gap_appearance_cb(guint8 status, const guint8 *pdu, guint16 plen, } atval = list->data[0] + 2; /* skip handle value */ - app = att_get_u16(atval); + app = get_le16(atval); DBG("GAP Appearance: 0x%04x", app); @@ -177,6 +178,7 @@ done: static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data) { + uint8_t bdaddr_type; struct gas *gas = user_data; uint16_t start, end, olen; size_t plen; @@ -187,8 +189,8 @@ static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data) return; } - start = att_get_u16(&pdu[3]); - end = att_get_u16(&pdu[5]); + start = get_le16(&pdu[3]); + end = get_le16(&pdu[5]); DBG("Service Changed start: 0x%04X end: 0x%04X", start, end); @@ -197,7 +199,8 @@ static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data) olen = enc_confirmation(opdu, plen); g_attrib_send(gas->attrib, 0, opdu, olen, NULL, NULL, NULL); - if (device_is_bonded(gas->device) == FALSE) { + bdaddr_type = btd_device_get_bdaddr_type(gas->device); + if (!device_is_bonded(gas->device, bdaddr_type)) { DBG("Ignoring Service Changed: device is not bonded"); return; } @@ -230,53 +233,36 @@ static void write_ccc(GAttrib *attrib, uint16_t handle, gpointer user_data) { uint8_t value[2]; - att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value); + put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value); gatt_write_char(attrib, handle, value, sizeof(value), ccc_written_cb, user_data); } -static void gatt_descriptors_cb(guint8 status, const guint8 *pdu, guint16 len, - gpointer user_data) +static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data) { struct gas *gas = user_data; - struct att_data_list *list; - int i; - uint8_t format; + struct gatt_desc *desc; - if (status) { - error("Discover all GATT characteristic descriptors: %s", + if (status != 0) { + error("Discover Service Changed CCC failed: %s", att_ecode2str(status)); return; } - list = dec_find_info_resp(pdu, len, &format); - if (list == NULL) - return; - - if (format != ATT_FIND_INFO_RESP_FMT_16BIT) - goto done; - - for (i = 0; i < list->num; i++) { - uint16_t uuid16, ccc; - uint8_t *value; + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; - value = list->data[i]; - ccc = att_get_u16(value); - uuid16 = att_get_u16(&value[2]); - DBG("CCC: 0x%04x UUID: 0x%04x", ccc, uuid16); - write_ccc(gas->attrib, ccc, user_data); - } - -done: - att_data_list_free(list); + DBG("CCC: 0x%04x", desc->handle); + write_ccc(gas->attrib, desc->handle, user_data); } -static void gatt_characteristic_cb(GSList *characteristics, guint8 status, - gpointer user_data) +static void gatt_characteristic_cb(uint8_t status, GSList *characteristics, + void *user_data) { struct gas *gas = user_data; struct gatt_char *chr; uint16_t start, end; + bt_uuid_t uuid; if (status) { error("Discover Service Changed handle: %s", att_ecode2str(status)); @@ -294,7 +280,10 @@ static void gatt_characteristic_cb(GSList *characteristics, guint8 status, } gas->changed_handle = chr->value_handle; - gatt_discover_char_desc(gas->attrib, start, end, gatt_descriptors_cb, + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + gatt_discover_desc(gas->attrib, start, end, &uuid, discover_ccc_cb, gas); } diff --git a/profiles/health/hdp.c b/profiles/health/hdp.c index 6203fa8..48dad52 100644 --- a/profiles/health/hdp.c +++ b/profiles/health/hdp.c @@ -27,19 +27,20 @@ #include #include #include -#include #include #include +#include #include #include -#include -#include -#include -#include -#include -#include +#include "src/dbus-common.h" +#include "src/log.h" +#include "src/error.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/sdpd.h" +#include "btio/btio.h" #include "mcap_lib.h" #include "hdp_types.h" @@ -2144,7 +2145,8 @@ static struct hdp_device *create_health_device(struct btd_device *device) if (!g_dbus_register_interface(btd_get_dbus_connection(), path, HEALTH_DEVICE, health_device_methods, - health_device_signals, NULL, + health_device_signals, + health_device_properties, dev, health_device_destroy)) { error("D-Bus failed to register %s interface", HEALTH_DEVICE); goto fail; diff --git a/profiles/health/hdp_main.c b/profiles/health/hdp_main.c index e705ee9..6dc9acf 100644 --- a/profiles/health/hdp_main.c +++ b/profiles/health/hdp_main.c @@ -25,10 +25,10 @@ #endif #include - #include -#include "plugin.h" +#include "src/plugin.h" + #include "hdp_manager.h" static int hdp_init(void) diff --git a/profiles/health/hdp_manager.c b/profiles/health/hdp_manager.c index 1bb6007..1882043 100644 --- a/profiles/health/hdp_manager.c +++ b/profiles/health/hdp_manager.c @@ -29,17 +29,16 @@ #include #include +#include "btio/btio.h" #include "lib/uuid.h" -#include -#include -#include -#include -#include -#include -#include +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/uuid-helper.h" +#include "src/log.h" #include "hdp_types.h" - #include "hdp_manager.h" #include "hdp.h" diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c index 7de87a8..58770b5 100644 --- a/profiles/health/hdp_util.c +++ b/profiles/health/hdp_util.c @@ -31,20 +31,23 @@ #include -#include -#include - -#include +#include +#include #include -#include -#include + +#include "src/adapter.h" +#include "src/device.h" + +#include "src/sdpd.h" +#include "src/sdp-client.h" +#include "src/uuid-helper.h" #include "lib/uuid.h" #include "btio/btio.h" -#include "log.h" +#include "src/log.h" +#include "src/dbus-common.h" -#include "dbus-common.h" #include "mcap.h" #include "mcap_lib.h" #include "hdp_types.h" @@ -864,7 +867,7 @@ gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app, bt_string2uuid(&uuid, HDP_UUID); if (bt_search_service(src, dst, &uuid, get_mdep_cb, mdep_data, - free_mdep_data) < 0) { + free_mdep_data, 0) < 0) { g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR, "Can't get remote SDP record"); g_free(mdep_data); @@ -1092,7 +1095,7 @@ gboolean hdp_establish_mcl(struct hdp_device *device, bt_string2uuid(&uuid, HDP_UUID); if (bt_search_service(src, dst, &uuid, search_cb, conn_data, - destroy_con_mcl_data) < 0) { + destroy_con_mcl_data, 0) < 0) { g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR, "Can't get remote SDP record"); g_free(conn_data); @@ -1161,7 +1164,7 @@ gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func, bt_string2uuid(&uuid, HDP_UUID); if (bt_search_service(src, dst, &uuid, get_dcpsm_cb, dcpsm_data, - free_dcpsm_data) < 0) { + free_dcpsm_data, 0) < 0) { g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR, "Can't get remote SDP record"); g_free(dcpsm_data); diff --git a/profiles/health/mcap.c b/profiles/health/mcap.c index 6d821f3..102ec85 100644 --- a/profiles/health/mcap.c +++ b/profiles/health/mcap.c @@ -34,9 +34,9 @@ #include #include -#include -#include -#include +#include "btio/btio.h" +#include "src/log.h" +#include "src/error.h" #include "mcap.h" #include "mcap_lib.h" diff --git a/profiles/health/mcap_sync.c b/profiles/health/mcap_sync.c index 0d9f17d..cc89d47 100644 --- a/profiles/health/mcap_sync.c +++ b/profiles/health/mcap_sync.c @@ -33,9 +33,10 @@ #include #include -#include -#include -#include + +#include "btio/btio.h" +#include "src/adapter.h" +#include "src/log.h" #include "mcap.h" #include "mcap_lib.h" @@ -55,7 +56,7 @@ struct mcap_csp { 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 */ + uint8_t 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 */ @@ -88,8 +89,6 @@ struct sync_set_data { gboolean role; }; -#define hton64(x) ntoh64(x) - static gboolean csp_caps_initialized = FALSE; struct csp_caps _caps; diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c index d87f0ff..287ea72 100644 --- a/profiles/heartrate/heartrate.c +++ b/profiles/heartrate/heartrate.c @@ -30,18 +30,19 @@ #include #include "lib/uuid.h" -#include "plugin.h" -#include "adapter.h" -#include "dbus-common.h" -#include "device.h" -#include "profile.h" -#include "service.h" -#include "error.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/dbus-common.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/shared/util.h" +#include "src/service.h" +#include "src/error.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" -#include "attio.h" -#include "log.h" +#include "src/attio.h" +#include "src/log.h" #define HEART_RATE_INTERFACE "org.bluez.HeartRate1" #define HEART_RATE_MANAGER_INTERFACE "org.bluez.HeartRateManager1" @@ -324,7 +325,7 @@ static void process_measurement(struct heartrate *hr, const uint8_t *pdu, return; } - m.value = att_get_u16(pdu); + m.value = get_le16(pdu); pdu += 2; len -= 2; } else { @@ -345,7 +346,7 @@ static void process_measurement(struct heartrate *hr, const uint8_t *pdu, } m.has_energy = TRUE; - m.energy = att_get_u16(pdu); + m.energy = get_le16(pdu); pdu += 2; len -= 2; } @@ -362,7 +363,7 @@ static void process_measurement(struct heartrate *hr, const uint8_t *pdu, m.interval = g_new(uint16_t, m.num_interval); for (i = 0; i < m.num_interval; pdu += 2, i++) - m.interval[i] = att_get_u16(pdu); + m.interval[i] = get_le16(pdu); } if (flags & SENSOR_CONTACT_SUPPORT) { @@ -390,13 +391,12 @@ static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) process_measurement(hr, pdu + 3, len - 3); } -static void discover_ccc_cb(guint8 status, const guint8 *pdu, - guint16 len, gpointer user_data) +static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data) { struct heartrate *hr = user_data; - struct att_data_list *list; - uint8_t format; - int i; + struct gatt_desc *desc; + uint8_t attr_val[2]; + char *msg; if (status != 0) { error("Discover Heart Rate Measurement descriptors failed: %s", @@ -404,50 +404,28 @@ static void discover_ccc_cb(guint8 status, const guint8 *pdu, return; } - list = dec_find_info_resp(pdu, len, &format); - if (list == NULL) - return; - - if (format != ATT_FIND_INFO_RESP_FMT_16BIT) - goto done; - - for (i = 0; i < list->num; i++) { - uint8_t *value; - uint16_t handle, uuid; - char *msg; - uint8_t attr_val[2]; - - value = list->data[i]; - handle = att_get_u16(value); - uuid = att_get_u16(value + 2); - - if (uuid != GATT_CLIENT_CHARAC_CFG_UUID) - continue; + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; - hr->measurement_ccc_handle = handle; + hr->measurement_ccc_handle = desc->handle; - if (g_slist_length(hr->hradapter->watchers) == 0) { - att_put_u16(0x0000, attr_val); - msg = g_strdup("Disable measurement"); - } else { - att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val); - msg = g_strdup("Enable measurement"); - } - - gatt_write_char(hr->attrib, handle, attr_val, - sizeof(attr_val), char_write_cb, msg); - - break; + if (g_slist_length(hr->hradapter->watchers) == 0) { + put_le16(0x0000, attr_val); + msg = g_strdup("Disable measurement"); + } else { + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val); + msg = g_strdup("Enable measurement"); } -done: - att_data_list_free(list); + gatt_write_char(hr->attrib, desc->handle, attr_val, sizeof(attr_val), + char_write_cb, msg); } static void discover_measurement_ccc(struct heartrate *hr, struct gatt_char *c, struct gatt_char *c_next) { uint16_t start, end; + bt_uuid_t uuid; start = c->value_handle + 1; @@ -461,10 +439,12 @@ static void discover_measurement_ccc(struct heartrate *hr, return; } - gatt_discover_char_desc(hr->attrib, start, end, discover_ccc_cb, hr); + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + gatt_discover_desc(hr->attrib, start, end, &uuid, discover_ccc_cb, hr); } -static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data) +static void discover_char_cb(uint8_t status, GSList *chars, void *user_data) { struct heartrate *hr = user_data; @@ -510,7 +490,7 @@ static void enable_measurement(gpointer data, gpointer user_data) if (hr->attrib == NULL || !handle) return; - att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); msg = g_strdup("Enable measurement"); gatt_write_char(hr->attrib, handle, value, sizeof(value), @@ -527,7 +507,7 @@ static void disable_measurement(gpointer data, gpointer user_data) if (hr->attrib == NULL || !handle) return; - att_put_u16(0x0000, value); + put_le16(0x0000, value); msg = g_strdup("Disable measurement"); gatt_write_char(hr->attrib, handle, value, sizeof(value), diff --git a/profiles/input/device.c b/profiles/input/device.c index 62f6dbb..52222ea 100644 --- a/profiles/input/device.c +++ b/profiles/input/device.c @@ -3,6 +3,7 @@ * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2014 Google Inc. * * * This program is free software; you can redistribute it and/or modify @@ -39,24 +40,27 @@ #include -#include "log.h" +#include "src/log.h" +#include "btio/btio.h" #include "lib/uuid.h" -#include "../src/adapter.h" -#include "../src/device.h" -#include "../src/profile.h" -#include "../src/service.h" -#include "../src/storage.h" -#include "../src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/storage.h" +#include "src/dbus-common.h" +#include "src/error.h" +#include "src/sdp-client.h" #include "device.h" -#include "error.h" -#include - -#include "sdp-client.h" +#include "hidp_defs.h" +#include "uhid_copy.h" #define INPUT_INTERFACE "org.bluez.Input1" +#define UHID_DEVICE_FILE "/dev/uhid" + enum reconnect_mode_t { RECONNECT_NONE = 0, RECONNECT_DEVICE, @@ -79,20 +83,33 @@ struct input_device { struct hidp_connadd_req *req; guint dc_id; bool disable_sdp; - char *name; enum reconnect_mode_t reconnect_mode; guint reconnect_timer; uint32_t reconnect_attempt; + bool uhid_enabled; + int uhid_fd; + guint uhid_watch; + bool uhid_created; + uint8_t report_req_pending; + guint report_req_timer; + uint32_t report_rsp_id; }; static int idle_timeout = 0; +static bool uhid_enabled = false; void input_set_idle_timeout(int timeout) { idle_timeout = timeout; } +void input_enable_userspace_hid(bool state) +{ + uhid_enabled = state; +} + static void input_device_enter_reconnect_mode(struct input_device *idev); +static int connection_disconnect(struct input_device *idev, uint32_t flags); static void input_device_free(struct input_device *idev) { @@ -101,7 +118,6 @@ static void input_device_free(struct input_device *idev) btd_service_unref(idev->service); btd_device_unref(idev->device); - g_free(idev->name); g_free(idev->path); if (idev->ctrl_watch > 0) @@ -127,14 +143,189 @@ static void input_device_free(struct input_device *idev) if (idev->reconnect_timer > 0) g_source_remove(idev->reconnect_timer); + if (idev->report_req_timer > 0) + g_source_remove(idev->report_req_timer); + g_free(idev); } +static bool hidp_send_message(GIOChannel *chan, uint8_t hdr, + const uint8_t *data, size_t size) +{ + int fd; + ssize_t len; + uint8_t msg[size + 1]; + + if (data == NULL) + size = 0; + + msg[0] = hdr; + if (size > 0) + memcpy(&msg[1], data, size); + ++size; + + fd = g_io_channel_unix_get_fd(chan); + + len = write(fd, msg, size); + if (len < 0) { + error("BT socket write error: %s (%d)", strerror(errno), errno); + return false; + } + + if ((size_t) len < size) { + error("BT socket write error: partial write (%zd of %zu bytes)", + len, size); + return false; + } + + return true; +} + +static bool hidp_send_ctrl_message(struct input_device *idev, uint8_t hdr, + const uint8_t *data, size_t size) +{ + return hidp_send_message(idev->ctrl_io, hdr, data, size); +} + +static bool hidp_send_intr_message(struct input_device *idev, uint8_t hdr, + const uint8_t *data, size_t size) +{ + return hidp_send_message(idev->intr_io, hdr, data, size); +} + +static bool uhid_send_feature_answer(struct input_device *idev, + const uint8_t *data, size_t size, + uint32_t id, uint16_t err) +{ + struct uhid_event ev; + ssize_t len; + + if (data == NULL) + size = 0; + + if (size > sizeof(ev.u.feature_answer.data)) + size = sizeof(ev.u.feature_answer.data); + + if (!idev->uhid_created) { + DBG("HID report (%zu bytes) dropped", size); + return false; + } + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_FEATURE_ANSWER; + ev.u.feature_answer.id = id; + ev.u.feature_answer.err = err; + ev.u.feature_answer.size = size; + + if (size > 0) + memcpy(ev.u.feature_answer.data, data, size); + + len = write(idev->uhid_fd, &ev, sizeof(ev)); + if (len < 0) { + error("uHID dev write error: %s (%d)", strerror(errno), errno); + return false; + } + + /* uHID kernel driver does not handle partial writes */ + if ((size_t) len < sizeof(ev)) { + error("uHID dev write error: partial write (%zd of %zu bytes)", + len, sizeof(ev)); + return false; + } + + DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd); + + return true; +} + +static bool uhid_send_input_report(struct input_device *idev, + const uint8_t *data, size_t size) +{ + struct uhid_event ev; + ssize_t len; + + if (data == NULL) + size = 0; + + if (size > sizeof(ev.u.input.data)) + size = sizeof(ev.u.input.data); + + if (!idev->uhid_created) { + DBG("HID report (%zu bytes) dropped", size); + return false; + } + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + ev.u.input.size = size; + + if (size > 0) + memcpy(ev.u.input.data, data, size); + + len = write(idev->uhid_fd, &ev, sizeof(ev)); + if (len < 0) { + error("uHID dev write error: %s (%d)", strerror(errno), errno); + return false; + } + + /* uHID kernel driver does not handle partial writes */ + if ((size_t) len < sizeof(ev)) { + error("uHID dev write error: partial write (%zd of %zu bytes)", + len, sizeof(ev)); + return false; + } + + DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd); + + return true; +} + +static bool hidp_recv_intr_data(GIOChannel *chan, struct input_device *idev) +{ + int fd; + ssize_t len; + uint8_t hdr; + uint8_t data[UHID_DATA_MAX + 1]; + + fd = g_io_channel_unix_get_fd(chan); + + len = read(fd, data, sizeof(data)); + if (len < 0) { + error("BT socket read error: %s (%d)", strerror(errno), errno); + return false; + } + + if (len == 0) { + DBG("BT socket read returned 0 bytes"); + return true; + } + + hdr = data[0]; + if (hdr != (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { + DBG("unsupported HIDP protocol header 0x%02x", hdr); + return true; + } + + if (len < 2) { + DBG("received empty HID report"); + return true; + } + + uhid_send_input_report(idev, data + 1, len - 1); + + return true; +} + static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct input_device *idev = data; char address[18]; + if (cond & G_IO_IN) { + if (hidp_recv_intr_data(chan, idev) && (cond == G_IO_IN)) + return TRUE; + } + ba2str(&idev->dst, address); DBG("Device %s disconnected", address); @@ -167,11 +358,170 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data return FALSE; } +static void hidp_recv_ctrl_handshake(struct input_device *idev, uint8_t param) +{ + bool pending_req_complete = false; + uint8_t pending_req_type; + + DBG(""); + + pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK; + + switch (param) { + case HIDP_HSHK_SUCCESSFUL: + if (pending_req_type == HIDP_TRANS_SET_REPORT) { + DBG("SET_REPORT successful"); + pending_req_complete = true; + } else + DBG("Spurious HIDP_HSHK_SUCCESSFUL"); + break; + + case HIDP_HSHK_NOT_READY: + case HIDP_HSHK_ERR_INVALID_REPORT_ID: + case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: + case HIDP_HSHK_ERR_INVALID_PARAMETER: + case HIDP_HSHK_ERR_UNKNOWN: + case HIDP_HSHK_ERR_FATAL: + if (pending_req_type == HIDP_TRANS_GET_REPORT) { + DBG("GET_REPORT failed (%u)", param); + uhid_send_feature_answer(idev, NULL, 0, + idev->report_rsp_id, EIO); + pending_req_complete = true; + } else if (pending_req_type == HIDP_TRANS_SET_REPORT) { + DBG("SET_REPORT failed (%u)", param); + pending_req_complete = true; + } else + DBG("Spurious HIDP_HSHK_ERR"); + + if (param == HIDP_HSHK_ERR_FATAL) + hidp_send_ctrl_message(idev, HIDP_TRANS_HID_CONTROL | + HIDP_CTRL_SOFT_RESET, NULL, 0); + break; + + default: + hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE | + HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); + break; + } + + if (pending_req_complete) { + idev->report_req_pending = 0; + if (idev->report_req_timer > 0) { + g_source_remove(idev->report_req_timer); + idev->report_req_timer = 0; + } + idev->report_rsp_id = 0; + } +} + +static void hidp_recv_ctrl_hid_control(struct input_device *idev, uint8_t param) +{ + DBG(""); + + if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) + connection_disconnect(idev, 0); +} + +static void hidp_recv_ctrl_data(struct input_device *idev, uint8_t param, + const uint8_t *data, size_t size) +{ + uint8_t pending_req_type; + uint8_t pending_req_param; + + DBG(""); + + pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK; + if (pending_req_type != HIDP_TRANS_GET_REPORT) { + DBG("Spurious DATA on control channel"); + return; + } + + pending_req_param = idev->report_req_pending & HIDP_HEADER_PARAM_MASK; + if (pending_req_param != param) { + DBG("Received DATA RTYPE doesn't match pending request RTYPE"); + return; + } + + switch (param) { + case HIDP_DATA_RTYPE_FEATURE: + case HIDP_DATA_RTYPE_INPUT: + case HIDP_DATA_RTYPE_OUPUT: + uhid_send_feature_answer(idev, data + 1, size - 1, + idev->report_rsp_id, 0); + break; + + case HIDP_DATA_RTYPE_OTHER: + DBG("Received DATA_RTYPE_OTHER"); + break; + + default: + hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE | + HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); + break; + } + + idev->report_req_pending = 0; + if (idev->report_req_timer > 0) { + g_source_remove(idev->report_req_timer); + idev->report_req_timer = 0; + } + idev->report_rsp_id = 0; +} + +static bool hidp_recv_ctrl_message(GIOChannel *chan, struct input_device *idev) +{ + int fd; + ssize_t len; + uint8_t hdr, type, param; + uint8_t data[UHID_DATA_MAX + 1]; + + fd = g_io_channel_unix_get_fd(chan); + + len = read(fd, data, sizeof(data)); + if (len < 0) { + error("BT socket read error: %s (%d)", strerror(errno), errno); + return false; + } + + if (len == 0) { + DBG("BT socket read returned 0 bytes"); + return true; + } + + hdr = data[0]; + type = hdr & HIDP_HEADER_TRANS_MASK; + param = hdr & HIDP_HEADER_PARAM_MASK; + + switch (type) { + case HIDP_TRANS_HANDSHAKE: + hidp_recv_ctrl_handshake(idev, param); + break; + case HIDP_TRANS_HID_CONTROL: + hidp_recv_ctrl_hid_control(idev, param); + break; + case HIDP_TRANS_DATA: + hidp_recv_ctrl_data(idev, param, data, len); + break; + default: + error("unsupported HIDP control message"); + hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE | + HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0); + break; + } + + return true; +} + static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct input_device *idev = data; char address[18]; + if (cond & G_IO_IN) { + if (hidp_recv_ctrl_message(chan, idev) && (cond == G_IO_IN)) + return TRUE; + } + ba2str(&idev->dst, address); DBG("Device %s disconnected", address); @@ -196,6 +546,205 @@ static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data return FALSE; } +#define REPORT_REQ_TIMEOUT 3 + +static gboolean hidp_report_req_timeout(gpointer data) +{ + struct input_device *idev = data; + uint8_t pending_req_type; + const char *req_type_str; + char address[18]; + + ba2str(&idev->dst, address); + pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK; + + switch (pending_req_type) { + case HIDP_TRANS_GET_REPORT: + req_type_str = "GET_REPORT"; + break; + case HIDP_TRANS_SET_REPORT: + req_type_str = "SET_REPORT"; + break; + default: + /* Should never happen */ + req_type_str = "OTHER_TRANS"; + break; + } + + DBG("Device %s HIDP %s request timed out", address, req_type_str); + + idev->report_req_pending = 0; + idev->report_req_timer = 0; + idev->report_rsp_id = 0; + + return FALSE; +} + +static void hidp_send_set_report(struct input_device *idev, + struct uhid_event *ev) +{ + uint8_t hdr; + bool sent; + + DBG(""); + + switch (ev->u.output.rtype) { + case UHID_FEATURE_REPORT: + /* Send SET_REPORT on control channel */ + if (idev->report_req_pending) { + DBG("Old GET_REPORT or SET_REPORT still pending"); + return; + } + + hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; + sent = hidp_send_ctrl_message(idev, hdr, ev->u.output.data, + ev->u.output.size); + if (sent) { + idev->report_req_pending = hdr; + idev->report_req_timer = + g_timeout_add_seconds(REPORT_REQ_TIMEOUT, + hidp_report_req_timeout, idev); + } + break; + case UHID_OUTPUT_REPORT: + /* Send DATA on interrupt channel */ + hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; + hidp_send_intr_message(idev, hdr, ev->u.output.data, + ev->u.output.size); + break; + default: + DBG("Unsupported HID report type %u", ev->u.output.rtype); + return; + } +} + +static void hidp_send_get_report(struct input_device *idev, + struct uhid_event *ev) +{ + uint8_t hdr; + bool sent; + + DBG(""); + + if (idev->report_req_pending) { + DBG("Old GET_REPORT or SET_REPORT still pending"); + uhid_send_feature_answer(idev, NULL, 0, ev->u.feature.id, + EBUSY); + return; + } + + /* Send GET_REPORT on control channel */ + switch (ev->u.feature.rtype) { + case UHID_FEATURE_REPORT: + hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; + break; + case UHID_INPUT_REPORT: + hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; + break; + case UHID_OUTPUT_REPORT: + hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT; + break; + default: + DBG("Unsupported HID report type %u", ev->u.feature.rtype); + return; + } + + sent = hidp_send_ctrl_message(idev, hdr, &ev->u.feature.rnum, + sizeof(ev->u.feature.rnum)); + if (sent) { + idev->report_req_pending = hdr; + idev->report_req_timer = + g_timeout_add_seconds(REPORT_REQ_TIMEOUT, + hidp_report_req_timeout, idev); + idev->report_rsp_id = ev->u.feature.id; + } +} + +static gboolean uhid_watch_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + struct input_device *idev = user_data; + int fd; + ssize_t len; + struct uhid_event ev; + + if (cond & (G_IO_ERR | G_IO_NVAL)) + goto failed; + + fd = g_io_channel_unix_get_fd(chan); + memset(&ev, 0, sizeof(ev)); + + len = read(fd, &ev, sizeof(ev)); + if (len < 0) { + error("uHID dev read error: %s (%d)", strerror(errno), errno); + goto failed; + } + + if ((size_t) len < sizeof(ev.type)) { + error("uHID dev read returned too few bytes"); + goto failed; + } + + DBG("uHID event type %u received (%zd bytes)", ev.type, len); + + switch (ev.type) { + case UHID_START: + case UHID_STOP: + /* These are called to start and stop the underlying hardware. + * For HID we open the channels before creating the device so + * the hardware is always ready. No need to handle these. + * Note that these are also called when the kernel switches + * between device-drivers loaded on the HID device. But we can + * simply keep the hardware alive during transitions and it + * works just fine. + * The kernel never destroys a device itself! Only an explicit + * UHID_DESTROY request can remove a device. + */ + break; + case UHID_OPEN: + case UHID_CLOSE: + /* OPEN/CLOSE are sent whenever user-space opens any interface + * provided by the kernel HID device. Whenever the open-count + * is non-zero we must be ready for I/O. As long as it is zero, + * we can decide to drop all I/O and put the device + * asleep This is optional, though. Moreover, some + * special device drivers are buggy in that regard, so + * maybe we just keep I/O always awake like HIDP in the + * kernel does. + */ + break; + case UHID_OUTPUT: + hidp_send_set_report(idev, &ev); + break; + case UHID_FEATURE: + hidp_send_get_report(idev, &ev); + break; + case UHID_OUTPUT_EV: + /* This is only sent by kernels prior to linux-3.11. It + * requires us to parse HID-descriptors in user-space to + * properly handle it. This is redundant as the kernel + * does it already. That's why newer kernels assemble + * the output-reports and send it to us via UHID_OUTPUT. + * We never implemented this, so we rely on users to use + * recent-enough kernels if they want this feature. No reason + * to implement this for older kernels. + */ + DBG("Unsupported uHID output event: type %u code %u value %d", + ev.u.output_ev.type, ev.u.output_ev.code, + ev.u.output_ev.value); + break; + default: + warn("unexpected uHID event"); + break; + } + + return TRUE; + +failed: + idev->uhid_watch = 0; + return FALSE; +} + static void epox_endian_quirk(unsigned char *data, int size) { /* USAGE_PAGE (Keyboard) 05 07 @@ -336,6 +885,100 @@ static int ioctl_connadd(struct hidp_connadd_req *req) return err; } +static bool ioctl_is_connected(struct input_device *idev) +{ + struct hidp_conninfo ci; + int ctl; + + /* Standard HID */ + ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); + if (ctl < 0) { + error("Can't open HIDP control socket"); + return false; + } + + memset(&ci, 0, sizeof(ci)); + bacpy(&ci.bdaddr, &idev->dst); + if (ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) { + error("Can't get HIDP connection info"); + close(ctl); + return false; + } + + close(ctl); + + if (ci.state != BT_CONNECTED) + return false; + + return true; +} + +static int ioctl_disconnect(struct input_device *idev, uint32_t flags) +{ + struct hidp_conndel_req req; + struct hidp_conninfo ci; + int ctl, err = 0; + + 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)) { + close(ctl); + return -ENOTCONN; + } + + memset(&req, 0, sizeof(req)); + bacpy(&req.bdaddr, &idev->dst); + req.flags = flags; + if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { + err = -errno; + error("Can't delete the HID device: %s (%d)", + strerror(-err), -err); + } + + close(ctl); + + return err; +} + +static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req) +{ + int err = 0; + struct uhid_event ev; + + if (!idev->uhid_created) { + /* create uHID device */ + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strncpy((char *) ev.u.create.name, req->name, + sizeof(ev.u.create.name) - 1); + ba2str(&idev->src, (char *) ev.u.create.phys); + ba2str(&idev->dst, (char *) ev.u.create.uniq); + ev.u.create.vendor = req->vendor; + ev.u.create.product = req->product; + ev.u.create.version = req->version; + ev.u.create.country = req->country; + ev.u.create.bus = BUS_BLUETOOTH; + ev.u.create.rd_data = req->rd_data; + ev.u.create.rd_size = req->rd_size; + + if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0) { + err = -errno; + error("Failed to create uHID device: %s (%d)", + strerror(-err), -err); + } else + idev->uhid_created = true; + } + + return err; +} + static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition, gpointer data) { @@ -344,7 +987,11 @@ static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition, DBG(""); - err = ioctl_connadd(idev->req); + if (idev->uhid_enabled) + err = uhid_connadd(idev, idev->req); + else + err = ioctl_connadd(idev->req); + if (err < 0) { error("ioctl_connadd(): %s (%d)", strerror(-err), -err); @@ -421,8 +1068,8 @@ static int hidp_add_connection(struct input_device *idev) req->product = btd_device_get_product(idev->device); req->version = btd_device_get_version(idev->device); - if (idev->name) - strncpy(req->name, idev->name, sizeof(req->name) - 1); + if (device_name_known(idev->device)) + device_get_name(idev->device, req->name, sizeof(req->name)); /* Encryption is mandatory for keyboards */ if (req->subclass & 0x40) { @@ -442,7 +1089,10 @@ static int hidp_add_connection(struct input_device *idev) return 0; } - err = ioctl_connadd(req); + if (idev->uhid_enabled) + err = uhid_connadd(idev, req); + else + err = ioctl_connadd(req); cleanup: g_free(req->rd_data); @@ -451,37 +1101,16 @@ cleanup: return err; } -static int is_connected(struct input_device *idev) +static bool is_connected(struct input_device *idev) { - struct hidp_conninfo ci; - int ctl; - - /* 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; + if (idev->uhid_enabled) + return (idev->intr_io != NULL && idev->ctrl_io != NULL); else - return 1; + return ioctl_is_connected(idev); } static int connection_disconnect(struct input_device *idev, uint32_t flags) { - struct hidp_conndel_req req; - struct hidp_conninfo ci; - int ctl, err = 0; - if (!is_connected(idev)) return -ENOTCONN; @@ -491,34 +1120,10 @@ static int connection_disconnect(struct input_device *idev, uint32_t flags) if (idev->ctrl_io) g_io_channel_shutdown(idev->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)) { - err = -ENOTCONN; - goto fail; - } - - memset(&req, 0, sizeof(req)); - bacpy(&req.bdaddr, &idev->dst); - req.flags = flags; - if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { - err = -errno; - error("Can't delete the HID device: %s(%d)", - strerror(-err), -err); - goto fail; - } - -fail: - close(ctl); - - return err; + if (idev->uhid_enabled) + return 0; + else + return ioctl_disconnect(idev, flags); } static void disconnect_cb(struct btd_device *device, gboolean removal, @@ -557,6 +1162,7 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct input_device *idev = user_data; + GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; int err; if (conn_err) { @@ -568,9 +1174,11 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, if (err < 0) goto failed; - idev->intr_watch = g_io_add_watch(idev->intr_io, - G_IO_HUP | G_IO_ERR | G_IO_NVAL, - intr_watch_cb, idev); + if (idev->uhid_enabled) + cond |= G_IO_IN; + + idev->intr_watch = g_io_add_watch(idev->intr_io, cond, intr_watch_cb, + idev); return; @@ -596,6 +1204,7 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct input_device *idev = user_data; + GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; GIOChannel *io; GError *err = NULL; @@ -620,9 +1229,11 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err, idev->intr_io = io; - idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, - G_IO_HUP | G_IO_ERR | G_IO_NVAL, - ctrl_watch_cb, idev); + if (idev->uhid_enabled) + cond |= G_IO_IN; + + idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, ctrl_watch_cb, + idev); return; @@ -641,12 +1252,12 @@ static int dev_connect(struct input_device *idev) bt_clear_cached_session(&idev->src, &idev->dst); io = bt_io_connect(control_connect_cb, idev, - 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); + 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); idev->ctrl_io = io; if (err == NULL) @@ -664,8 +1275,9 @@ static gboolean input_device_auto_reconnect(gpointer user_data) DBG("path=%s, attempt=%d", idev->path, idev->reconnect_attempt); - /* Stop the recurrent reconnection attempts if the device is reconnected - * or is marked for removal. */ + /* Stop the recurrent reconnection attempts if the device is + * reconnected or is marked for removal. + */ if (device_is_temporary(idev->device) || btd_device_is_connected(idev->device)) return FALSE; @@ -704,14 +1316,16 @@ static void input_device_enter_reconnect_mode(struct input_device *idev) DBG("path=%s reconnect_mode=%s", idev->path, reconnect_mode_to_string(idev->reconnect_mode)); - /* Only attempt an auto-reconnect when the device is required to accept - * reconnections from the host. */ + /* Only attempt an auto-reconnect when the device is required to + * accept reconnections from the host. + */ if (idev->reconnect_mode != RECONNECT_ANY && idev->reconnect_mode != RECONNECT_HOST) return; - /* If the device is temporary we are not required to reconnect with the - * device. This is likely the case of a removing device. */ + /* If the device is temporary we are not required to reconnect + * with the device. This is likely the case of a removing device. + */ if (device_is_temporary(idev->device) || btd_device_is_connected(idev->device)) return; @@ -809,7 +1423,6 @@ static struct input_device *input_device_new(struct btd_service *service) const sdp_record_t *rec = btd_device_get_record(device, p->remote_uuid); struct btd_adapter *adapter = device_get_adapter(device); struct input_device *idev; - char name[HCI_MAX_NAME_LENGTH + 1]; if (!rec) return NULL; @@ -822,10 +1435,7 @@ static struct input_device *input_device_new(struct btd_service *service) idev->path = g_strdup(path); idev->handle = rec->handle; idev->disable_sdp = is_device_sdp_disable(rec); - - device_get_name(device, name, HCI_MAX_NAME_LENGTH); - if (strlen(name) > 0) - idev->name = g_strdup(name); + idev->uhid_enabled = uhid_enabled; /* Initialize device properties */ extract_hid_props(idev, rec); @@ -855,6 +1465,8 @@ int input_device_register(struct btd_service *service) struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); struct input_device *idev; + int err; + GIOChannel *io; DBG("%s", path); @@ -862,6 +1474,24 @@ int input_device_register(struct btd_service *service) if (!idev) return -EINVAL; + if (idev->uhid_enabled) { + idev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC); + if (idev->uhid_fd < 0) { + err = errno; + error("Failed to open uHID device: %s (%d)", + strerror(err), err); + input_device_free(idev); + return -err; + } + + io = g_io_channel_unix_new(idev->uhid_fd); + g_io_channel_set_encoding(io, NULL, NULL); + idev->uhid_watch = g_io_add_watch(io, + G_IO_IN | G_IO_ERR | G_IO_NVAL, + uhid_watch_cb, idev); + g_io_channel_unref(io); + } + if (g_dbus_register_interface(btd_get_dbus_connection(), idev->path, INPUT_INTERFACE, NULL, NULL, @@ -883,7 +1513,7 @@ static struct input_device *find_device(const bdaddr_t *src, struct btd_device *device; struct btd_service *service; - device = btd_adapter_find_device(adapter_find(src), dst); + device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR); if (device == NULL) return NULL; @@ -899,12 +1529,31 @@ void input_device_unregister(struct btd_service *service) struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); struct input_device *idev = btd_service_get_user_data(service); + struct uhid_event ev; DBG("%s", path); g_dbus_unregister_interface(btd_get_dbus_connection(), idev->path, INPUT_INTERFACE); + if (idev->uhid_enabled) { + if (idev->uhid_watch) { + g_source_remove(idev->uhid_watch); + idev->uhid_watch = 0; + } + + if (idev->uhid_created) { + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0) + error("Failed to destroy uHID device: %s (%d)", + strerror(errno), errno); + } + + close(idev->uhid_fd); + idev->uhid_fd = -1; + } + input_device_free(idev); } @@ -913,17 +1562,15 @@ static int input_device_connadd(struct input_device *idev) int err; err = input_device_connected(idev); - if (err < 0) - goto error; - - return 0; + if (err == 0) + return 0; -error: if (idev->ctrl_io) { g_io_channel_shutdown(idev->ctrl_io, FALSE, NULL); g_io_channel_unref(idev->ctrl_io); idev->ctrl_io = NULL; } + if (idev->intr_io) { g_io_channel_shutdown(idev->intr_io, FALSE, NULL); g_io_channel_unref(idev->intr_io); @@ -945,28 +1592,30 @@ 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); + GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; DBG("idev %p psm %d", idev, psm); if (!idev) return -ENOENT; + if (idev->uhid_enabled) + cond |= G_IO_IN; + switch (psm) { case L2CAP_PSM_HIDP_CTRL: if (idev->ctrl_io) return -EALREADY; idev->ctrl_io = g_io_channel_ref(io); - idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, - G_IO_HUP | G_IO_ERR | G_IO_NVAL, - ctrl_watch_cb, idev); + idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, + ctrl_watch_cb, idev); break; case L2CAP_PSM_HIDP_INTR: if (idev->intr_io) return -EALREADY; idev->intr_io = g_io_channel_ref(io); - idev->intr_watch = g_io_add_watch(idev->intr_io, - G_IO_HUP | G_IO_ERR | G_IO_NVAL, - intr_watch_cb, idev); + idev->intr_watch = g_io_add_watch(idev->intr_io, cond, + intr_watch_cb, idev); break; } diff --git a/profiles/input/device.h b/profiles/input/device.h index da2149c..51a9aee 100644 --- a/profiles/input/device.h +++ b/profiles/input/device.h @@ -28,6 +28,7 @@ struct input_device; struct input_conn; void input_set_idle_timeout(int timeout); +void input_enable_userspace_hid(bool state); int input_device_register(struct btd_service *service); void input_device_unregister(struct btd_service *service); diff --git a/profiles/input/hidp_defs.h b/profiles/input/hidp_defs.h new file mode 100644 index 0000000..5dc479a --- /dev/null +++ b/profiles/input/hidp_defs.h @@ -0,0 +1,79 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2014 Marcel Holtmann + * Copyright (C) 2014 Google 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __HIDP_DEFS_H +#define __HIDP_DEFS_H + +/* HIDP header masks */ +#define HIDP_HEADER_TRANS_MASK 0xf0 +#define HIDP_HEADER_PARAM_MASK 0x0f + +/* HIDP transaction types */ +#define HIDP_TRANS_HANDSHAKE 0x00 +#define HIDP_TRANS_HID_CONTROL 0x10 +#define HIDP_TRANS_GET_REPORT 0x40 +#define HIDP_TRANS_SET_REPORT 0x50 +#define HIDP_TRANS_GET_PROTOCOL 0x60 +#define HIDP_TRANS_SET_PROTOCOL 0x70 +#define HIDP_TRANS_GET_IDLE 0x80 +#define HIDP_TRANS_SET_IDLE 0x90 +#define HIDP_TRANS_DATA 0xa0 +#define HIDP_TRANS_DATC 0xb0 + +/* HIDP handshake results */ +#define HIDP_HSHK_SUCCESSFUL 0x00 +#define HIDP_HSHK_NOT_READY 0x01 +#define HIDP_HSHK_ERR_INVALID_REPORT_ID 0x02 +#define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST 0x03 +#define HIDP_HSHK_ERR_INVALID_PARAMETER 0x04 +#define HIDP_HSHK_ERR_UNKNOWN 0x0e +#define HIDP_HSHK_ERR_FATAL 0x0f + +/* HIDP control operation parameters */ +#define HIDP_CTRL_NOP 0x00 +#define HIDP_CTRL_HARD_RESET 0x01 +#define HIDP_CTRL_SOFT_RESET 0x02 +#define HIDP_CTRL_SUSPEND 0x03 +#define HIDP_CTRL_EXIT_SUSPEND 0x04 +#define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG 0x05 + +/* HIDP data transaction headers */ +#define HIDP_DATA_RTYPE_MASK 0x03 +#define HIDP_DATA_RSRVD_MASK 0x0c +#define HIDP_DATA_RTYPE_OTHER 0x00 +#define HIDP_DATA_RTYPE_INPUT 0x01 +#define HIDP_DATA_RTYPE_OUPUT 0x02 +#define HIDP_DATA_RTYPE_FEATURE 0x03 + +/* HIDP protocol header parameters */ +#define HIDP_PROTO_BOOT 0x00 +#define HIDP_PROTO_REPORT 0x01 + +#define HIDP_VIRTUAL_CABLE_UNPLUG 0 +#define HIDP_BOOT_PROTOCOL_MODE 1 +#define HIDP_BLUETOOTH_VENDOR_ID 9 +#define HIDP_WAITING_FOR_RETURN 10 +#define HIDP_WAITING_FOR_SEND_ACK 11 + +#endif /* __HIDP_DEFS_H */ diff --git a/profiles/input/hog.c b/profiles/input/hog.c index 4647fef..e82e827 100644 --- a/profiles/input/hog.c +++ b/profiles/input/hog.c @@ -40,19 +40,21 @@ #include -#include "log.h" +#include "src/log.h" #include "lib/uuid.h" #include "src/adapter.h" #include "src/device.h" #include "src/profile.h" #include "src/service.h" +#include "src/shared/util.h" + +#include "src/plugin.h" -#include "plugin.h" #include "suspend.h" #include "attrib/att.h" #include "attrib/gattrib.h" -#include "attio.h" +#include "src/attio.h" #include "attrib/gatt.h" #define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb" @@ -74,6 +76,7 @@ #define HOG_REPORT_MAP_MAX_SIZE 512 #define HID_INFO_SIZE 4 +#define ATT_NOTIFICATION_HEADER_SIZE 3 struct hog_device { uint16_t id; @@ -100,46 +103,55 @@ struct report { struct hog_device *hogdev; }; -struct disc_desc_cb_data { - uint16_t end; - gpointer data; -}; - static gboolean suspend_supported = FALSE; static GSList *devices = NULL; -static void report_value_cb(const uint8_t *pdu, uint16_t len, - gpointer user_data) +static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data) { struct report *report = user_data; struct hog_device *hogdev = report->hogdev; struct uhid_event ev; - uint16_t report_size = len - 3; uint8_t *buf; + ssize_t status; - if (len < 3) { /* 1-byte opcode + 2-byte handle */ + if (len < ATT_NOTIFICATION_HEADER_SIZE) { error("Malformed ATT notification"); return; } + pdu += ATT_NOTIFICATION_HEADER_SIZE; + len -= ATT_NOTIFICATION_HEADER_SIZE; + memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; - ev.u.input.size = MIN(report_size, UHID_DATA_MAX); - buf = ev.u.input.data; + if (hogdev->has_report_id) { - *buf = report->id; - buf++; - ev.u.input.size++; + buf[0] = report->id; + len = MIN(len, sizeof(ev.u.input.data) - 1); + memcpy(buf + 1, pdu, len); + ev.u.input.size = ++len; + } else { + len = MIN(len, sizeof(ev.u.input.data)); + memcpy(buf, pdu, len); + ev.u.input.size = len; } - memcpy(buf, &pdu[3], MIN(report_size, UHID_DATA_MAX)); + status = write(hogdev->uhid_fd, &ev, sizeof(ev)); + if (status < 0) { + error("uHID dev write error: %s (%d)", strerror(errno), errno); + return; + } - if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0) - error("uHID write failed: %s", strerror(errno)); - else - DBG("Report from HoG device 0x%04X written to uHID fd %d", - hogdev->id, hogdev->uhid_fd); + /* uHID kernel driver does not handle partial writes */ + if ((size_t) status < sizeof(ev)) { + error("uHID dev write error: partial write (%zd of %zu bytes)", + status, sizeof(ev)); + return; + } + + DBG("HoG report (%u bytes) -> uHID fd %d", ev.u.input.size, + hogdev->uhid_fd); } static void report_ccc_written_cb(guint8 status, const guint8 *pdu, @@ -197,94 +209,56 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data); -static void discover_descriptor_cb(guint8 status, const guint8 *pdu, - guint16 len, gpointer user_data) +static void discover_descriptor_cb(uint8_t status, GSList *descs, + void *user_data) { - struct disc_desc_cb_data *ddcb_data = user_data; struct report *report; struct hog_device *hogdev; - struct att_data_list *list = NULL; GAttrib *attrib = NULL; - uint8_t format; - uint16_t handle = 0xffff; - uint16_t end = ddcb_data->end; - int i; - - if (status == ATT_ECODE_ATTR_NOT_FOUND) { - DBG("Discover all characteristic descriptors finished"); - goto done; - } if (status != 0) { - error("Discover all characteristic descriptors failed: %s", + error("Discover all descriptors failed: %s", att_ecode2str(status)); - goto done; - } - - list = dec_find_info_resp(pdu, len, &format); - if (list == NULL) return; + } - if (format != ATT_FIND_INFO_RESP_FMT_16BIT) - goto done; - - for (i = 0; i < list->num; i++) { - uint16_t uuid16; - uint8_t *value; - - value = list->data[i]; - handle = att_get_u16(value); - uuid16 = att_get_u16(&value[2]); + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; - switch (uuid16) { + switch (desc->uuid16) { case GATT_CLIENT_CHARAC_CFG_UUID: - report = ddcb_data->data; + report = user_data; attrib = report->hogdev->attrib; - write_ccc(handle, report); + write_ccc(desc->handle, report); break; case GATT_REPORT_REFERENCE: - report = ddcb_data->data; + report = user_data; attrib = report->hogdev->attrib; - gatt_read_char(attrib, handle, + gatt_read_char(attrib, desc->handle, report_reference_cb, report); break; case GATT_EXTERNAL_REPORT_REFERENCE: - hogdev = ddcb_data->data; + hogdev = user_data; attrib = hogdev->attrib; - gatt_read_char(attrib, handle, + gatt_read_char(attrib, desc->handle, external_report_reference_cb, hogdev); break; } } - -done: - att_data_list_free(list); - - if (handle != 0xffff && handle < end) - gatt_discover_char_desc(attrib, handle + 1, end, - discover_descriptor_cb, ddcb_data); - else - g_free(ddcb_data); } static void discover_descriptor(GAttrib *attrib, uint16_t start, uint16_t end, gpointer user_data) { - struct disc_desc_cb_data *ddcb_data; - if (start > end) return; - ddcb_data = g_new0(struct disc_desc_cb_data, 1); - ddcb_data->end = end; - ddcb_data->data = user_data; - - gatt_discover_char_desc(attrib, start, end, discover_descriptor_cb, - ddcb_data); + gatt_discover_desc(attrib, start, end, NULL, + discover_descriptor_cb, user_data); } -static void external_service_char_cb(GSList *chars, guint8 status, - gpointer user_data) +static void external_service_char_cb(uint8_t status, GSList *chars, + void *user_data) { struct hog_device *hogdev = user_data; struct gatt_primary *prim = hogdev->hog_primary; @@ -335,7 +309,7 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu, return; } - uuid16 = att_get_u16(&pdu[1]); + uuid16 = get_le16(&pdu[1]); DBG("External report reference read, external report characteristic " "UUID: 0x%04x", uuid16); bt_uuid16_create(&uuid, uuid16); @@ -343,14 +317,78 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu, external_service_char_cb, hogdev); } +static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len, + bool *is_long) +{ + if (!blen) + return false; + + *is_long = (buf[0] == 0xfe); + + if (*is_long) { + if (blen < 3) + return false; + + /* + * long item: + * byte 0 -> 0xFE + * byte 1 -> data size + * byte 2 -> tag + * + data + */ + + *len = buf[1] + 3; + } else { + uint8_t b_size; + + /* + * short item: + * byte 0[1..0] -> data size (=0, 1, 2, 4) + * byte 0[3..2] -> type + * byte 0[7..4] -> tag + * + data + */ + + b_size = buf[0] & 0x03; + *len = (b_size ? 1 << (b_size - 1) : 0) + 1; + } + + /* item length should be no more than input buffer length */ + return *len <= blen; +} + +static char *item2string(char *str, uint8_t *buf, uint8_t len) +{ + char *p = str; + int i; + + /* + * Since long item tags are not defined except for vendor ones, we + * just ensure that short items are printed properly (up to 5 bytes). + */ + for (i = 0; i < 6 && i < len; i++) + p += sprintf(p, " %02x", buf[i]); + + /* + * If there are some data left, just add continuation mark to indicate + * this. + */ + if (i < len) + sprintf(p, " ..."); + + return str; +} + static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct hog_device *hogdev = user_data; + struct btd_adapter *adapter = device_get_adapter(hogdev->device); uint8_t value[HOG_REPORT_MAP_MAX_SIZE]; struct uhid_event ev; uint16_t vendor_src, vendor, product, version; ssize_t vlen; + char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */ int i; if (status != 0) { @@ -365,19 +403,25 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen, } DBG("Report MAP:"); - for (i = 0; i < vlen; i++) { - switch (value[i]) { - case 0x85: - case 0x86: - case 0x87: - hogdev->has_report_id = TRUE; - } + for (i = 0; i < vlen;) { + ssize_t ilen = 0; + bool long_item = false; + + if (get_descriptor_item_info(&value[i], vlen - i, &ilen, + &long_item)) { + /* Report ID is short item with prefix 100001xx */ + if (!long_item && (value[i] & 0xfc) == 0x84) + hogdev->has_report_id = TRUE; + + DBG("\t%s", item2string(itemstr, &value[i], ilen)); - if (i % 2 == 0) { - if (i + 1 == vlen) - DBG("\t %02x", value[i]); - else - DBG("\t %02x %02x", value[i], value[i + 1]); + i += ilen; + } else { + error("Report Map parsing failed at %d", i); + + /* Just print remaining items at once and break */ + DBG("\t%s", item2string(itemstr, &value[i], vlen - i)); + break; } } @@ -391,7 +435,13 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen, /* create uHID device */ memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; - strcpy((char *) ev.u.create.name, "bluez-hog-device"); + if (device_name_known(hogdev->device)) + device_get_name(hogdev->device, (char *) ev.u.create.name, + sizeof(ev.u.create.name)); + else + strcpy((char *) ev.u.create.name, "bluez-hog-device"); + ba2str(btd_adapter_get_address(adapter), (char *) ev.u.create.phys); + ba2str(device_get_address(hogdev->device), (char *) ev.u.create.uniq); ev.u.create.vendor = vendor; ev.u.create.product = product; ev.u.create.version = version; @@ -423,7 +473,7 @@ static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen, return; } - hogdev->bcdhid = att_get_u16(&value[0]); + hogdev->bcdhid = get_le16(&value[0]); hogdev->bcountrycode = value[2]; hogdev->flags = value[3]; @@ -463,7 +513,7 @@ static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen, hogdev->id); } -static void char_discovered_cb(GSList *chars, guint8 status, gpointer user_data) +static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct hog_device *hogdev = user_data; struct gatt_primary *prim = hogdev->hog_primary; @@ -589,10 +639,10 @@ static void forward_report(struct hog_device *hogdev, if (hogdev->attrib == NULL) return; - if (report->decl->properties & ATT_CHAR_PROPER_WRITE) + if (report->decl->properties & GATT_CHR_PROP_WRITE) gatt_write_char(hogdev->attrib, report->decl->value_handle, data, size, output_written_cb, hogdev); - else if (report->decl->properties & ATT_CHAR_PROPER_WRITE_WITHOUT_RESP) + else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP) gatt_write_cmd(hogdev->attrib, report->decl->value_handle, data, size, NULL, NULL); } diff --git a/profiles/input/input.conf b/profiles/input/input.conf index abfb64f..3e1d65a 100644 --- a/profiles/input/input.conf +++ b/profiles/input/input.conf @@ -7,3 +7,7 @@ # Set idle timeout (in minutes) before the connection will # be disconnect (defaults to 0 for no timeout) #IdleTimeout=30 + +# Enable HID protocol handling in userspace input profile +# Defaults to false (HIDP handled in HIDP kernel module) +#UserspaceHID=true diff --git a/profiles/input/manager.c b/profiles/input/manager.c index 660043e..9712d2c 100644 --- a/profiles/input/manager.c +++ b/profiles/input/manager.c @@ -32,14 +32,14 @@ #include #include -#include "log.h" -#include "plugin.h" +#include "src/log.h" +#include "src/plugin.h" #include "lib/uuid.h" -#include "../src/adapter.h" -#include "../src/device.h" -#include "../src/profile.h" -#include "../src/service.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" #include "device.h" #include "server.h" @@ -97,15 +97,24 @@ static int input_init(void) config = load_config_file(CONFIGDIR "/input.conf"); if (config) { int idle_timeout; + gboolean uhid_enabled; idle_timeout = g_key_file_get_integer(config, "General", - "IdleTimeout", &err); - if (err) { - DBG("input.conf: %s", err->message); - g_error_free(err); - } - - input_set_idle_timeout(idle_timeout * 60); + "IdleTimeout", &err); + if (!err) { + DBG("input.conf: IdleTimeout=%d", idle_timeout); + input_set_idle_timeout(idle_timeout * 60); + } else + g_clear_error(&err); + + uhid_enabled = g_key_file_get_boolean(config, "General", + "UserspaceHID", &err); + if (!err) { + DBG("input.conf: UserspaceHID=%s", uhid_enabled ? + "true" : "false"); + input_enable_userspace_hid(uhid_enabled); + } else + g_clear_error(&err); } btd_profile_register(&input_profile); diff --git a/profiles/input/server.c b/profiles/input/server.c index f6f85a0..772a605 100644 --- a/profiles/input/server.c +++ b/profiles/input/server.c @@ -35,14 +35,14 @@ #include #include -#include "log.h" +#include "src/log.h" -#include "glib-helper.h" +#include "src/uuid-helper.h" #include "btio/btio.h" #include "lib/uuid.h" -#include "../src/adapter.h" -#include "../src/device.h" -#include "../src/profile.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" #include "device.h" #include "server.h" @@ -68,15 +68,10 @@ struct sixaxis_data { uint16_t psm; }; -static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data); - static void sixaxis_sdp_cb(struct btd_device *dev, int err, void *user_data) { struct sixaxis_data *data = user_data; - struct input_server *server; - GError *gerr = NULL; const bdaddr_t *src; - GSList *l; DBG("err %d (%s)", err, strerror(-err)); @@ -85,29 +80,10 @@ static void sixaxis_sdp_cb(struct btd_device *dev, int err, void *user_data) src = btd_adapter_get_address(device_get_adapter(dev)); - l = g_slist_find_custom(servers, src, server_cmp); - if (!l) - goto fail; - - server = l->data; - - err = input_device_set_channel(src, device_get_address(dev), - data->psm, data->chan); - if (err < 0) + if (input_device_set_channel(src, device_get_address(dev), data->psm, + data->chan) < 0) goto fail; - if (server->confirm) { - if (!bt_io_accept(server->confirm, connect_event_cb, server, - NULL, &gerr)) { - error("bt_io_accept: %s", gerr->message); - g_error_free(gerr); - goto fail; - } - - g_io_channel_unref(server->confirm); - server->confirm = NULL; - } - g_io_channel_unref(data->chan); g_free(data); @@ -125,10 +101,7 @@ static void sixaxis_browse_sdp(const bdaddr_t *src, const bdaddr_t *dst, struct btd_device *device; struct sixaxis_data *data; - if (psm != L2CAP_PSM_HIDP_CTRL) - return; - - device = btd_adapter_find_device(adapter_find(src), dst); + device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR); if (!device) return; @@ -136,25 +109,33 @@ static void sixaxis_browse_sdp(const bdaddr_t *src, const bdaddr_t *dst, data->chan = g_io_channel_ref(chan); data->psm = psm; - device_discover_services(device); + if (psm == L2CAP_PSM_HIDP_CTRL) + device_discover_services(device); + device_wait_for_svc_complete(device, sixaxis_sdp_cb, data); } static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst) { struct btd_device *device; + uint16_t vid, pid; - device = btd_adapter_find_device(adapter_find(src), dst); + device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR); if (!device) return false; - if (btd_device_get_vendor(device) != 0x054c) - return false; + vid = btd_device_get_vendor(device); + pid = btd_device_get_product(device); - if (btd_device_get_product(device) != 0x0268) - return false; + /* DualShock 3 */ + if (vid == 0x054c && pid == 0x0268) + return true; + + /* DualShock 4 */ + if (vid == 0x054c && pid == 0x05c4) + return true; - return true; + return false; } static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data) @@ -228,7 +209,7 @@ static void auth_callback(DBusError *derr, void *user_data) goto reject; } - if (!input_device_exists(&src, &dst) && dev_is_sixaxis(&src, &dst)) + if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst)) return; if (!bt_io_accept(server->confirm, connect_event_cb, server, diff --git a/profiles/input/suspend-dummy.c b/profiles/input/suspend-dummy.c index 75dd536..542ae25 100644 --- a/profiles/input/suspend-dummy.c +++ b/profiles/input/suspend-dummy.c @@ -37,7 +37,7 @@ #include -#include "log.h" +#include "src/log.h" #include "suspend.h" #define HOG_SUSPEND_FIFO "/tmp/hogsuspend" diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c index 0a719a2..87304c5 100644 --- a/profiles/network/bnep.c +++ b/profiles/network/bnep.c @@ -42,9 +42,12 @@ #include -#include "log.h" -#include "bnep.h" +#include "src/log.h" +#include "src/shared/util.h" #include "lib/uuid.h" +#include "btio/btio.h" + +#include "bnep.h" #define CON_SETUP_RETRIES 3 #define CON_SETUP_TO 9 @@ -67,30 +70,21 @@ struct __service_16 { uint16_t src; } __attribute__ ((packed)); -struct bnep_conn { +struct bnep { GIOChannel *io; uint16_t src; uint16_t dst; + bdaddr_t dst_addr; + char iface[16]; guint attempts; guint setup_to; - void *data; + guint watch; bnep_connect_cb conn_cb; + void *conn_data; + bnep_disconnect_cb disconn_cb; + void *disconn_data; }; -static void free_bnep_connect(struct bnep_conn *bc) -{ - if (!bc) - return; - - if (bc->io) { - g_io_channel_unref(bc->io); - bc->io = NULL; - } - - g_free(bc); - bc = NULL; -} - uint16_t bnep_service_id(const char *svc) { int i; @@ -161,7 +155,7 @@ int bnep_cleanup(void) return 0; } -int bnep_kill_connection(const bdaddr_t *dst) +static int bnep_conndel(const bdaddr_t *dst) { struct bnep_conndel_req req; @@ -177,13 +171,14 @@ int bnep_kill_connection(const bdaddr_t *dst) return 0; } -int bnep_connadd(int sk, uint16_t role, char *dev) +static int bnep_connadd(int sk, uint16_t role, char *dev) { struct bnep_connadd_req req; - memset(dev, 0, 16); memset(&req, 0, sizeof(req)); - strcpy(req.device, "bnep%d"); + strncpy(req.device, dev, 16); + req.device[15] = '\0'; + req.sock = sk; req.role = role; if (ioctl(ctl, BNEPCONNADD, &req) < 0) { @@ -197,7 +192,7 @@ int bnep_connadd(int sk, uint16_t role, char *dev) return 0; } -int bnep_if_up(const char *devname) +static int bnep_if_up(const char *devname) { struct ifreq ifr; int sk, err; @@ -210,7 +205,7 @@ int bnep_if_up(const char *devname) ifr.ifr_flags |= IFF_UP; ifr.ifr_flags |= IFF_MULTICAST; - err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr); + err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr); close(sk); @@ -222,7 +217,7 @@ int bnep_if_up(const char *devname) return 0; } -int bnep_if_down(const char *devname) +static int bnep_if_down(const char *devname) { struct ifreq ifr; int sk, err; @@ -235,7 +230,7 @@ int bnep_if_down(const char *devname) ifr.ifr_flags &= ~IFF_UP; /* Bring down the interface */ - err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr); + err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr); close(sk); @@ -247,23 +242,33 @@ int bnep_if_down(const char *devname) return 0; } +static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct bnep *session = data; + + if (session->disconn_cb) + session->disconn_cb(session->disconn_data); + + return FALSE; +} + static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - struct bnep_conn *bc = data; + struct bnep *session = data; struct bnep_control_rsp *rsp; struct timeval timeo; char pkt[BNEP_MTU]; - char iface[16]; ssize_t r; int sk; if (cond & G_IO_NVAL) - goto failed; + return FALSE; - if (bc->setup_to > 0) { - g_source_remove(bc->setup_to); - bc->setup_to = 0; + if (session->setup_to > 0) { + g_source_remove(session->setup_to); + session->setup_to = 0; } if (cond & (G_IO_HUP | G_IO_ERR)) { @@ -310,30 +315,35 @@ static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond, timeo.tv_sec = 0; setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); - sk = g_io_channel_unix_get_fd(bc->io); - if (bnep_connadd(sk, bc->src, iface)) { + sk = g_io_channel_unix_get_fd(session->io); + if (bnep_connadd(sk, session->src, session->iface)) { error("bnep conn could not be added"); goto failed; } - if (bnep_if_up(iface)) { - error("could not up %s", iface); + if (bnep_if_up(session->iface)) { + error("could not up %s", session->iface); + bnep_conndel(&session->dst_addr); goto failed; } - bc->conn_cb(chan, iface, 0, bc->data); - free_bnep_connect(bc); + session->watch = g_io_add_watch(session->io, + G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) bnep_watchdog_cb, session); + g_io_channel_unref(session->io); + session->io = NULL; + + session->conn_cb(session->iface, 0, session->conn_data); return FALSE; failed: - bc->conn_cb(NULL, NULL, -EIO, bc->data); - free_bnep_connect(bc); + session->conn_cb(NULL, -EIO, session->conn_data); return FALSE; } -static int bnep_setup_conn_req(struct bnep_conn *bc) +static int bnep_setup_conn_req(struct bnep *session) { struct bnep_setup_conn_req *req; struct __service_16 *s; @@ -346,67 +356,142 @@ static int bnep_setup_conn_req(struct bnep_conn *bc) req->ctrl = BNEP_SETUP_CONN_REQ; req->uuid_size = 2; /* 16bit UUID */ s = (void *) req->service; - s->src = htons(bc->src); - s->dst = htons(bc->dst); + s->src = htons(session->src); + s->dst = htons(session->dst); - fd = g_io_channel_unix_get_fd(bc->io); + fd = g_io_channel_unix_get_fd(session->io); if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) { error("bnep connection req send failed: %s", strerror(errno)); return -errno; } - bc->attempts++; + session->attempts++; return 0; } static gboolean bnep_conn_req_to(gpointer user_data) { - struct bnep_conn *bc = user_data; + struct bnep *session = user_data; - if (bc->attempts == CON_SETUP_RETRIES) { + if (session->attempts == CON_SETUP_RETRIES) { error("Too many bnep connection attempts"); } else { error("bnep connection setup TO, retrying..."); - if (bnep_setup_conn_req(bc) == 0) + if (bnep_setup_conn_req(session) == 0) return TRUE; } - bc->conn_cb(NULL, NULL, -ETIMEDOUT, bc->data); - free_bnep_connect(bc); + session->conn_cb(NULL, -ETIMEDOUT, session->conn_data); return FALSE; } -int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb, - void *data) +struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role, + char *iface) { - struct bnep_conn *bc; + struct bnep *session; + int dup_fd; + + dup_fd = dup(sk); + if (dup_fd < 0) + return NULL; + + session = g_new0(struct bnep, 1); + session->io = g_io_channel_unix_new(dup_fd); + session->src = local_role; + session->dst = remote_role; + strncpy(session->iface, iface, 16); + session->iface[15] = '\0'; + + g_io_channel_set_close_on_unref(session->io, TRUE); + session->watch = g_io_add_watch(session->io, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) bnep_setup_cb, session); + + return session; +} + +void bnep_free(struct bnep *session) +{ + if (!session) + return; + + if (session->io) { + g_io_channel_shutdown(session->io, FALSE, NULL); + g_io_channel_unref(session->io); + session->io = NULL; + } + + if (session->watch > 0) { + g_source_remove(session->watch); + session->watch = 0; + } + + g_free(session); +} + +int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb, void *data) +{ + GError *gerr = NULL; int err; - if (!conn_cb) + if (!session || !conn_cb) return -EINVAL; - bc = g_new0(struct bnep_conn, 1); - bc->io = g_io_channel_unix_new(sk); - bc->attempts = 0; - bc->src = src; - bc->dst = dst; - bc->conn_cb = conn_cb; - bc->data = data; + session->attempts = 0; + session->conn_cb = conn_cb; + session->conn_data = data; + + bt_io_get(session->io, &gerr, BT_IO_OPT_DEST_BDADDR, &session->dst_addr, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + return -EINVAL; + } - err = bnep_setup_conn_req(bc); + err = bnep_setup_conn_req(session); if (err < 0) return err; - bc->setup_to = g_timeout_add_seconds(CON_SETUP_TO, - bnep_conn_req_to, bc); - g_io_add_watch(bc->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - bnep_setup_cb, bc); + session->setup_to = g_timeout_add_seconds(CON_SETUP_TO, + bnep_conn_req_to, session); return 0; } -int bnep_add_to_bridge(const char *devname, const char *bridge) +void bnep_disconnect(struct bnep *session) +{ + if (!session) + return; + + if (session->watch > 0) { + g_source_remove(session->watch); + session->watch = 0; + } + + if (session->io) { + g_io_channel_unref(session->io); + session->io = NULL; + } + + bnep_if_down(session->iface); + bnep_conndel(&session->dst_addr); +} + +void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb, + void *data) +{ + if (!session || !disconn_cb) + return; + + if (!session->disconn_cb && !session->disconn_data) { + session->disconn_cb = disconn_cb; + session->disconn_data = data; + } +} + +static int bnep_add_to_bridge(const char *devname, const char *bridge) { int ifindex; struct ifreq ifr; @@ -437,15 +522,17 @@ int bnep_add_to_bridge(const char *devname, const char *bridge) return 0; } -int bnep_del_from_bridge(const char *devname, const char *bridge) +static int bnep_del_from_bridge(const char *devname, const char *bridge) { - int ifindex = if_nametoindex(devname); + int ifindex; struct ifreq ifr; int sk, err; if (!devname || !bridge) return -EINVAL; + ifindex = if_nametoindex(devname); + sk = socket(AF_INET, SOCK_STREAM, 0); if (sk < 0) return -1; @@ -465,3 +552,119 @@ int bnep_del_from_bridge(const char *devname, const char *bridge) return 0; } + +int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface, + const bdaddr_t *addr) +{ + if (!bridge || !bridge || !iface || !addr) + return -EINVAL; + + if (bnep_connadd(sk, dst, iface) < 0) { + error("Can't add connection to the bridge %s: %s(%d)", + bridge, strerror(errno), errno); + return -errno; + } + + if (bnep_add_to_bridge(iface, bridge) < 0) { + error("Can't add %s to the bridge %s: %s(%d)", + iface, bridge, strerror(errno), errno); + bnep_conndel(addr); + return -errno; + } + + if (bnep_if_up(iface) < 0) { + error("Can't up the interface %s: %s(%d)", + iface, strerror(errno), errno); + return -errno; + } + + return 0; +} + +void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr) +{ + if (!bridge || !iface || !addr) + return; + + bnep_del_from_bridge(iface, bridge); + bnep_if_down(iface); + bnep_conndel(addr); +} + +ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp) +{ + struct bnep_control_rsp rsp; + + rsp.type = type; + rsp.ctrl = ctrl; + rsp.resp = htons(resp); + + return send(sk, &rsp, sizeof(rsp), 0); +} + +uint16_t bnep_setup_chk(uint16_t dst, uint16_t src) +{ + /* Allowed PAN Profile scenarios */ + switch (dst) { + case BNEP_SVC_NAP: + case BNEP_SVC_GN: + if (src == BNEP_SVC_PANU) + return 0; + return BNEP_CONN_INVALID_SRC; + case BNEP_SVC_PANU: + if (src == BNEP_SVC_PANU || src == BNEP_SVC_GN || + src == BNEP_SVC_NAP) + return 0; + + return BNEP_CONN_INVALID_SRC; + } + + return BNEP_CONN_INVALID_DST; +} + +uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst, + uint16_t *src) +{ + const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; + uint8_t *dest, *source; + uint32_t val; + + dest = req->service; + source = req->service + req->uuid_size; + + switch (req->uuid_size) { + case 2: /* UUID16 */ + *dst = get_be16(dest); + *src = get_be16(source); + break; + case 16: /* UUID128 */ + /* Check that the bytes in the UUID, except the service ID + * itself, are correct. The service ID is checked in + * bnep_setup_chk(). */ + if (memcmp(&dest[4], bt_base, sizeof(bt_base)) != 0) + return BNEP_CONN_INVALID_DST; + if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0) + return BNEP_CONN_INVALID_SRC; + + /* Intentional no-break */ + + case 4: /* UUID32 */ + val = get_be32(dest); + if (val > 0xffff) + return BNEP_CONN_INVALID_DST; + + *dst = val; + + val = get_be32(source); + if (val > 0xffff) + return BNEP_CONN_INVALID_SRC; + + *src = val; + break; + default: + return BNEP_CONN_INVALID_SVC; + } + + return BNEP_SUCCESS; +} diff --git a/profiles/network/bnep.h b/profiles/network/bnep.h index 9043e46..bc43d4f 100644 --- a/profiles/network/bnep.h +++ b/profiles/network/bnep.h @@ -21,6 +21,8 @@ * */ +struct bnep; + int bnep_init(void); int bnep_cleanup(void); @@ -28,15 +30,22 @@ 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(const bdaddr_t *dst); - -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); -int bnep_del_from_bridge(const char *devname, const char *bridge); +struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role, + char *iface); +void bnep_free(struct bnep *session); -typedef void (*bnep_connect_cb) (GIOChannel *chan, char *iface, int err, - void *data); -int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb, +typedef void (*bnep_connect_cb) (char *iface, int err, void *data); +int bnep_connect(struct bnep *b, bnep_connect_cb conn_cb, void *data); +typedef void (*bnep_disconnect_cb) (void *data); +void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb, void *data); +void bnep_disconnect(struct bnep *session); + +int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface, + const bdaddr_t *addr); +void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr); + +ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp); +uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role); +uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst, + uint16_t *src); diff --git a/profiles/network/connection.c b/profiles/network/connection.c index 9aff319..cc73989 100644 --- a/profiles/network/connection.c +++ b/profiles/network/connection.c @@ -37,20 +37,21 @@ #include #include -#include -#include "log.h" -#include "dbus-common.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" +#include "btio/btio.h" +#include "src/log.h" +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/error.h" -#include "error.h" #include "bnep.h" #include "connection.h" #define NETWORK_PEER_INTERFACE "org.bluez.Network1" +#define BNEP_INTERFACE "bnep%d" typedef enum { CONNECTED, @@ -72,6 +73,7 @@ struct network_conn { guint dc_id; struct network_peer *peer; DBusMessage *connect; + struct bnep *session; }; static GSList *peers = NULL; @@ -106,8 +108,7 @@ static struct network_conn *find_connection_by_state(GSList *list, return NULL; } -static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) +static void bnep_disconn_cb(gpointer data) { struct network_conn *nc = data; DBusConnection *conn = btd_get_dbus_connection(); @@ -126,12 +127,13 @@ static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond, 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"); + strncpy(nc->dev, BNEP_INTERFACE, 16); + nc->dev[15] = '\0'; - return FALSE; + bnep_free(nc->session); + nc->session = NULL; } static void local_connect_cb(struct network_conn *nc, int err) @@ -155,12 +157,21 @@ static void local_connect_cb(struct network_conn *nc, int err) static void cancel_connection(struct network_conn *nc, int err) { btd_service_connecting_complete(nc->service, err); + if (nc->connect) local_connect_cb(nc, err); - g_io_channel_shutdown(nc->io, TRUE, NULL); - g_io_channel_unref(nc->io); - nc->io = NULL; + if (nc->io) { + g_io_channel_shutdown(nc->io, FALSE, NULL); + g_io_channel_unref(nc->io); + nc->io = NULL; + } + + if (nc->state == CONNECTED) + bnep_disconnect(nc->session); + + bnep_free(nc->session); + nc->session = NULL; nc->state = DISCONNECTED; } @@ -169,11 +180,7 @@ 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(device_get_address(nc->peer->device)); - } else if (nc->io) - cancel_connection(nc, -EIO); + cancel_connection(nc, -EIO); } static void disconnect_cb(struct btd_device *device, gboolean removal, @@ -186,7 +193,7 @@ static void disconnect_cb(struct btd_device *device, gboolean removal, connection_destroy(NULL, user_data); } -static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data) +static void bnep_conn_cb(char *iface, int err, void *data) { struct network_conn *nc = data; const char *path; @@ -220,10 +227,6 @@ static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data) nc->state = CONNECTED; nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb, nc, NULL); - g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - bnep_watchdog_cb, nc); - g_io_channel_unref(nc->io); - nc->io = NULL; return; @@ -242,12 +245,23 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer data) } sk = g_io_channel_unix_get_fd(nc->io); - perr = bnep_connect(sk, BNEP_SVC_PANU, nc->id, bnep_conn_cb, nc); + nc->session = bnep_new(sk, BNEP_SVC_PANU, nc->id, BNEP_INTERFACE); + if (!nc->session) + goto failed; + + perr = bnep_connect(nc->session, bnep_conn_cb, nc); if (perr < 0) { error("bnep connect(): %s (%d)", strerror(-perr), -perr); goto failed; } + bnep_set_disconnect(nc->session, bnep_disconn_cb, nc); + + if (nc->io) { + g_io_channel_unref(nc->io); + nc->io = NULL; + } + return; failed: diff --git a/profiles/network/manager.c b/profiles/network/manager.c index 8ac2dec..0fe98a0 100644 --- a/profiles/network/manager.c +++ b/profiles/network/manager.c @@ -35,14 +35,15 @@ #include #include -#include "log.h" -#include "plugin.h" +#include "src/log.h" +#include "src/plugin.h" #include "lib/uuid.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" + #include "bnep.h" #include "connection.h" #include "server.h" @@ -200,8 +201,6 @@ static int network_init(void) static void network_exit(void) { - server_exit(); - btd_profile_unregister(&panu_profile); btd_profile_unregister(&gn_profile); btd_profile_unregister(&nap_profile); diff --git a/profiles/network/server.c b/profiles/network/server.c index b3aab11..3fb031f 100644 --- a/profiles/network/server.c +++ b/profiles/network/server.c @@ -38,20 +38,20 @@ #include #include -#include +#include "btio/btio.h" #include "lib/uuid.h" -#include "../src/dbus-common.h" -#include "../src/adapter.h" - -#include "log.h" -#include "error.h" -#include "sdpd.h" +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/log.h" +#include "src/error.h" +#include "src/sdpd.h" #include "bnep.h" #include "server.h" #define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1" +#define BNEP_INTERFACE "bnep%d" #define SETUP_TIMEOUT 1 /* Pending Authorization */ @@ -251,112 +251,6 @@ static sdp_record_t *server_record_new(const char *name, uint16_t id) 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; - - 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); - - strncpy(session->dev, devname, sizeof(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) -{ - const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, - 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; - uint8_t *dest, *source; - uint32_t val; - - dest = req->service; - source = req->service + req->uuid_size; - - switch (req->uuid_size) { - case 2: /* UUID16 */ - *dst_role = bt_get_be16(dest); - *src_role = bt_get_be16(source); - break; - case 16: /* UUID128 */ - /* Check that the bytes in the UUID, except the service ID - * itself, are correct. The service ID is checked in - * bnep_setup_chk(). */ - if (memcmp(&dest[4], bt_base, sizeof(bt_base)) != 0) - return BNEP_CONN_INVALID_DST; - if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0) - return BNEP_CONN_INVALID_SRC; - - /* Intentional no-break */ - - case 4: /* UUID32 */ - val = bt_get_be32(dest); - if (val > 0xffff) - return BNEP_CONN_INVALID_DST; - *dst_role = val; - - val = bt_get_be32(source); - if (val > 0xffff) - return BNEP_CONN_INVALID_SRC; - *src_role = val; - break; - default: - return BNEP_CONN_INVALID_SVC; - } - - return BNEP_SUCCESS; -} - static void session_free(void *data) { struct network_session *session = data; @@ -454,7 +348,11 @@ static gboolean bnep_setup(GIOChannel *chan, goto reply; } - if (server_connadd(ns, na->setup, dst_role) < 0) + strncpy(na->setup->dev, BNEP_INTERFACE, 16); + na->setup->dev[15] = '\0'; + + if (bnep_server_add(sk, dst_role, ns->bridge, na->setup->dev, + &na->setup->dst) < 0) goto reply; na->setup = NULL; @@ -462,7 +360,7 @@ static gboolean bnep_setup(GIOChannel *chan, rsp = BNEP_SUCCESS; reply: - send_bnep_ctrl_rsp(sk, rsp); + bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp); return FALSE; } @@ -570,10 +468,6 @@ int server_init(gboolean secure) return 0; } -void server_exit(void) -{ -} - static uint32_t register_server_record(struct network_server *ns) { sdp_record_t *record; @@ -605,10 +499,7 @@ static void server_remove_sessions(struct network_server *ns) if (*session->dev == '\0') continue; - bnep_del_from_bridge(session->dev, ns->bridge); - bnep_if_down(session->dev); - - bnep_kill_connection(&session->dst); + bnep_server_delete(ns->bridge, session->dev, &session->dst); } g_slist_free_full(ns->sessions, session_free); diff --git a/profiles/network/server.h b/profiles/network/server.h index 2edd342..a76e6f7 100644 --- a/profiles/network/server.h +++ b/profiles/network/server.h @@ -22,6 +22,5 @@ */ int server_init(gboolean secure); -void server_exit(void); int server_register(struct btd_adapter *adapter, uint16_t id); int server_unregister(struct btd_adapter *adapter, uint16_t id); diff --git a/profiles/proximity/immalert.c b/profiles/proximity/immalert.c index 2f8d4e7..3d50b8d 100644 --- a/profiles/proximity/immalert.c +++ b/profiles/proximity/immalert.c @@ -27,23 +27,24 @@ #include #include -#include #include #include #include "lib/uuid.h" -#include "log.h" +#include "src/log.h" +#include "src/adapter.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "attrib/att-database.h" #include "attrib/gatt-service.h" -#include "attrib-server.h" -#include "device.h" -#include "profile.h" -#include "attio.h" -#include "dbus-common.h" +#include "src/attrib-server.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/attio.h" +#include "src/dbus-common.h" + #include "reporter.h" #include "immalert.h" @@ -251,7 +252,7 @@ void imm_alert_register(struct btd_adapter *adapter) /* Alert level characteristic */ GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID, GATT_OPT_CHR_PROPS, - ATT_CHAR_PROPER_WRITE_WITHOUT_RESP, + GATT_CHR_PROP_WRITE_WITHOUT_RESP, GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, imm_alert_alert_lvl_write, imadapter, GATT_OPT_INVALID); diff --git a/profiles/proximity/linkloss.c b/profiles/proximity/linkloss.c index a7ed96c..476803a 100644 --- a/profiles/proximity/linkloss.c +++ b/profiles/proximity/linkloss.c @@ -27,23 +27,25 @@ #include #include -#include #include #include #include "lib/uuid.h" -#include "log.h" +#include "src/log.h" +#include "src/adapter.h" +#include "src/device.h" #include "attrib/att-database.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "attrib/gatt-service.h" -#include "attrib-server.h" -#include "device.h" -#include "profile.h" -#include "attio.h" -#include "dbus-common.h" +#include "src/attrib-server.h" +#include "src/service.h" +#include "src/profile.h" +#include "src/attio.h" +#include "src/dbus-common.h" + #include "reporter.h" #include "linkloss.h" @@ -292,7 +294,7 @@ void link_loss_register(struct btd_adapter *adapter) /* Alert level characteristic */ GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID, GATT_OPT_CHR_PROPS, - ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_WRITE, + GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, link_loss_alert_lvl_read, lladapter, GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, diff --git a/profiles/proximity/main.c b/profiles/proximity/main.c index 46468d2..8a350d4 100644 --- a/profiles/proximity/main.c +++ b/profiles/proximity/main.c @@ -31,10 +31,9 @@ #include #include -#include "log.h" -#include "plugin.h" +#include "src/log.h" +#include "src/plugin.h" #include "manager.h" -#include "hcid.h" static GKeyFile *config = NULL; diff --git a/profiles/proximity/manager.c b/profiles/proximity/manager.c index 7dab23f..3f0f63c 100644 --- a/profiles/proximity/manager.c +++ b/profiles/proximity/manager.c @@ -32,10 +32,10 @@ #include #include "lib/uuid.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" #include "attrib/att.h" #include "attrib/gattrib.h" #include "attrib/gatt.h" diff --git a/profiles/proximity/monitor.c b/profiles/proximity/monitor.c index eaa5b0d..f2e0739 100644 --- a/profiles/proximity/monitor.c +++ b/profiles/proximity/monitor.c @@ -39,17 +39,18 @@ #include #include "lib/uuid.h" -#include "dbus-common.h" -#include "adapter.h" -#include "device.h" -#include "error.h" -#include "log.h" +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/error.h" +#include "src/log.h" #include "attrib/att.h" #include "attrib/gattrib.h" #include "attrib/gatt.h" -#include "attio.h" +#include "src/attio.h" +#include "src/textfile.h" + #include "monitor.h" -#include "textfile.h" #define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor1" @@ -190,8 +191,8 @@ static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen, PROXIMITY_INTERFACE, "LinkLossAlertLevel"); } -static void char_discovered_cb(GSList *characteristics, guint8 status, - gpointer user_data) +static void char_discovered_cb(uint8_t status, GSList *characteristics, + void *user_data) { struct monitor *monitor = user_data; struct gatt_char *chr; @@ -259,8 +260,8 @@ static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen, DBG("Tx Power Level: %02x", (int8_t) value[0]); } -static void tx_power_handle_cb(GSList *characteristics, guint8 status, - gpointer user_data) +static void tx_power_handle_cb(uint8_t status, GSList *characteristics, + void *user_data) { struct monitor *monitor = user_data; struct gatt_char *chr; @@ -346,8 +347,8 @@ static void write_immediate_alert(struct monitor *monitor) immediate_written, monitor); } -static void immediate_handle_cb(GSList *characteristics, guint8 status, - gpointer user_data) +static void immediate_handle_cb(uint8_t status, GSList *characteristics, + void *user_data) { struct monitor *monitor = user_data; struct gatt_char *chr; diff --git a/profiles/proximity/reporter.c b/profiles/proximity/reporter.c index dbb593a..fb91bc1 100644 --- a/profiles/proximity/reporter.c +++ b/profiles/proximity/reporter.c @@ -30,25 +30,25 @@ #include #include -#include #include #include -#include "log.h" +#include "src/log.h" #include "lib/uuid.h" -#include "dbus-common.h" -#include "error.h" -#include "device.h" -#include "profile.h" -#include "service.h" -#include "hcid.h" +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/shared/util.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "attrib/att-database.h" -#include "attrib-server.h" +#include "src/attrib-server.h" + #include "reporter.h" #include "linkloss.h" #include "immalert.h" @@ -116,19 +116,19 @@ static void register_tx_power(struct btd_adapter *adapter) /* Primary service definition */ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); - att_put_u16(TX_POWER_SVC_UUID, &atval[0]); + put_le16(TX_POWER_SVC_UUID, &atval[0]); attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* Power level characteristic */ bt_uuid16_create(&uuid, GATT_CHARAC_UUID); - atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY; - att_put_u16(h + 1, &atval[1]); - att_put_u16(POWER_LEVEL_CHR_UUID, &atval[3]); + atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_NOTIFY; + put_le16(h + 1, &atval[1]); + put_le16(POWER_LEVEL_CHR_UUID, &atval[3]); attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); /* Power level value */ bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID); - att_put_u8(0x00, &atval[0]); + atval[0] = 0x00; attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1); /* Client characteristic configuration */ diff --git a/profiles/sap/main.c b/profiles/sap/main.c index 8cc9533..ad55cd2 100644 --- a/profiles/sap/main.c +++ b/profiles/sap/main.c @@ -24,7 +24,7 @@ #include #include -#include "plugin.h" +#include "src/plugin.h" #include "manager.h" static int sap_init(void) diff --git a/profiles/sap/manager.c b/profiles/sap/manager.c index bfb81a5..5c2a0f1 100644 --- a/profiles/sap/manager.c +++ b/profiles/sap/manager.c @@ -24,11 +24,11 @@ #include -#include "log.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" +#include "src/log.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" #include "manager.h" #include "server.h" diff --git a/profiles/sap/sap-dummy.c b/profiles/sap/sap-dummy.c index 47dedf7..854105e 100644 --- a/profiles/sap/sap-dummy.c +++ b/profiles/sap/sap-dummy.c @@ -30,9 +30,9 @@ #include #include -#include "dbus-common.h" -#include "error.h" -#include "log.h" +#include "src/dbus-common.h" +#include "src/error.h" +#include "src/log.h" #include "sap.h" #define SAP_DUMMY_IFACE "org.bluez.SimAccessTest1" diff --git a/profiles/sap/sap-u8500.c b/profiles/sap/sap-u8500.c index 39169a0..d043029 100644 --- a/profiles/sap/sap-u8500.c +++ b/profiles/sap/sap-u8500.c @@ -36,7 +36,7 @@ #include #include -#include "log.h" +#include "src/log.h" #include "sap.h" #define STE_SIMD_SOCK "/dev/socket/catd_a" diff --git a/profiles/sap/server.c b/profiles/sap/server.c index 119862d..20c6cab 100644 --- a/profiles/sap/server.c +++ b/profiles/sap/server.c @@ -35,11 +35,12 @@ #include "lib/uuid.h" #include "btio/btio.h" -#include "adapter.h" -#include "sdpd.h" -#include "log.h" -#include "error.h" -#include "dbus-common.h" +#include "src/adapter.h" +#include "src/sdpd.h" +#include "src/log.h" +#include "src/error.h" +#include "src/dbus-common.h" +#include "src/shared/util.h" #include "sap.h" #include "server.h" @@ -312,7 +313,7 @@ static void connect_req(struct sap_server *server, stop_guard_timer(server); - maxmsgsize = bt_get_be16(¶m->val); + maxmsgsize = get_be16(¶m->val); DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize); @@ -667,7 +668,7 @@ int sap_connect_rsp(void *sap_device, uint8_t status) param = (struct sap_parameter *) &buf[size]; param->id = SAP_PARAM_ID_MAX_MSG_SIZE; param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN); - bt_put_be16(SAP_BUF_SIZE, ¶m->val); + put_be16(SAP_BUF_SIZE, ¶m->val); size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN); /* fall */ diff --git a/profiles/scanparam/scan.c b/profiles/scanparam/scan.c index 0236af6..25c906e 100644 --- a/profiles/scanparam/scan.c +++ b/profiles/scanparam/scan.c @@ -30,16 +30,17 @@ #include #include "lib/uuid.h" -#include "log.h" -#include "plugin.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" +#include "src/log.h" +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/shared/util.h" #include "attrib/att.h" #include "attrib/gattrib.h" #include "attrib/gatt.h" -#include "attio.h" +#include "src/attio.h" #define SCAN_PARAMETERS_UUID "00001813-0000-1000-8000-00805f9b34fb" @@ -66,8 +67,8 @@ static void write_scan_params(GAttrib *attrib, uint16_t handle) { uint8_t value[4]; - att_put_u16(SCAN_INTERVAL, &value[0]); - att_put_u16(SCAN_WINDOW, &value[2]); + put_le16(SCAN_INTERVAL, &value[0]); + put_le16(SCAN_WINDOW, &value[2]); gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL); } @@ -101,43 +102,33 @@ static void ccc_written_cb(guint8 status, const guint8 *pdu, refresh_value_cb, scan, NULL); } -static void discover_descriptor_cb(guint8 status, const guint8 *pdu, - guint16 len, gpointer user_data) +static void discover_descriptor_cb(uint8_t status, GSList *descs, + void *user_data) { struct scan *scan = user_data; - struct att_data_list *list; - uint8_t *ptr; - uint16_t uuid16, handle; + struct gatt_desc *desc; uint8_t value[2]; - uint8_t format; - list = dec_find_info_resp(pdu, len, &format); - if (list == NULL) + if (status != 0) { + error("Discover descriptors failed: %s", att_ecode2str(status)); return; + } - if (format != ATT_FIND_INFO_RESP_FMT_16BIT) - goto done; - - ptr = list->data[0]; - handle = att_get_u16(ptr); - uuid16 = att_get_u16(&ptr[2]); - - if (uuid16 != GATT_CLIENT_CHARAC_CFG_UUID) - goto done; + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; - att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); - gatt_write_char(scan->attrib, handle, value, sizeof(value), + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + gatt_write_char(scan->attrib, desc->handle, value, sizeof(value), ccc_written_cb, user_data); -done: - att_data_list_free(list); } -static void refresh_discovered_cb(GSList *chars, guint8 status, - gpointer user_data) +static void refresh_discovered_cb(uint8_t status, GSList *chars, + void *user_data) { struct scan *scan = user_data; struct gatt_char *chr; uint16_t start, end; + bt_uuid_t uuid; if (status) { error("Scan Refresh %s", att_ecode2str(status)); @@ -161,12 +152,13 @@ static void refresh_discovered_cb(GSList *chars, guint8 status, scan->refresh_handle = chr->value_handle; - gatt_discover_char_desc(scan->attrib, start, end, + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + gatt_discover_desc(scan->attrib, start, end, &uuid, discover_descriptor_cb, user_data); } -static void iwin_discovered_cb(GSList *chars, guint8 status, - gpointer user_data) +static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct scan *scan = user_data; struct gatt_char *chr; diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c index bd8413d..e3ab923 100644 --- a/profiles/thermometer/thermometer.c +++ b/profiles/thermometer/thermometer.c @@ -30,16 +30,17 @@ #include #include "lib/uuid.h" -#include "plugin.h" -#include "dbus-common.h" -#include "adapter.h" -#include "device.h" -#include "profile.h" -#include "service.h" -#include "error.h" -#include "log.h" +#include "src/plugin.h" +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/shared/util.h" +#include "src/error.h" +#include "src/log.h" #include "attrib/gattrib.h" -#include "attio.h" +#include "src/attio.h" #include "attrib/att.h" #include "attrib/gatt.h" @@ -361,7 +362,7 @@ static void proc_measurement(struct thermometer *t, const uint8_t *pdu, return; } - raw = att_get_u32(pdu); + raw = get_le32(pdu); m.mant = raw & 0x00FFFFFF; m.exp = ((int32_t) raw) >> 24; @@ -382,7 +383,7 @@ static void proc_measurement(struct thermometer *t, const uint8_t *pdu, return; } - ts.tm_year = att_get_u16(pdu) - 1900; + ts.tm_year = get_le16(pdu) - 1900; ts.tm_mon = *(pdu + 2) - 1; ts.tm_mday = *(pdu + 3); ts.tm_hour = *(pdu + 4); @@ -466,7 +467,7 @@ static void interval_ind_handler(const uint8_t *pdu, uint16_t len, return; } - interval = att_get_u16(pdu + 3); + interval = get_le16(pdu + 3); change_property(t, "Interval", &interval); opdu = g_attrib_get_buffer(t->attrib, &plen); @@ -501,8 +502,8 @@ static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len, return; } - min = att_get_u16(&value[0]); - max = att_get_u16(&value[2]); + min = get_le16(&value[0]); + max = get_le16(&value[2]); if (min == 0 || min > max) { DBG("Invalid range"); @@ -568,18 +569,14 @@ static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid, return; } - att_put_u16(val, atval); + put_le16(val, atval); gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval), write_ccc_cb, msg); } -static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len, - gpointer user_data) +static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data) { struct characteristic *ch = user_data; - struct att_data_list *list = NULL; - uint8_t format; - int i; if (status != 0) { error("Discover all characteristic descriptors failed [%s]: %s", @@ -587,27 +584,13 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len, goto done; } - list = dec_find_info_resp(pdu, len, &format); - if (list == NULL) - goto done; - - if (format != ATT_FIND_INFO_RESP_FMT_16BIT) - goto done; + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; - for (i = 0; i < list->num; i++) { - uint8_t *value; - uint16_t handle, uuid; - - value = list->data[i]; - handle = att_get_u16(value); - uuid = att_get_u16(value + 2); - - process_thermometer_desc(ch, uuid, handle); + process_thermometer_desc(ch, desc->uuid16, desc->handle); } done: - if (list != NULL) - att_data_list_free(list); g_free(ch); } @@ -633,7 +616,7 @@ static void discover_desc(struct thermometer *t, struct gatt_char *c, ch->t = t; memcpy(ch->uuid, c->uuid, sizeof(c->uuid)); - gatt_discover_char_desc(t->attrib, start, end, discover_desc_cb, ch); + gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch); } static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len, @@ -689,7 +672,7 @@ static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len, return; } - interval = att_get_u16(&value[0]); + interval = get_le16(&value[0]); change_property(t, "Interval", &interval); } @@ -720,12 +703,12 @@ static void process_thermometer_char(struct thermometer *t, gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t); - if (c->properties & ATT_CHAR_PROPER_WRITE) { + if (c->properties & GATT_CHR_PROP_WRITE) { t->interval_val_handle = c->value_handle; need_desc = true; } - if (c->properties & ATT_CHAR_PROPER_INDICATE) { + if (c->properties & GATT_CHR_PROP_INDICATE) { t->attio_interval_id = g_attrib_register(t->attrib, ATT_OP_HANDLE_IND, c->value_handle, interval_ind_handler, t, NULL); @@ -737,8 +720,8 @@ static void process_thermometer_char(struct thermometer *t, } } -static void configure_thermometer_cb(GSList *characteristics, guint8 status, - gpointer user_data) +static void configure_thermometer_cb(uint8_t status, GSList *characteristics, + void *user_data) { struct thermometer *t = user_data; GSList *l; @@ -789,7 +772,7 @@ static void enable_final_measurement(gpointer data, gpointer user_data) if (t->attrib == NULL || !handle) return; - att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value); + put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value); msg = g_strdup("Enable Temperature Measurement indications"); gatt_write_char(t->attrib, handle, value, sizeof(value), @@ -806,7 +789,7 @@ static void enable_intermediate_measurement(gpointer data, gpointer user_data) if (t->attrib == NULL || !handle) return; - att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); msg = g_strdup("Enable Intermediate Temperature notifications"); gatt_write_char(t->attrib, handle, value, sizeof(value), @@ -823,7 +806,7 @@ static void disable_final_measurement(gpointer data, gpointer user_data) if (t->attrib == NULL || !handle) return; - att_put_u16(0x0000, value); + put_le16(0x0000, value); msg = g_strdup("Disable Temperature Measurement indications"); gatt_write_char(t->attrib, handle, value, sizeof(value), @@ -840,7 +823,7 @@ static void disable_intermediate_measurement(gpointer data, gpointer user_data) if (t->attrib == NULL || !handle) return; - att_put_u16(0x0000, value); + put_le16(0x0000, value); msg = g_strdup("Disable Intermediate Temperature notifications"); gatt_write_char(t->attrib, handle, value, sizeof(value), @@ -1070,7 +1053,7 @@ static void property_set_interval(const GDBusPropertyTable *property, return; } - att_put_u16(val, &atval[0]); + put_le16(val, &atval[0]); interval_data = g_new0(struct tmp_interval_data, 1); interval_data->thermometer = t; diff --git a/profiles/time/server.c b/profiles/time/server.c index 178d4d2..1716a5e 100644 --- a/profiles/time/server.c +++ b/profiles/time/server.c @@ -31,19 +31,20 @@ #include #include -#include -#include -#include -#include +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/plugin.h" #include "lib/uuid.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "attrib/att-database.h" -#include "attrib-server.h" +#include "src/shared/util.h" +#include "src/attrib-server.h" #include "attrib/gatt-service.h" -#include "log.h" +#include "src/log.h" #define CURRENT_TIME_SVC_UUID 0x1805 #define REF_TIME_UPDATE_SVC_UUID 0x1806 @@ -90,7 +91,7 @@ static int encode_current_time(uint8_t value[10]) return -EINVAL; } - att_put_u16(1900 + tm.tm_year, &value[0]); /* Year */ + put_le16(1900 + tm.tm_year, &value[0]); /* Year */ value[2] = tm.tm_mon + 1; /* Month */ value[3] = tm.tm_mday; /* Day */ value[4] = tm.tm_hour; /* Hours */ @@ -153,14 +154,14 @@ static gboolean register_current_time_service(struct btd_adapter *adapter) return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid, /* CT Time characteristic */ GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ | - ATT_CHAR_PROPER_NOTIFY, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ | + GATT_CHR_PROP_NOTIFY, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, current_time_read, adapter, /* Local Time Information characteristic */ GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, local_time_info_read, adapter, @@ -217,13 +218,13 @@ static gboolean register_ref_time_update_service(struct btd_adapter *adapter) /* Time Update control point */ GATT_OPT_CHR_UUID16, TIME_UPDATE_CTRL_CHR_UUID, GATT_OPT_CHR_PROPS, - ATT_CHAR_PROPER_WRITE_WITHOUT_RESP, + GATT_CHR_PROP_WRITE_WITHOUT_RESP, GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, time_update_control, adapter, /* Time Update status */ GATT_OPT_CHR_UUID16, TIME_UPDATE_STAT_CHR_UUID, - GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ, GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, time_update_status, adapter, diff --git a/src/adapter.c b/src/adapter.c index 9480103..f5f8c8c 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -27,6 +27,7 @@ #endif #include +#include #include #include #include @@ -51,6 +52,7 @@ #include "lib/uuid.h" #include "lib/mgmt.h" #include "src/shared/mgmt.h" +#include "src/shared/util.h" #include "hcid.h" #include "sdpd.h" @@ -59,7 +61,7 @@ #include "profile.h" #include "dbus-common.h" #include "error.h" -#include "glib-helper.h" +#include "uuid-helper.h" #include "agent.h" #include "storage.h" #include "attrib/gattrib.h" @@ -70,15 +72,6 @@ #define ADAPTER_INTERFACE "org.bluez.Adapter1" -/* 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 MODE_OFF 0x00 #define MODE_CONNECTABLE 0x01 #define MODE_DISCOVERABLE 0x02 @@ -104,6 +97,33 @@ static uint8_t mgmt_revision = 0; static GSList *adapter_drivers = NULL; +static GSList *disconnect_list = NULL; +static GSList *conn_fail_list = NULL; + +struct link_key_info { + bdaddr_t bdaddr; + unsigned char key[16]; + uint8_t type; + uint8_t pin_len; +}; + +struct smp_ltk_info { + bdaddr_t bdaddr; + uint8_t bdaddr_type; + uint8_t authenticated; + bool master; + uint8_t enc_size; + uint16_t ediv; + uint64_t rand; + uint8_t val[16]; +}; + +struct irk_info { + bdaddr_t bdaddr; + uint8_t bdaddr_type; + uint8_t val[16]; +}; + struct watch_client { struct btd_adapter *adapter; char *owner; @@ -112,6 +132,7 @@ struct watch_client { struct service_auth { guint id; + unsigned int svc_id; service_auth_cb cb; void *user_data; const char *uuid; @@ -405,6 +426,7 @@ static void store_adapter_info(struct btd_adapter *adapter) static void trigger_pairable_timeout(struct btd_adapter *adapter); static void adapter_start(struct btd_adapter *adapter); static void adapter_stop(struct btd_adapter *adapter); +static void trigger_passive_scanning(struct btd_adapter *adapter); static void settings_changed(struct btd_adapter *adapter, uint32_t settings) { @@ -434,6 +456,12 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings) } } + if (changed_mask & MGMT_SETTING_LE) { + if ((adapter->current_settings & MGMT_SETTING_POWERED) && + (adapter->current_settings & MGMT_SETTING_LE)) + trigger_passive_scanning(adapter); + } + if (changed_mask & MGMT_SETTING_CONNECTABLE) g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Connectable"); @@ -464,7 +492,7 @@ static void new_settings_callback(uint16_t index, uint16_t length, return; } - settings = bt_get_le32(param); + settings = get_le32(param); if (settings == adapter->current_settings) return; @@ -691,23 +719,36 @@ int adapter_set_name(struct btd_adapter *adapter, const char *name) } struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, - const bdaddr_t *dst) + const bdaddr_t *dst, + uint8_t bdaddr_type) { + struct device_addr_type addr; struct btd_device *device; - char addr[18]; GSList *list; if (!adapter) return NULL; - ba2str(dst, addr); + bacpy(&addr.bdaddr, dst); + addr.bdaddr_type = bdaddr_type; - list = g_slist_find_custom(adapter->devices, addr, device_address_cmp); + list = g_slist_find_custom(adapter->devices, &addr, + device_addr_type_cmp); if (!list) return NULL; device = list->data; + /* + * If we're looking up based on public address and the address + * was not previously used over this bearer we may need to + * update LE or BR/EDR support information. + */ + if (bdaddr_type == BDADDR_BREDR) + device_set_bredr_support(device); + else + device_set_le_support(device, bdaddr_type); + return device; } @@ -1023,6 +1064,10 @@ static void service_auth_cancel(struct service_auth *auth) { DBusError derr; + if (auth->svc_id > 0) + device_remove_svc_complete_callback(auth->device, + auth->svc_id); + dbus_error_init(&derr); dbus_set_error_const(&derr, ERROR_INTERFACE ".Canceled", NULL); @@ -1031,13 +1076,13 @@ static void service_auth_cancel(struct service_auth *auth) dbus_error_free(&derr); if (auth->agent != NULL) - agent_cancel(auth->agent); + agent_unref(auth->agent); g_free(auth); } -static void adapter_remove_device(struct btd_adapter *adapter, - struct btd_device *dev) +void btd_adapter_remove_device(struct btd_adapter *adapter, + struct btd_device *dev) { GList *l; @@ -1081,7 +1126,7 @@ struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter, if (!adapter) return NULL; - device = btd_adapter_find_device(adapter, addr); + device = btd_adapter_find_device(adapter, addr, addr_type); if (device) return device; @@ -1531,7 +1576,7 @@ static gboolean remove_temp_devices(gpointer user_data) next = g_slist_next(l); if (device_is_temporary(dev)) - adapter_remove_device(adapter, dev); + btd_adapter_remove_device(adapter, dev); } return FALSE; @@ -2099,7 +2144,7 @@ static gboolean property_get_uuids(const GDBusPropertyTable *property, dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &uuid); - g_free(uuid); + free(uuid); } dbus_message_iter_close_container(iter, &entry); @@ -2159,7 +2204,7 @@ static DBusMessage *remove_device(DBusConnection *conn, btd_device_set_temporary(device, TRUE); if (!btd_device_is_connected(device)) { - adapter_remove_device(adapter, device); + btd_adapter_remove_device(adapter, device); return dbus_message_new_method_return(msg); } @@ -2219,13 +2264,17 @@ static struct link_key_info *get_key_info(GKeyFile *key_file, const char *peer) char *str; str = g_key_file_get_string(key_file, "LinkKey", "Key", NULL); - if (!str || strlen(str) != 34) + if (!str || strlen(str) < 32) goto failed; info = g_new0(struct link_key_info, 1); str2ba(peer, &info->bdaddr); - str2buf(&str[2], info->key, sizeof(info->key)); + + if (!strncmp(str, "0x", 2)) + str2buf(&str[2], info->key, sizeof(info->key)); + else + str2buf(&str[0], info->key, sizeof(info->key)); info->type = g_key_file_get_integer(key_file, "LinkKey", "Type", NULL); info->pin_len = g_key_file_get_integer(key_file, "LinkKey", "PINLength", @@ -2237,57 +2286,126 @@ failed: return info; } -static struct smp_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer) +static struct smp_ltk_info *get_ltk(GKeyFile *key_file, const char *peer, + uint8_t peer_type, const char *group) { struct smp_ltk_info *ltk = NULL; + GError *gerr = NULL; + bool master; char *key; char *rand = NULL; - char *type = NULL; - uint8_t bdaddr_type; - key = g_key_file_get_string(key_file, "LongTermKey", "Key", NULL); - if (!key || strlen(key) != 34) + key = g_key_file_get_string(key_file, group, "Key", NULL); + if (!key || strlen(key) < 32) goto failed; - rand = g_key_file_get_string(key_file, "LongTermKey", "Rand", NULL); - if (!rand || strlen(rand) != 18) + rand = g_key_file_get_string(key_file, group, "Rand", NULL); + if (!rand) goto failed; - type = g_key_file_get_string(key_file, "General", "AddressType", NULL); - if (!type) - goto failed; + ltk = g_new0(struct smp_ltk_info, 1); - if (g_str_equal(type, "public")) - bdaddr_type = BDADDR_LE_PUBLIC; - else if (g_str_equal(type, "static")) - bdaddr_type = BDADDR_LE_RANDOM; - else + /* Default to assuming a master key */ + ltk->master = true; + + str2ba(peer, <k->bdaddr); + ltk->bdaddr_type = peer_type; + + /* + * Long term keys should respond to an identity address which can + * either be a public address or a random static address. Keys + * stored for resolvable random and unresolvable random addresses + * are ignored. + * + * This is an extra sanity check for older kernel versions or older + * daemons that might have been instructed to store long term keys + * for these temporary addresses. + */ + if (ltk->bdaddr_type == BDADDR_LE_RANDOM && + (ltk->bdaddr.b[5] & 0xc0) != 0xc0) { + g_free(ltk); + ltk = NULL; goto failed; + } - ltk = g_new0(struct smp_ltk_info, 1); + if (!strncmp(key, "0x", 2)) + str2buf(&key[2], ltk->val, sizeof(ltk->val)); + else + str2buf(&key[0], ltk->val, sizeof(ltk->val)); - str2ba(peer, <k->bdaddr); - ltk->bdaddr_type = bdaddr_type; - str2buf(&key[2], ltk->val, sizeof(ltk->val)); - str2buf(&rand[2], ltk->rand, sizeof(ltk->rand)); + if (!strncmp(rand, "0x", 2)) { + uint64_t rand_le; + str2buf(&rand[2], (uint8_t *) &rand_le, sizeof(rand_le)); + ltk->rand = le64_to_cpu(rand_le); + } else { + sscanf(rand, "%" PRIu64, <k->rand); + } - ltk->authenticated = g_key_file_get_integer(key_file, "LongTermKey", + ltk->authenticated = g_key_file_get_integer(key_file, group, "Authenticated", NULL); - ltk->master = g_key_file_get_integer(key_file, "LongTermKey", "Master", - NULL); - ltk->enc_size = g_key_file_get_integer(key_file, "LongTermKey", - "EncSize", NULL); - ltk->ediv = g_key_file_get_integer(key_file, "LongTermKey", "EDiv", - NULL); + ltk->enc_size = g_key_file_get_integer(key_file, group, "EncSize", + NULL); + ltk->ediv = g_key_file_get_integer(key_file, group, "EDiv", NULL); + + master = g_key_file_get_boolean(key_file, group, "Master", &gerr); + if (gerr) + g_error_free(gerr); + else + ltk->master = master; failed: g_free(key); g_free(rand); - g_free(type); return ltk; } +static GSList *get_ltk_info(GKeyFile *key_file, const char *peer, + uint8_t bdaddr_type) +{ + struct smp_ltk_info *ltk; + GSList *l = NULL; + + DBG("%s", peer); + + ltk = get_ltk(key_file, peer, bdaddr_type, "LongTermKey"); + if (ltk) + l = g_slist_append(l, ltk); + + ltk = get_ltk(key_file, peer, bdaddr_type, "SlaveLongTermKey"); + if (ltk) { + ltk->master = false; + l = g_slist_append(l, ltk); + } + + return l; +} + +static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer, + uint8_t bdaddr_type) +{ + struct irk_info *irk; + char *str; + + str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL); + if (!str || strlen(str) < 32) + return NULL; + + irk = g_new0(struct irk_info, 1); + + str2ba(peer, &irk->bdaddr); + irk->bdaddr_type = bdaddr_type; + + if (!strncmp(str, "0x", 2)) + str2buf(&str[2], irk->val, sizeof(irk->val)); + else + str2buf(&str[0], irk->val, sizeof(irk->val)); + + g_free(str); + + return irk; +} + static void load_link_keys_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -2442,9 +2560,9 @@ static void load_ltks(struct btd_adapter *adapter, GSList *keys) bacpy(&key->addr.bdaddr, &info->bdaddr); key->addr.type = info->bdaddr_type; memcpy(key->val, info->val, sizeof(info->val)); - memcpy(key->rand, info->rand, sizeof(info->rand)); - memcpy(&key->ediv, &info->ediv, sizeof(key->ediv)); - key->authenticated = info->authenticated; + key->rand = cpu_to_le64(info->rand); + key->ediv = cpu_to_le16(info->ediv); + key->type = info->authenticated; key->master = info->master; key->enc_size = info->enc_size; } @@ -2470,12 +2588,105 @@ static void load_ltks(struct btd_adapter *adapter, GSList *keys) load_ltks_timeout, adapter); } +static void load_irks_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct btd_adapter *adapter = user_data; + + if (status == MGMT_STATUS_UNKNOWN_COMMAND) { + info("Load IRKs failed: Kernel doesn't support LE Privacy"); + return; + } + + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to load IRKs for hci%u: %s (0x%02x)", + adapter->dev_id, mgmt_errstr(status), status); + return; + } + + DBG("IRKs loaded for hci%u", adapter->dev_id); +} + +static void load_irks(struct btd_adapter *adapter, GSList *irks) +{ + struct mgmt_cp_load_irks *cp; + struct mgmt_irk_info *irk; + size_t irk_count, cp_size; + unsigned int id; + GSList *l; + + /* + * If the controller does not support LE Privacy operation, + * there is no support for loading identity resolving keys + * into the kernel. + */ + if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY)) + return; + + irk_count = g_slist_length(irks); + + DBG("hci%u irks %zu", adapter->dev_id, irk_count); + + cp_size = sizeof(*cp) + (irk_count * sizeof(*irk)); + + cp = g_try_malloc0(cp_size); + if (cp == NULL) { + error("No memory for IRKs for hci%u", adapter->dev_id); + return; + } + + /* + * Even if the list of stored keys is empty, it is important to + * load an empty list into the kernel. That way we tell the + * kernel that we are able to handle New IRK events. + */ + cp->irk_count = htobs(irk_count); + + for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++) { + struct irk_info *info = l->data; + + bacpy(&irk->addr.bdaddr, &info->bdaddr); + irk->addr.type = info->bdaddr_type; + memcpy(irk->val, info->val, sizeof(irk->val)); + } + + id = mgmt_send(adapter->mgmt, MGMT_OP_LOAD_IRKS, adapter->dev_id, + cp_size, cp, load_irks_complete, adapter, NULL); + + g_free(cp); + + if (id == 0) + error("Failed to IRKs for hci%u", adapter->dev_id); +} + +static uint8_t get_le_addr_type(GKeyFile *keyfile) +{ + uint8_t addr_type; + char *type; + + type = g_key_file_get_string(keyfile, "General", "AddressType", NULL); + if (!type) + return BDADDR_LE_PUBLIC; + + if (g_str_equal(type, "public")) + addr_type = BDADDR_LE_PUBLIC; + else if (g_str_equal(type, "static")) + addr_type = BDADDR_LE_RANDOM; + else + addr_type = BDADDR_LE_PUBLIC; + + g_free(type); + + return addr_type; +} + static void load_devices(struct btd_adapter *adapter) { char filename[PATH_MAX + 1]; char srcaddr[18]; GSList *keys = NULL; GSList *ltks = NULL; + GSList *irks = NULL; DIR *dir; struct dirent *entry; @@ -2495,8 +2706,9 @@ static void load_devices(struct btd_adapter *adapter) char filename[PATH_MAX + 1]; GKeyFile *key_file; struct link_key_info *key_info; - struct smp_ltk_info *ltk_info; - GSList *list; + GSList *list, *ltk_info; + struct irk_info *irk_info; + uint8_t bdaddr_type; if (entry->d_type != DT_DIR || bachk(entry->d_name) < 0) continue; @@ -2511,9 +2723,14 @@ static void load_devices(struct btd_adapter *adapter) if (key_info) keys = g_slist_append(keys, key_info); - ltk_info = get_ltk_info(key_file, entry->d_name); - if (ltk_info) - ltks = g_slist_append(ltks, ltk_info); + bdaddr_type = get_le_addr_type(key_file); + + ltk_info = get_ltk_info(key_file, entry->d_name, bdaddr_type); + ltks = g_slist_concat(ltks, ltk_info); + + irk_info = get_irk_info(key_file, entry->d_name, bdaddr_type); + if (irk_info) + irks = g_slist_append(irks, irk_info); list = g_slist_find_custom(adapter->devices, entry->d_name, device_address_cmp); @@ -2537,9 +2754,14 @@ static void load_devices(struct btd_adapter *adapter) device_probe_profiles(device, list); device_exist: - if (key_info || ltk_info) { - device_set_paired(device, TRUE); - device_set_bonded(device, TRUE); + if (key_info) { + device_set_paired(device, BDADDR_BREDR); + device_set_bonded(device, BDADDR_BREDR); + } + + if (ltk_info) { + device_set_paired(device, bdaddr_type); + device_set_bonded(device, bdaddr_type); } free: @@ -2553,6 +2775,8 @@ free: load_ltks(adapter, ltks); g_slist_free_full(ltks, g_free); + load_irks(adapter, irks); + g_slist_free_full(irks, g_free); } int btd_adapter_block_address(struct btd_adapter *adapter, @@ -2673,15 +2897,16 @@ void adapter_remove_profile(struct btd_adapter *adapter, gpointer p) } static void adapter_add_connection(struct btd_adapter *adapter, - struct btd_device *device) + struct btd_device *device, + uint8_t bdaddr_type) { + device_add_connection(device, bdaddr_type); + if (g_slist_find(adapter->connections, device)) { error("Device is already marked as connected"); return; } - device_add_connection(device); - adapter->connections = g_slist_append(adapter->connections, device); } @@ -2724,7 +2949,7 @@ static void get_connections_complete(uint8_t status, uint16_t length, device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); if (device) - adapter_add_connection(adapter, device); + adapter_add_connection(adapter, device, addr->type); } } @@ -2958,7 +3183,7 @@ static void adapter_free(gpointer user_data) g_free(adapter->system_name); g_free(adapter->stored_alias); g_free(adapter->current_alias); - g_free(adapter->modalias); + free(adapter->modalias); g_free(adapter); } @@ -3283,7 +3508,7 @@ static gboolean record_has_uuid(const sdp_record_t *rec, ret = strcasecmp(uuid, profile_uuid); - g_free(uuid); + free(uuid); if (ret == 0) return TRUE; @@ -3424,8 +3649,8 @@ static void convert_sdp_entry(char *key, char *value, void *user_data) failed: sdp_record_free(rec); - g_free(prim_uuid); - g_free(att_uuid); + free(prim_uuid); + free(att_uuid); } static void convert_primaries_entry(char *key, char *value, void *user_data) @@ -3506,7 +3731,7 @@ static void convert_primaries_entry(char *key, char *value, void *user_data) end: g_free(data); - g_free(prim_uuid); + free(prim_uuid); g_key_file_free(key_file); } @@ -3515,16 +3740,16 @@ static void convert_ccc_entry(char *key, char *value, void *user_data) char *src_addr = user_data; char dst_addr[18]; char type = BDADDR_BREDR; - int handle, ret; + uint16_t handle; + int ret, err; char filename[PATH_MAX + 1]; GKeyFile *key_file; struct stat st; - int err; char group[6]; char *data; gsize length = 0; - ret = sscanf(key, "%17s#%hhu#%04X", dst_addr, &type, &handle); + ret = sscanf(key, "%17s#%hhu#%04hX", dst_addr, &type, &handle); if (ret < 3) return; @@ -3565,16 +3790,16 @@ static void convert_gatt_entry(char *key, char *value, void *user_data) char *src_addr = user_data; char dst_addr[18]; char type = BDADDR_BREDR; - int handle, ret; + uint16_t handle; + int ret, err; char filename[PATH_MAX + 1]; GKeyFile *key_file; struct stat st; - int err; char group[6]; char *data; gsize length = 0; - ret = sscanf(key, "%17s#%hhu#%04X", dst_addr, &type, &handle); + ret = sscanf(key, "%17s#%hhu#%04hX", dst_addr, &type, &handle); if (ret < 3) return; @@ -4082,36 +4307,33 @@ static void update_found_devices(struct btd_adapter *adapter, { struct btd_device *dev; struct eir_data eir_data; + bool name_known, discoverable; char addr[18]; - GSList *list; - bool name_known; memset(&eir_data, 0, sizeof(eir_data)); eir_parse(&eir_data, data, data_len); - /* Avoid creating LE device if it's not discoverable */ - if (bdaddr_type != BDADDR_BREDR && - !(eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC))) { - eir_data_free(&eir_data); - return; - } + if (bdaddr_type == BDADDR_BREDR) + discoverable = true; + else + discoverable = eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC); ba2str(bdaddr, addr); - list = g_slist_find_custom(adapter->devices, bdaddr, device_bdaddr_cmp); - if (!list) { + dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type); + if (!dev) { /* - * If no client has requested discovery, then do not - * create new device objects. + * If no client has requested discovery or the device is + * not marked as discoverable, then do not create new + * device objects. */ - if (!adapter->discovery_list) { + if (!adapter->discovery_list || !discoverable) { eir_data_free(&eir_data); return; } dev = adapter_create_device(adapter, bdaddr, bdaddr_type); - } else - dev = list->data; + } if (!dev) { error("Unable to create object for found device %s", addr); @@ -4119,6 +4341,18 @@ static void update_found_devices(struct btd_adapter *adapter, return; } + device_update_last_seen(dev, bdaddr_type); + + /* + * FIXME: We need to check for non-zero flags first because + * older kernels send separate adv_ind and scan_rsp. Newer + * kernels send them merged, so once we know which mgmt version + * supports this we can make the non-zero check conditional. + */ + if (bdaddr_type != BDADDR_BREDR && eir_data.flags && + !(eir_data.flags & EIR_BREDR_UNSUP)) + device_set_bredr_support(dev); + if (eir_data.name != NULL && eir_data.name_complete) device_store_cached_name(dev, eir_data.name); @@ -4147,6 +4381,13 @@ static void update_found_devices(struct btd_adapter *adapter, if (eir_data.class != 0) device_set_class(dev, eir_data.class); + if (eir_data.did_source || eir_data.did_vendor || + eir_data.did_product || eir_data.did_version) + btd_device_set_pnpid(dev, eir_data.did_source, + eir_data.did_vendor, + eir_data.did_product, + eir_data.did_version); + device_add_eir_uuids(dev, eir_data.services); eir_data_free(&eir_data); @@ -4185,7 +4426,7 @@ connect_le: * connect_list stop passive scanning so that a connection * attempt to it can be made */ - if (device_is_le(dev) && !btd_device_is_connected(dev) && + if (bdaddr_type != BDADDR_BREDR && !btd_device_is_connected(dev) && g_slist_find(adapter->connect_list, dev)) { adapter->connect_le = dev; stop_passive_scanning(adapter); @@ -4241,7 +4482,8 @@ struct agent *adapter_get_agent(struct btd_adapter *adapter) } static void adapter_remove_connection(struct btd_adapter *adapter, - struct btd_device *device) + struct btd_device *device, + uint8_t bdaddr_type) { DBG(""); @@ -4250,18 +4492,22 @@ static void adapter_remove_connection(struct btd_adapter *adapter, return; } - device_remove_connection(device); - - adapter->connections = g_slist_remove(adapter->connections, device); + device_remove_connection(device, bdaddr_type); if (device_is_authenticating(device)) device_cancel_authentication(device, TRUE); + /* If another bearer is still connected */ + if (btd_device_is_connected(device)) + return; + + adapter->connections = g_slist_remove(adapter->connections, device); + if (device_is_temporary(device) && !device_is_retrying(device)) { const char *path = device_get_path(device); DBG("Removing temporary device %s", path); - adapter_remove_device(adapter, device); + btd_adapter_remove_device(adapter, device); } } @@ -4288,7 +4534,11 @@ static void adapter_stop(struct btd_adapter *adapter) while (adapter->connections) { struct btd_device *device = adapter->connections->data; - adapter_remove_connection(adapter, device); + uint8_t addr_type = btd_device_get_bdaddr_type(device); + + adapter_remove_connection(adapter, device, BDADDR_BREDR); + if (addr_type != BDADDR_BREDR) + adapter_remove_connection(adapter, device, addr_type); } g_dbus_emit_property_changed(dbus_conn, adapter->path, @@ -4373,6 +4623,10 @@ static gboolean process_auth_queue(gpointer user_data) struct btd_device *device = auth->device; const char *dev_path; + /* Wait services to be resolved before asking authorization */ + if (auth->svc_id > 0) + return TRUE; + if (device_is_trusted(device) == TRUE) { auth->cb(NULL, auth->user_data); goto next; @@ -4409,6 +4663,22 @@ next: return FALSE; } +static void svc_complete(struct btd_device *dev, int err, void *user_data) +{ + struct service_auth *auth = user_data; + struct btd_adapter *adapter = auth->adapter; + + auth->svc_id = 0; + + if (adapter->auths->length != 1) + return; + + if (adapter->auth_idle_id != 0) + return; + + adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter); +} + static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst, const char *uuid, service_auth_cb cb, void *user_data) @@ -4417,7 +4687,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst, struct btd_device *device; static guint id = 0; - device = btd_adapter_find_device(adapter, dst); + device = btd_adapter_find_device(adapter, dst, BDADDR_BREDR); if (!device) return 0; @@ -4435,17 +4705,10 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst, auth->device = device; auth->adapter = adapter; auth->id = ++id; + auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth); g_queue_push_tail(adapter->auths, auth); - if (adapter->auths->length != 1) - return auth->id; - - if (adapter->auth_idle_id != 0) - return auth->id; - - adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter); - return auth->id; } @@ -4504,12 +4767,14 @@ int btd_cancel_authorization(guint id) if (auth == NULL) return -EPERM; + if (auth->svc_id > 0) + device_remove_svc_complete_callback(auth->device, + auth->svc_id); + g_queue_remove(auth->adapter->auths, auth); - if (auth->agent) { - agent_cancel(auth->agent); + if (auth->agent) agent_unref(auth->agent); - } g_free(auth); @@ -4623,7 +4888,7 @@ int btd_adapter_pincode_reply(struct btd_adapter *adapter, /* Since a pincode was requested, update the starting time to * the point where the pincode is provided. */ - device = btd_adapter_find_device(adapter, bdaddr); + device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR); device_bonding_restart_timer(device); id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY, @@ -4793,7 +5058,7 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length, return; } - passkey = bt_get_le32(&ev->passkey); + passkey = get_le32(&ev->passkey); DBG("passkey %06u entered %u", passkey, ev->entered); @@ -4957,10 +5222,10 @@ static void bonding_complete(struct btd_adapter *adapter, if (status == 0) device = btd_adapter_get_device(adapter, bdaddr, addr_type); else - device = btd_adapter_find_device(adapter, bdaddr); + device = btd_adapter_find_device(adapter, bdaddr, addr_type); if (device != NULL) - device_bonding_complete(device, status); + device_bonding_complete(device, addr_type, status); resume_discovery(adapter); @@ -4984,7 +5249,7 @@ static void bonding_attempt_complete(struct btd_adapter *adapter, if (status == 0) device = btd_adapter_get_device(adapter, bdaddr, addr_type); else - device = btd_adapter_find_device(adapter, bdaddr); + device = btd_adapter_find_device(adapter, bdaddr, addr_type); if (status == MGMT_STATUS_AUTH_FAILED && adapter->pincode_requested) { /* On faliure, issue a bonding_retry if possible. */ @@ -5132,6 +5397,16 @@ int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr, return 0; } +static void disconnect_notify(struct btd_device *dev, uint8_t reason) +{ + GSList *l; + + for (l = disconnect_list; l; l = g_slist_next(l)) { + btd_disconnect_cb disconnect_cb = l->data; + disconnect_cb(dev, reason); + } +} + static void dev_disconnected(struct btd_adapter *adapter, const struct mgmt_addr_info *addr, uint8_t reason) @@ -5143,21 +5418,35 @@ static void dev_disconnected(struct btd_adapter *adapter, DBG("Device %s disconnected, reason %u", dst, reason); - device = btd_adapter_find_device(adapter, &addr->bdaddr); - if (device) - adapter_remove_connection(adapter, device); + device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type); + if (device) { + adapter_remove_connection(adapter, device, addr->type); + disconnect_notify(device, reason); + } bonding_attempt_complete(adapter, &addr->bdaddr, addr->type, MGMT_STATUS_DISCONNECTED); } +void btd_add_disconnect_cb(btd_disconnect_cb func) +{ + disconnect_list = g_slist_append(disconnect_list, func); +} + +void btd_remove_disconnect_cb(btd_disconnect_cb func) +{ + disconnect_list = g_slist_remove(disconnect_list, func); +} + static void disconnect_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { const struct mgmt_rp_disconnect *rp = param; struct btd_adapter *adapter = user_data; - if (status != MGMT_STATUS_SUCCESS) { + if (status == MGMT_STATUS_NOT_CONNECTED) { + warn("Disconnecting failed: already disconnected"); + } else if (status != MGMT_STATUS_SUCCESS) { error("Failed to disconnect device: %s (0x%02x)", mgmt_errstr(status), status); return; @@ -5214,7 +5503,7 @@ static void store_link_key(struct btd_adapter *adapter, char filename[PATH_MAX + 1]; GKeyFile *key_file; gsize length = 0; - char key_str[35]; + char key_str[33]; char *str; int i; @@ -5228,10 +5517,8 @@ static void store_link_key(struct btd_adapter *adapter, key_file = g_key_file_new(); g_key_file_load_from_file(key_file, filename, 0, NULL); - key_str[0] = '0'; - key_str[1] = 'x'; for (i = 0; i < 16; i++) - sprintf(key_str + 2 + (i * 2), "%2.2X", key[i]); + sprintf(key_str + (i * 2), "%2.2X", key[i]); g_key_file_set_string(key_file, "LinkKey", "Key", key_str); @@ -5284,7 +5571,7 @@ static void new_link_key_callback(uint16_t index, uint16_t length, store_link_key(adapter, device, key->val, key->type, key->pin_len); - device_set_bonded(device, TRUE); + device_set_bonded(device, BDADDR_BREDR); if (device_is_temporary(device)) btd_device_set_temporary(device, FALSE); @@ -5297,18 +5584,23 @@ static void store_longtermkey(const bdaddr_t *local, const bdaddr_t *peer, uint8_t bdaddr_type, const unsigned char *key, uint8_t master, uint8_t authenticated, uint8_t enc_size, uint16_t ediv, - const uint8_t rand[8]) + uint64_t rand) { + const char *group = master ? "LongTermKey" : "SlaveLongTermKey"; char adapter_addr[18]; char device_addr[18]; char filename[PATH_MAX + 1]; GKeyFile *key_file; - char key_str[35]; - char rand_str[19]; + char key_str[33]; gsize length = 0; char *str; int i; + if (master != 0x00 && master != 0x01) { + error("Unsupported LTK type %u", master); + return; + } + ba2str(local, adapter_addr); ba2str(peer, device_addr); @@ -5319,25 +5611,20 @@ static void store_longtermkey(const bdaddr_t *local, const bdaddr_t *peer, key_file = g_key_file_new(); g_key_file_load_from_file(key_file, filename, 0, NULL); - key_str[0] = '0'; - key_str[1] = 'x'; - for (i = 0; i < 16; i++) - sprintf(key_str + 2 + (i * 2), "%2.2X", key[i]); + /* Old files may contain this so remove it in case it exists */ + g_key_file_remove_key(key_file, "LongTermKey", "Master", NULL); - g_key_file_set_string(key_file, "LongTermKey", "Key", key_str); + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", key[i]); - g_key_file_set_integer(key_file, "LongTermKey", "Authenticated", - authenticated); - g_key_file_set_integer(key_file, "LongTermKey", "Master", master); - g_key_file_set_integer(key_file, "LongTermKey", "EncSize", enc_size); - g_key_file_set_integer(key_file, "LongTermKey", "EDiv", ediv); + g_key_file_set_string(key_file, group, "Key", key_str); - rand_str[0] = '0'; - rand_str[1] = 'x'; - for (i = 0; i < 8; i++) - sprintf(rand_str + 2 + (i * 2), "%2.2X", rand[i]); + g_key_file_set_integer(key_file, group, "Authenticated", + authenticated); + g_key_file_set_integer(key_file, group, "EncSize", enc_size); - g_key_file_set_string(key_file, "LongTermKey", "Rand", rand_str); + g_key_file_set_integer(key_file, group, "EDiv", ediv); + g_key_file_set_uint64(key_file, group, "Rand", rand); create_file(filename, S_IRUSR | S_IWUSR); @@ -5355,6 +5642,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length, const struct mgmt_addr_info *addr = &ev->key.addr; struct btd_adapter *adapter = user_data; struct btd_device *device; + bool persistent; char dst[18]; if (length < sizeof(*ev)) { @@ -5364,8 +5652,8 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length, ba2str(&addr->bdaddr, dst); - DBG("hci%u new LTK for %s authenticated %u enc_size %u", - adapter->dev_id, dst, ev->key.authenticated, ev->key.enc_size); + DBG("hci%u new LTK for %s type %u enc_size %u", + adapter->dev_id, dst, ev->key.type, ev->key.enc_size); device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); if (!device) { @@ -5373,23 +5661,216 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length, return; } - if (ev->store_hint) { + /* + * Some older kernel versions set store_hint for long term keys + * from resolvable and unresolvable random addresses, but there + * is no point in storing these. Next time around the device + * address will be invalid. + * + * So only for identity addresses (public and static random) use + * the store_hint as an indication if the long term key should + * be persistently stored. + * + */ + if (addr->type == BDADDR_LE_RANDOM && + (addr->bdaddr.b[5] & 0xc0) != 0xc0) + persistent = false; + else + persistent = !!ev->store_hint; + + if (persistent) { const struct mgmt_ltk_info *key = &ev->key; const bdaddr_t *bdaddr = btd_adapter_get_address(adapter); + uint16_t ediv; + uint64_t rand; + + ediv = le16_to_cpu(key->ediv); + rand = le64_to_cpu(key->rand); store_longtermkey(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, key->master, - key->authenticated, key->enc_size, - key->ediv, key->rand); + key->type, key->enc_size, ediv, rand); - device_set_bonded(device, TRUE); + device_set_bonded(device, addr->type); if (device_is_temporary(device)) btd_device_set_temporary(device, FALSE); } - if (ev->key.master) - bonding_complete(adapter, &addr->bdaddr, addr->type, 0); + bonding_complete(adapter, &addr->bdaddr, addr->type, 0); +} + +static void store_csrk(const bdaddr_t *local, const bdaddr_t *peer, + uint8_t bdaddr_type, const unsigned char *key, + uint8_t master) +{ + const char *group; + char adapter_addr[18]; + char device_addr[18]; + char filename[PATH_MAX + 1]; + GKeyFile *key_file; + char key_str[33]; + gsize length = 0; + char *str; + int i; + + if (master == 0x00) + group = "LocalSignatureKey"; + else if (master == 0x01) + group = "RemoteSignatureKey"; + else { + warn("Unsupported CSRK type %u", master); + return; + } + + ba2str(local, adapter_addr); + ba2str(peer, device_addr); + + snprintf(filename, sizeof(filename), STORAGEDIR "/%s/%s/info", + adapter_addr, device_addr); + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, filename, 0, NULL); + + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", key[i]); + + g_key_file_set_string(key_file, group, "Key", key_str); + + create_file(filename, S_IRUSR | S_IWUSR); + + str = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(filename, str, length, NULL); + g_free(str); + + g_key_file_free(key_file); +} + +static void new_csrk_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_csrk *ev = param; + const struct mgmt_addr_info *addr = &ev->key.addr; + const struct mgmt_csrk_info *key = &ev->key; + struct btd_adapter *adapter = user_data; + const bdaddr_t *bdaddr = btd_adapter_get_address(adapter); + struct btd_device *device; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small CSRK event"); + return; + } + + ba2str(&addr->bdaddr, dst); + + DBG("hci%u new CSRK for %s master %u", adapter->dev_id, dst, + ev->key.master); + + device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); + if (!device) { + error("Unable to get device object for %s", dst); + return; + } + + if (!ev->store_hint) + return; + + store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, + key->master); + + if (device_is_temporary(device)) + btd_device_set_temporary(device, FALSE); +} + +static void store_irk(struct btd_adapter *adapter, const bdaddr_t *peer, + uint8_t bdaddr_type, const unsigned char *key) +{ + char adapter_addr[18]; + char device_addr[18]; + char filename[PATH_MAX + 1]; + GKeyFile *key_file; + char *store_data; + char str[33]; + size_t length = 0; + int i; + + ba2str(&adapter->bdaddr, adapter_addr); + ba2str(peer, device_addr); + + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr, + device_addr); + filename[PATH_MAX] = '\0'; + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, filename, 0, NULL); + + for (i = 0; i < 16; i++) + sprintf(str + (i * 2), "%2.2X", key[i]); + + g_key_file_set_string(key_file, "IdentityResolvingKey", "Key", str); + + create_file(filename, S_IRUSR | S_IWUSR); + + store_data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(filename, store_data, length, NULL); + g_free(store_data); + + g_key_file_free(key_file); +} + +static void new_irk_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_irk *ev = param; + const struct mgmt_addr_info *addr = &ev->key.addr; + const struct mgmt_irk_info *irk = &ev->key; + struct btd_adapter *adapter = user_data; + struct btd_device *device, *duplicate; + bool persistent; + char dst[18], rpa[18]; + + if (length < sizeof(*ev)) { + error("Too small New IRK event"); + return; + } + + ba2str(&addr->bdaddr, dst); + ba2str(&ev->rpa, rpa); + + DBG("hci%u new IRK for %s RPA %s", adapter->dev_id, dst, rpa); + + if (bacmp(&ev->rpa, BDADDR_ANY)) { + device = btd_adapter_get_device(adapter, &ev->rpa, + BDADDR_LE_RANDOM); + duplicate = btd_adapter_find_device(adapter, &addr->bdaddr, + addr->type); + if (duplicate == device) + duplicate = NULL; + } else { + device = btd_adapter_get_device(adapter, &addr->bdaddr, + addr->type); + duplicate = NULL; + } + + if (!device) { + error("Unable to get device object for %s", dst); + return; + } + + device_update_addr(device, &addr->bdaddr, addr->type); + + if (duplicate) + device_merge_duplicate(device, duplicate); + + persistent = !!ev->store_hint; + if (!persistent) + return; + + store_irk(adapter, &addr->bdaddr, addr->type, irk->val); + + if (device_is_temporary(device)) + btd_device_set_temporary(device, FALSE); } int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap) @@ -5732,7 +6213,7 @@ static void connected_callback(uint16_t index, uint16_t length, if (eir_data.class != 0) device_set_class(device, eir_data.class); - adapter_add_connection(adapter, device); + adapter_add_connection(adapter, device, ev->addr.type); if (eir_data.name != NULL) { device_store_cached_name(device, eir_data.name); @@ -5758,7 +6239,8 @@ static void device_blocked_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s blocked", index, addr); - device = btd_adapter_find_device(adapter, &ev->addr.bdaddr); + device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (device) device_block(device, TRUE); } @@ -5779,11 +6261,32 @@ static void device_unblocked_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s unblocked", index, addr); - device = btd_adapter_find_device(adapter, &ev->addr.bdaddr); + device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (device) device_unblock(device, FALSE, TRUE); } +static void conn_fail_notify(struct btd_device *dev, uint8_t status) +{ + GSList *l; + + for (l = conn_fail_list; l; l = g_slist_next(l)) { + btd_conn_fail_cb conn_fail_cb = l->data; + conn_fail_cb(dev, status); + } +} + +void btd_add_conn_fail_cb(btd_conn_fail_cb func) +{ + conn_fail_list = g_slist_append(conn_fail_list, func); +} + +void btd_remove_conn_fail_cb(btd_conn_fail_cb func) +{ + conn_fail_list = g_slist_remove(conn_fail_list, func); +} + static void connect_failed_callback(uint16_t index, uint16_t length, const void *param, void *user_data) { @@ -5801,8 +6304,11 @@ static void connect_failed_callback(uint16_t index, uint16_t length, DBG("hci%u %s status %u", index, addr, ev->status); - device = btd_adapter_find_device(adapter, &ev->addr.bdaddr); + device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (device) { + conn_fail_notify(device, ev->status); + /* If the device is in a bonding process cancel any auth request * sent to the agent before proceeding, but keep the bonding * request structure. */ @@ -5827,7 +6333,43 @@ static void connect_failed_callback(uint16_t index, uint16_t length, * when it is temporary. */ if (device && !device_is_bonding(device, NULL) && device_is_temporary(device)) - adapter_remove_device(adapter, device); + btd_adapter_remove_device(adapter, device); +} + +static void remove_keys(struct btd_adapter *adapter, + struct btd_device *device, uint8_t type) +{ + char adapter_addr[18]; + char device_addr[18]; + char filename[PATH_MAX + 1]; + GKeyFile *key_file; + gsize length = 0; + char *str; + + ba2str(btd_adapter_get_address(adapter), adapter_addr); + ba2str(device_get_address(device), device_addr); + + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr, + device_addr); + filename[PATH_MAX] = '\0'; + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, filename, 0, NULL); + + if (type == BDADDR_BREDR) { + g_key_file_remove_group(key_file, "LinkKey", NULL); + } else { + g_key_file_remove_group(key_file, "LongTermKey", NULL); + g_key_file_remove_group(key_file, "LocalSignatureKey", NULL); + g_key_file_remove_group(key_file, "RemoteSignatureKey", NULL); + g_key_file_remove_group(key_file, "IdentityResolvingKey", NULL); + } + + str = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(filename, str, length, NULL); + g_free(str); + + g_key_file_free(key_file); } static void unpaired_callback(uint16_t index, uint16_t length, @@ -5847,18 +6389,15 @@ static void unpaired_callback(uint16_t index, uint16_t length, DBG("hci%u addr %s", index, addr); - device = btd_adapter_find_device(adapter, &ev->addr.bdaddr); + device = btd_adapter_find_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (!device) { warn("No device object for unpaired device %s", addr); return; } - btd_device_set_temporary(device, TRUE); - - if (btd_device_is_connected(device)) - device_request_disconnect(device, NULL); - else - adapter_remove_device(adapter, device); + remove_keys(adapter, device, ev->addr.type); + device_set_unpaired(device, ev->addr.type); } static void read_info_complete(uint8_t status, uint16_t length, @@ -5975,6 +6514,16 @@ static void read_info_complete(uint8_t status, uint16_t length, new_long_term_key_callback, adapter, NULL); + mgmt_register(adapter->mgmt, MGMT_EV_NEW_CSRK, + adapter->dev_id, + new_csrk_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_NEW_IRK, + adapter->dev_id, + new_irk_callback, + adapter, NULL); + mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_BLOCKED, adapter->dev_id, device_blocked_callback, diff --git a/src/adapter.h b/src/adapter.h index de5b07d..f88c339 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -36,6 +36,7 @@ #define INVALID_PASSKEY 0xffffffff struct btd_adapter; +struct btd_device; struct btd_adapter *btd_adapter_get_default(void); bool btd_adapter_is_default(struct btd_adapter *adapter); @@ -58,28 +59,18 @@ struct oob_handler { void *user_data; }; -struct link_key_info { - bdaddr_t bdaddr; - unsigned char key[16]; - uint8_t type; - uint8_t pin_len; -}; - -struct smp_ltk_info { - bdaddr_t bdaddr; - uint8_t bdaddr_type; - uint8_t authenticated; - uint8_t master; - uint8_t enc_size; - uint16_t ediv; - uint8_t rand[8]; - uint8_t val[16]; -}; - int adapter_init(void); void adapter_cleanup(void); void adapter_shutdown(void); +typedef void (*btd_disconnect_cb) (struct btd_device *device, uint8_t reason); +void btd_add_disconnect_cb(btd_disconnect_cb func); +void btd_remove_disconnect_cb(btd_disconnect_cb func); + +typedef void (*btd_conn_fail_cb) (struct btd_device *device, uint8_t status); +void btd_add_conn_fail_cb(btd_conn_fail_cb func); +void btd_remove_conn_fail_cb(btd_conn_fail_cb func); + struct btd_adapter *adapter_find(const bdaddr_t *sba); struct btd_adapter *adapter_find_by_id(int id); void adapter_foreach(adapter_cb func, gpointer user_data); @@ -90,13 +81,16 @@ bool btd_adapter_get_connectable(struct btd_adapter *adapter); uint32_t btd_adapter_get_class(struct btd_adapter *adapter); const char *btd_adapter_get_name(struct btd_adapter *adapter); +void btd_adapter_remove_device(struct btd_adapter *adapter, + struct btd_device *dev); struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter, const bdaddr_t *addr, uint8_t addr_type); sdp_list_t *btd_adapter_get_services(struct btd_adapter *adapter); struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, - const bdaddr_t *dst); + const bdaddr_t *dst, + uint8_t dst_type); const char *adapter_get_path(struct btd_adapter *adapter); const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter); diff --git a/src/agent.c b/src/agent.c index 4c63cb9..8c1211c 100644 --- a/src/agent.c +++ b/src/agent.c @@ -46,6 +46,7 @@ #include "adapter.h" #include "device.h" #include "agent.h" +#include "shared/queue.h" #define IO_CAPABILITY_DISPLAYONLY 0x00 #define IO_CAPABILITY_DISPLAYYESNO 0x01 @@ -58,7 +59,7 @@ #define AGENT_INTERFACE "org.bluez.Agent1" static GHashTable *agent_list; -static struct agent *default_agent = NULL; +struct queue *default_agents = NULL; typedef enum { AGENT_REQUEST_PASSKEY, @@ -66,7 +67,6 @@ typedef enum { AGENT_REQUEST_AUTHORIZATION, AGENT_REQUEST_PINCODE, AGENT_REQUEST_AUTHORIZE_SERVICE, - AGENT_REQUEST_CONFIRM_MODE, AGENT_REQUEST_DISPLAY_PINCODE, } agent_request_type_t; @@ -153,18 +153,38 @@ static void set_io_cap(struct btd_adapter *adapter, gpointer user_data) adapter_set_io_capability(adapter, io_cap); } -static void set_default_agent(struct agent *agent) +static bool add_default_agent(struct agent *agent) { - if (default_agent == agent) + if (queue_peek_head(default_agents) == agent) + return true; + + queue_remove(default_agents, agent); + + if (!queue_push_head(default_agents, agent)) + return false; + + DBG("Default agent set to %s %s", agent->owner, agent->path); + + adapter_foreach(set_io_cap, agent); + + return true; +} + +static void remove_default_agent(struct agent *agent) +{ + if (queue_peek_head(default_agents) != agent) { + queue_remove(default_agents, agent); return; + } + queue_remove(default_agents, agent); + + agent = queue_peek_head(default_agents); if (agent) DBG("Default agent set to %s %s", agent->owner, agent->path); else DBG("Default agent cleared"); - default_agent = agent; - adapter_foreach(set_io_cap, agent); } @@ -179,8 +199,7 @@ static void agent_disconnect(DBusConnection *conn, void *user_data) agent->watch = 0; } - if (agent == default_agent) - set_default_agent(NULL); + remove_default_agent(agent); g_hash_table_remove(agent_list, agent->owner); } @@ -248,8 +267,8 @@ struct agent *agent_get(const char *owner) return agent_ref(agent); } - if (default_agent) - return agent_ref(default_agent); + if (!queue_isempty(default_agents)) + return agent_ref(queue_peek_head(default_agents)); return NULL; } @@ -330,7 +349,6 @@ static void simple_agent_reply(DBusPendingCall *call, void *user_data) if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { error("Timed out waiting for reply from agent"); - agent_cancel(agent); dbus_message_unref(message); dbus_error_free(&err); agent_unref(agent); @@ -891,6 +909,8 @@ static void agent_destroy(gpointer data) agent_release(agent); } + remove_default_agent(agent); + agent_unref(agent); } @@ -989,7 +1009,8 @@ static DBusMessage *request_default(DBusConnection *conn, DBusMessage *msg, if (g_str_equal(path, agent->path) == FALSE) return btd_error_does_not_exist(msg); - set_default_agent(agent); + if (!add_default_agent(agent)) + return btd_error_failed(msg, "Failed to set as default"); return dbus_message_new_method_return(msg); } @@ -1010,6 +1031,8 @@ void btd_agent_init(void) agent_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, agent_destroy); + default_agents = queue_new(); + g_dbus_register_interface(btd_get_dbus_connection(), "/org/bluez", "org.bluez.AgentManager1", methods, NULL, NULL, NULL, NULL); @@ -1020,6 +1043,6 @@ void btd_agent_cleanup(void) g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez", "org.bluez.AgentManager1"); - set_default_agent(NULL); g_hash_table_destroy(agent_list); + queue_destroy(default_agents, NULL); } diff --git a/src/attrib-server.c b/src/attrib-server.c index a6f1066..e65fff2 100644 --- a/src/attrib-server.c +++ b/src/attrib-server.c @@ -39,17 +39,16 @@ #include #include "lib/uuid.h" -#include +#include "btio/btio.h" #include "log.h" -#include -#include "sdpd.h" -#include "hcid.h" #include "adapter.h" #include "device.h" +#include "src/shared/util.h" #include "attrib/gattrib.h" #include "attrib/att.h" #include "attrib/gatt.h" #include "attrib/att-database.h" +#include "textfile.h" #include "storage.h" #include "attrib-server.h" @@ -69,8 +68,6 @@ struct gatt_server { }; struct gatt_channel { - bdaddr_t src; - bdaddr_t dst; GAttrib *attrib; guint mtu; gboolean le; @@ -101,6 +98,15 @@ static bt_uuid_t ccc_uuid = { .value.u16 = GATT_CLIENT_CHARAC_CFG_UUID }; +static inline void put_uuid_le(const bt_uuid_t *src, void *dst) +{ + if (src->type == BT_UUID16) + put_le16(src->value.u16, dst); + else + /* Convert from 128-bit BE to LE */ + bswap_128(&src->value.u128, dst); +} + static void attrib_free(void *data) { struct attribute *a = data; @@ -307,10 +313,14 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server, 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 + sdp_uuid16_create(&svc, get_le16(a->data)); + else if (a->len == 16) { + uint8_t be128[16]; + + /* Converting from LE to BE */ + bswap_128(a->data, be128); + sdp_uuid128_create(&svc, be128); + } else return 0; record = server_record_new(&svc, handle, end); @@ -503,8 +513,8 @@ static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start, value = (void *) adl->data[i]; - att_put_u16(cur->handle, value); - att_put_u16(cur->end, &value[2]); + put_le16(cur->handle, value); + put_le16(cur->end, &value[2]); /* Attribute Value */ memcpy(&value[4], cur->data, cur->len); } @@ -592,7 +602,7 @@ static uint16_t read_by_type(struct gatt_channel *channel, uint16_t start, value = (void *) adl->data[i]; - att_put_u16(a->handle, value); + put_le16(a->handle, value); /* Attribute Value */ memcpy(&value[2], a->data, a->len); @@ -672,10 +682,10 @@ static uint16_t find_info(struct gatt_channel *channel, uint16_t start, value = (void *) adl->data[i]; - att_put_u16(a->handle, value); + put_le16(a->handle, value); /* Attribute Value */ - att_put_uuid(a->uuid, &value[2]); + put_uuid_le(&a->uuid, &value[2]); } length = enc_find_info_resp(format, adl, pdu, len); @@ -801,7 +811,7 @@ static uint16_t read_value(struct gatt_channel *channel, uint16_t handle, read_device_ccc(channel->device, handle, &cccval) == 0) { uint8_t config[2]; - att_put_u16(cccval, config); + put_le16(cccval, config); return enc_read_resp(config, sizeof(config), pdu, len); } @@ -842,7 +852,7 @@ static uint16_t read_blob(struct gatt_channel *channel, uint16_t handle, read_device_ccc(channel->device, handle, &cccval) == 0) { uint8_t config[2]; - att_put_u16(cccval, config); + put_le16(cccval, config); return enc_read_blob_resp(config, sizeof(config), offset, pdu, len); } @@ -894,7 +904,7 @@ static uint16_t write_value(struct gatt_channel *channel, uint16_t handle, status, pdu, len); } } else { - uint16_t cccval = att_get_u16(value); + uint16_t cccval = get_le16(value); char *filename; GKeyFile *key_file; char group[6], value[5]; @@ -913,7 +923,7 @@ static uint16_t write_value(struct gatt_channel *channel, uint16_t handle, g_key_file_load_from_file(key_file, filename, 0, NULL); sprintf(group, "%hu", handle); - sprintf(value, "%hhX", cccval); + sprintf(value, "%hX", cccval); g_key_file_set_string(key_file, group, "Value", value); data = g_key_file_to_data(key_file, &length, NULL); @@ -944,10 +954,12 @@ static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu, io = g_attrib_get_channel(channel->attrib); bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID); - - if (gerr) - return enc_error_resp(ATT_OP_MTU_REQ, 0, - ATT_ECODE_UNLIKELY, pdu, len); + if (gerr) { + error("bt_io_get: %s", gerr->message); + g_error_free(gerr); + return enc_error_resp(ATT_OP_MTU_REQ, 0, ATT_ECODE_UNLIKELY, + pdu, len); + } channel->mtu = MIN(mtu, imtu); g_attrib_set_mtu(channel->attrib, channel->mtu); @@ -983,6 +995,12 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len, DBG("op 0x%02x", ipdu[0]); + if (len > vlen) { + error("Too much data on ATT socket"); + status = ATT_ECODE_INVALID_PDU; + goto done; + } + switch (ipdu[0]) { case ATT_OP_READ_BY_GROUP_REQ: length = dec_read_by_grp_req(ipdu, len, &start, &end, &uuid); @@ -1098,53 +1116,70 @@ done: g_attrib_send(channel->attrib, 0, opdu, length, NULL, NULL, NULL); } +GAttrib *attrib_from_device(struct btd_device *device) +{ + struct btd_adapter *adapter = device_get_adapter(device); + struct gatt_server *server; + GSList *l; + + l = g_slist_find_custom(servers, adapter, adapter_cmp); + if (!l) + return NULL; + + server = l->data; + + for (l = server->clients; l; l = l->next) { + struct gatt_channel *channel = l->data; + + if (channel->device == device) + return g_attrib_ref(channel->attrib); + } + + return NULL; +} + guint attrib_channel_attach(GAttrib *attrib) { struct gatt_server *server; struct btd_device *device; struct gatt_channel *channel; + bdaddr_t src, dst; GIOChannel *io; GError *gerr = NULL; + uint8_t bdaddr_type; uint16_t cid; guint mtu = 0; io = g_attrib_get_channel(attrib); - channel = g_new0(struct gatt_channel, 1); - bt_io_get(io, &gerr, - BT_IO_OPT_SOURCE_BDADDR, &channel->src, - BT_IO_OPT_DEST_BDADDR, &channel->dst, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST_TYPE, &bdaddr_type, BT_IO_OPT_CID, &cid, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_INVALID); if (gerr) { error("bt_io_get: %s", gerr->message); g_error_free(gerr); - g_free(channel); return 0; } - server = find_gatt_server(&channel->src); - if (server == NULL) { - char src[18]; - - ba2str(&channel->src, src); - error("No GATT server found in %s", src); - g_free(channel); + server = find_gatt_server(&src); + if (server == NULL) return 0; - } + channel = g_new0(struct gatt_channel, 1); channel->server = server; - device = btd_adapter_find_device(server->adapter, &channel->dst); + device = btd_adapter_find_device(server->adapter, &dst, bdaddr_type); if (device == NULL) { error("Device object not found for attrib server"); g_free(channel); return 0; } - if (device_is_bonded(device) == FALSE) { + if (!device_is_bonded(device, bdaddr_type)) { char *filename; filename = btd_device_get_storage_path(device, "ccc"); @@ -1222,7 +1257,10 @@ gboolean attrib_channel_detach(GAttrib *attrib, guint id) static void connect_event(GIOChannel *io, GError *gerr, void *user_data) { - GAttrib *attrib; + struct btd_adapter *adapter; + struct btd_device *device; + uint8_t dst_type; + bdaddr_t src, dst; DBG(""); @@ -1231,9 +1269,26 @@ static void connect_event(GIOChannel *io, GError *gerr, void *user_data) return; } - attrib = g_attrib_new(io); - attrib_channel_attach(attrib); - g_attrib_unref(attrib); + bt_io_get(io, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST_TYPE, &dst_type, + BT_IO_OPT_INVALID); + if (gerr) { + error("bt_io_get: %s", gerr->message); + g_error_free(gerr); + return; + } + + adapter = adapter_find(&src); + if (!adapter) + return; + + device = btd_adapter_get_device(adapter, &dst, dst_type); + if (!device) + return; + + device_attach_attrib(device, io); } static gboolean register_core_services(struct gatt_server *server) @@ -1244,16 +1299,16 @@ static gboolean register_core_services(struct gatt_server *server) /* GAP service: primary service definition */ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); - att_put_u16(GENERIC_ACCESS_PROFILE_ID, &atval[0]); + put_le16(GENERIC_ACCESS_PROFILE_ID, &atval[0]); attrib_db_add_new(server, 0x0001, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); /* GAP service: device name characteristic */ server->name_handle = 0x0006; bt_uuid16_create(&uuid, GATT_CHARAC_UUID); - atval[0] = ATT_CHAR_PROPER_READ; - att_put_u16(server->name_handle, &atval[1]); - att_put_u16(GATT_CHARAC_DEVICE_NAME, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(server->name_handle, &atval[1]); + put_le16(GATT_CHARAC_DEVICE_NAME, &atval[3]); attrib_db_add_new(server, 0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); @@ -1265,15 +1320,15 @@ static gboolean register_core_services(struct gatt_server *server) /* GAP service: device appearance characteristic */ server->appearance_handle = 0x0008; bt_uuid16_create(&uuid, GATT_CHARAC_UUID); - atval[0] = ATT_CHAR_PROPER_READ; - att_put_u16(server->appearance_handle, &atval[1]); - att_put_u16(GATT_CHARAC_APPEARANCE, &atval[3]); + atval[0] = GATT_CHR_PROP_READ; + put_le16(server->appearance_handle, &atval[1]); + put_le16(GATT_CHARAC_APPEARANCE, &atval[3]); attrib_db_add_new(server, 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]); + put_le16(appearance, &atval[0]); attrib_db_add_new(server, server->appearance_handle, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); server->gap_sdp_handle = attrib_create_sdp_new(server, 0x0001, @@ -1285,7 +1340,7 @@ static gboolean register_core_services(struct gatt_server *server) /* GATT service: primary service definition */ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); - att_put_u16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]); + put_le16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]); attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); diff --git a/src/attrib-server.h b/src/attrib-server.h index 90ba17c..063cb66 100644 --- a/src/attrib-server.h +++ b/src/attrib-server.h @@ -37,5 +37,6 @@ int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid, uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle, const char *name); void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle); +GAttrib *attrib_from_device(struct btd_device *device); guint attrib_channel_attach(GAttrib *attrib); gboolean attrib_channel_detach(GAttrib *attrib, guint id); diff --git a/src/bluetooth.conf b/src/bluetooth.conf index 0495200..ad8891a 100644 --- a/src/bluetooth.conf +++ b/src/bluetooth.conf @@ -18,6 +18,7 @@ + diff --git a/src/device.c b/src/device.c index 18543ee..8222610 100644 --- a/src/device.c +++ b/src/device.c @@ -42,10 +42,10 @@ #include #include #include -#include #include "log.h" +#include "btio/btio.h" #include "lib/uuid.h" #include "lib/mgmt.h" #include "attrib/att.h" @@ -58,10 +58,11 @@ #include "service.h" #include "dbus-common.h" #include "error.h" -#include "glib-helper.h" +#include "uuid-helper.h" #include "sdp-client.h" #include "attrib/gatt.h" #include "agent.h" +#include "textfile.h" #include "storage.h" #include "attrib-server.h" @@ -84,6 +85,7 @@ struct bonding_req { DBusMessage *msg; guint listener_id; struct btd_device *device; + uint8_t bdaddr_type; struct agent *agent; struct btd_adapter_pin_cb_iter *cb_iter; uint8_t status; @@ -118,6 +120,7 @@ struct browse_req { int search_uuid; int reconnect_attempt; guint listener_id; + uint16_t sdp_flags; }; struct included_search { @@ -150,14 +153,23 @@ struct att_callbacks { gpointer user_data; }; +/* Per-bearer (LE or BR/EDR) device state */ +struct bearer_state { + bool paired; + bool bonded; + bool connected; + bool svc_resolved; +}; + struct btd_device { int ref_count; bdaddr_t bdaddr; uint8_t bdaddr_type; char *path; + bool bredr; + bool le; bool pending_paired; /* "Paired" waiting for SDP */ - bool svc_resolved; bool svc_refreshed; GSList *svc_callbacks; GSList *eir_uuids; @@ -190,14 +202,16 @@ struct btd_device { GSList *attios_offline; guint attachid; /* Attrib server attach */ - gboolean connected; + struct bearer_state bredr_state; + struct bearer_state le_state; sdp_list_t *tmp_records; + time_t bredr_seen; + time_t le_seen; + gboolean trusted; - gboolean paired; gboolean blocked; - gboolean bonded; gboolean auto_connect; gboolean disable_auto_connect; gboolean general_connect; @@ -220,6 +234,15 @@ static const uint16_t uuid_list[] = { static int device_browse_primary(struct btd_device *device, DBusMessage *msg); static int device_browse_sdp(struct btd_device *device, DBusMessage *msg); +static struct bearer_state *get_state(struct btd_device *dev, + uint8_t bdaddr_type) +{ + if (bdaddr_type == BDADDR_BREDR) + return &dev->bredr_state; + else + return &dev->le_state; +} + static GSList *find_service_with_profile(GSList *list, struct btd_profile *p) { GSList *l; @@ -249,6 +272,31 @@ static GSList *find_service_with_state(GSList *list, return NULL; } +static void update_technologies(GKeyFile *file, struct btd_device *dev) +{ + const char *list[2]; + size_t len = 0; + + if (dev->bredr) + list[len++] = "BR/EDR"; + + if (dev->le) { + const char *type; + + if (dev->bdaddr_type == BDADDR_LE_PUBLIC) + type = "public"; + else + type = "static"; + + g_key_file_set_string(file, "General", "AddressType", type); + + list[len++] = "LE"; + } + + g_key_file_set_string_list(file, "General", "SupportedTechnologies", + list, len); +} + static gboolean store_device_info_cb(gpointer user_data) { struct btd_device *device = user_data; @@ -294,34 +342,7 @@ static gboolean store_device_info_cb(gpointer user_data) g_key_file_remove_key(key_file, "General", "Appearance", NULL); } - switch (device->bdaddr_type) { - case BDADDR_BREDR: - g_key_file_set_string(key_file, "General", - "SupportedTechnologies", "BR/EDR"); - g_key_file_remove_key(key_file, "General", - "AddressType", NULL); - break; - - case BDADDR_LE_PUBLIC: - g_key_file_set_string(key_file, "General", - "SupportedTechnologies", "LE"); - g_key_file_set_string(key_file, "General", - "AddressType", "public"); - break; - - case BDADDR_LE_RANDOM: - g_key_file_set_string(key_file, "General", - "SupportedTechnologies", "LE"); - g_key_file_set_string(key_file, "General", - "AddressType", "static"); - break; - - default: - g_key_file_remove_key(key_file, "General", - "SupportedTechnologies", NULL); - g_key_file_remove_key(key_file, "General", - "AddressType", NULL); - } + update_technologies(key_file, device); g_key_file_set_boolean(key_file, "General", "Trusted", device->trusted); @@ -532,28 +553,22 @@ static void device_free(gpointer user_data) g_free(device->path); g_free(device->alias); - g_free(device->modalias); + free(device->modalias); g_free(device); } -gboolean device_is_bredr(struct btd_device *device) +bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type) { - return (device->bdaddr_type == BDADDR_BREDR); -} + struct bearer_state *state = get_state(device, bdaddr_type); -gboolean device_is_le(struct btd_device *device) -{ - return (device->bdaddr_type != BDADDR_BREDR); + return state->paired; } -gboolean device_is_paired(struct btd_device *device) +bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type) { - return device->paired; -} + struct bearer_state *state = get_state(device, bdaddr_type); -gboolean device_is_bonded(struct btd_device *device) -{ - return device->bonded; + return state->bonded; } gboolean device_is_trusted(struct btd_device *device) @@ -753,8 +768,13 @@ static gboolean dev_property_get_icon(const GDBusPropertyTable *property, static gboolean dev_property_get_paired(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { - struct btd_device *device = data; - gboolean val = device_is_paired(device); + struct btd_device *dev = data; + dbus_bool_t val; + + if (dev->bredr_state.paired || dev->le_state.paired) + val = TRUE; + else + val = FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); @@ -890,10 +910,15 @@ static void dev_property_set_blocked(const GDBusPropertyTable *property, static gboolean dev_property_get_connected(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { - struct btd_device *device = data; + struct btd_device *dev = data; + dbus_bool_t connected; - dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, - &device->connected); + if (dev->bredr_state.connected || dev->le_state.connected) + connected = TRUE; + else + connected = FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected); return TRUE; } @@ -901,19 +926,19 @@ static gboolean dev_property_get_connected(const GDBusPropertyTable *property, static gboolean dev_property_get_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { - struct btd_device *device = data; + struct btd_device *dev = data; DBusMessageIter entry; GSList *l; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &entry); - if (device->svc_resolved) - l = device->uuids; - else if (device->eir_uuids) - l = device->eir_uuids; + if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved) + l = dev->uuids; + else if (dev->eir_uuids) + l = dev->eir_uuids; else - l = device->uuids; + l = dev->uuids; for (; l != NULL; l = l->next) dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, @@ -957,13 +982,18 @@ static gboolean dev_property_get_adapter(const GDBusPropertyTable *property, return TRUE; } -static gboolean do_disconnect(gpointer user_data) +static gboolean disconnect_all(gpointer user_data) { struct btd_device *device = user_data; device->disconn_timer = 0; - btd_adapter_disconnect_device(device->adapter, &device->bdaddr, + if (device->bredr_state.connected) + btd_adapter_disconnect_device(device->adapter, &device->bdaddr, + BDADDR_BREDR); + + if (device->le_state.connected) + btd_adapter_disconnect_device(device->adapter, &device->bdaddr, device->bdaddr_type); return FALSE; @@ -976,8 +1006,7 @@ int device_block(struct btd_device *device, gboolean update_only) if (device->blocked) return 0; - if (device->connected) - do_disconnect(device); + disconnect_all(device); while (device->services != NULL) { struct btd_service *service = device->services->data; @@ -986,9 +1015,16 @@ int device_block(struct btd_device *device, gboolean update_only) service_remove(service); } - if (!update_only) - err = btd_adapter_block_address(device->adapter, - &device->bdaddr, device->bdaddr_type); + if (!update_only) { + if (device->le) + err = btd_adapter_block_address(device->adapter, + &device->bdaddr, + device->bdaddr_type); + if (!err && device->bredr) + err = btd_adapter_block_address(device->adapter, + &device->bdaddr, + BDADDR_BREDR); + } if (err < 0) return err; @@ -1071,7 +1107,7 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg) device->connect = NULL; } - if (device->connected && msg) + if (btd_device_is_connected(device) && msg) device->disconnects = g_slist_append(device->disconnects, dbus_message_ref(msg)); @@ -1099,14 +1135,15 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg) g_free(data); } - if (!device->connected) { + if (!btd_device_is_connected(device)) { if (msg) g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID); return; } device->disconn_timer = g_timeout_add_seconds(DISCONNECT_TIMER, - do_disconnect, device); + disconnect_all, + device); } static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg, @@ -1157,7 +1194,7 @@ static void device_profile_connected(struct btd_device *dev, if (dev->pending == NULL) return; - if (!dev->connected) { + if (!btd_device_is_connected(dev)) { switch (-err) { case EHOSTDOWN: /* page timeout */ case EHOSTUNREACH: /* adapter not powered */ @@ -1199,7 +1236,7 @@ done: btd_error_failed(dev->connect, strerror(-err))); else { /* Start passive SDP discovery to update known services */ - if (device_is_bredr(dev) && !dev->svc_refreshed) + if (dev->bredr && !dev->svc_refreshed) device_browse_sdp(dev, NULL); g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID); } @@ -1216,7 +1253,7 @@ void device_add_eir_uuids(struct btd_device *dev, GSList *uuids) GSList *l; bool added = false; - if (dev->svc_resolved) + if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved) return; for (l = uuids; l != NULL; l = l->next) { @@ -1294,9 +1331,33 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid) return dev->pending; } -static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg, - const char *uuid) +int btd_device_connect_services(struct btd_device *dev, GSList *services) { + GSList *l; + + if (dev->pending || dev->connect || dev->browse) + return -EBUSY; + + if (!btd_adapter_get_powered(dev->adapter)) + return -ENETDOWN; + + if (!dev->bredr_state.svc_resolved) + return -ENOENT; + + for (l = services; l; l = g_slist_next(l)) { + struct btd_service *service = l->data; + + dev->pending = g_slist_append(dev->pending, + btd_service_ref(service)); + } + + return connect_next(dev); +} + +static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type, + DBusMessage *msg, const char *uuid) +{ + struct bearer_state *state = get_state(dev, bdaddr_type); int err; DBG("%s %s, client %s", dev->path, uuid ? uuid : "(all)", @@ -1310,7 +1371,7 @@ static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg, btd_device_set_temporary(dev, FALSE); - if (!dev->svc_resolved) + if (!state->svc_resolved) goto resolve_services; dev->pending = create_pending_list(dev, uuid); @@ -1337,7 +1398,7 @@ static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg, resolve_services: DBG("Resolving services for %s", dev->path); - if (device_is_bredr(dev)) + if (bdaddr_type == BDADDR_BREDR) err = device_browse_sdp(dev, msg); else err = device_browse_primary(dev, msg); @@ -1347,15 +1408,55 @@ resolve_services: return NULL; } +#define NVAL_TIME ((time_t) -1) +#define SEEN_TRESHHOLD 300 + +static uint8_t select_conn_bearer(struct btd_device *dev) +{ + time_t bredr_last = NVAL_TIME, le_last = NVAL_TIME; + time_t current = time(NULL); + + if (dev->bredr_seen) { + bredr_last = current - dev->bredr_seen; + if (bredr_last > SEEN_TRESHHOLD) + bredr_last = NVAL_TIME; + } + + if (dev->le_seen) { + le_last = current - dev->le_seen; + if (le_last > SEEN_TRESHHOLD) + le_last = NVAL_TIME; + } + + if (dev->bredr && (!dev->le || le_last == NVAL_TIME)) + return BDADDR_BREDR; + + if (dev->le && (!dev->bredr || bredr_last == NVAL_TIME)) + return dev->bdaddr_type; + + if (bredr_last < le_last) + return BDADDR_BREDR; + + return dev->bdaddr_type; +} + static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_device *dev = user_data; + uint8_t bdaddr_type; - if (device_is_le(dev)) { + if (dev->bredr_state.connected) + bdaddr_type = dev->bdaddr_type; + else if (dev->le_state.connected && dev->bredr) + bdaddr_type = BDADDR_BREDR; + else + bdaddr_type = select_conn_bearer(dev); + + if (bdaddr_type != BDADDR_BREDR) { int err; - if (btd_device_is_connected(dev)) + if (dev->le_state.connected) return dbus_message_new_method_return(msg); btd_device_set_temporary(dev, FALSE); @@ -1371,7 +1472,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg, return NULL; } - return connect_profiles(dev, msg, NULL); + return connect_profiles(dev, bdaddr_type, msg, NULL); } static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg, @@ -1387,8 +1488,8 @@ static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg, return btd_error_invalid_args(msg); uuid = bt_name2string(pattern); - reply = connect_profiles(dev, msg, uuid); - g_free(uuid); + reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid); + free(uuid); return reply; } @@ -1429,7 +1530,7 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg, return btd_error_invalid_args(msg); service = find_connectable_service(dev, uuid); - g_free(uuid); + free(uuid); if (!service) return btd_error_invalid_args(msg); @@ -1452,21 +1553,23 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg, return btd_error_failed(msg, strerror(-err)); } -static void device_svc_resolved(struct btd_device *dev, int err) +static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type, + int err) { + struct bearer_state *state = get_state(dev, bdaddr_type); DBusMessage *reply; struct browse_req *req = dev->browse; DBG("%s err %d", dev->path, err); - dev->svc_resolved = true; + state->svc_resolved = true; dev->browse = NULL; /* Disconnection notification can happen before this function * gets called, so don't set svc_refreshed for a disconnected * device. */ - if (dev->connected) + if (state->connected) dev->svc_refreshed = true; g_slist_free_full(dev->eir_uuids, g_free); @@ -1491,6 +1594,9 @@ static void device_svc_resolved(struct btd_device *dev, int err) g_free(cb); } + if (!dev->temporary) + store_device_info(dev); + if (!req || !req->msg) return; @@ -1523,6 +1629,7 @@ static void device_svc_resolved(struct btd_device *dev, int err) static struct bonding_req *bonding_request_new(DBusMessage *msg, struct btd_device *device, + uint8_t bdaddr_type, struct agent *agent) { struct bonding_req *bonding; @@ -1534,6 +1641,7 @@ static struct bonding_req *bonding_request_new(DBusMessage *msg, bonding = g_new0(struct bonding_req, 1); bonding->msg = dbus_message_ref(msg); + bonding->bdaddr_type = bdaddr_type; bonding->cb_iter = btd_adapter_pin_cb_iter_new(device->adapter); @@ -1609,6 +1717,8 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, { struct btd_device *device = data; struct btd_adapter *adapter = device->adapter; + struct bearer_state *state; + uint8_t bdaddr_type; const char *sender; struct agent *agent; struct bonding_req *bonding; @@ -1623,7 +1733,16 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, if (device->bonding) return btd_error_in_progress(msg); - if (device_is_bonded(device)) + if (device->bredr_state.bonded) + bdaddr_type = device->bdaddr_type; + else if (device->le_state.bonded) + bdaddr_type = BDADDR_BREDR; + else + bdaddr_type = select_conn_bearer(device); + + state = get_state(device, bdaddr_type); + + if (state->bonded) return btd_error_already_exists(msg); sender = dbus_message_get_sender(msg); @@ -1634,7 +1753,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, else io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT; - bonding = bonding_request_new(msg, device, agent); + bonding = bonding_request_new(msg, device, bdaddr_type, agent); if (agent) agent_unref(agent); @@ -1651,11 +1770,17 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, * channel first and only then start pairing (there's code for * this in the ATT connect callback) */ - if (device_is_le(device) && !btd_device_is_connected(device)) - err = device_connect_le(device); - else + if (bdaddr_type != BDADDR_BREDR) { + if (!state->connected) + err = device_connect_le(device); + else + err = adapter_create_bonding(adapter, &device->bdaddr, + device->bdaddr_type, + io_cap); + } else { err = adapter_create_bonding(adapter, &device->bdaddr, - device->bdaddr_type, io_cap); + BDADDR_BREDR, io_cap); + } if (err < 0) return btd_error_failed(msg, strerror(-err)); @@ -1710,7 +1835,6 @@ static void bonding_request_free(struct bonding_req *bonding) g_free(bonding->cb_iter); if (bonding->agent) { - agent_cancel(bonding->agent); agent_unref(bonding->agent); bonding->agent = NULL; } @@ -1797,38 +1921,54 @@ static const GDBusPropertyTable device_properties[] = { { } }; -gboolean btd_device_is_connected(struct btd_device *device) +uint8_t btd_device_get_bdaddr_type(struct btd_device *dev) +{ + return dev->bdaddr_type; +} + +bool btd_device_is_connected(struct btd_device *dev) { - return device->connected; + return dev->bredr_state.connected || dev->le_state.connected; } -void device_add_connection(struct btd_device *device) +void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type) { - if (device->connected) { + struct bearer_state *state = get_state(dev, bdaddr_type); + + device_update_last_seen(dev, bdaddr_type); + + if (state->connected) { char addr[18]; - ba2str(&device->bdaddr, addr); + ba2str(&dev->bdaddr, addr); error("Device %s is already connected", addr); return; } - device->connected = TRUE; + /* If this is the first connection over this bearer */ + if (bdaddr_type == BDADDR_BREDR) + device_set_bredr_support(dev); + else + device_set_le_support(dev, bdaddr_type); - g_dbus_emit_property_changed(dbus_conn, device->path, - DEVICE_INTERFACE, "Connected"); + state->connected = true; + + if (dev->le_state.connected && dev->bredr_state.connected) + return; + + g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, + "Connected"); } -void device_remove_connection(struct btd_device *device) +void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type) { - if (!device->connected) { - char addr[18]; - ba2str(&device->bdaddr, addr); - error("Device %s isn't connected", addr); + struct bearer_state *state = get_state(device, bdaddr_type); + + if (!state->connected) return; - } - device->connected = FALSE; - device->general_connect = FALSE; + state->connected = false; device->svc_refreshed = false; + device->general_connect = FALSE; if (device->disconn_timer > 0) { g_source_remove(device->disconn_timer); @@ -1843,8 +1983,12 @@ void device_remove_connection(struct btd_device *device) dbus_message_unref(msg); } - if (device_is_paired(device) && !device_is_bonded(device)) - device_set_paired(device, FALSE); + if (state->paired && !state->bonded) + btd_adapter_remove_bonding(device->adapter, &device->bdaddr, + bdaddr_type); + + if (device->bredr_state.connected || device->le_state.connected) + return; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Connected"); @@ -1924,8 +2068,6 @@ static void load_info(struct btd_device *device, const char *local, char **uuids; int source, vendor, product, version; char **techno, **t; - gboolean bredr = FALSE; - gboolean le = FALSE; /* Load device name from storage info file, if that fails fall back to * the cache. @@ -1971,18 +2113,16 @@ static void load_info(struct btd_device *device, const char *local, for (t = techno; *t; t++) { if (g_str_equal(*t, "BR/EDR")) - bredr = TRUE; + device->bredr = true; else if (g_str_equal(*t, "LE")) - le = TRUE; + device->le = true; else error("Unknown device technology"); } - if (bredr && le) { - /* TODO: Add correct type for dual mode device */ - } else if (bredr) { + if (!device->le) { device->bdaddr_type = BDADDR_BREDR; - } else if (le) { + } else { str = g_key_file_get_string(key_file, "General", "AddressType", NULL); @@ -2029,7 +2169,7 @@ next: g_strfreev(uuids); /* Discovered services restored from storage */ - device->svc_resolved = true; + device->bredr_state.svc_resolved = true; } /* Load device id */ @@ -2129,7 +2269,7 @@ static void load_att_info(struct btd_device *device, const char *local, service_uuid = bt_uuid2string(&uuid); memcpy(prim->uuid, service_uuid, MAX_LEN_UUID_STR); - g_free(service_uuid); + free(service_uuid); g_free(str); device->primaries = g_slist_append(device->primaries, prim); @@ -2137,7 +2277,7 @@ static void load_att_info(struct btd_device *device, const char *local, g_strfreev(groups); g_key_file_free(key_file); - g_free(prim_uuid); + free(prim_uuid); } static struct btd_device *device_new(struct btd_adapter *adapter, @@ -2214,6 +2354,12 @@ struct btd_device *device_create(struct btd_adapter *adapter, return NULL; device->bdaddr_type = bdaddr_type; + + if (bdaddr_type == BDADDR_BREDR) + device->bredr = true; + else + device->le = true; + sba = btd_adapter_get_address(adapter); ba2str(sba, src); @@ -2270,7 +2416,10 @@ void btd_device_device_set_name(struct btd_device *device, const char *name) void device_get_name(struct btd_device *device, char *name, size_t len) { - strncpy(name, device->name, len); + if (name != NULL && len > 0) { + strncpy(name, device->name, len - 1); + name[len - 1] = '\0'; + } } bool device_name_known(struct btd_device *device) @@ -2295,6 +2444,90 @@ void device_set_class(struct btd_device *device, uint32_t class) DEVICE_INTERFACE, "Icon"); } +void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr, + uint8_t bdaddr_type) +{ + if (!bacmp(bdaddr, &device->bdaddr) && + bdaddr_type == device->bdaddr_type) + return; + + /* Since this function is only used for LE SMP Identity + * Resolving purposes we can now assume LE is supported. + */ + device->le = true; + + bacpy(&device->bdaddr, bdaddr); + device->bdaddr_type = bdaddr_type; + + store_device_info(device); + + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "Address"); +} + +void device_set_bredr_support(struct btd_device *device) +{ + if (device->bredr) + return; + + device->bredr = true; + store_device_info(device); +} + +void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type) +{ + if (device->le) + return; + + device->le = true; + device->bdaddr_type = bdaddr_type; + + store_device_info(device); +} + +void device_update_last_seen(struct btd_device *device, uint8_t bdaddr_type) +{ + if (bdaddr_type == BDADDR_BREDR) + device->bredr_seen = time(NULL); + else + device->le_seen = time(NULL); +} + +/* It is possible that we have two device objects for the same device in + * case it has first been discovered over BR/EDR and has a private + * address when discovered over LE for the first time. In such a case we + * need to inherit critical values from the duplicate so that we don't + * ovewrite them when writing to storage. The next time bluetoothd + * starts the device will show up as a single instance. + */ +void device_merge_duplicate(struct btd_device *dev, struct btd_device *dup) +{ + GSList *l; + + DBG(""); + + dev->bredr = dup->bredr; + + dev->trusted = dup->trusted; + dev->blocked = dup->blocked; + + for (l = dup->uuids; l; l = g_slist_next(l)) + dev->uuids = g_slist_append(dev->uuids, g_strdup(l->data)); + + if (dev->name[0] == '\0') + strcpy(dev->name, dup->name); + + if (!dev->alias) + dev->alias = g_strdup(dup->alias); + + dev->class = dup->class; + + dev->vendor_src = dup->vendor_src; + dev->vendor = dup->vendor; + dev->product = dup->product; + dev->version = dup->version; +} + uint32_t btd_device_get_class(struct btd_device *device) { return device->class; @@ -2351,7 +2584,6 @@ static void delete_folder_tree(const char *dirname) static void device_remove_stored(struct btd_device *device) { const bdaddr_t *src = btd_adapter_get_address(device->adapter); - uint8_t dst_type = device->bdaddr_type; char adapter_addr[18]; char device_addr[18]; char filename[PATH_MAX + 1]; @@ -2359,13 +2591,21 @@ static void device_remove_stored(struct btd_device *device) char *data; gsize length = 0; - if (device_is_bonded(device)) { - device_set_bonded(device, FALSE); - device->paired = FALSE; + if (device->bredr_state.bonded) { + device->bredr_state.bonded = false; btd_adapter_remove_bonding(device->adapter, &device->bdaddr, - dst_type); + BDADDR_BREDR); + } + + if (device->le_state.bonded) { + device->le_state.bonded = false; + btd_adapter_remove_bonding(device->adapter, &device->bdaddr, + device->bdaddr_type); } + device->bredr_state.paired = false; + device->le_state.paired = false; + if (device->blocked) device_unblock(device, TRUE, FALSE); @@ -2402,7 +2642,7 @@ void device_remove(struct btd_device *device, gboolean remove_stored) if (device->bonding) { uint8_t status; - if (device->connected) + if (device->bredr_state.connected) status = MGMT_STATUS_DISCONNECTED; else status = MGMT_STATUS_CONNECT_FAILED; @@ -2423,8 +2663,8 @@ void device_remove(struct btd_device *device, gboolean remove_stored) g_slist_free(device->pending); device->pending = NULL; - if (device->connected) - do_disconnect(device); + if (btd_device_is_connected(device)) + disconnect_all(device); if (device->store_id > 0) { g_source_remove(device->store_id); @@ -2458,6 +2698,47 @@ int device_bdaddr_cmp(gconstpointer a, gconstpointer b) return bacmp(&device->bdaddr, bdaddr); } +static bool addr_is_public(uint8_t addr_type) +{ + if (addr_type == BDADDR_BREDR || addr_type == BDADDR_LE_PUBLIC) + return true; + + return false; +} + +int device_addr_type_cmp(gconstpointer a, gconstpointer b) +{ + const struct btd_device *dev = a; + const struct device_addr_type *addr = b; + int cmp; + + cmp = bacmp(&dev->bdaddr, &addr->bdaddr); + + /* + * Address matches and both old and new are public addresses + * (doesn't matter whether LE or BR/EDR, then consider this a + * match. + */ + if (!cmp && addr_is_public(addr->bdaddr_type) && + addr_is_public(dev->bdaddr_type)) + return 0; + + if (addr->bdaddr_type == BDADDR_BREDR) { + if (!dev->bredr) + return -1; + + return cmp; + } + + if (!dev->le) + return -1; + + if (addr->bdaddr_type != dev->bdaddr_type) + return -1; + + return cmp; +} + static gboolean record_has_uuid(const sdp_record_t *rec, const char *profile_uuid) { @@ -2473,7 +2754,7 @@ static gboolean record_has_uuid(const sdp_record_t *rec, ret = strcasecmp(uuid, profile_uuid); - g_free(uuid); + free(uuid); if (ret == 0) return TRUE; @@ -2504,7 +2785,6 @@ static bool device_match_profile(struct btd_device *device, struct probe_data { struct btd_device *dev; GSList *uuids; - char addr[18]; }; static void dev_probe(struct btd_profile *p, void *user_data) @@ -2579,15 +2859,16 @@ void device_probe_profiles(struct btd_device *device, GSList *uuids) { struct probe_data d = { device, uuids }; GSList *l; + char addr[18]; - ba2str(&device->bdaddr, d.addr); + ba2str(&device->bdaddr, addr); if (device->blocked) { - DBG("Skipping profiles for blocked device %s", d.addr); + DBG("Skipping profiles for blocked device %s", addr); goto add_uuids; } - DBG("Probing profiles for device %s", d.addr); + DBG("Probing profiles for device %s", addr); btd_profile_foreach(dev_probe, &d); @@ -2675,8 +2956,8 @@ static void store_primaries_from_sdp_record(GKeyFile *key_file, g_key_file_set_integer(key_file, handle, "EndGroupHandle", end); done: - g_free(prim_uuid); - g_free(att_uuid); + free(prim_uuid); + free(att_uuid); } static int rec_cmp(const void *a, const void *b) @@ -2799,7 +3080,7 @@ static void update_bredr_services(struct browse_req *req, sdp_list_t *recs) store_primaries_from_sdp_record(att_key_file, rec); next: - g_free(profile_uuid); + free(profile_uuid); sdp_list_free(svcclass, free); } @@ -2888,7 +3169,7 @@ static GSList *device_services_from_record(struct btd_device *device, prim_list = g_slist_append(prim_list, prim); } - g_free(att_uuid); + free(att_uuid); return prim_list; } @@ -2939,11 +3220,7 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data) DEVICE_INTERFACE, "UUIDs"); send_reply: - device_svc_resolved(device, err); - - if (!device->temporary) - store_device_info(device); - + device_svc_resolved(device, BDADDR_BREDR, err); browse_request_free(req); } @@ -2971,7 +3248,8 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data) sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); bt_search_service(btd_adapter_get_address(adapter), &device->bdaddr, &uuid, - browse_cb, user_data, NULL); + browse_cb, user_data, NULL, + req->sdp_flags); return; } @@ -3049,7 +3327,7 @@ static void store_services(struct btd_device *device) g_file_set_contents(filename, data, length, NULL); } - g_free(prim_uuid); + free(prim_uuid); g_free(data); g_key_file_free(key_file); } @@ -3126,7 +3404,7 @@ static void register_all_services(struct browse_req *req, GSList *services) g_slist_free_full(device->primaries, g_free); device->primaries = NULL; - device_register_primaries(device, g_slist_copy(services), -1); + device_register_primaries(device, services, -1); device_probe_profiles(device, req->profiles_added); @@ -3136,7 +3414,7 @@ static void register_all_services(struct browse_req *req, GSList *services) g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "UUIDs"); - device_svc_resolved(device, 0); + device_svc_resolved(device, device->bdaddr_type, 0); store_services(device); @@ -3151,29 +3429,66 @@ static int service_by_range_cmp(gconstpointer a, gconstpointer b) return memcmp(&prim->range, range, sizeof(*range)); } -static void find_included_cb(GSList *includes, uint8_t status, - gpointer user_data) +static void send_le_browse_response(struct browse_req *req) +{ + struct btd_device *dev = req->device; + struct bearer_state *state = &dev->le_state; + DBusMessage *reply, *msg = req->msg; + + if (!msg) + return; + + if (!dbus_message_is_method_call(msg, DEVICE_INTERFACE, "Pair")) { + reply = btd_error_failed(msg, "Service discovery failed"); + g_dbus_send_message(dbus_conn, reply); + return; + } + + if (!state->paired) { + reply = btd_error_failed(msg, "Not paired"); + g_dbus_send_message(dbus_conn, reply); + return; + } + + if (!dev->bredr_state.paired && dev->pending_paired) { + g_dbus_emit_property_changed(dbus_conn, dev->path, + DEVICE_INTERFACE, "Paired"); + dev->pending_paired = false; + } + + g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID); +} + +static void find_included_cb(uint8_t status, GSList *includes, void *user_data) { struct included_search *search = user_data; struct btd_device *device = search->req->device; struct gatt_primary *prim; GSList *l; - if (device->attrib == NULL) { - error("Disconnected while doing included discovery"); - g_slist_free(search->services); - g_free(search); - return; - } + DBG("status %u", status); + + if (device->attrib == NULL || status) { + struct browse_req *req = device->browse; - if (status != 0) { - error("Find included services failed: %s (%d)", + if (status) + error("Find included services failed: %s (%d)", att_ecode2str(status), status); - goto done; + else + error("Disconnected while doing included discovery"); + + if (!req) + goto complete; + + send_le_browse_response(req); + device->browse = NULL; + browse_request_free(req); + + goto complete; } if (includes == NULL) - goto done; + goto next; for (l = includes; l; l = l->next) { struct gatt_included *incl = l->data; @@ -3189,18 +3504,22 @@ static void find_included_cb(GSList *includes, uint8_t status, search->services = g_slist_append(search->services, prim); } -done: +next: search->current = search->current->next; if (search->current == NULL) { register_all_services(search->req, search->services); - g_slist_free(search->services); - g_free(search); - return; + search->services = NULL; + goto complete; } prim = search->current->data; gatt_find_included(device->attrib, prim->range.start, prim->range.end, find_included_cb, search); + return; + +complete: + g_slist_free_full(search->services, g_free); + g_free(search); } static void find_included_services(struct browse_req *req, GSList *services) @@ -3208,13 +3527,31 @@ static void find_included_services(struct browse_req *req, GSList *services) struct btd_device *device = req->device; struct included_search *search; struct gatt_primary *prim; + GSList *l; + + DBG("service count %u", g_slist_length(services)); - if (services == NULL) + if (services == NULL) { + DBG("No services found"); + register_all_services(req, NULL); return; + } search = g_new0(struct included_search, 1); search->req = req; - search->services = g_slist_copy(services); + + /* We have to completely duplicate the data in order to have a + * clearly defined responsibility of freeing regardless of + * failure or success. Otherwise memory leaks are inevitable. + */ + for (l = services; l; l = g_slist_next(l)) { + struct gatt_primary *dup; + + dup = g_memdup(l->data, sizeof(struct gatt_primary)); + + search->services = g_slist_append(search->services, dup); + } + search->current = search->services; prim = search->current->data; @@ -3222,20 +3559,16 @@ static void find_included_services(struct browse_req *req, GSList *services) find_included_cb, search); } -static void primary_cb(GSList *services, guint8 status, gpointer user_data) +static void primary_cb(uint8_t status, GSList *services, void *user_data) { struct browse_req *req = user_data; + DBG("status %u", status); + if (status) { struct btd_device *device = req->device; - if (req->msg) { - DBusMessage *reply; - reply = btd_error_failed(req->msg, - att_ecode2str(status)); - g_dbus_send_message(dbus_conn, reply); - } - + send_le_browse_response(req); device->browse = NULL; browse_request_free(req); return; @@ -3244,13 +3577,36 @@ static void primary_cb(GSList *services, guint8 status, gpointer user_data) find_included_services(req, services); } +bool device_attach_attrib(struct btd_device *dev, GIOChannel *io) +{ + GAttrib *attrib; + + attrib = g_attrib_new(io); + if (!attrib) { + error("Unable to create new GAttrib instance"); + return false; + } + + dev->attachid = attrib_channel_attach(attrib); + if (dev->attachid == 0) { + g_attrib_unref(attrib); + error("Attribute server attach failure!"); + return false; + } + + dev->attrib = attrib; + dev->cleanup_id = g_io_add_watch(io, G_IO_HUP, + attrib_disconnected_cb, dev); + + return true; +} + static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) { struct att_callbacks *attcb = user_data; struct btd_device *device = attcb->user_data; DBusMessage *reply; uint8_t io_cap; - GAttrib *attrib; int err = 0; g_io_channel_unref(device->att_io); @@ -3266,14 +3622,8 @@ static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) goto done; } - attrib = g_attrib_new(io); - device->attachid = attrib_channel_attach(attrib); - if (device->attachid == 0) - error("Attribute server attach failure!"); - - device->attrib = attrib; - device->cleanup_id = g_io_add_watch(io, G_IO_HUP, - attrib_disconnected_cb, device); + if (!device_attach_attrib(device, io)) + goto done; if (attcb->success) attcb->success(user_data); @@ -3297,7 +3647,7 @@ done: } if (device->connect) { - if (!device->svc_resolved) + if (!device->le_state.svc_resolved) device_browse_primary(device, NULL); if (err < 0) @@ -3368,7 +3718,7 @@ int device_connect_le(struct btd_device *dev) attcb->success = att_success_cb; attcb->user_data = dev; - if (dev->paired) + if (dev->le_state.paired) sec_level = BT_IO_SEC_MEDIUM; else sec_level = BT_IO_SEC_LOW; @@ -3415,13 +3765,7 @@ static void att_browse_error_cb(const GError *gerr, gpointer user_data) struct btd_device *device = attcb->user_data; struct browse_req *req = device->browse; - if (req->msg) { - DBusMessage *reply; - - reply = btd_error_failed(req->msg, gerr->message); - g_dbus_send_message(dbus_conn, reply); - } - + send_le_browse_response(req); device->browse = NULL; browse_request_free(req); } @@ -3494,6 +3838,31 @@ done: return 0; } +static uint16_t get_sdp_flags(struct btd_device *device) +{ + uint16_t vid, pid; + + vid = btd_device_get_vendor(device); + pid = btd_device_get_product(device); + + /* Sony DualShock 4 is not respecting negotiated L2CAP MTU. This might + * results in SDP response being dropped by kernel. Workaround this by + * forcing SDP code to use bigger MTU while connecting. + */ + if (vid == 0x054c && pid == 0x05c4) + return SDP_LARGE_MTU; + + if (btd_adapter_ssp_enabled(device->adapter)) + return 0; + + /* if no EIR try matching Sony DualShock 4 with name and class */ + if (!strncmp(device->name, "Wireless Controller", MAX_NAME_LENGTH) && + device->class == 0x2508) + return SDP_LARGE_MTU; + + return 0; +} + static int device_browse_sdp(struct btd_device *device, DBusMessage *msg) { struct btd_adapter *adapter = device->adapter; @@ -3508,8 +3877,11 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg) req->device = device; sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); + req->sdp_flags = get_sdp_flags(device); + err = bt_search_service(btd_adapter_get_address(adapter), - &device->bdaddr, &uuid, browse_cb, req, NULL); + &device->bdaddr, &uuid, browse_cb, req, NULL, + req->sdp_flags); if (err < 0) { browse_request_free(req); return err; @@ -3536,7 +3908,7 @@ int device_discover_services(struct btd_device *device) { int err; - if (device_is_bredr(device)) + if (device->bredr) err = device_browse_sdp(device, NULL); else err = device_browse_primary(device, NULL); @@ -3609,14 +3981,17 @@ void btd_device_set_trusted(struct btd_device *device, gboolean trusted) DEVICE_INTERFACE, "Trusted"); } -void device_set_bonded(struct btd_device *device, gboolean bonded) +void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type) { if (!device) return; - DBG("bonded %d", bonded); + DBG(""); - device->bonded = bonded; + if (bdaddr_type == BDADDR_BREDR) + device->bredr_state.bonded = true; + else + device->le_state.bonded = true; } void device_set_legacy(struct btd_device *device, bool legacy) @@ -3672,7 +4047,7 @@ static void device_set_auto_connect(struct btd_device *device, gboolean enable) { char addr[18]; - if (!device) + if (!device || !device->le) return; ba2str(&device->bdaddr, addr); @@ -3700,7 +4075,7 @@ static gboolean start_discovery(gpointer user_data) { struct btd_device *device = user_data; - if (device_is_bredr(device)) + if (device->bredr) device_browse_sdp(device, NULL); else device_browse_primary(device, NULL); @@ -3710,29 +4085,75 @@ static gboolean start_discovery(gpointer user_data) return FALSE; } -void device_set_paired(struct btd_device *device, gboolean value) +void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type) { - if (device->paired == value) + struct bearer_state *state = get_state(dev, bdaddr_type); + + if (state->paired) return; - if (!value) - btd_adapter_remove_bonding(device->adapter, &device->bdaddr, - device->bdaddr_type); + state->paired = true; + + /* If the other bearer state was alraedy true we don't need to + * send any property signals. + */ + if (dev->bredr_state.paired == dev->le_state.paired) + return; - device->paired = value; + if (!state->svc_resolved) { + dev->pending_paired = true; + return; + } - if (device->paired && !device->svc_resolved) - device->pending_paired = true; - else - g_dbus_emit_property_changed(dbus_conn, device->path, + g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "Paired"); } +void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type) +{ + struct bearer_state *state = get_state(dev, bdaddr_type); + + if (!state->paired) + return; + + state->paired = false; + + /* + * If the other bearer state is still true we don't need to + * send any property signals or remove device. + */ + if (dev->bredr_state.paired != dev->le_state.paired) { + /* TODO disconnect only unpaired bearer */ + if (state->connected) + device_request_disconnect(dev, NULL); + + return; + } + + g_dbus_emit_property_changed(dbus_conn, dev->path, + DEVICE_INTERFACE, "Paired"); + + btd_device_set_temporary(dev, TRUE); + + if (btd_device_is_connected(dev)) + device_request_disconnect(dev, NULL); + else + btd_adapter_remove_device(dev->adapter, dev); +} + static void device_auth_req_free(struct btd_device *device) { - if (device->authr) - g_free(device->authr->pincode); - g_free(device->authr); + struct authentication_req *authr = device->authr; + + if (!authr) + return; + + if (authr->agent) + agent_unref(authr->agent); + + g_free(authr->pincode); + g_free(authr); + device->authr = NULL; } @@ -3743,10 +4164,12 @@ bool device_is_retrying(struct btd_device *device) return bonding && bonding->retry_timer > 0; } -void device_bonding_complete(struct btd_device *device, uint8_t status) +void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type, + uint8_t status) { struct bonding_req *bonding = device->bonding; struct authentication_req *auth = device->authr; + struct bearer_state *state = get_state(device, bdaddr_type); DBG("bonding %p status 0x%02x", bonding, status); @@ -3762,15 +4185,15 @@ void device_bonding_complete(struct btd_device *device, uint8_t status) device_auth_req_free(device); /* If we're already paired nothing more is needed */ - if (device->paired) + if (state->paired) return; - device_set_paired(device, TRUE); + device_set_paired(device, bdaddr_type); /* If services are already resolved just reply to the pairing * request */ - if (device->svc_resolved && bonding) { + if (state->svc_resolved && bonding) { g_dbus_send_reply(dbus_conn, bonding->msg, DBUS_TYPE_INVALID); bonding_request_free(bonding); return; @@ -3789,13 +4212,13 @@ void device_bonding_complete(struct btd_device *device, uint8_t status) device->discov_timer = 0; } - if (device_is_bredr(device)) + if (bdaddr_type == BDADDR_BREDR) device_browse_sdp(device, bonding->msg); else device_browse_primary(device, bonding->msg); bonding_request_free(bonding); - } else if (!device->svc_resolved) { + } else if (!state->svc_resolved) { if (!device->browse && !device->discov_timer && main_opts.reverse_sdp) { /* If we are not initiators and there is no currently @@ -3828,6 +4251,8 @@ unsigned int device_wait_for_svc_complete(struct btd_device *dev, device_svc_cb_t func, void *user_data) { + /* This API is only used for BR/EDR (for now) */ + struct bearer_state *state = &dev->bredr_state; static unsigned int id = 0; struct svc_callback *cb; @@ -3839,7 +4264,7 @@ unsigned int device_wait_for_svc_complete(struct btd_device *dev, dev->svc_callbacks = g_slist_prepend(dev->svc_callbacks, cb); - if (dev->svc_resolved || !main_opts.reverse_sdp) + if (state->svc_resolved || !main_opts.reverse_sdp) cb->idle_id = g_idle_add(svc_idle_cb, cb); else if (dev->discov_timer > 0) { g_source_remove(dev->discov_timer); @@ -3913,7 +4338,8 @@ static gboolean device_bonding_retry(gpointer data) err = adapter_bonding_attempt(adapter, &device->bdaddr, device->bdaddr_type, io_cap); if (err < 0) - device_bonding_complete(device, bonding->status); + device_bonding_complete(device, bonding->bdaddr_type, + bonding->status); return FALSE; } @@ -4392,6 +4818,9 @@ void device_set_appearance(struct btd_device *device, uint16_t value) { const char *icon = gap_appearance_to_icon(value); + if (device->appearance == value) + return; + g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Appearance"); @@ -4435,6 +4864,11 @@ guint btd_device_add_attio_callback(struct btd_device *device, device_set_auto_connect(device, TRUE); + /* Check if there is no GAttrib associated to the device created by a + * incoming connection */ + if (!device->attrib) + device->attrib = attrib_from_device(device); + if (device->attrib && cfunc) { device->attios_offline = g_slist_append(device->attios_offline, attio); @@ -4489,12 +4923,16 @@ gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id) void btd_device_set_pnpid(struct btd_device *device, uint16_t source, uint16_t vendor, uint16_t product, uint16_t version) { + if (device->vendor_src == source && device->version == version && + device->vendor == vendor && device->product == product) + return; + device->vendor_src = source; device->vendor = vendor; device->product = product; device->version = version; - g_free(device->modalias); + free(device->modalias); device->modalias = bt_modalias(source, vendor, product, version); g_dbus_emit_property_changed(dbus_conn, device->path, diff --git a/src/device.h b/src/device.h index 3a33cb2..2e0473e 100644 --- a/src/device.h +++ b/src/device.h @@ -38,6 +38,12 @@ void device_store_cached_name(struct btd_device *dev, const char *name); void device_get_name(struct btd_device *device, char *name, size_t len); bool device_name_known(struct btd_device *device); void device_set_class(struct btd_device *device, uint32_t class); +void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr, + uint8_t bdaddr_type); +void device_set_bredr_support(struct btd_device *device); +void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type); +void device_update_last_seen(struct btd_device *device, uint8_t bdaddr_type); +void device_merge_duplicate(struct btd_device *dev, struct btd_device *dup); uint32_t btd_device_get_class(struct btd_device *device); uint16_t btd_device_get_vendor(struct btd_device *device); uint16_t btd_device_get_vendor_src(struct btd_device *device); @@ -46,6 +52,14 @@ uint16_t btd_device_get_version(struct btd_device *device); void device_remove(struct btd_device *device, gboolean remove_stored); int device_address_cmp(gconstpointer a, gconstpointer b); int device_bdaddr_cmp(gconstpointer a, gconstpointer b); + +/* Struct used by device_addr_type_cmp() */ +struct device_addr_type { + bdaddr_t bdaddr; + uint8_t bdaddr_type; +}; + +int device_addr_type_cmp(gconstpointer a, gconstpointer b); GSList *btd_device_get_uuids(struct btd_device *device); void device_probe_profiles(struct btd_device *device, GSList *profiles); const sdp_record_t *btd_device_get_record(struct btd_device *device, @@ -55,6 +69,7 @@ struct gatt_primary *btd_device_get_primary(struct btd_device *device, GSList *btd_device_get_primaries(struct btd_device *device); void btd_device_gatt_set_service_changed(struct btd_device *device, uint16_t start, uint16_t end); +bool device_attach_attrib(struct btd_device *dev, GIOChannel *io); void btd_device_add_uuid(struct btd_device *device, const char *uuid); void device_add_eir_uuids(struct btd_device *dev, GSList *uuids); void device_probe_profile(gpointer a, gpointer b); @@ -62,21 +77,22 @@ void device_remove_profile(gpointer a, gpointer b); struct btd_adapter *device_get_adapter(struct btd_device *device); const bdaddr_t *device_get_address(struct btd_device *device); const char *device_get_path(const struct btd_device *device); -gboolean device_is_bredr(struct btd_device *device); -gboolean device_is_le(struct btd_device *device); gboolean device_is_temporary(struct btd_device *device); -gboolean device_is_paired(struct btd_device *device); -gboolean device_is_bonded(struct btd_device *device); +bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type); +bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type); gboolean device_is_trusted(struct btd_device *device); -void device_set_paired(struct btd_device *device, gboolean paired); +void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type); +void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type); void btd_device_set_temporary(struct btd_device *device, gboolean temporary); void btd_device_set_trusted(struct btd_device *device, gboolean trusted); -void device_set_bonded(struct btd_device *device, gboolean bonded); +void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type); void device_set_legacy(struct btd_device *device, bool legacy); void device_set_rssi(struct btd_device *device, int8_t rssi); -gboolean btd_device_is_connected(struct btd_device *device); +bool btd_device_is_connected(struct btd_device *dev); +uint8_t btd_device_get_bdaddr_type(struct btd_device *dev); bool device_is_retrying(struct btd_device *device); -void device_bonding_complete(struct btd_device *device, uint8_t status); +void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type, + uint8_t status); gboolean device_is_bonding(struct btd_device *device, const char *sender); void device_bonding_attempt_failed(struct btd_device *device, uint8_t status); void device_bonding_failed(struct btd_device *device, uint8_t status); @@ -94,8 +110,8 @@ int device_notify_pincode(struct btd_device *device, gboolean secure, const char *pincode); void device_cancel_authentication(struct btd_device *device, gboolean aborted); gboolean device_is_authenticating(struct btd_device *device); -void device_add_connection(struct btd_device *device); -void device_remove_connection(struct btd_device *device); +void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type); +void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type); void device_request_disconnect(struct btd_device *device, DBusMessage *msg); typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal, @@ -132,6 +148,7 @@ struct btd_service *btd_device_get_service(struct btd_device *dev, const char *remote_uuid); int device_discover_services(struct btd_device *device); +int btd_device_connect_services(struct btd_device *dev, GSList *services); void btd_device_init(void); void btd_device_cleanup(void); diff --git a/src/eir.c b/src/eir.c index 7745ff3..d22ad91 100644 --- a/src/eir.c +++ b/src/eir.c @@ -30,20 +30,22 @@ #include #include #include +#include #include #include #include #include -#include "glib-helper.h" +#include "src/shared/util.h" +#include "uuid-helper.h" #include "eir.h" #define EIR_OOB_MIN (2 + 6) void eir_data_free(struct eir_data *eir) { - g_slist_free_full(eir->services, g_free); + g_slist_free_full(eir->services, free); eir->services = NULL; g_free(eir->name); eir->name = NULL; @@ -63,9 +65,11 @@ static void eir_parse_uuid16(struct eir_data *eir, const void *data, service.type = SDP_UUID16; for (i = 0; i < len / 2; i++, uuid16++) { - service.value.uuid16 = bt_get_le16(uuid16); + service.value.uuid16 = get_le16(uuid16); uuid_str = bt_uuid2string(&service); + if (!uuid_str) + continue; eir->services = g_slist_append(eir->services, uuid_str); } } @@ -80,9 +84,11 @@ static void eir_parse_uuid32(struct eir_data *eir, const void *data, service.type = SDP_UUID32; for (i = 0; i < len / 4; i++, uuid32++) { - service.value.uuid32 = bt_get_le32(uuid32); + service.value.uuid32 = get_le32(uuid32); uuid_str = bt_uuid2string(&service); + if (!uuid_str) + continue; eir->services = g_slist_append(eir->services, uuid_str); } } @@ -101,6 +107,8 @@ static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data, for (k = 0; k < 16; k++) service.value.uuid128.data[k] = uuid_ptr[16 - k - 1]; uuid_str = bt_uuid2string(&service); + if (!uuid_str) + continue; eir->services = g_slist_append(eir->services, uuid_str); uuid_ptr += 16; } @@ -133,7 +141,7 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) { uint16_t len = 0; - eir->flags = -1; + eir->flags = 0; eir->tx_power = 127; /* No EIR data to parse */ @@ -208,7 +216,7 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) case EIR_GAP_APPEARANCE: if (data_len < 2) break; - eir->appearance = bt_get_le16(data); + eir->appearance = get_le16(data); break; case EIR_SSP_HASH: @@ -222,6 +230,16 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) break; eir->randomizer = g_memdup(data, 16); break; + + case EIR_DEVICE_ID: + if (data_len < 8) + break; + + eir->did_source = data[0] | (data[1] << 8); + eir->did_vendor = data[2] | (data[3] << 8); + eir->did_product = data[4] | (data[5] << 8); + eir->did_version = data[6] | (data[7] << 8); + break; } eir_data += field_len + 1; @@ -234,7 +252,7 @@ int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len) if (eir_len < EIR_OOB_MIN) return -1; - if (eir_len != bt_get_le16(eir_data)) + if (eir_len != get_le16(eir_data)) return -1; eir_data += sizeof(uint16_t); @@ -259,7 +277,7 @@ static void eir_generate_uuid128(sdp_list_t *list, uint8_t *ptr, int i, k, uuid_count = 0; uint16_t len = *eir_len; uint8_t *uuid128; - gboolean truncated = FALSE; + bool truncated = false; /* Store UUIDs in place, skip 2 bytes to write type and length later */ uuid128 = ptr + 2; @@ -274,7 +292,7 @@ static void eir_generate_uuid128(sdp_list_t *list, uint8_t *ptr, /* Stop if not enough space to put next UUID128 */ if ((len + 2 + SIZEOF_UUID128) > HCI_MAX_EIR_LENGTH) { - truncated = TRUE; + truncated = true; break; } @@ -323,7 +341,7 @@ int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod, uint16_t eir_total_len; uint16_t uuid16[HCI_MAX_EIR_LENGTH / 2]; int i, uuid_count = 0; - gboolean truncated = FALSE; + bool truncated = false; size_t name_len; eir_total_len = sizeof(uint16_t) + sizeof(bdaddr_t); @@ -418,7 +436,7 @@ int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod, /* Stop if not enough space to put next UUID16 */ if ((eir_optional_len + 2 + sizeof(uint16_t)) > HCI_MAX_EIR_LENGTH) { - truncated = TRUE; + truncated = true; break; } @@ -456,7 +474,7 @@ int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod, eir_total_len += eir_optional_len; /* store total length */ - bt_put_le16(eir_total_len, data); + put_le16(eir_total_len, data); return eir_total_len; } diff --git a/src/eir.h b/src/eir.h index 411986e..e486fa2 100644 --- a/src/eir.h +++ b/src/eir.h @@ -38,17 +38,30 @@ #define EIR_DEVICE_ID 0x10 /* device ID */ #define EIR_GAP_APPEARANCE 0x19 /* GAP appearance */ +/* 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_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) */ + struct eir_data { GSList *services; - int flags; + unsigned int flags; char *name; uint32_t class; uint16_t appearance; - gboolean name_complete; + bool name_complete; int8_t tx_power; uint8_t *hash; uint8_t *randomizer; bdaddr_t addr; + uint16_t did_vendor; + uint16_t did_product; + uint16_t did_version; + uint16_t did_source; }; void eir_data_free(struct eir_data *eir); diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c new file mode 100644 index 0000000..c22e8af --- /dev/null +++ b/src/gatt-dbus.c @@ -0,0 +1,658 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 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 +#endif + +#include +#include + +#include +#include +#include + +#include "adapter.h" +#include "device.h" +#include "lib/uuid.h" +#include "dbus-common.h" +#include "log.h" + +#include "error.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "gatt.h" +#include "gatt-dbus.h" + +#define GATT_MGR_IFACE "org.bluez.GattManager1" +#define GATT_SERVICE_IFACE "org.bluez.GattService1" +#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1" +#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1" + +struct external_service { + char *owner; + char *path; + DBusMessage *reg; + GDBusClient *client; + GSList *proxies; + struct btd_attribute *service; +}; + +struct proxy_write_data { + btd_attr_write_result_t result_cb; + void *user_data; +}; + +/* + * Attribute to Proxy hash table. Used to map incoming + * ATT operations to its external characteristic proxy. + */ +static GHashTable *proxy_hash; + +static GSList *external_services; + +static int external_service_path_cmp(gconstpointer a, gconstpointer b) +{ + const struct external_service *esvc = a; + const char *path = b; + + return g_strcmp0(esvc->path, path); +} + +static gboolean external_service_destroy(void *user_data) +{ + struct external_service *esvc = user_data; + + g_dbus_client_unref(esvc->client); + + if (esvc->reg) + dbus_message_unref(esvc->reg); + + g_free(esvc->owner); + g_free(esvc->path); + g_free(esvc); + + return FALSE; +} + +static void external_service_free(void *user_data) +{ + struct external_service *esvc = user_data; + + /* + * Set callback to NULL to avoid potential race condition + * when calling remove_service and GDBusClient unref. + */ + g_dbus_client_set_disconnect_watch(esvc->client, NULL, NULL); + + external_service_destroy(user_data); +} + +static void remove_service(DBusConnection *conn, void *user_data) +{ + struct external_service *esvc = user_data; + + external_services = g_slist_remove(external_services, esvc); + + if (esvc->service) + btd_gatt_remove_service(esvc->service); + + /* + * Do not run in the same loop, this may be a disconnect + * watch call and GDBusClient should not be destroyed. + */ + g_idle_add(external_service_destroy, esvc); +} + +static int proxy_path_cmp(gconstpointer a, gconstpointer b) +{ + GDBusProxy *proxy1 = (GDBusProxy *) a; + GDBusProxy *proxy2 = (GDBusProxy *) b; + const char *path1 = g_dbus_proxy_get_path(proxy1); + const char *path2 = g_dbus_proxy_get_path(proxy2); + + return g_strcmp0(path1, path2); +} + +static uint8_t flags_string2int(const char *proper) +{ + uint8_t value; + + /* Regular Properties: See core spec 4.1 page 2183 */ + if (!strcmp("broadcast", proper)) + value = GATT_CHR_PROP_BROADCAST; + else if (!strcmp("read", proper)) + value = GATT_CHR_PROP_READ; + else if (!strcmp("write-without-response", proper)) + value = GATT_CHR_PROP_WRITE_WITHOUT_RESP; + else if (!strcmp("write", proper)) + value = GATT_CHR_PROP_WRITE; + else if (!strcmp("notify", proper)) + value = GATT_CHR_PROP_NOTIFY; + else if (!strcmp("indicate", proper)) + value = GATT_CHR_PROP_INDICATE; + else if (!strcmp("authenticated-signed-writes", proper)) + value = GATT_CHR_PROP_AUTH; + else + value = 0; + + /* TODO: Extended properties. Ref core spec 4.1 page 2185 */ + + return value; +} + +static uint8_t flags_get_bitmask(DBusMessageIter *iter) +{ + DBusMessageIter istr; + uint8_t propmask = 0, prop; + const char *str; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + goto fail; + + dbus_message_iter_recurse(iter, &istr); + + do { + if (dbus_message_iter_get_arg_type(&istr) != DBUS_TYPE_STRING) + goto fail; + + dbus_message_iter_get_basic(&istr, &str); + prop = flags_string2int(str); + if (!prop) + goto fail; + + propmask |= prop; + } while (dbus_message_iter_next(&istr)); + + return propmask; + +fail: + error("Characteristic Flags: Invalid argument!"); + + return 0; +} + +static void proxy_added(GDBusProxy *proxy, void *user_data) +{ + struct external_service *esvc = user_data; + const char *interface, *path; + + interface = g_dbus_proxy_get_interface(proxy); + path = g_dbus_proxy_get_path(proxy); + + if (!g_str_has_prefix(path, esvc->path)) + return; + + if (g_strcmp0(interface, GATT_CHR_IFACE) != 0 && + g_strcmp0(interface, GATT_SERVICE_IFACE) != 0 && + g_strcmp0(interface, GATT_DESCRIPTOR_IFACE) != 0) + return; + + DBG("path %s iface %s", path, interface); + + /* + * Object path follows a hierarchical organization. Add the + * proxies sorted by path helps the logic to register the + * object path later. + */ + esvc->proxies = g_slist_insert_sorted(esvc->proxies, proxy, + proxy_path_cmp); +} + +static void proxy_removed(GDBusProxy *proxy, void *user_data) +{ + struct external_service *esvc = user_data; + const char *interface, *path; + + interface = g_dbus_proxy_get_interface(proxy); + path = g_dbus_proxy_get_path(proxy); + + DBG("path %s iface %s", path, interface); + + esvc->proxies = g_slist_remove(esvc->proxies, proxy); +} + +static void proxy_read_cb(struct btd_attribute *attr, + btd_attr_read_result_t result, void *user_data) +{ + DBusMessageIter iter, array; + GDBusProxy *proxy; + uint8_t *value; + int len; + + /* + * Remote device is trying to read the informed attribute, + * "Value" should be read from the proxy. GDBusProxy tracks + * properties changes automatically, it is not necessary to + * get the value directly from the GATT server. + */ + proxy = g_hash_table_lookup(proxy_hash, attr); + if (!proxy) { + result(-ENOENT, NULL, 0, user_data); + return; + } + + if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) { + /* Unusual situation, read property will checked earlier */ + result(-EPERM, NULL, 0, user_data); + return; + } + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { + DBG("External service inconsistent!"); + result(-EPERM, NULL, 0, user_data); + return; + } + + dbus_message_iter_recurse(&iter, &array); + dbus_message_iter_get_fixed_array(&array, &value, &len); + + DBG("attribute: %p read %d bytes", attr, len); + + result(0, value, len, user_data); +} + +static void proxy_write_reply(const DBusError *derr, void *user_data) +{ + struct proxy_write_data *wdata = user_data; + int err; + + /* + * Security requirements shall be handled by the core. If external + * applications returns an error, the reasons will be restricted to + * invalid argument or application specific errors. + */ + + if (!dbus_error_is_set(derr)) { + err = 0; + goto done; + } + + DBG("Write reply: %s", derr->message); + + if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) + err = -ETIMEDOUT; + else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments")) + err = -EINVAL; + else + err = -EPROTO; + +done: + if (wdata && wdata->result_cb) + wdata->result_cb(err, wdata->user_data); +} + +static void proxy_write_cb(struct btd_attribute *attr, + const uint8_t *value, size_t len, + btd_attr_write_result_t result, + void *user_data) +{ + GDBusProxy *proxy; + + proxy = g_hash_table_lookup(proxy_hash, attr); + if (!proxy) { + result(-ENOENT, user_data); + return; + } + + /* + * "result" callback defines if the core wants to receive the + * operation result, allowing to select ATT Write Request or Write + * Command. Descriptors requires Write Request operation. For + * Characteristics, the implementation will define which operations + * are allowed based on the properties/flags. + * TODO: Write Long Characteristics/Descriptors. + */ + + if (result) { + struct proxy_write_data *wdata; + + wdata = g_new0(struct proxy_write_data, 1); + wdata->result_cb = result; + wdata->user_data = user_data; + + if (!g_dbus_proxy_set_property_array(proxy, "Value", + DBUS_TYPE_BYTE, value, len, + proxy_write_reply, + wdata, g_free)) { + g_free(wdata); + result(-ENOENT, user_data); + } + } else { + /* + * Caller is not interested in the Set method call result. + * This flow implements the ATT Write Command scenario, where + * the remote doesn't receive ATT response. + */ + g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE, + value, len, proxy_write_reply, + NULL, NULL); + } + + DBG("Server: Write attribute callback %s", + g_dbus_proxy_get_path(proxy)); + +} + +static int register_external_service(struct external_service *esvc, + GDBusProxy *proxy) +{ + DBusMessageIter iter; + const char *str, *path, *iface; + bt_uuid_t uuid; + + path = g_dbus_proxy_get_path(proxy); + iface = g_dbus_proxy_get_interface(proxy); + if (g_strcmp0(esvc->path, path) != 0 || + g_strcmp0(iface, GATT_SERVICE_IFACE) != 0) + return -EINVAL; + + if (!g_dbus_proxy_get_property(proxy, "UUID", &iter)) + return -EINVAL; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return -EINVAL; + + dbus_message_iter_get_basic(&iter, &str); + + if (bt_string_to_uuid(&uuid, str) < 0) + return -EINVAL; + + esvc->service = btd_gatt_add_service(&uuid); + if (!esvc->service) + return -EINVAL; + + return 0; +} + +static int add_char(GDBusProxy *proxy, const bt_uuid_t *uuid) +{ + DBusMessageIter iter; + struct btd_attribute *attr; + btd_attr_write_t write_cb; + btd_attr_read_t read_cb; + uint8_t propmask = 0; + + /* + * Optional property. If is not informed, read and write + * procedures will be allowed. Upper-layer should handle + * characteristic requirements. + */ + if (g_dbus_proxy_get_property(proxy, "Flags", &iter)) + propmask = flags_get_bitmask(&iter); + else + propmask = GATT_CHR_PROP_WRITE_WITHOUT_RESP + | GATT_CHR_PROP_WRITE + | GATT_CHR_PROP_READ; + if (!propmask) + return -EINVAL; + + if (propmask & GATT_CHR_PROP_READ) + read_cb = proxy_read_cb; + else + read_cb = NULL; + + if (propmask & (GATT_CHR_PROP_WRITE | GATT_CHR_PROP_WRITE_WITHOUT_RESP)) + write_cb = proxy_write_cb; + else + write_cb = NULL; + + attr = btd_gatt_add_char(uuid, propmask, read_cb, write_cb); + if (!attr) + return -ENOMEM; + + g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy)); + + return 0; +} + +static int add_char_desc(GDBusProxy *proxy, const bt_uuid_t *uuid) +{ + struct btd_attribute *attr; + + attr = btd_gatt_add_char_desc(uuid, proxy_read_cb, proxy_write_cb); + if (!attr) + return -ENOMEM; + + g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy)); + + return 0; +} + +static int register_external_characteristics(GSList *proxies) + +{ + GSList *list; + + for (list = proxies; list; list = g_slist_next(list)) { + GDBusProxy *proxy = list->data; + DBusMessageIter iter; + bt_uuid_t uuid; + const char *path, *iface, *str; + int ret; + + /* Mandatory property */ + if (!g_dbus_proxy_get_property(proxy, "UUID", &iter)) + return -EINVAL; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return -EINVAL; + + dbus_message_iter_get_basic(&iter, &str); + + if (bt_string_to_uuid(&uuid, str) < 0) + return -EINVAL; + + iface = g_dbus_proxy_get_interface(proxy); + path = g_dbus_proxy_get_path(proxy); + + if (!strcmp(GATT_CHR_IFACE, iface)) + ret = add_char(proxy, &uuid); + else + ret = add_char_desc(proxy, &uuid); + + if (ret < 0) + return ret; + + DBG("Added GATT: %s (%s)", path, str); + } + + return 0; +} + +static void client_ready(GDBusClient *client, void *user_data) +{ + struct external_service *esvc = user_data; + GDBusProxy *proxy; + DBusConnection *conn = btd_get_dbus_connection(); + DBusMessage *reply; + + if (!esvc->proxies) + goto fail; + + proxy = esvc->proxies->data; + if (register_external_service(esvc, proxy) < 0) + goto fail; + + if (register_external_characteristics(g_slist_next(esvc->proxies)) < 0) + goto fail; + + DBG("Added GATT service %s", esvc->path); + + reply = dbus_message_new_method_return(esvc->reg); + g_dbus_send_message(conn, reply); + + dbus_message_unref(esvc->reg); + esvc->reg = NULL; + + return; + +fail: + error("Could not register external service: %s", esvc->path); + + /* + * Set callback to NULL to avoid potential race condition + * when calling remove_service and GDBusClient unref. + */ + g_dbus_client_set_disconnect_watch(esvc->client, NULL, NULL); + + remove_service(conn, esvc); + + reply = btd_error_invalid_args(esvc->reg); + g_dbus_send_message(conn, reply); +} + +static struct external_service *external_service_new(DBusConnection *conn, + DBusMessage *msg, const char *path) +{ + struct external_service *esvc; + GDBusClient *client; + const char *sender = dbus_message_get_sender(msg); + + client = g_dbus_client_new(conn, sender, "/"); + if (!client) + return NULL; + + esvc = g_new0(struct external_service, 1); + esvc->owner = g_strdup(sender); + esvc->reg = dbus_message_ref(msg); + esvc->client = client; + esvc->path = g_strdup(path); + + g_dbus_client_set_disconnect_watch(client, remove_service, esvc); + + g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, + NULL, esvc); + + g_dbus_client_set_ready_watch(client, client_ready, esvc); + + return esvc; +} + +static DBusMessage *register_service(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct external_service *esvc; + DBusMessageIter iter; + const char *path; + + if (!dbus_message_iter_init(msg, &iter)) + return btd_error_invalid_args(msg); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) + return btd_error_invalid_args(msg); + + dbus_message_iter_get_basic(&iter, &path); + + if (g_slist_find_custom(external_services, path, + external_service_path_cmp)) + return btd_error_already_exists(msg); + + esvc = external_service_new(conn, msg, path); + if (!esvc) + return btd_error_failed(msg, "Not enough resources"); + + external_services = g_slist_prepend(external_services, esvc); + + DBG("New service %p: %s", esvc, path); + + return NULL; +} + +static DBusMessage *unregister_service(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct external_service *esvc; + DBusMessageIter iter; + const char *path; + GSList *list; + + if (!dbus_message_iter_init(msg, &iter)) + return btd_error_invalid_args(msg); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) + return btd_error_invalid_args(msg); + + dbus_message_iter_get_basic(&iter, &path); + + list = g_slist_find_custom(external_services, path, + external_service_path_cmp); + if (!list) + return btd_error_does_not_exist(msg); + + esvc = list->data; + if (!strcmp(dbus_message_get_sender(msg), esvc->owner)) + return btd_error_does_not_exist(msg); + + /* + * Set callback to NULL to avoid potential race condition + * when calling remove_service and GDBusClient unref. + */ + g_dbus_client_set_disconnect_watch(esvc->client, NULL, NULL); + + remove_service(conn, esvc); + + return dbus_message_new_method_return(msg); +} + +static const GDBusMethodTable methods[] = { + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService", + GDBUS_ARGS({ "service", "o"}, + { "options", "a{sv}"}), + NULL, register_service) }, + { GDBUS_EXPERIMENTAL_METHOD("UnregisterService", + GDBUS_ARGS({"service", "o"}), + NULL, unregister_service) }, + { } +}; + +gboolean gatt_dbus_manager_register(void) +{ + if (!g_dbus_register_interface(btd_get_dbus_connection(), + "/org/bluez", GATT_MGR_IFACE, + methods, NULL, NULL, NULL, NULL)) + return FALSE; + + proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) g_dbus_proxy_unref); + + return TRUE; +} + +void gatt_dbus_manager_unregister(void) +{ + /* We might not have initialized if experimental features are + * not enabled. + */ + if (!proxy_hash) + return; + + g_hash_table_destroy(proxy_hash); + proxy_hash = NULL; + + g_slist_free_full(external_services, external_service_free); + + g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez", + GATT_MGR_IFACE); +} diff --git a/src/gatt-dbus.h b/src/gatt-dbus.h new file mode 100644 index 0000000..310cfa9 --- /dev/null +++ b/src/gatt-dbus.h @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 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 + * + */ + +gboolean gatt_dbus_manager_register(void); +void gatt_dbus_manager_unregister(void); diff --git a/src/gatt.c b/src/gatt.c new file mode 100644 index 0000000..3060462 --- /dev/null +++ b/src/gatt.c @@ -0,0 +1,320 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 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 +#endif + +#include +#include + +#include "log.h" +#include "lib/uuid.h" +#include "attrib/att.h" +#include "src/shared/util.h" + +#include "gatt-dbus.h" +#include "gatt.h" + +/* Common GATT UUIDs */ +static const bt_uuid_t primary_uuid = { .type = BT_UUID16, + .value.u16 = GATT_PRIM_SVC_UUID }; + +static const bt_uuid_t chr_uuid = { .type = BT_UUID16, + .value.u16 = GATT_CHARAC_UUID }; + +struct btd_attribute { + uint16_t handle; + bt_uuid_t type; + btd_attr_read_t read_cb; + btd_attr_write_t write_cb; + uint16_t value_len; + uint8_t value[0]; +}; + +static GList *local_attribute_db; +static uint16_t next_handle = 0x0001; + +static inline void put_uuid_le(const bt_uuid_t *src, void *dst) +{ + if (src->type == BT_UUID16) + put_le16(src->value.u16, dst); + else if (src->type == BT_UUID32) + put_le32(src->value.u32, dst); + else + /* Convert from 128-bit BE to LE */ + bswap_128(&src->value.u128, dst); +} + +/* + * Helper function to create new attributes containing constant/static values. + * eg: declaration of services/characteristics, and characteristics with + * fixed values. + */ +static struct btd_attribute *new_const_attribute(const bt_uuid_t *type, + const uint8_t *value, + uint16_t len) +{ + struct btd_attribute *attr; + + attr = malloc0(sizeof(struct btd_attribute) + len); + if (!attr) + return NULL; + + attr->type = *type; + memcpy(&attr->value, value, len); + attr->value_len = len; + + return attr; +} + +static struct btd_attribute *new_attribute(const bt_uuid_t *type, + btd_attr_read_t read_cb, + btd_attr_write_t write_cb) +{ + struct btd_attribute *attr; + + attr = new0(struct btd_attribute, 1); + if (!attr) + return NULL; + + attr->type = *type; + attr->read_cb = read_cb; + attr->write_cb = write_cb; + + return attr; +} + +static bool is_service(const struct btd_attribute *attr) +{ + if (attr->type.type != BT_UUID16) + return false; + + if (attr->type.value.u16 == GATT_PRIM_SVC_UUID || + attr->type.value.u16 == GATT_SND_SVC_UUID) + return true; + + return false; +} + +static int local_database_add(uint16_t handle, struct btd_attribute *attr) +{ + attr->handle = handle; + + local_attribute_db = g_list_append(local_attribute_db, attr); + + return 0; +} + +struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid) +{ + struct btd_attribute *attr; + uint16_t len = bt_uuid_len(uuid); + uint8_t value[len]; + + /* + * Service DECLARATION + * + * TYPE ATTRIBUTE VALUE + * +-------+---------------------------------+ + * |0x2800 | 0xYYYY... | + * | (1) | (2) | + * +------+----------------------------------+ + * (1) - 2 octets: Primary/Secondary Service UUID + * (2) - 2 or 16 octets: Service UUID + */ + + /* Set attribute value */ + put_uuid_le(uuid, value); + + attr = new_const_attribute(&primary_uuid, value, len); + if (!attr) + return NULL; + + if (local_database_add(next_handle, attr) < 0) { + free(attr); + return NULL; + } + + /* TODO: missing overflow checking */ + next_handle = next_handle + 1; + + return attr; +} + +void btd_gatt_remove_service(struct btd_attribute *service) +{ + GList *list = g_list_find(local_attribute_db, service); + bool first_node; + + if (!list) + return; + + first_node = local_attribute_db == list; + + /* Remove service declaration attribute */ + free(list->data); + list = g_list_delete_link(list, list); + + /* Remove all characteristics until next service declaration */ + while (list && !is_service(list->data)) { + free(list->data); + list = g_list_delete_link(list, list); + } + + /* + * When removing the first node, local attribute database head + * needs to be updated. Node removed from middle doesn't change + * the list head address. + */ + if (first_node) + local_attribute_db = list; +} + +struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid, + uint8_t properties, + btd_attr_read_t read_cb, + btd_attr_write_t write_cb) +{ + struct btd_attribute *char_decl, *char_value = NULL; + + /* Attribute value length */ + uint16_t len = 1 + 2 + bt_uuid_len(uuid); + uint8_t value[len]; + + /* + * Characteristic DECLARATION + * + * TYPE ATTRIBUTE VALUE + * +-------+---------------------------------+ + * |0x2803 | 0xXX 0xYYYY 0xZZZZ... | + * | (1) | (2) (3) (4) | + * +------+----------------------------------+ + * (1) - 2 octets: Characteristic declaration UUID + * (2) - 1 octet : Properties + * (3) - 2 octets: Handle of the characteristic Value + * (4) - 2 or 16 octets: Characteristic UUID + */ + + value[0] = properties; + + /* + * Since we don't know yet the characteristic value attribute + * handle, we skip and set it later. + */ + + put_uuid_le(uuid, &value[3]); + + char_decl = new_const_attribute(&chr_uuid, value, len); + if (!char_decl) + goto fail; + + char_value = new_attribute(uuid, read_cb, write_cb); + if (!char_value) + goto fail; + + if (local_database_add(next_handle, char_decl) < 0) + goto fail; + + next_handle = next_handle + 1; + + /* + * Characteristic VALUE + * + * TYPE ATTRIBUTE VALUE + * +----------+---------------------------------+ + * |0xZZZZ... | 0x... | + * | (1) | (2) | + * +----------+---------------------------------+ + * (1) - 2 or 16 octets: Characteristic UUID + * (2) - N octets: Value is read dynamically from the service + * implementation (external entity). + */ + + if (local_database_add(next_handle, char_value) < 0) + /* TODO: remove declaration */ + goto fail; + + next_handle = next_handle + 1; + + /* + * Update characteristic value handle in characteristic declaration + * attribute. For local attributes, we can assume that the handle + * representing the characteristic value will get the next available + * handle. However, for remote attribute this assumption is not valid. + */ + put_le16(char_value->handle, &char_decl->value[1]); + + return char_value; + +fail: + free(char_decl); + free(char_value); + + return NULL; +} + +struct btd_attribute *btd_gatt_add_char_desc(const bt_uuid_t *uuid, + btd_attr_read_t read_cb, + btd_attr_write_t write_cb) +{ + struct btd_attribute *attr; + + /* + * From Core SPEC 4.1 page 2184: + * "Characteristic descriptor declaration permissions are defined by a + * higher layer profile or are implementation specific. A client shall + * not assume all characteristic descriptor declarations are readable." + * + * The read/write callbacks presence will define the descriptor + * permissions managed directly by the core. The upper layer can define + * additional permissions constraints. + */ + + attr = new_attribute(uuid, read_cb, write_cb); + if (!attr) + return NULL; + + if (local_database_add(next_handle, attr) < 0) { + free(attr); + return NULL; + } + + next_handle = next_handle + 1; + + return attr; +} + +void gatt_init(void) +{ + DBG("Starting GATT server"); + + gatt_dbus_manager_register(); +} + +void gatt_cleanup(void) +{ + DBG("Stopping GATT server"); + + gatt_dbus_manager_unregister(); +} diff --git a/src/gatt.h b/src/gatt.h new file mode 100644 index 0000000..f16541e --- /dev/null +++ b/src/gatt.h @@ -0,0 +1,121 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 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 + * + */ + +struct btd_attribute; + +void gatt_init(void); + +void gatt_cleanup(void); + +/* + * Read operation result callback. Called from the service implementation + * informing the core (ATT layer) the result of read operation. + * @err: error in -errno format. + * @value: value of the attribute read. + * @len: length of value. + * @user_data: user_data passed in btd_attr_read_t callback. + */ +typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len, + void *user_data); +/* + * Service implementation callback passed to core (ATT layer). It manages read + * operations received from remote devices. + * @attr: reference of the attribute to be read. + * @result: callback called from the service implementation informing the + * value of attribute read. + * @user_data: user_data passed in btd_attr_read_result_t callback. + */ +typedef void (*btd_attr_read_t) (struct btd_attribute *attr, + btd_attr_read_result_t result, + void *user_data); + +/* + * Write operation result callback. Called from the service implementation + * informing the core (ATT layer) the result of the write operation. It is used + * to manage Write Request operations. + * @err: error in -errno format. + * @user_data: user_data passed in btd_attr_write_t callback. + */ +typedef void (*btd_attr_write_result_t) (int err, void *user_data); +/* + * Service implementation callback passed to core (ATT layer). It manages write + * operations received from remote devices. + * @attr: reference of the attribute to be changed. + * @value: new attribute value. + * @len: length of value. + * @result: callback called from the service implementation informing the + * result of the write operation. + * @user_data: user_data passed in btd_attr_write_result_t callback. + */ +typedef void (*btd_attr_write_t) (struct btd_attribute *attr, + const uint8_t *value, size_t len, + btd_attr_write_result_t result, + void *user_data); + +/* btd_gatt_add_service - Add a service declaration to local attribute database. + * @uuid: Service UUID. + * + * Returns a reference to service declaration attribute. In case of error, + * NULL is returned. + */ +struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid); + +/* + * btd_gatt_remove_service - Remove a service (along with all its + * characteristics) from the local attribute database. + * @service: Service declaration attribute. + */ +void btd_gatt_remove_service(struct btd_attribute *service); + +/* + * btd_gatt_add_char - Add a characteristic (declaration and value attributes) + * to local attribute database. + * @uuid: Characteristic UUID (16-bits or 128-bits). + * @properties: Characteristic properties. See Core SPEC 4.1 page 2183. + * @read_cb: Callback used to provide the characteristic value. + * @write_cb: Callback called to notify the implementation that a new value + * is available. + * + * Returns a reference to characteristic value attribute. In case of error, + * NULL is returned. + */ +struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid, + uint8_t properties, + btd_attr_read_t read_cb, + btd_attr_write_t write_cb); + +/* + * btd_gatt_add_char_desc - Add a characteristic descriptor to the local + * attribute database. + * @uuid: Characteristic Descriptor UUID (16-bits or 128-bits). + * @read_cb: Callback that should be called once the characteristic + * descriptor attribute is read. + * @write_cb: Callback that should be called once the characteristic + * descriptor attribute is written. + * + * Returns a reference to characteristic descriptor attribute. In case of + * error, NULL is returned. + */ +struct btd_attribute *btd_gatt_add_char_desc(const bt_uuid_t *uuid, + btd_attr_read_t read_cb, + btd_attr_write_t write_cb); diff --git a/src/hcid.h b/src/hcid.h index ea67cc2..6040c71 100644 --- a/src/hcid.h +++ b/src/hcid.h @@ -47,4 +47,6 @@ void plugin_cleanup(void); void rfkill_init(void); void rfkill_exit(void); +GKeyFile *btd_get_main_conf(void); + void btd_exit(void); diff --git a/src/main.c b/src/main.c index 91d90b4..15f98cf 100644 --- a/src/main.c +++ b/src/main.c @@ -55,6 +55,7 @@ #include "dbus-common.h" #include "agent.h" #include "profile.h" +#include "gatt.h" #include "systemd.h" #define BLUEZ_NAME "org.bluez" @@ -65,6 +66,7 @@ #define SHUTDOWN_GRACE_SECONDS 10 struct main_opts main_opts; +static GKeyFile *main_conf; static const char * const supported_options[] = { "Name", @@ -78,6 +80,11 @@ static const char * const supported_options[] = { "DebugKeys", }; +GKeyFile *btd_get_main_conf(void) +{ + return main_conf; +} + static GKeyFile *load_config(const char *file) { GError *err = NULL; @@ -132,6 +139,7 @@ done: static void check_config(GKeyFile *config) { + const char *valid_groups[] = { "General", "Policy", NULL }; char **keys; int i; @@ -141,7 +149,17 @@ static void check_config(GKeyFile *config) keys = g_key_file_get_groups(config, NULL); for (i = 0; keys != NULL && keys[i] != NULL; i++) { - if (!g_str_equal(keys[i], "General")) + const char **group; + bool match = false; + + for (group = valid_groups; *group; group++) { + if (g_str_equal(keys[i], *group)) { + match = true; + break; + } + } + + if (!match) warn("Unknown group %s in main.conf", keys[i]); } @@ -487,7 +505,6 @@ int main(int argc, char *argv[]) uint16_t sdp_mtu = 0; uint32_t sdp_flags = 0; int gdbus_flags = 0; - GKeyFile *config; guint signal, watchdog; const char *watchdog_usec; @@ -522,15 +539,22 @@ int main(int argc, char *argv[]) sd_notify(0, "STATUS=Starting up"); - config = load_config(CONFIGDIR "/main.conf"); + main_conf = load_config(CONFIGDIR "/main.conf"); - parse_config(config); + parse_config(main_conf); if (connect_dbus() < 0) { error("Unable to get on D-Bus"); exit(1); } + if (option_experimental) + gdbus_flags = G_DBUS_FLAG_ENABLE_EXPERIMENTAL; + + g_dbus_set_flags(gdbus_flags); + + gatt_init(); + if (adapter_init() < 0) { error("Adapter handling initialization failed"); exit(1); @@ -540,11 +564,6 @@ int main(int argc, char *argv[]) btd_agent_init(); btd_profile_init(); - if (option_experimental) - gdbus_flags = G_DBUS_FLAG_ENABLE_EXPERIMENTAL; - - g_dbus_set_flags(gdbus_flags); - if (option_compat == TRUE) sdp_flags |= SDP_SERVER_COMPAT; @@ -598,14 +617,16 @@ int main(int argc, char *argv[]) adapter_cleanup(); + gatt_cleanup(); + rfkill_exit(); stop_sdp_server(); g_main_loop_unref(event_loop); - if (config) - g_key_file_free(config); + if (main_conf) + g_key_file_free(main_conf); disconnect_dbus(); diff --git a/src/main.conf b/src/main.conf index a94274a..3ebadde 100644 --- a/src/main.conf +++ b/src/main.conf @@ -46,3 +46,12 @@ # makes debug link keys valid only for the duration of the connection # that they were created for. #DebugKeys = false + +#[Policy] +# +# The ReconnectUUIDs defines the set of remote services that should try +# to be reconnected to in case of a link loss (link supervision +# timeout). The policy plugin should contain a sane set of values by +# default, but this list can be overridden here. By setting the list to +# empty the reconnection feature gets disabled. +#ReconnectUUIDs= diff --git a/src/plugin.c b/src/plugin.c index 085e7a5..edb47db 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -33,11 +33,11 @@ #include #include -#include -#include "plugin.h" -#include "log.h" -#include "hcid.h" +#include "btio/btio.h" +#include "src/plugin.h" +#include "src/log.h" +#include "src/hcid.h" static GSList *plugins = NULL; @@ -110,7 +110,7 @@ static gboolean enable_plugin(const char *name, char **cli_enable, return TRUE; } -#include "builtin.h" +#include "src/builtin.h" gboolean plugin_init(const char *enable, const char *disable) { diff --git a/src/profile.c b/src/profile.c index 3c0d27c..f30f4f6 100644 --- a/src/profile.c +++ b/src/profile.c @@ -43,7 +43,7 @@ #include "sdpd.h" #include "log.h" #include "error.h" -#include "glib-helper.h" +#include "uuid-helper.h" #include "dbus-common.h" #include "sdp-client.h" #include "sdp-xml.h" @@ -482,6 +482,9 @@ \ \ \ + \ + \ + \ " #define SYNC_RECORD \ @@ -619,7 +622,6 @@ struct ext_io { uint8_t chan; guint auth_id; - unsigned int svc_id; DBusPendingCall *pending; }; @@ -708,10 +710,6 @@ static void ext_io_destroy(gpointer p) if (ext_io->auth_id != 0) btd_cancel_authorization(ext_io->auth_id); - if (ext_io->svc_id != 0) - device_remove_svc_complete_callback(ext_io->device, - ext_io->svc_id); - if (ext_io->pending) { dbus_pending_call_cancel(ext_io->pending); dbus_pending_call_unref(ext_io->pending); @@ -754,7 +752,9 @@ static gboolean ext_io_disconnected(GIOChannel *io, GIOCondition cond, DBG("%s disconnected from %s", ext->name, addr); drop: - btd_service_disconnecting_complete(conn->service, 0); + if (conn->service) + btd_service_disconnecting_complete(conn->service, 0); + ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); return FALSE; @@ -776,7 +776,9 @@ static void new_conn_reply(DBusPendingCall *call, void *user_data) conn->pending = NULL; if (!dbus_error_is_set(&err)) { - btd_service_connecting_complete(conn->service, 0); + if (conn->service) + btd_service_connecting_complete(conn->service, 0); + conn->connected = true; return; } @@ -784,7 +786,8 @@ static void new_conn_reply(DBusPendingCall *call, void *user_data) error("%s replied with an error: %s, %s", ext->name, err.name, err.message); - btd_service_connecting_complete(conn->service, -ECONNREFUSED); + if (conn->service) + btd_service_connecting_complete(conn->service, -ECONNREFUSED); dbus_error_free(&err); @@ -808,14 +811,18 @@ static void disconn_reply(DBusPendingCall *call, void *user_data) conn->pending = NULL; if (!dbus_error_is_set(&err)) { - btd_service_disconnecting_complete(conn->service, 0); + if (conn->service) + btd_service_disconnecting_complete(conn->service, 0); + goto disconnect; } error("%s replied with an error: %s, %s", ext->name, err.name, err.message); - btd_service_disconnecting_complete(conn->service, -ECONNREFUSED); + if (conn->service) + btd_service_disconnecting_complete(conn->service, + -ECONNREFUSED); dbus_error_free(&err); @@ -988,9 +995,13 @@ static void ext_connect(GIOChannel *io, GError *err, gpointer user_data) return; drop: - btd_service_connecting_complete(conn->service, err ? -err->code : -EIO); + if (conn->service) + btd_service_connecting_complete(conn->service, + err ? -err->code : -EIO); + if (io_err) g_error_free(io_err); + ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); } @@ -1017,12 +1028,6 @@ static void ext_auth(DBusError *err, void *user_data) goto drop; } - if (conn->svc_id > 0) { - DBG("Connection from %s authorized but still waiting for SDP", - addr); - return; - } - if (!bt_io_accept(conn->io, ext_connect, conn, NULL, &gerr)) { error("bt_io_accept: %s", gerr->message); g_error_free(gerr); @@ -1047,13 +1052,19 @@ static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io, GIOCondition cond; char addr[18]; - device = btd_adapter_find_device(server->adapter, dst); + device = btd_adapter_find_device(server->adapter, dst, BDADDR_BREDR); if (device == NULL) { ba2str(dst, addr); error("%s device %s not found", server->ext->name, addr); return NULL; } + /* Do not add UUID if client role is not enabled */ + if (!server->ext->enable_client) { + service = NULL; + goto done; + } + btd_device_add_uuid(device, server->ext->remote_uuid); service = btd_device_get_service(device, server->ext->remote_uuid); if (service == NULL) { @@ -1063,13 +1074,16 @@ static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io, return NULL; } +done: conn = g_new0(struct ext_io, 1); conn->io = g_io_channel_ref(io); conn->proto = server->proto; conn->ext = server->ext; conn->adapter = btd_adapter_ref(server->adapter); conn->device = btd_device_ref(device); - conn->service = btd_service_ref(service); + + if (service) + conn->service = btd_service_ref(service); cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; conn->io_id = g_io_add_watch(io, cond, ext_io_disconnected, conn); @@ -1077,47 +1091,6 @@ static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io, return conn; } -static void ext_svc_complete(struct btd_device *dev, int err, void *user_data) -{ - struct ext_io *conn = user_data; - struct ext_profile *ext = conn->ext; - const bdaddr_t *bdaddr; - GError *gerr = NULL; - char addr[18]; - - conn->svc_id = 0; - - bdaddr = device_get_address(dev); - ba2str(bdaddr, addr); - - if (err < 0) { - error("Service resolving failed for %s: %s (%d)", - addr, strerror(-err), -err); - goto drop; - } - - DBG("Services resolved for %s", addr); - - if (conn->auth_id > 0) { - DBG("Services resolved but still waiting for authorization"); - return; - } - - if (!bt_io_accept(conn->io, ext_connect, conn, NULL, &gerr)) { - error("bt_io_accept: %s", gerr->message); - g_error_free(gerr); - goto drop; - } - - DBG("%s authorized to connect to %s", addr, ext->name); - - return; - -drop: - ext->conns = g_slist_remove(ext->conns, conn); - ext_io_destroy(conn); -} - static void ext_confirm(GIOChannel *io, gpointer user_data) { struct ext_io *server = user_data; @@ -1156,10 +1129,6 @@ static void ext_confirm(GIOChannel *io, gpointer user_data) ext->conns = g_slist_append(ext->conns, conn); - conn->svc_id = device_wait_for_svc_complete(conn->device, - ext_svc_complete, - conn); - DBG("%s authorizing connection from %s", ext->name, addr); } @@ -1590,7 +1559,9 @@ static void record_cb(sdp_list_t *recs, int err, gpointer user_data) return; failed: - btd_service_connecting_complete(conn->service, err); + if (conn->service) + btd_service_connecting_complete(conn->service, err); + ext->conns = g_slist_remove(ext->conns, conn); ext_io_destroy(conn); } @@ -1605,7 +1576,7 @@ static int resolve_service(struct ext_io *conn, const bdaddr_t *src, bt_string2uuid(&uuid, ext->remote_uuid); sdp_uuid128_to_uuid(&uuid); - err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL); + err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL, 0); if (err == 0) conn->resolving = true; @@ -2146,7 +2117,7 @@ static int parse_ext_opt(struct ext_profile *ext, const char *key, if (type != DBUS_TYPE_STRING) return -EINVAL; dbus_message_iter_get_basic(value, &str); - g_free(ext->service); + free(ext->service); ext->service = bt_name2string(str); } @@ -2156,27 +2127,27 @@ static int parse_ext_opt(struct ext_profile *ext, const char *key, static void set_service(struct ext_profile *ext) { if (strcasecmp(ext->uuid, HSP_HS_UUID) == 0) { - ext->service = g_strdup(ext->uuid); + ext->service = strdup(ext->uuid); } else if (strcasecmp(ext->uuid, HSP_AG_UUID) == 0) { ext->service = ext->uuid; - ext->uuid = g_strdup(HSP_HS_UUID); + ext->uuid = strdup(HSP_HS_UUID); } else if (strcasecmp(ext->uuid, HFP_HS_UUID) == 0) { - ext->service = g_strdup(ext->uuid); + ext->service = strdup(ext->uuid); } else if (strcasecmp(ext->uuid, HFP_AG_UUID) == 0) { ext->service = ext->uuid; - ext->uuid = g_strdup(HFP_HS_UUID); + ext->uuid = strdup(HFP_HS_UUID); } else if (strcasecmp(ext->uuid, OBEX_SYNC_UUID) == 0 || strcasecmp(ext->uuid, OBEX_OPP_UUID) == 0 || strcasecmp(ext->uuid, OBEX_FTP_UUID) == 0) { - ext->service = g_strdup(ext->uuid); + ext->service = strdup(ext->uuid); } else if (strcasecmp(ext->uuid, OBEX_PSE_UUID) == 0 || strcasecmp(ext->uuid, OBEX_PCE_UUID) == 0) { ext->service = ext->uuid; - ext->uuid = g_strdup(OBEX_PBAP_UUID); + ext->uuid = strdup(OBEX_PBAP_UUID); } else if (strcasecmp(ext->uuid, OBEX_MAS_UUID) == 0 || strcasecmp(ext->uuid, OBEX_MNS_UUID) == 0) { ext->service = ext->uuid; - ext->uuid = g_strdup(OBEX_MAP_UUID); + ext->uuid = strdup(OBEX_MAP_UUID); } } @@ -2238,7 +2209,7 @@ static struct ext_profile *create_ext(const char *owner, const char *path, p->local_uuid = ext->service ? ext->service : ext->uuid; p->remote_uuid = ext->remote_uuid; - if (ext->enable_server || ext->record || ext->get_record) { + if (ext->enable_server) { p->adapter_probe = ext_adapter_probe; p->adapter_remove = ext_adapter_remove; } @@ -2275,8 +2246,8 @@ static void remove_ext(struct ext_profile *ext) g_free(ext->remote_uuid); g_free(ext->name); g_free(ext->owner); - g_free(ext->uuid); - g_free(ext->service); + free(ext->uuid); + free(ext->service); g_free(ext->role); g_free(ext->path); g_free(ext->record); @@ -2397,7 +2368,7 @@ bool btd_profile_add_custom_prop(const char *uuid, const char *type, prop = g_new0(struct btd_profile_custom_property, 1); - prop->uuid = g_strdup(uuid); + prop->uuid = strdup(uuid); prop->type = g_strdup(type); prop->name = g_strdup(name); prop->exists = exists; diff --git a/src/sdp-client.c b/src/sdp-client.c index 51f3048..edbdaec 100644 --- a/src/sdp-client.c +++ b/src/sdp-client.c @@ -33,7 +33,7 @@ #include -#include +#include "btio/btio.h" #include "log.h" #include "sdp-client.h" @@ -86,17 +86,6 @@ static sdp_session_t *get_cached_sdp_session(const bdaddr_t *src, const bdaddr_t return NULL; } -static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst) -{ - sdp_session_t *session; - - session = get_cached_sdp_session(src, dst); - if (session) - 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) { @@ -275,7 +264,7 @@ failed: static int create_search_context(struct search_context **ctxt, const bdaddr_t *src, const bdaddr_t *dst, - uuid_t *uuid) + uuid_t *uuid, uint16_t flags) { sdp_session_t *s; GIOChannel *chan; @@ -285,7 +274,10 @@ static int create_search_context(struct search_context **ctxt, if (!ctxt) return -EINVAL; - s = get_sdp_session(src, dst); + s = get_cached_sdp_session(src, dst); + if (!s) + s = sdp_connect(src, dst, SDP_NON_BLOCKING | flags); + if (!s) return -errno; @@ -319,7 +311,7 @@ static int create_search_context(struct search_context **ctxt, 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) + bt_destroy_t destroy, uint16_t flags) { struct search_context *ctxt = NULL; int err; @@ -327,7 +319,7 @@ int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst, if (!cb) return -EINVAL; - err = create_search_context(&ctxt, src, dst, uuid); + err = create_search_context(&ctxt, src, dst, uuid, flags); if (err < 0) return err; diff --git a/src/sdp-client.h b/src/sdp-client.h index 9191594..9aa5a4d 100644 --- a/src/sdp-client.h +++ b/src/sdp-client.h @@ -26,6 +26,6 @@ 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); + bt_destroy_t destroy, uint16_t flags); int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst); void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst); diff --git a/src/sdpd-request.c b/src/sdpd-request.c index fbeb488..3e58c22 100644 --- a/src/sdpd-request.c +++ b/src/sdpd-request.c @@ -37,6 +37,8 @@ #include #include +#include "src/shared/util.h" + #include "sdpd.h" #include "log.h" @@ -174,7 +176,7 @@ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *p struct attrid *aid; aid = malloc(sizeof(struct attrid)); aid->dtd = dataType; - aid->uint16 = bt_get_be16(p); + aid->uint16 = get_be16(p); pElem = (char *) aid; } else { uint16_t tmp; @@ -182,7 +184,7 @@ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *p memcpy(&tmp, p, sizeof(tmp)); pElem = malloc(sizeof(uint16_t)); - bt_put_be16(tmp, pElem); + put_be16(tmp, pElem); } p += sizeof(uint16_t); seqlen += sizeof(uint16_t); @@ -201,7 +203,7 @@ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *p struct attrid *aid; aid = malloc(sizeof(struct attrid)); aid->dtd = dataType; - aid->uint32 = bt_get_be32(p); + aid->uint32 = get_be32(p); pElem = (char *) aid; } else { @@ -210,7 +212,7 @@ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *p memcpy(&tmp, p, sizeof(tmp)); pElem = malloc(sizeof(uint32_t)); - bt_put_be32(tmp, pElem); + put_be32(tmp, pElem); } p += sizeof(uint32_t); seqlen += sizeof(uint32_t); @@ -392,7 +394,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf) goto done; } - expected = bt_get_be16(pdata); + expected = get_be16(pdata); SDPDBG("Expected count: %d", expected); SDPDBG("Bytes scanned : %d", scanned); @@ -417,13 +419,13 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf) /* total service record count = 0 */ pTotalRecordCount = pdata; - bt_put_be16(0, pdata); + put_be16(0, pdata); pdata += sizeof(uint16_t); buf->data_size += sizeof(uint16_t); /* current service record count = 0 */ pCurrentRecordCount = pdata; - bt_put_be16(0, pdata); + put_be16(0, pdata); pdata += sizeof(uint16_t); buf->data_size += sizeof(uint16_t); @@ -440,7 +442,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf) if (sdp_match_uuid(pattern, rec->pattern) > 0 && sdp_check_access(rec->handle, &req->device)) { rsp_count++; - bt_put_be32(rec->handle, pdata); + put_be32(rec->handle, pdata); pdata += sizeof(uint32_t); handleSize += sizeof(uint32_t); } @@ -449,8 +451,8 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf) SDPDBG("Match count: %d", rsp_count); buf->data_size += handleSize; - bt_put_be16(rsp_count, pTotalRecordCount); - bt_put_be16(rsp_count, pCurrentRecordCount); + put_be16(rsp_count, pTotalRecordCount); + put_be16(rsp_count, pCurrentRecordCount); if (rsp_count > actual) { /* cache the rsp and generate a continuation state */ @@ -479,7 +481,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf) if (pCache) { pCacheBuffer = pCache->data; /* get the rsp_count from the cached buffer */ - rsp_count = bt_get_be16(pCacheBuffer); + rsp_count = get_be16(pCacheBuffer); /* get index of the last sdp_record_t sent */ lastIndex = cstate->cStateValue.lastIndexSent; @@ -515,8 +517,8 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf) } buf->data_size += handleSize; - bt_put_be16(rsp_count, pTotalRecordCount); - bt_put_be16(i - lastIndex, pCurrentRecordCount); + put_be16(rsp_count, pTotalRecordCount); + put_be16(i - lastIndex, pCurrentRecordCount); if (i == rsp_count) { /* set "null" continuationState */ @@ -646,7 +648,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf) goto done; } - handle = bt_get_be32(pdata); + handle = get_be32(pdata); pdata += sizeof(uint32_t); data_left -= sizeof(uint32_t); @@ -656,7 +658,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf) goto done; } - max_rsp_size = bt_get_be16(pdata); + max_rsp_size = get_be16(pdata); pdata += sizeof(uint16_t); data_left -= sizeof(uint16_t); @@ -772,7 +774,7 @@ done: return status; /* set attribute list byte count */ - bt_put_be16(buf->data_size - cstate_size, buf->data); + put_be16(buf->data_size - cstate_size, buf->data); buf->data_size += sizeof(uint16_t); return 0; } @@ -813,7 +815,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf) goto done; } - max = bt_get_be16(pdata); + max = get_be16(pdata); pdata += sizeof(uint16_t); data_left -= sizeof(uint16_t); @@ -943,7 +945,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf) if (!status) { /* set attribute list byte count */ - bt_put_be16(buf->data_size - cstate_size, buf->data); + put_be16(buf->data_size - cstate_size, buf->data); buf->data_size += sizeof(uint16_t); } @@ -1027,7 +1029,7 @@ static void process_request(sdp_req_t *req) send_rsp: if (status) { rsphdr->pdu_id = SDP_ERROR_RSP; - bt_put_be16(status, rsp.data); + put_be16(status, rsp.data); rsp.data_size = sizeof(uint16_t); } diff --git a/src/sdpd-service.c b/src/sdpd-service.c index 38bf808..a90b299 100644 --- a/src/sdpd-service.c +++ b/src/sdpd-service.c @@ -39,6 +39,7 @@ #include +#include "src/shared/util.h" #include "sdpd.h" #include "log.h" @@ -320,7 +321,7 @@ static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p, return NULL; } - lookAheadAttrId = bt_get_be16(p + sizeof(uint8_t)); + lookAheadAttrId = get_be16(p + sizeof(uint8_t)); SDPDBG("Look ahead attr id : %d", lookAheadAttrId); @@ -330,7 +331,7 @@ static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p, SDPDBG("Unexpected end of packet"); return NULL; } - handle = bt_get_be32(p + sizeof(uint8_t) + sizeof(uint16_t) + + handle = get_be32(p + sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint8_t)); SDPDBG("SvcRecHandle : 0x%x", handle); rec = sdp_record_find(handle); @@ -365,7 +366,7 @@ static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p, seqlen, localExtractedLength); dtd = *(uint8_t *) p; - attrId = bt_get_be16(p + attrSize); + attrId = get_be16(p + attrSize); attrSize += sizeof(uint16_t); SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId); @@ -456,13 +457,13 @@ success: update_db_timestamp(); /* Build a rsp buffer */ - bt_put_be32(rec->handle, rsp->data); + put_be32(rec->handle, rsp->data); rsp->data_size = sizeof(uint32_t); return 0; invalid: - bt_put_be16(SDP_INVALID_SYNTAX, rsp->data); + put_be16(SDP_INVALID_SYNTAX, rsp->data); rsp->data_size = sizeof(uint16_t); return -1; @@ -477,7 +478,7 @@ int service_update_req(sdp_req_t *req, sdp_buf_t *rsp) 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 = bt_get_be32(p); + uint32_t handle = get_be32(p); SDPDBG("Svc Rec Handle: 0x%x", handle); @@ -505,7 +506,7 @@ int service_update_req(sdp_req_t *req, sdp_buf_t *rsp) done: p = rsp->data; - bt_put_be16(status, p); + put_be16(status, p); rsp->data_size = sizeof(uint16_t); return status; } @@ -516,7 +517,7 @@ done: 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 = bt_get_be32(p); + uint32_t handle = get_be32(p); sdp_record_t *rec; int status = 0; @@ -535,7 +536,7 @@ int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp) } p = rsp->data; - bt_put_be16(status, p); + put_be16(status, p); rsp->data_size = sizeof(uint16_t); return status; diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c index 58c9f1d..17a872c 100644 --- a/src/shared/btsnoop.c +++ b/src/shared/btsnoop.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or @@ -25,26 +25,15 @@ #include #endif +#include #include #include #include #include #include +#include -#include "btsnoop.h" - -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; -} - -#define hton64(x) ntoh64(x) +#include "src/shared/btsnoop.h" struct btsnoop_hdr { uint8_t id[8]; /* Identification Pattern */ @@ -68,13 +57,24 @@ static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, static const uint32_t btsnoop_version = 1; +struct pklg_pkt { + uint32_t len; + uint64_t ts; + uint8_t type; +} __attribute__ ((packed)); +#define PKLG_PKT_SIZE (sizeof(struct pklg_pkt)) + struct btsnoop { int ref_count; int fd; + unsigned long flags; uint32_t type; + uint16_t index; + bool aborted; + bool pklg_format; }; -struct btsnoop *btsnoop_open(const char *path) +struct btsnoop *btsnoop_open(const char *path, unsigned long flags) { struct btsnoop *btsnoop; struct btsnoop_hdr hdr; @@ -90,17 +90,34 @@ struct btsnoop *btsnoop_open(const char *path) return NULL; } + btsnoop->flags = flags; + len = read(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE); if (len < 0 || len != BTSNOOP_HDR_SIZE) goto failed; - if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) - goto failed; + if (!memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) { + /* Check for BTSnoop version 1 format */ + if (be32toh(hdr.version) != btsnoop_version) + goto failed; - if (ntohl(hdr.version) != btsnoop_version) - goto failed; + btsnoop->type = be32toh(hdr.type); + btsnoop->index = 0xffff; + } else { + if (!(btsnoop->flags & BTSNOOP_FLAG_PKLG_SUPPORT)) + goto failed; - btsnoop->type = ntohl(hdr.type); + /* Check for Apple Packet Logger format */ + if (hdr.id[0] != 0x00 || hdr.id[1] != 0x00) + goto failed; + + btsnoop->type = BTSNOOP_TYPE_MONITOR; + btsnoop->index = 0xffff; + btsnoop->pklg_format = true; + + /* Apple Packet Logger format has no header */ + lseek(btsnoop->fd, 0, SEEK_SET); + } return btsnoop_ref(btsnoop); @@ -129,10 +146,11 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t type) } btsnoop->type = type; + btsnoop->index = 0xffff; memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id)); - hdr.version = htonl(btsnoop_version); - hdr.type = htonl(btsnoop->type); + hdr.version = htobe32(btsnoop_version); + hdr.type = htobe32(btsnoop->type); written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE); if (written < 0) { @@ -188,11 +206,11 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec; - pkt.size = htonl(size); - pkt.len = htonl(size); - pkt.flags = htonl(flags); - pkt.drops = htonl(0); - pkt.ts = hton64(ts + 0x00E03AB44A676000ll); + pkt.size = htobe32(size); + pkt.len = htobe32(size); + pkt.flags = htobe32(flags); + pkt.drops = htobe32(0); + pkt.ts = htobe64(ts + 0x00E03AB44A676000ll); written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE); if (written < 0) @@ -207,6 +225,61 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, return true; } +static uint32_t get_flags_from_opcode(uint16_t opcode) +{ + switch (opcode) { + case BTSNOOP_OPCODE_NEW_INDEX: + case BTSNOOP_OPCODE_DEL_INDEX: + break; + case BTSNOOP_OPCODE_COMMAND_PKT: + return 0x02; + case BTSNOOP_OPCODE_EVENT_PKT: + return 0x03; + case BTSNOOP_OPCODE_ACL_TX_PKT: + return 0x00; + case BTSNOOP_OPCODE_ACL_RX_PKT: + return 0x01; + case BTSNOOP_OPCODE_SCO_TX_PKT: + case BTSNOOP_OPCODE_SCO_RX_PKT: + break; + } + + return 0xff; +} + +bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv, + uint16_t index, uint16_t opcode, + const void *data, uint16_t size) +{ + uint32_t flags; + + if (!btsnoop) + return false; + + switch (btsnoop->type) { + case BTSNOOP_TYPE_HCI: + if (btsnoop->index == 0xffff) + btsnoop->index = index; + + if (index != btsnoop->index) + return false; + + flags = get_flags_from_opcode(opcode); + if (flags == 0xff) + return false; + break; + + case BTSNOOP_TYPE_MONITOR: + flags = (index << 16) | opcode; + break; + + default: + return false; + } + + return btsnoop_write(btsnoop, tv, flags, data, size); +} + bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv, uint16_t frequency, const void *data, uint16_t size) { @@ -226,3 +299,169 @@ bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv, return btsnoop_write(btsnoop, tv, flags, data, size); } + +static uint16_t get_opcode_from_pklg(uint8_t type) +{ + switch (type) { + case 0x00: + return BTSNOOP_OPCODE_COMMAND_PKT; + case 0x01: + return BTSNOOP_OPCODE_EVENT_PKT; + case 0x02: + return BTSNOOP_OPCODE_ACL_TX_PKT; + case 0x03: + return BTSNOOP_OPCODE_ACL_RX_PKT; + } + + return 0xffff; +} + +static bool pklg_read_hci(struct btsnoop *btsnoop, struct timeval *tv, + uint16_t *index, uint16_t *opcode, + void *data, uint16_t *size) +{ + struct pklg_pkt pkt; + uint32_t toread; + uint64_t ts; + ssize_t len; + + len = read(btsnoop->fd, &pkt, PKLG_PKT_SIZE); + if (len == 0) + return false; + + if (len < 0 || len != PKLG_PKT_SIZE) { + btsnoop->aborted = true; + return false; + } + + toread = be32toh(pkt.len) - 9; + + ts = be64toh(pkt.ts); + tv->tv_sec = ts >> 32; + tv->tv_usec = ts & 0xffffffff; + + *index = 0; + *opcode = get_opcode_from_pklg(pkt.type); + + len = read(btsnoop->fd, data, toread); + if (len < 0) { + btsnoop->aborted = true; + return false; + } + + *size = toread; + + return true; +} + +static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags) +{ + switch (type) { + case 0x01: + return BTSNOOP_OPCODE_COMMAND_PKT; + case 0x02: + if (flags & 0x01) + return BTSNOOP_OPCODE_ACL_RX_PKT; + else + return BTSNOOP_OPCODE_ACL_TX_PKT; + case 0x03: + if (flags & 0x01) + return BTSNOOP_OPCODE_SCO_RX_PKT; + else + return BTSNOOP_OPCODE_SCO_TX_PKT; + case 0x04: + return BTSNOOP_OPCODE_EVENT_PKT; + case 0xff: + if (flags & 0x02) { + if (flags & 0x01) + return BTSNOOP_OPCODE_EVENT_PKT; + else + return BTSNOOP_OPCODE_COMMAND_PKT; + } else { + if (flags & 0x01) + return BTSNOOP_OPCODE_ACL_RX_PKT; + else + return BTSNOOP_OPCODE_ACL_TX_PKT; + } + break; + } + + return 0xffff; +} + +bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv, + uint16_t *index, uint16_t *opcode, + void *data, uint16_t *size) +{ + struct btsnoop_pkt pkt; + uint32_t toread, flags; + uint64_t ts; + uint8_t pkt_type; + ssize_t len; + + if (!btsnoop || btsnoop->aborted) + return false; + + if (btsnoop->pklg_format) + return pklg_read_hci(btsnoop, tv, index, opcode, data, size); + + len = read(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE); + if (len == 0) + return false; + + if (len < 0 || len != BTSNOOP_PKT_SIZE) { + btsnoop->aborted = true; + return false; + } + + toread = be32toh(pkt.size); + flags = be32toh(pkt.flags); + + ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll; + tv->tv_sec = (ts / 1000000ll) + 946684800ll; + tv->tv_usec = ts % 1000000ll; + + switch (btsnoop->type) { + case BTSNOOP_TYPE_HCI: + *index = 0; + *opcode = get_opcode_from_flags(0xff, flags); + break; + + case BTSNOOP_TYPE_UART: + len = read(btsnoop->fd, &pkt_type, 1); + if (len < 0) { + btsnoop->aborted = true; + return false; + } + toread--; + + *index = 0; + *opcode = get_opcode_from_flags(pkt_type, flags); + break; + + case BTSNOOP_TYPE_MONITOR: + *index = flags >> 16; + *opcode = flags & 0xffff; + break; + + default: + btsnoop->aborted = true; + return false; + } + + len = read(btsnoop->fd, data, toread); + if (len < 0) { + btsnoop->aborted = true; + return false; + } + + *size = toread; + + return true; +} + +bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv, + uint16_t *frequency, void *data, uint16_t *size) +{ + return false; +} diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h index 24580d4..2c55d02 100644 --- a/src/shared/btsnoop.h +++ b/src/shared/btsnoop.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or @@ -33,6 +33,8 @@ #define BTSNOOP_TYPE_MONITOR 2001 #define BTSNOOP_TYPE_SIMULATOR 2002 +#define BTSNOOP_FLAG_PKLG_SUPPORT (1 << 0) + #define BTSNOOP_OPCODE_NEW_INDEX 0 #define BTSNOOP_OPCODE_DEL_INDEX 1 #define BTSNOOP_OPCODE_COMMAND_PKT 2 @@ -42,9 +44,16 @@ #define BTSNOOP_OPCODE_SCO_TX_PKT 6 #define BTSNOOP_OPCODE_SCO_RX_PKT 7 +struct btsnoop_opcode_new_index { + uint8_t type; + uint8_t bus; + uint8_t bdaddr[6]; + char name[8]; +} __attribute__((packed)); + struct btsnoop; -struct btsnoop *btsnoop_open(const char *path); +struct btsnoop *btsnoop_open(const char *path, unsigned long flags); struct btsnoop *btsnoop_create(const char *path, uint32_t type); struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop); @@ -54,5 +63,14 @@ uint32_t btsnoop_get_type(struct btsnoop *btsnoop); bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, uint32_t flags, const void *data, uint16_t size); +bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv, + uint16_t index, uint16_t opcode, + const void *data, uint16_t size); bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv, uint16_t frequency, const void *data, uint16_t size); + +bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv, + uint16_t *index, uint16_t *opcode, + void *data, uint16_t *size); +bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv, + uint16_t *frequency, void *data, uint16_t *size); diff --git a/src/shared/crypto.c b/src/shared/crypto.c new file mode 100644 index 0000000..cc7536a --- /dev/null +++ b/src/shared/crypto.c @@ -0,0 +1,470 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include + +#include "src/shared/util.h" +#include "src/shared/crypto.h" + +#ifndef PF_ALG +#include + +struct sockaddr_alg { + __u16 salg_family; + __u8 salg_type[14]; + __u32 salg_feat; + __u32 salg_mask; + __u8 salg_name[64]; +}; + +struct af_alg_iv { + __u32 ivlen; + __u8 iv[0]; +}; + +#define ALG_SET_KEY 1 +#define ALG_SET_IV 2 +#define ALG_SET_OP 3 + +#define ALG_OP_DECRYPT 0 +#define ALG_OP_ENCRYPT 1 + +#define PF_ALG 38 /* Algorithm sockets. */ +#define AF_ALG PF_ALG +#else +#include +#endif + +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + +struct bt_crypto { + int ref_count; + int ecb_aes; + int urandom; +}; + +static int urandom_setup(void) +{ + int fd; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + return -1; + + return fd; +} + +static int ecb_aes_setup(void) +{ + struct sockaddr_alg salg; + int fd; + + fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); + if (fd < 0) + return -1; + + memset(&salg, 0, sizeof(salg)); + salg.salg_family = AF_ALG; + strcpy((char *) salg.salg_type, "skcipher"); + strcpy((char *) salg.salg_name, "ecb(aes)"); + + if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) { + close(fd); + return -1; + } + + return fd; +} + +struct bt_crypto *bt_crypto_new(void) +{ + struct bt_crypto *crypto; + + crypto = new0(struct bt_crypto, 1); + if (!crypto) + return NULL; + + crypto->ecb_aes = ecb_aes_setup(); + if (crypto->ecb_aes < 0) { + free(crypto); + return NULL; + } + + crypto->urandom = urandom_setup(); + if (crypto->urandom < 0) { + close(crypto->ecb_aes); + free(crypto); + return NULL; + } + + return bt_crypto_ref(crypto); +} + +struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto) +{ + if (!crypto) + return NULL; + + __sync_fetch_and_add(&crypto->ref_count, 1); + + return crypto; +} + +void bt_crypto_unref(struct bt_crypto *crypto) +{ + if (!crypto) + return; + + if (__sync_sub_and_fetch(&crypto->ref_count, 1)) + return; + + close(crypto->urandom); + close(crypto->ecb_aes); + + free(crypto); +} + +bool bt_crypto_random_bytes(struct bt_crypto *crypto, + uint8_t *buf, uint8_t num_bytes) +{ + ssize_t len; + + if (!crypto) + return false; + + len = read(crypto->urandom, buf, num_bytes); + if (len < num_bytes) + return false; + + return true; +} + +static int alg_new(int fd, const void *keyval, socklen_t keylen) +{ + if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0) + return -1; + + /* FIXME: This should use accept4() with SOCK_CLOEXEC */ + return accept(fd, NULL, 0); +} + +static bool alg_encrypt(int fd, const void *inbuf, size_t inlen, + void *outbuf, size_t outlen) +{ + __u32 alg_op = ALG_OP_ENCRYPT; + char cbuf[CMSG_SPACE(sizeof(alg_op))]; + struct cmsghdr *cmsg; + struct msghdr msg; + struct iovec iov; + ssize_t len; + + memset(cbuf, 0, sizeof(cbuf)); + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_ALG; + cmsg->cmsg_type = ALG_SET_OP; + cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op)); + memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op)); + + iov.iov_base = (void *) inbuf; + iov.iov_len = inlen; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + len = sendmsg(fd, &msg, 0); + if (len < 0) + return false; + + len = read(fd, outbuf, outlen); + if (len < 0) + return false; + + return true; +} + +static inline void swap128(const uint8_t src[16], uint8_t dst[16]) +{ + int i; + + for (i = 0; i < 16; i++) + dst[15 - i] = src[i]; +} + +/* + * Security function e + * + * Security function e generates 128-bit encryptedData from a 128-bit key + * and 128-bit plaintextData using the AES-128-bit block cypher: + * + * encryptedData = e(key, plaintextData) + * + * The most significant octet of key corresponds to key[0], the most + * significant octet of plaintextData corresponds to in[0] and the + * most significant octet of encryptedData corresponds to out[0]. + * + */ +bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16], + const uint8_t plaintext[16], uint8_t encrypted[16]) +{ + uint8_t tmp[16], in[16], out[16]; + int fd; + + if (!crypto) + return false; + + /* The most significant octet of key corresponds to key[0] */ + swap128(key, tmp); + + fd = alg_new(crypto->ecb_aes, tmp, 16); + if (fd < 0) + return false; + + + /* Most significant octet of plaintextData corresponds to in[0] */ + swap128(plaintext, in); + + if (!alg_encrypt(fd, in, 16, out, 16)) { + close(fd); + return false; + } + + /* Most significant octet of encryptedData corresponds to out[0] */ + swap128(out, encrypted); + + close(fd); + + return true; +} + +/* + * Random Address Hash function ah + * + * The random address hash function ah is used to generate a hash value + * that is used in resolvable private addresses. + * + * The following are inputs to the random address hash function ah: + * + * k is 128 bits + * r is 24 bits + * padding is 104 bits + * + * r is concatenated with padding to generate r' which is used as the + * 128-bit input parameter plaintextData to security function e: + * + * r' = padding || r + * + * The least significant octet of r becomes the least significant octet + * of r’ and the most significant octet of padding becomes the most + * significant octet of r'. + * + * For example, if the 24-bit value r is 0x423456 then r' is + * 0x00000000000000000000000000423456. + * + * The output of the random address function ah is: + * + * ah(k, r) = e(k, r') mod 2^24 + * + * The output of the security function e is then truncated to 24 bits by + * taking the least significant 24 bits of the output of e as the result + * of ah. + */ +bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16], + const uint8_t r[3], uint8_t hash[3]) +{ + uint8_t rp[16]; + uint8_t encrypted[16]; + + if (!crypto) + return false; + + /* r' = padding || r */ + memcpy(rp, r, 3); + memset(rp + 3, 0, 13); + + /* e(k, r') */ + if (!bt_crypto_e(crypto, k, rp, encrypted)) + return false; + + /* ah(k, r) = e(k, r') mod 2^24 */ + memcpy(hash, encrypted, 3); + + return true; +} + +typedef struct { + uint64_t a, b; +} u128; + +static inline void u128_xor(const uint8_t p[16], const uint8_t q[16], + uint8_t r[16]) +{ + u128 pp, qq, rr; + + memcpy(&pp, p, 16); + memcpy(&qq, q, 16); + + rr.a = pp.a ^ qq.a; + rr.b = pp.b ^ qq.b; + + memcpy(r, &rr, 16); +} + +/* + * Confirm value generation function c1 + * + * During the pairing process confirm values are exchanged. This confirm + * value generation function c1 is used to generate the confirm values. + * + * The following are inputs to the confirm value generation function c1: + * + * k is 128 bits + * r is 128 bits + * pres is 56 bits + * preq is 56 bits + * iat is 1 bit + * ia is 48 bits + * rat is 1 bit + * ra is 48 bits + * padding is 32 bits of 0 + * + * iat is concatenated with 7-bits of 0 to create iat' which is 8 bits + * in length. iat is the least significant bit of iat' + * + * rat is concatenated with 7-bits of 0 to create rat' which is 8 bits + * in length. rat is the least significant bit of rat' + * + * pres, preq, rat' and iat' are concatenated to generate p1 which is + * XORed with r and used as 128-bit input parameter plaintextData to + * security function e: + * + * p1 = pres || preq || rat' || iat' + * + * The octet of iat' becomes the least significant octet of p1 and the + * most significant octet of pres becomes the most significant octet of + * p1. + * + * ra is concatenated with ia and padding to generate p2 which is XORed + * with the result of the security function e using p1 as the input + * paremter plaintextData and is then used as the 128-bit input + * parameter plaintextData to security function e: + * + * p2 = padding || ia || ra + * + * The least significant octet of ra becomes the least significant octet + * of p2 and the most significant octet of padding becomes the most + * significant octet of p2. + * + * The output of the confirm value generation function c1 is: + * + * c1(k, r, preq, pres, iat, rat, ia, ra) = e(k, e(k, r XOR p1) XOR p2) + * + * The 128-bit output of the security function e is used as the result + * of confirm value generation function c1. + */ +bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16], + const uint8_t r[16], const uint8_t pres[7], + const uint8_t preq[7], uint8_t iat, + const uint8_t ia[6], uint8_t rat, + const uint8_t ra[6], uint8_t res[16]) +{ + uint8_t p1[16], p2[16]; + + /* p1 = pres || preq || _rat || _iat */ + p1[0] = iat; + p1[1] = rat; + memcpy(p1 + 2, preq, 7); + memcpy(p1 + 9, pres, 7); + + /* p2 = padding || ia || ra */ + memcpy(p2, ra, 6); + memcpy(p2 + 6, ia, 6); + memset(p2 + 12, 0, 4); + + /* res = r XOR p1 */ + u128_xor(r, p1, res); + + /* res = e(k, res) */ + if (!bt_crypto_e(crypto, k, res, res)) + return false; + + /* res = res XOR p2 */ + u128_xor(res, p2, res); + + /* res = e(k, res) */ + return bt_crypto_e(crypto, k, res, res); +} + +/* + * Key generation function s1 + * + * The key generation function s1 is used to generate the STK during the + * pairing process. + * + * The following are inputs to the key generation function s1: + * + * k is 128 bits + * r1 is 128 bits + * r2 is 128 bits + * + * The most significant 64-bits of r1 are discarded to generate r1' and + * the most significant 64-bits of r2 are discarded to generate r2'. + * + * r1' is concatenated with r2' to generate r' which is used as the + * 128-bit input parameter plaintextData to security function e: + * + * r' = r1' || r2' + * + * The least significant octet of r2' becomes the least significant + * octet of r' and the most significant octet of r1' becomes the most + * significant octet of r'. + * + * The output of the key generation function s1 is: + * + * s1(k, r1, r2) = e(k, r') + * + * The 128-bit output of the security function e is used as the result + * of key generation function s1. + */ +bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16], + const uint8_t r1[16], const uint8_t r2[16], + uint8_t res[16]) +{ + memcpy(res, r2, 8); + memcpy(res + 8, r1, 8); + + return bt_crypto_e(crypto, k, res, res); +} diff --git a/src/shared/crypto.h b/src/shared/crypto.h new file mode 100644 index 0000000..cae8daa --- /dev/null +++ b/src/shared/crypto.h @@ -0,0 +1,48 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#include + +struct bt_crypto; + +struct bt_crypto *bt_crypto_new(void); + +struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto); +void bt_crypto_unref(struct bt_crypto *crypto); + +bool bt_crypto_random_bytes(struct bt_crypto *crypto, + uint8_t *buf, uint8_t num_bytes); + +bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16], + const uint8_t plaintext[16], uint8_t encrypted[16]); +bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16], + const uint8_t r[3], uint8_t hash[3]); +bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16], + const uint8_t r[16], const uint8_t pres[7], + const uint8_t preq[7], uint8_t iat, + const uint8_t ia[6], uint8_t rat, + const uint8_t ra[6], uint8_t res[16]); +bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16], + const uint8_t r1[16], const uint8_t r2[16], + uint8_t res[16]); diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c new file mode 100644 index 0000000..36316af --- /dev/null +++ b/src/shared/gatt-db.c @@ -0,0 +1,735 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 + +#include "lib/uuid.h" +#include "src/shared/util.h" +#include "src/shared/queue.h" +#include "src/shared/gatt-db.h" + +#define MAX_CHAR_DECL_VALUE_LEN 19 +#define MAX_INCLUDED_VALUE_LEN 6 + +static const bt_uuid_t primary_service_uuid = { .type = BT_UUID16, + .value.u16 = GATT_PRIM_SVC_UUID }; +static const bt_uuid_t secondary_service_uuid = { .type = BT_UUID16, + .value.u16 = GATT_SND_SVC_UUID }; +static const bt_uuid_t characteristic_uuid = { .type = BT_UUID16, + .value.u16 = GATT_CHARAC_UUID }; +static const bt_uuid_t included_service_uuid = { .type = BT_UUID16, + .value.u16 = GATT_INCLUDE_UUID }; + +struct gatt_db { + uint16_t next_handle; + struct queue *services; +}; + +struct gatt_db_attribute { + uint16_t handle; + bt_uuid_t uuid; + uint8_t permissions; + uint16_t value_len; + uint8_t *value; + + gatt_db_read_t read_func; + gatt_db_write_t write_func; + void *user_data; +}; + +struct gatt_db_service { + bool active; + uint16_t num_handles; + struct gatt_db_attribute **attributes; +}; + +static bool match_service_by_handle(const void *data, const void *user_data) +{ + const struct gatt_db_service *service = data; + + return service->attributes[0]->handle == PTR_TO_INT(user_data); +} + +static struct gatt_db_attribute *new_attribute(const bt_uuid_t *type, + const uint8_t *val, + uint16_t len) +{ + struct gatt_db_attribute *attribute; + + attribute = new0(struct gatt_db_attribute, 1); + if (!attribute) + return NULL; + + attribute->uuid = *type; + attribute->value_len = len; + if (len) { + attribute->value = malloc0(len); + if (!attribute->value) { + free(attribute); + return NULL; + } + + memcpy(attribute->value, val, len); + } + + return attribute; +} + +static void attribute_destroy(void *data) +{ + struct gatt_db_attribute *attribute = data; + + /* Attribute was not initialized by user */ + if (!attribute) + return; + + free(attribute->value); + free(attribute); +} + +struct gatt_db *gatt_db_new(void) +{ + struct gatt_db *db; + + db = new0(struct gatt_db, 1); + if (!db) + return NULL; + + db->services = queue_new(); + if (!db->services) { + free(db); + return NULL; + } + + db->next_handle = 0x0001; + + return db; +} + +static void gatt_db_service_destroy(void *data) +{ + struct gatt_db_service *service = data; + int i; + + for (i = 0; i < service->num_handles; i++) + attribute_destroy(service->attributes[i]); + + free(service->attributes); + free(service); +} + +void gatt_db_destroy(struct gatt_db *db) +{ + queue_destroy(db->services, gatt_db_service_destroy); + free(db); +} + +static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst) +{ + bt_uuid_t uuid128; + + if (uuid->type == BT_UUID16) { + put_le16(uuid->value.u16, dst); + return bt_uuid_len(uuid); + } + + bt_uuid_to_uuid128(uuid, &uuid128); + bswap_128(&uuid128.value.u128, dst); + return bt_uuid_len(&uuid128); +} + +uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid, + bool primary, uint16_t num_handles) +{ + struct gatt_db_service *service; + const bt_uuid_t *type; + uint8_t value[16]; + uint16_t len; + + if (num_handles < 1 || (num_handles + db->next_handle) > UINT16_MAX) + return 0; + + service = new0(struct gatt_db_service, 1); + if (!service) + return 0; + + service->attributes = new0(struct gatt_db_attribute *, num_handles); + if (!service->attributes) { + free(service); + return 0; + } + + if (primary) + type = &primary_service_uuid; + else + type = &secondary_service_uuid; + + len = uuid_to_le(uuid, value); + + service->attributes[0] = new_attribute(type, value, len); + if (!service->attributes[0]) { + gatt_db_service_destroy(service); + return 0; + } + + if (!queue_push_tail(db->services, service)) { + gatt_db_service_destroy(service); + return 0; + } + + /* TODO now we get next handle from database. We should first look + * for 'holes' between existing services first, and assign next_handle + * only if enough space was not found. + */ + service->attributes[0]->handle = db->next_handle; + db->next_handle += num_handles; + service->num_handles = num_handles; + + return service->attributes[0]->handle; +} + +bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle) +{ + struct gatt_db_service *service; + + service = queue_remove_if(db->services, match_service_by_handle, + INT_TO_PTR(handle)); + if (!service) + return false; + + gatt_db_service_destroy(service); + + return true; +} + +static uint16_t get_attribute_index(struct gatt_db_service *service, + int end_offset) +{ + int i = 0; + + /* Here we look for first free attribute index with given offset */ + while (i < (service->num_handles - end_offset) && + service->attributes[i]) + i++; + + return i == (service->num_handles - end_offset) ? 0 : i; +} + +static uint16_t get_handle_at_index(struct gatt_db_service *service, + int index) +{ + return service->attributes[index]->handle; +} + +static uint16_t update_attribute_handle(struct gatt_db_service *service, + int index) +{ + uint16_t previous_handle; + + /* We call this function with index > 0, because index 0 is reserved + * for service declaration, and is set in add_service() + */ + previous_handle = service->attributes[index - 1]->handle; + service->attributes[index]->handle = previous_handle + 1; + + return service->attributes[index]->handle; +} + +static void set_attribute_data(struct gatt_db_attribute *attribute, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + uint8_t permissions, + void *user_data) +{ + attribute->permissions = permissions; + attribute->read_func = read_func; + attribute->write_func = write_func; + attribute->user_data = user_data; +} + +uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle, + const bt_uuid_t *uuid, + uint8_t permissions, + uint8_t properties, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + void *user_data) +{ + uint8_t value[MAX_CHAR_DECL_VALUE_LEN]; + struct gatt_db_service *service; + uint16_t len = 0; + int i; + + service = queue_find(db->services, match_service_by_handle, + INT_TO_PTR(handle)); + if (!service) + return 0; + + i = get_attribute_index(service, 1); + if (!i) + return 0; + + value[0] = properties; + len += sizeof(properties); + /* We set handle of characteristic value, which will be added next */ + put_le16(get_handle_at_index(service, i - 1) + 2, &value[1]); + len += sizeof(uint16_t); + len += uuid_to_le(uuid, &value[3]); + + service->attributes[i] = new_attribute(&characteristic_uuid, value, + len); + if (!service->attributes[i]) + return 0; + + update_attribute_handle(service, i++); + + service->attributes[i] = new_attribute(uuid, NULL, 0); + if (!service->attributes[i]) { + free(service->attributes[i - 1]); + return 0; + } + + set_attribute_data(service->attributes[i], read_func, write_func, + permissions, user_data); + + return update_attribute_handle(service, i); +} + +uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle, + const bt_uuid_t *uuid, + uint8_t permissions, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + void *user_data) +{ + struct gatt_db_service *service; + int i; + + service = queue_find(db->services, match_service_by_handle, + INT_TO_PTR(handle)); + if (!service) + return 0; + + i = get_attribute_index(service, 0); + if (!i) + return 0; + + service->attributes[i] = new_attribute(uuid, NULL, 0); + if (!service->attributes[i]) + return 0; + + set_attribute_data(service->attributes[i], read_func, write_func, + permissions, user_data); + + return update_attribute_handle(service, i); +} + +uint16_t gatt_db_add_included_service(struct gatt_db *db, uint16_t handle, + uint16_t included_handle) +{ + struct gatt_db_service *included_service; + uint8_t value[MAX_INCLUDED_VALUE_LEN]; + uint16_t len = 0; + struct gatt_db_service *service; + int index; + + service = queue_find(db->services, match_service_by_handle, + INT_TO_PTR(handle)); + if (!service) + return 0; + + included_service = queue_find(db->services, match_service_by_handle, + INT_TO_PTR(included_handle)); + + if (!included_service) + return 0; + + put_le16(included_handle, &value[len]); + len += sizeof(uint16_t); + + put_le16(included_handle + included_service->num_handles - 1, + &value[len]); + len += sizeof(uint16_t); + + /* The Service UUID shall only be present when the UUID is a 16-bit + * Bluetooth UUID. Vol 2. Part G. 3.2 + */ + if (included_service->attributes[0]->value_len == sizeof(uint16_t)) { + memcpy(&value[len], included_service->attributes[0]->value, + included_service->attributes[0]->value_len); + len += included_service->attributes[0]->value_len; + } + + index = get_attribute_index(service, 0); + if (!index) + return 0; + + service->attributes[index] = new_attribute(&included_service_uuid, + value, len); + if (!service->attributes[index]) + return 0; + + /* The Attribute Permissions shall be read only and not require + * authentication or authorization. Vol 2. Part G. 3.2 + * + * TODO handle permissions + */ + set_attribute_data(service->attributes[index], NULL, NULL, 0, NULL); + + return update_attribute_handle(service, index); +} + +bool gatt_db_service_set_active(struct gatt_db *db, uint16_t handle, + bool active) +{ + struct gatt_db_service *service; + + service = queue_find(db->services, match_service_by_handle, + INT_TO_PTR(handle)); + if (!service) + return false; + + service->active = active; + + return true; +} + +struct read_by_group_type_data { + struct queue *queue; + bt_uuid_t uuid; + uint16_t start_handle; + uint16_t end_handle; + uint16_t uuid_size; + bool stop_search; +}; + +static void read_by_group_type(void *data, void *user_data) +{ + struct read_by_group_type_data *search_data = user_data; + struct gatt_db_service *service = data; + + if (!service->active) + return; + + /* Don't want more results as they have different size */ + if (search_data->stop_search) + return; + + if (bt_uuid_cmp(&search_data->uuid, &service->attributes[0]->uuid)) + return; + + if (service->attributes[0]->handle < search_data->start_handle) + return; + + /* Remember size of uuid */ + if (!search_data->uuid_size) { + search_data->uuid_size = service->attributes[0]->value_len; + } else if (search_data->uuid_size != + service->attributes[0]->value_len) { + /* Don't want more results. This is last */ + search_data->stop_search = true; + return; + } + + queue_push_tail(search_data->queue, + UINT_TO_PTR(service->attributes[0]->handle)); +} + +void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle, + uint16_t end_handle, + const bt_uuid_t type, + struct queue *queue) +{ + struct read_by_group_type_data data; + + data.uuid = type; + data.start_handle = start_handle; + data.end_handle = end_handle; + data.queue = queue; + data.uuid_size = 0; + data.stop_search = false; + + queue_foreach(db->services, read_by_group_type, &data); +} + +struct find_by_type_value_data { + struct queue *queue; + bt_uuid_t uuid; + uint16_t start_handle; + uint16_t end_handle; +}; + +static void find_by_type(void *data, void *user_data) +{ + struct find_by_type_value_data *search_data = user_data; + struct gatt_db_service *service = data; + struct gatt_db_attribute *attribute; + int i; + + if (!service->active) + return; + + for (i = 0; i < service->num_handles; i++) { + attribute = service->attributes[i]; + + if (!attribute) + continue; + + if ((attribute->handle < search_data->start_handle) || + (attribute->handle > search_data->end_handle)) + continue; + + if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid)) + continue; + + queue_push_tail(search_data->queue, + UINT_TO_PTR(attribute->handle)); + } +} + +void gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle, + uint16_t end_handle, + const bt_uuid_t *type, + struct queue *queue) +{ + struct find_by_type_value_data data; + + data.uuid = *type; + data.start_handle = start_handle; + data.end_handle = end_handle; + data.queue = queue; + + queue_foreach(db->services, find_by_type, &data); +} + +struct read_by_type_data { + struct queue *queue; + bt_uuid_t uuid; + uint16_t start_handle; + uint16_t end_handle; +}; + +static void read_by_type(void *data, void *user_data) +{ + struct read_by_type_data *search_data = user_data; + struct gatt_db_service *service = data; + struct gatt_db_attribute *attribute; + int i; + + if (!service->active) + return; + + for (i = 0; i < service->num_handles; i++) { + attribute = service->attributes[i]; + if (!attribute) + continue; + + if (attribute->handle < search_data->start_handle) + continue; + + if (attribute->handle > search_data->end_handle) + return; + + if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid)) + continue; + + queue_push_tail(search_data->queue, + UINT_TO_PTR(attribute->handle)); + } +} + +void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle, + uint16_t end_handle, + const bt_uuid_t type, + struct queue *queue) +{ + struct read_by_type_data data; + data.uuid = type; + data.start_handle = start_handle; + data.end_handle = end_handle; + data.queue = queue; + + queue_foreach(db->services, read_by_type, &data); +} + + +struct find_information_data { + struct queue *queue; + uint16_t start_handle; + uint16_t end_handle; +}; + +static void find_information(void *data, void *user_data) +{ + struct find_information_data *search_data = user_data; + struct gatt_db_service *service = data; + struct gatt_db_attribute *attribute; + int i; + + if (!service->active) + return; + + /* Check if service is in range */ + if ((service->attributes[0]->handle + service->num_handles - 1) < + search_data->start_handle) + return; + + for (i = 0; i < service->num_handles; i++) { + attribute = service->attributes[i]; + if (!attribute) + continue; + + if (attribute->handle < search_data->start_handle) + continue; + + if (attribute->handle > search_data->end_handle) + return; + + queue_push_tail(search_data->queue, + UINT_TO_PTR(attribute->handle)); + } +} + +void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle, + uint16_t end_handle, + struct queue *queue) +{ + struct find_information_data data; + + data.start_handle = start_handle; + data.end_handle = end_handle; + data.queue = queue; + + queue_foreach(db->services, find_information, &data); +} + +static bool find_service_for_handle(const void *data, const void *user_data) +{ + const struct gatt_db_service *service = data; + uint16_t handle = PTR_TO_INT(user_data); + uint16_t start, end; + + start = service->attributes[0]->handle; + end = start + service->num_handles; + + return (start <= handle) && (handle < end); +} + +bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset, + uint8_t att_opcode, bdaddr_t *bdaddr, + uint8_t **value, int *length) +{ + struct gatt_db_service *service; + uint16_t service_handle; + struct gatt_db_attribute *a; + + if (!value || !length) + return false; + + service = queue_find(db->services, find_service_for_handle, + INT_TO_PTR(handle)); + if (!service) + return false; + + service_handle = service->attributes[0]->handle; + + a = service->attributes[handle - service_handle]; + if (!a) + return false; + + /* + * We call callback, and set length to -1, to notify user that callback + * has been called. Otherwise we set length to value length in database. + */ + if (a->read_func) { + *value = NULL; + *length = -1; + a->read_func(handle, offset, att_opcode, bdaddr, a->user_data); + } else { + if (offset > a->value_len) + return false; + + *value = &a->value[offset]; + *length = a->value_len - offset; + } + + return true; +} + +bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t att_opcode, bdaddr_t *bdaddr) +{ + struct gatt_db_service *service; + uint16_t service_handle; + struct gatt_db_attribute *a; + + service = queue_find(db->services, find_service_for_handle, + INT_TO_PTR(handle)); + if (!service) + return false; + + service_handle = service->attributes[0]->handle; + + a = service->attributes[handle - service_handle]; + if (!a || !a->write_func) + return false; + + a->write_func(handle, offset, value, len, att_opcode, bdaddr, + a->user_data); + + return true; +} + +const bt_uuid_t *gatt_db_get_attribute_type(struct gatt_db *db, + uint16_t handle) +{ + struct gatt_db_service *service; + struct gatt_db_attribute *attribute; + uint16_t service_handle; + + service = queue_find(db->services, find_service_for_handle, + INT_TO_PTR(handle)); + if (!service) + return NULL; + + service_handle = service->attributes[0]->handle; + + attribute = service->attributes[handle - service_handle]; + if (!attribute) + return NULL; + + return &attribute->uuid; +} + +uint16_t gatt_db_get_end_handle(struct gatt_db *db, uint16_t handle) +{ + struct gatt_db_service *service; + + service = queue_find(db->services, find_service_for_handle, + INT_TO_PTR(handle)); + if (!service) + return 0; + + return service->attributes[0]->handle + service->num_handles - 1; +} diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h new file mode 100644 index 0000000..3d46730 --- /dev/null +++ b/src/shared/gatt-db.h @@ -0,0 +1,93 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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 + * + */ + +struct gatt_db; + +struct gatt_db *gatt_db_new(void); +void gatt_db_destroy(struct gatt_db *db); + +uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid, + bool primary, uint16_t num_handles); +bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle); + +typedef void (*gatt_db_read_t) (uint16_t handle, uint16_t offset, + uint8_t att_opcode, bdaddr_t *bdaddr, + void *user_data); + +typedef void (*gatt_db_write_t) (uint16_t handle, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t att_opcode, bdaddr_t *bdaddr, + void *user_data); + +uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle, + const bt_uuid_t *uuid, + uint8_t permissions, + uint8_t properties, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + void *user_data); + +uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle, + const bt_uuid_t *uuid, + uint8_t permissions, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + void *user_data); + +uint16_t gatt_db_add_included_service(struct gatt_db *db, uint16_t handle, + uint16_t included_handle); + +bool gatt_db_service_set_active(struct gatt_db *db, uint16_t handle, + bool active); + +void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle, + uint16_t end_handle, + const bt_uuid_t type, + struct queue *queue); + +void gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle, + uint16_t end_handle, + const bt_uuid_t *type, + struct queue *queue); + +void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle, + uint16_t end_handle, + const bt_uuid_t type, + struct queue *queue); + +void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle, + uint16_t end_handle, + struct queue *queue); + +bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset, + uint8_t att_opcode, bdaddr_t *bdaddr, + uint8_t **value, int *length); + +bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t att_opcode, bdaddr_t *bdaddr); + +const bt_uuid_t *gatt_db_get_attribute_type(struct gatt_db *db, + uint16_t handle); + +uint16_t gatt_db_get_end_handle(struct gatt_db *db, uint16_t handle); diff --git a/src/shared/hci.c b/src/shared/hci.c new file mode 100644 index 0000000..6d0a5f8 --- /dev/null +++ b/src/shared/hci.c @@ -0,0 +1,618 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include + +#include "monitor/bt.h" +#include "monitor/mainloop.h" +#include "src/shared/io.h" +#include "src/shared/util.h" +#include "src/shared/queue.h" +#include "src/shared/hci.h" + +#define BTPROTO_HCI 1 +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; +#define HCI_CHANNEL_RAW 0 +#define HCI_CHANNEL_USER 1 + +#define SOL_HCI 0 +#define HCI_FILTER 2 +struct hci_filter { + uint32_t type_mask; + uint32_t event_mask[2]; + uint16_t opcode; +}; + +struct bt_hci { + int ref_count; + struct io *io; + bool is_stream; + bool writer_active; + uint8_t num_cmds; + unsigned int next_cmd_id; + unsigned int next_evt_id; + struct queue *cmd_queue; + struct queue *rsp_queue; + struct queue *evt_list; +}; + +struct cmd { + unsigned int id; + uint16_t opcode; + void *data; + uint8_t size; + bt_hci_callback_func_t callback; + bt_hci_destroy_func_t destroy; + void *user_data; +}; + +struct evt { + unsigned int id; + uint8_t event; + bt_hci_callback_func_t callback; + bt_hci_destroy_func_t destroy; + void *user_data; +}; + +static void cmd_free(void *data) +{ + struct cmd *cmd = data; + + if (cmd->destroy) + cmd->destroy(cmd->user_data); + + free(cmd->data); + free(cmd); +} + +static void evt_free(void *data) +{ + struct evt *evt = data; + + if (evt->destroy) + evt->destroy(evt->user_data); + + free(evt); +} + +static void send_command(struct bt_hci *hci, uint16_t opcode, + void *data, uint8_t size) +{ + uint8_t type = BT_H4_CMD_PKT; + struct bt_hci_cmd_hdr hdr; + struct iovec iov[3]; + int fd, iovcnt; + + if (hci->num_cmds < 1) + return; + + hdr.opcode = cpu_to_le16(opcode); + hdr.plen = size; + + iov[0].iov_base = &type; + iov[0].iov_len = 1; + iov[1].iov_base = &hdr; + iov[1].iov_len = sizeof(hdr); + + if (size > 0) { + iov[2].iov_base = data; + iov[2].iov_len = size; + iovcnt = 3; + } else + iovcnt = 2; + + fd = io_get_fd(hci->io); + if (fd < 0) + return; + + if (writev(fd, iov, iovcnt) < 0) + return; + + hci->num_cmds--; +} + +static bool io_write_callback(struct io *io, void *user_data) +{ + struct bt_hci *hci = user_data; + struct cmd *cmd; + + cmd = queue_pop_head(hci->cmd_queue); + if (cmd) { + send_command(hci, cmd->opcode, cmd->data, cmd->size); + queue_push_tail(hci->rsp_queue, cmd); + } + + hci->writer_active = false; + + return false; +} + +static void wakeup_writer(struct bt_hci *hci) +{ + if (hci->writer_active) + return; + + if (hci->num_cmds < 1) + return; + + if (queue_isempty(hci->cmd_queue)) + return; + + if (!io_set_write_handler(hci->io, io_write_callback, hci, NULL)) + return; + + hci->writer_active = true; +} + +static bool match_cmd_opcode(const void *a, const void *b) +{ + const struct cmd *cmd = a; + uint16_t opcode = PTR_TO_UINT(b); + + return cmd->opcode == opcode; +} + +static void process_response(struct bt_hci *hci, uint16_t opcode, + const void *data, size_t size) +{ + struct cmd *cmd; + + if (opcode == BT_HCI_CMD_NOP) + goto done; + + cmd = queue_remove_if(hci->rsp_queue, match_cmd_opcode, + UINT_TO_PTR(opcode)); + if (!cmd) + return; + + if (cmd->callback) + cmd->callback(data, size, cmd->user_data); + + cmd_free(cmd); + +done: + wakeup_writer(hci); +} + +static void process_notify(void *data, void *user_data) +{ + struct bt_hci_evt_hdr *hdr = user_data; + struct evt *evt = data; + + if (evt->event == hdr->evt) + evt->callback(user_data + sizeof(struct bt_hci_evt_hdr), + hdr->plen, evt->user_data); +} + +static void process_event(struct bt_hci *hci, const void *data, size_t size) +{ + const struct bt_hci_evt_hdr *hdr = data; + const struct bt_hci_evt_cmd_complete *cc; + const struct bt_hci_evt_cmd_status *cs; + + if (size < sizeof(struct bt_hci_evt_hdr)) + return; + + data += sizeof(struct bt_hci_evt_hdr); + size -= sizeof(struct bt_hci_evt_hdr); + + if (hdr->plen != size) + return; + + switch (hdr->evt) { + case BT_HCI_EVT_CMD_COMPLETE: + if (size < sizeof(*cc)) + return; + cc = data; + hci->num_cmds = cc->ncmd; + process_response(hci, le16_to_cpu(cc->opcode), + data + sizeof(*cc), + size - sizeof(*cc)); + break; + + case BT_HCI_EVT_CMD_STATUS: + if (size < sizeof(*cs)) + return; + cs = data; + hci->num_cmds = cs->ncmd; + process_response(hci, le16_to_cpu(cs->opcode), &cs->status, 1); + break; + + default: + queue_foreach(hci->evt_list, process_notify, (void *) hdr); + break; + } +} + +static bool io_read_callback(struct io *io, void *user_data) +{ + struct bt_hci *hci = user_data; + uint8_t buf[512]; + ssize_t len; + int fd; + + fd = io_get_fd(hci->io); + if (fd < 0) + return false; + + if (hci->is_stream) + return false; + + len = read(fd, buf, sizeof(buf)); + if (len < 0) + return false; + + if (len < 1) + return true; + + switch (buf[0]) { + case BT_H4_EVT_PKT: + process_event(hci, buf + 1, len - 1); + break; + } + + return true; +} + +static struct bt_hci *create_hci(int fd) +{ + struct bt_hci *hci; + + if (fd < 0) + return NULL; + + hci = new0(struct bt_hci, 1); + if (!hci) + return NULL; + + hci->io = io_new(fd); + if (!hci->io) { + free(hci); + return NULL; + } + + hci->is_stream = true; + hci->writer_active = false; + hci->num_cmds = 1; + hci->next_cmd_id = 1; + hci->next_evt_id = 1; + + hci->cmd_queue = queue_new(); + if (!hci->cmd_queue) { + io_destroy(hci->io); + free(hci); + return NULL; + } + + hci->rsp_queue = queue_new(); + if (!hci->rsp_queue) { + queue_destroy(hci->cmd_queue, NULL); + io_destroy(hci->io); + free(hci); + return NULL; + } + + hci->evt_list = queue_new(); + if (!hci->evt_list) { + queue_destroy(hci->rsp_queue, NULL); + queue_destroy(hci->cmd_queue, NULL); + io_destroy(hci->io); + free(hci); + return NULL; + } + + if (!io_set_read_handler(hci->io, io_read_callback, hci, NULL)) { + queue_destroy(hci->evt_list, NULL); + queue_destroy(hci->rsp_queue, NULL); + queue_destroy(hci->cmd_queue, NULL); + io_destroy(hci->io); + free(hci); + return NULL; + } + + return bt_hci_ref(hci); +} + +struct bt_hci *bt_hci_new(int fd) +{ + struct bt_hci *hci; + + hci = create_hci(fd); + if (!hci) + return NULL; + + return hci; +} + +static int create_socket(uint16_t index, uint16_t channel) +{ + struct sockaddr_hci addr; + int fd; + + fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, + BTPROTO_HCI); + if (fd < 0) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.hci_family = AF_BLUETOOTH; + addr.hci_dev = index; + addr.hci_channel = channel; + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(fd); + return -1; + } + + return fd; +} + +struct bt_hci *bt_hci_new_user_channel(uint16_t index) +{ + struct bt_hci *hci; + int fd; + + fd = create_socket(index, HCI_CHANNEL_USER); + if (fd < 0) + return NULL; + + hci = create_hci(fd); + if (!hci) { + close(fd); + return NULL; + } + + hci->is_stream = false; + + bt_hci_set_close_on_unref(hci, true); + + return hci; +} + +struct bt_hci *bt_hci_new_raw_device(uint16_t index) +{ + struct bt_hci *hci; + struct hci_filter flt; + int fd; + + fd = create_socket(index, HCI_CHANNEL_RAW); + if (fd < 0) + return NULL; + + memset(&flt, 0, sizeof(flt)); + flt.type_mask = 1 << BT_H4_EVT_PKT; + flt.event_mask[0] = 0xffffffff; + flt.event_mask[1] = 0xffffffff; + + if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { + close(fd); + return NULL; + } + + hci = create_hci(fd); + if (!hci) { + close(fd); + return NULL; + } + + hci->is_stream = false; + + bt_hci_set_close_on_unref(hci, true); + + return hci; +} + +struct bt_hci *bt_hci_ref(struct bt_hci *hci) +{ + if (!hci) + return NULL; + + __sync_fetch_and_add(&hci->ref_count, 1); + + return hci; +} + +void bt_hci_unref(struct bt_hci *hci) +{ + if (!hci) + return; + + if (__sync_sub_and_fetch(&hci->ref_count, 1)) + return; + + queue_destroy(hci->evt_list, evt_free); + queue_destroy(hci->cmd_queue, cmd_free); + queue_destroy(hci->rsp_queue, cmd_free); + + io_destroy(hci->io); + + free(hci); +} + +bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close) +{ + if (!hci) + return false; + + return io_set_close_on_destroy(hci->io, do_close); +} + +unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode, + const void *data, uint8_t size, + bt_hci_callback_func_t callback, + void *user_data, bt_hci_destroy_func_t destroy) +{ + struct cmd *cmd; + + if (!hci) + return 0; + + cmd = new0(struct cmd, 1); + if (!cmd) + return 0; + + cmd->opcode = opcode; + cmd->size = size; + + if (cmd->size > 0) { + cmd->data = malloc(cmd->size); + if (!cmd->data) { + free(cmd); + return 0; + } + + memcpy(cmd->data, data, cmd->size); + } + + if (hci->next_cmd_id < 1) + hci->next_cmd_id = 1; + + cmd->id = hci->next_cmd_id++; + + cmd->callback = callback; + cmd->destroy = destroy; + cmd->user_data = user_data; + + if (!queue_push_tail(hci->cmd_queue, cmd)) { + free(cmd->data); + free(cmd); + return 0; + } + + wakeup_writer(hci); + + return cmd->id; +} + +static bool match_cmd_id(const void *a, const void *b) +{ + const struct cmd *cmd = a; + unsigned int id = PTR_TO_UINT(b); + + return cmd->id == id; +} + +bool bt_hci_cancel(struct bt_hci *hci, unsigned int id) +{ + struct cmd *cmd; + + if (!hci || !id) + return false; + + cmd = queue_remove_if(hci->cmd_queue, match_cmd_id, UINT_TO_PTR(id)); + if (!cmd) { + cmd = queue_remove_if(hci->rsp_queue, match_cmd_id, + UINT_TO_PTR(id)); + if (!cmd) + return false; + } + + cmd_free(cmd); + + wakeup_writer(hci); + + return true; +} + +bool bt_hci_flush(struct bt_hci *hci) +{ + if (!hci) + return false; + + if (hci->writer_active) { + io_set_write_handler(hci->io, NULL, NULL, NULL); + hci->writer_active = false; + } + + queue_remove_all(hci->cmd_queue, NULL, NULL, cmd_free); + queue_remove_all(hci->rsp_queue, NULL, NULL, cmd_free); + + return true; +} + +unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event, + bt_hci_callback_func_t callback, + void *user_data, bt_hci_destroy_func_t destroy) +{ + struct evt *evt; + + if (!hci) + return 0; + + evt = new0(struct evt, 1); + if (!evt) + return 0; + + evt->event = event; + + if (hci->next_evt_id < 1) + hci->next_evt_id = 1; + + evt->id = hci->next_evt_id++; + + evt->callback = callback; + evt->destroy = destroy; + evt->user_data = user_data; + + if (!queue_push_tail(hci->evt_list, evt)) { + free(evt); + return 0; + } + + return evt->id; +} + +static bool match_evt_id(const void *a, const void *b) +{ + const struct evt *evt = a; + unsigned int id = PTR_TO_UINT(b); + + return evt->id == id; +} + +bool bt_hci_unregister(struct bt_hci *hci, unsigned int id) +{ + struct evt *evt; + + if (!hci || !id) + return false; + + evt = queue_remove_if(hci->evt_list, match_evt_id, UINT_TO_PTR(id)); + if (!evt) + return false; + + evt_free(evt); + + return true; +} diff --git a/src/shared/hci.h b/src/shared/hci.h new file mode 100644 index 0000000..dba0f11 --- /dev/null +++ b/src/shared/hci.h @@ -0,0 +1,53 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#include + +typedef void (*bt_hci_destroy_func_t)(void *user_data); + +struct bt_hci; + +struct bt_hci *bt_hci_new(int fd); +struct bt_hci *bt_hci_new_user_channel(uint16_t index); +struct bt_hci *bt_hci_new_raw_device(uint16_t index); + +struct bt_hci *bt_hci_ref(struct bt_hci *hci); +void bt_hci_unref(struct bt_hci *hci); + +bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close); + +typedef void (*bt_hci_callback_func_t)(const void *data, uint8_t size, + void *user_data); + +unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode, + const void *data, uint8_t size, + bt_hci_callback_func_t callback, + void *user_data, bt_hci_destroy_func_t destroy); +bool bt_hci_cancel(struct bt_hci *hci, unsigned int id); +bool bt_hci_flush(struct bt_hci *hci); + +unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event, + bt_hci_callback_func_t callback, + void *user_data, bt_hci_destroy_func_t destroy); +bool bt_hci_unregister(struct bt_hci *hci, unsigned int id); diff --git a/src/shared/hciemu.c b/src/shared/hciemu.c index 0ea191f..6c93005 100644 --- a/src/shared/hciemu.c +++ b/src/shared/hciemu.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -41,8 +42,9 @@ #include "monitor/bt.h" #include "emulator/btdev.h" #include "emulator/bthost.h" - -#include "hciemu.h" +#include "src/shared/util.h" +#include "src/shared/queue.h" +#include "src/shared/hciemu.h" struct hciemu { int ref_count; @@ -53,7 +55,7 @@ struct hciemu { guint host_source; guint master_source; guint client_source; - GList *post_command_hooks; + struct queue *post_command_hooks; char bdaddr_str[18]; }; @@ -62,11 +64,27 @@ struct hciemu_command_hook { void *user_data; }; -static void destroy_command_hook(gpointer data, gpointer user_data) +static void destroy_command_hook(void *data) +{ + struct hciemu_command_hook *hook = data; + + free(hook); +} + +struct run_data { + uint16_t opcode; + const void *data; + uint8_t len; +}; + +static void run_command_hook(void *data, void *user_data) { struct hciemu_command_hook *hook = data; + struct run_data *run_data = user_data; - g_free(hook); + if (hook->function) + hook->function(run_data->opcode, run_data->data, + run_data->len, hook->user_data); } static void master_command_callback(uint16_t opcode, @@ -74,17 +92,12 @@ static void master_command_callback(uint16_t opcode, btdev_callback callback, void *user_data) { struct hciemu *hciemu = user_data; - GList *list; + struct run_data run_data = { .opcode = opcode, + .data = data, .len = len }; btdev_command_default(callback); - for (list = g_list_first(hciemu->post_command_hooks); list; - list = g_list_next(list)) { - struct hciemu_command_hook *hook = list->data; - - if (hook->function) - hook->function(opcode, data, len, hook->user_data); - } + queue_foreach(hciemu->post_command_hooks, run_command_hook, &run_data); } static void client_command_callback(uint16_t opcode, @@ -216,6 +229,7 @@ static bool create_vhci(struct hciemu *hciemu) fd = open("/dev/vhci", O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { + perror("Opening /dev/vhci failed"); btdev_destroy(btdev); return false; } @@ -291,7 +305,7 @@ struct hciemu *hciemu_new(enum hciemu_type type) { struct hciemu *hciemu; - hciemu = g_try_new0(struct hciemu, 1); + hciemu = new0(struct hciemu, 1); if (!hciemu) return NULL; @@ -309,15 +323,23 @@ struct hciemu *hciemu_new(enum hciemu_type type) return NULL; } + hciemu->post_command_hooks = queue_new(); + if (!hciemu->post_command_hooks) { + free(hciemu); + return NULL; + } + if (!create_vhci(hciemu)) { - g_free(hciemu); + queue_destroy(hciemu->post_command_hooks, NULL); + free(hciemu); return NULL; } if (!create_stack(hciemu)) { g_source_remove(hciemu->master_source); btdev_destroy(hciemu->master_dev); - g_free(hciemu); + queue_destroy(hciemu->post_command_hooks, NULL); + free(hciemu); return NULL; } @@ -341,11 +363,10 @@ void hciemu_unref(struct hciemu *hciemu) if (!hciemu) return; - if (__sync_sub_and_fetch(&hciemu->ref_count, 1) > 0) + if (__sync_sub_and_fetch(&hciemu->ref_count, 1)) return; - g_list_foreach(hciemu->post_command_hooks, destroy_command_hook, NULL); - g_list_free(hciemu->post_command_hooks); + queue_destroy(hciemu->post_command_hooks, destroy_command_hook); bthost_stop(hciemu->host_stack); @@ -357,7 +378,7 @@ void hciemu_unref(struct hciemu *hciemu) btdev_destroy(hciemu->client_dev); btdev_destroy(hciemu->master_dev); - g_free(hciemu); + free(hciemu); } const char *hciemu_get_address(struct hciemu *hciemu) @@ -405,15 +426,17 @@ bool hciemu_add_master_post_command_hook(struct hciemu *hciemu, if (!hciemu) return false; - hook = g_try_new0(struct hciemu_command_hook, 1); + hook = new0(struct hciemu_command_hook, 1); if (!hook) return false; hook->function = function; hook->user_data = user_data; - hciemu->post_command_hooks = g_list_append(hciemu->post_command_hooks, - hook); + if (!queue_push_tail(hciemu->post_command_hooks, hook)) { + free(hook); + return false; + } return true; } diff --git a/src/shared/hciemu.h b/src/shared/hciemu.h index d17eaf7..d948867 100644 --- a/src/shared/hciemu.h +++ b/src/shared/hciemu.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or diff --git a/src/shared/hfp.c b/src/shared/hfp.c new file mode 100644 index 0000000..36c8c3e --- /dev/null +++ b/src/shared/hfp.c @@ -0,0 +1,819 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include +#include + +#include "src/shared/util.h" +#include "src/shared/ringbuf.h" +#include "src/shared/queue.h" +#include "src/shared/io.h" +#include "src/shared/hfp.h" + +struct hfp_gw { + int ref_count; + int fd; + bool close_on_unref; + struct io *io; + struct ringbuf *read_buf; + struct ringbuf *write_buf; + struct queue *cmd_handlers; + bool writer_active; + bool permissive_syntax; + bool result_pending; + hfp_command_func_t command_callback; + hfp_destroy_func_t command_destroy; + void *command_data; + hfp_debug_func_t debug_callback; + hfp_destroy_func_t debug_destroy; + void *debug_data; + + hfp_disconnect_func_t disconnect_callback; + hfp_destroy_func_t disconnect_destroy; + void *disconnect_data; + + bool in_disconnect; + bool destroyed; +}; + +struct cmd_handler { + char *prefix; + void *user_data; + hfp_destroy_func_t destroy; + hfp_result_func_t callback; +}; + +struct hfp_gw_result { + const char *data; + unsigned int offset; +}; + +static void destroy_cmd_handler(void *data) +{ + struct cmd_handler *handler = data; + + if (handler->destroy) + handler->destroy(handler->user_data); + + free(handler->prefix); + + free(handler); +} + +static bool match_handler_prefix(const void *a, const void *b) +{ + const struct cmd_handler *handler = a; + const char *prefix = b; + + if (strlen(handler->prefix) != strlen(prefix)) + return false; + + if (memcmp(handler->prefix, prefix, strlen(prefix))) + return false; + + return true; +} + +static void write_watch_destroy(void *user_data) +{ + struct hfp_gw *hfp = user_data; + + hfp->writer_active = false; +} + +static bool can_write_data(struct io *io, void *user_data) +{ + struct hfp_gw *hfp = user_data; + ssize_t bytes_written; + + bytes_written = ringbuf_write(hfp->write_buf, hfp->fd); + if (bytes_written < 0) + return false; + + if (ringbuf_len(hfp->write_buf) > 0) + return true; + + return false; +} + +static void wakeup_writer(struct hfp_gw *hfp) +{ + if (hfp->writer_active) + return; + + if (!ringbuf_len(hfp->write_buf)) + return; + + if (!io_set_write_handler(hfp->io, can_write_data, + hfp, write_watch_destroy)) + return; + + hfp->writer_active = true; +} + +static void skip_whitespace(struct hfp_gw_result *result) +{ + while (result->data[result->offset] == ' ') + result->offset++; +} + +static bool call_prefix_handler(struct hfp_gw *hfp, const char *data) +{ + struct cmd_handler *handler; + const char *separators = ";?=\0"; + struct hfp_gw_result result; + enum hfp_gw_cmd_type type; + char lookup_prefix[18]; + uint8_t pref_len = 0; + const char *prefix; + int i; + + result.offset = 0; + result.data = data; + + skip_whitespace(&result); + + if (strlen(data + result.offset) < 3) + return false; + + if (strncmp(data + result.offset, "AT", 2)) + if (strncmp(data + result.offset, "at", 2)) + return false; + + result.offset += 2; + prefix = data + result.offset; + + if (isalpha(prefix[0])) { + lookup_prefix[pref_len++] = toupper(prefix[0]); + } else { + pref_len = strcspn(prefix, separators); + if (pref_len > 17 || pref_len < 2) + return false; + + for (i = 0; i < pref_len; i++) + lookup_prefix[i] = toupper(prefix[i]); + } + + lookup_prefix[pref_len] = '\0'; + result.offset += pref_len; + + if (lookup_prefix[0] == 'D') { + type = HFP_GW_CMD_TYPE_SET; + goto done; + } + + if (data[result.offset] == '=') { + result.offset++; + if (data[result.offset] == '?') { + result.offset++; + type = HFP_GW_CMD_TYPE_TEST; + } else { + type = HFP_GW_CMD_TYPE_SET; + } + goto done; + } + + if (data[result.offset] == '?') { + result.offset++; + type = HFP_GW_CMD_TYPE_READ; + goto done; + } + + type = HFP_GW_CMD_TYPE_COMMAND; + +done: + + handler = queue_find(hfp->cmd_handlers, match_handler_prefix, + lookup_prefix); + if (!handler) + return false; + + handler->callback(&result, type, handler->user_data); + + return true; +} + +static void next_field(struct hfp_gw_result *result) +{ + if (result->data[result->offset] == ',') + result->offset++; +} + +bool hfp_gw_result_get_number_default(struct hfp_gw_result *result, + unsigned int *val, + unsigned int default_val) +{ + skip_whitespace(result); + + if (result->data[result->offset] == ',') { + if (val) + *val = default_val; + + result->offset++; + return true; + } + + return hfp_gw_result_get_number(result, val); +} + +bool hfp_gw_result_get_number(struct hfp_gw_result *result, unsigned int *val) +{ + unsigned int i; + int tmp = 0; + + skip_whitespace(result); + + i = result->offset; + + while (result->data[i] >= '0' && result->data[i] <= '9') + tmp = tmp * 10 + result->data[i++] - '0'; + + if (i == result->offset) + return false; + + if (val) + *val = tmp; + result->offset = i; + + skip_whitespace(result); + next_field(result); + + return true; +} + +bool hfp_gw_result_open_container(struct hfp_gw_result *result) +{ + skip_whitespace(result); + + /* The list shall be preceded by a left parenthesis "(") */ + if (result->data[result->offset] != '(') + return false; + + result->offset++; + + return true; +} + +bool hfp_gw_result_close_container(struct hfp_gw_result *result) +{ + skip_whitespace(result); + + /* The list shall be followed by a right parenthesis (")" V250 5.7.3.1*/ + if (result->data[result->offset] != ')') + return false; + + result->offset++; + + return true; +} + +bool hfp_gw_result_get_string(struct hfp_gw_result *result, char *buf, + uint8_t len) +{ + int i = 0; + const char *data = result->data; + unsigned int offset; + + skip_whitespace(result); + + if (data[result->offset] != '"') + return false; + + offset = result->offset; + offset++; + + while (data[offset] != '\0' && data[offset] != '"') { + if (i == len) + return false; + + buf[i++] = data[offset]; + offset++; + } + + if (i == len) + return false; + + buf[i] = '\0'; + + if (data[offset] == '"') + offset++; + else + return false; + + result->offset = offset; + + skip_whitespace(result); + next_field(result); + + return true; +} + +bool hfp_gw_result_get_unquoted_string(struct hfp_gw_result *result, char *buf, + uint8_t len) +{ + const char *data = result->data; + unsigned int offset; + int i = 0; + char c; + + skip_whitespace(result); + + c = data[result->offset]; + if (c == '"' || c == ')' || c == '(') + return false; + + offset = result->offset; + + while (data[offset] != '\0' && data[offset] != ',' && + data[offset] != ')') { + if (i == len) + return false; + + buf[i++] = data[offset]; + offset++; + } + + if (i == len) + return false; + + buf[i] = '\0'; + + result->offset = offset; + + next_field(result); + + return true; +} + +bool hfp_gw_result_has_next(struct hfp_gw_result *result) +{ + return result->data[result->offset] != '\0'; +} + +static void process_input(struct hfp_gw *hfp) +{ + char *str, *ptr; + size_t len, count; + + str = ringbuf_peek(hfp->read_buf, 0, &len); + if (!str) + return; + + ptr = memchr(str, '\r', len); + if (!ptr) { + char *str2; + size_t len2; + + /* If there is no more data in ringbuffer, + * it's just an incomplete command. + */ + if (len == ringbuf_len(hfp->read_buf)) + return; + + str2 = ringbuf_peek(hfp->read_buf, len, &len2); + if (!str2) + return; + + ptr = memchr(str2, '\r', len2); + if (!ptr) + return; + + *ptr = '\0'; + count = asprintf(&ptr, "%s%s", str, str2); + str = ptr; + } else { + count = ptr - str; + *ptr = '\0'; + } + + hfp->result_pending = true; + + if (!call_prefix_handler(hfp, str)) { + if (hfp->command_callback) + hfp->command_callback(str, hfp->command_data); + else + hfp_gw_send_result(hfp, HFP_RESULT_ERROR); + } + + len = ringbuf_drain(hfp->read_buf, count + 1); + + if (str == ptr) + free(ptr); +} + +static void read_watch_destroy(void *user_data) +{ +} + +static bool can_read_data(struct io *io, void *user_data) +{ + struct hfp_gw *hfp = user_data; + ssize_t bytes_read; + + bytes_read = ringbuf_read(hfp->read_buf, hfp->fd); + if (bytes_read < 0) + return false; + + if (hfp->result_pending) + return true; + + process_input(hfp); + + return true; +} + +struct hfp_gw *hfp_gw_new(int fd) +{ + struct hfp_gw *hfp; + + if (fd < 0) + return NULL; + + hfp = new0(struct hfp_gw, 1); + if (!hfp) + return NULL; + + hfp->fd = fd; + hfp->close_on_unref = false; + + hfp->read_buf = ringbuf_new(4096); + if (!hfp->read_buf) { + free(hfp); + return NULL; + } + + hfp->write_buf = ringbuf_new(4096); + if (!hfp->write_buf) { + ringbuf_free(hfp->read_buf); + free(hfp); + return NULL; + } + + hfp->io = io_new(fd); + if (!hfp->io) { + ringbuf_free(hfp->write_buf); + ringbuf_free(hfp->read_buf); + free(hfp); + return NULL; + } + + hfp->cmd_handlers = queue_new(); + if (!hfp->cmd_handlers) { + io_destroy(hfp->io); + ringbuf_free(hfp->write_buf); + ringbuf_free(hfp->read_buf); + free(hfp); + return NULL; + } + + if (!io_set_read_handler(hfp->io, can_read_data, + hfp, read_watch_destroy)) { + queue_destroy(hfp->cmd_handlers, + destroy_cmd_handler); + io_destroy(hfp->io); + ringbuf_free(hfp->write_buf); + ringbuf_free(hfp->read_buf); + free(hfp); + return NULL; + } + + hfp->writer_active = false; + hfp->permissive_syntax = false; + hfp->result_pending = false; + + return hfp_gw_ref(hfp); +} + +struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp) +{ + if (!hfp) + return NULL; + + __sync_fetch_and_add(&hfp->ref_count, 1); + + return hfp; +} + +void hfp_gw_unref(struct hfp_gw *hfp) +{ + if (!hfp) + return; + + if (__sync_sub_and_fetch(&hfp->ref_count, 1)) + return; + + hfp_gw_set_command_handler(hfp, NULL, NULL, NULL); + + io_set_write_handler(hfp->io, NULL, NULL, NULL); + io_set_read_handler(hfp->io, NULL, NULL, NULL); + io_set_disconnect_handler(hfp->io, NULL, NULL, NULL); + + io_destroy(hfp->io); + hfp->io = NULL; + + if (hfp->close_on_unref) + close(hfp->fd); + + hfp_gw_set_debug(hfp, NULL, NULL, NULL); + + ringbuf_free(hfp->read_buf); + hfp->read_buf = NULL; + + ringbuf_free(hfp->write_buf); + hfp->write_buf = NULL; + + queue_destroy(hfp->cmd_handlers, destroy_cmd_handler); + hfp->cmd_handlers = NULL; + + if (!hfp->in_disconnect) { + free(hfp); + return; + } + + hfp->destroyed = true; +} + +static void read_tracing(const void *buf, size_t count, void *user_data) +{ + struct hfp_gw *hfp = user_data; + + util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data); +} + +static void write_tracing(const void *buf, size_t count, void *user_data) +{ + struct hfp_gw *hfp = user_data; + + util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data); +} + +bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback, + void *user_data, hfp_destroy_func_t destroy) +{ + if (!hfp) + return false; + + if (hfp->debug_destroy) + hfp->debug_destroy(hfp->debug_data); + + hfp->debug_callback = callback; + hfp->debug_destroy = destroy; + hfp->debug_data = user_data; + + if (hfp->debug_callback) { + ringbuf_set_input_tracing(hfp->read_buf, read_tracing, hfp); + ringbuf_set_input_tracing(hfp->write_buf, write_tracing, hfp); + } else { + ringbuf_set_input_tracing(hfp->read_buf, NULL, NULL); + ringbuf_set_input_tracing(hfp->write_buf, NULL, NULL); + } + + return true; +} + +bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close) +{ + if (!hfp) + return false; + + hfp->close_on_unref = do_close; + + return true; +} + +bool hfp_gw_set_permissive_syntax(struct hfp_gw *hfp, bool permissive) +{ + if (!hfp) + return false; + + hfp->permissive_syntax = permissive; + + return true; +} + +bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result) +{ + const char *str; + + if (!hfp) + return false; + + switch (result) { + case HFP_RESULT_OK: + str = "OK"; + break; + case HFP_RESULT_ERROR: + str = "ERROR"; + break; + default: + return false; + } + + if (ringbuf_printf(hfp->write_buf, "\r\n%s\r\n", str) < 0) + return false; + + wakeup_writer(hfp); + + hfp->result_pending = false; + + return true; +} + +bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error) +{ + if (!hfp) + return false; + + if (ringbuf_printf(hfp->write_buf, "\r\n+CME ERROR: %u\r\n", error) < 0) + return false; + + wakeup_writer(hfp); + + hfp->result_pending = false; + + return true; +} + +bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...) +{ + va_list ap; + char *fmt; + int len; + + if (!hfp || !format) + return false; + + if (asprintf(&fmt, "\r\n%s\r\n", format) < 0) + return false; + + va_start(ap, format); + len = ringbuf_vprintf(hfp->write_buf, fmt, ap); + va_end(ap); + + free(fmt); + + if (len < 0) + return false; + + if (hfp->result_pending) + return true; + + wakeup_writer(hfp); + + return true; +} + +bool hfp_gw_set_command_handler(struct hfp_gw *hfp, + hfp_command_func_t callback, + void *user_data, hfp_destroy_func_t destroy) +{ + if (!hfp) + return false; + + if (hfp->command_destroy) + hfp->command_destroy(hfp->command_data); + + hfp->command_callback = callback; + hfp->command_destroy = destroy; + hfp->command_data = user_data; + + return true; +} + +bool hfp_gw_register(struct hfp_gw *hfp, hfp_result_func_t callback, + const char *prefix, + void *user_data, + hfp_destroy_func_t destroy) +{ + struct cmd_handler *handler; + + handler = new0(struct cmd_handler, 1); + if (!handler) + return false; + + handler->callback = callback; + handler->user_data = user_data; + + handler->prefix = strdup(prefix); + if (!handler->prefix) { + free(handler); + return false; + } + + if (queue_find(hfp->cmd_handlers, match_handler_prefix, + handler->prefix)) { + destroy_cmd_handler(handler); + return false; + } + + handler->destroy = destroy; + + return queue_push_tail(hfp->cmd_handlers, handler); +} + +bool hfp_gw_unregister(struct hfp_gw *hfp, const char *prefix) +{ + struct cmd_handler *handler; + char *lookup_prefix; + + lookup_prefix = strdup(prefix); + if (!lookup_prefix) + return false; + + handler = queue_remove_if(hfp->cmd_handlers, match_handler_prefix, + lookup_prefix); + free(lookup_prefix); + + if (!handler) + return false; + + destroy_cmd_handler(handler); + + return true; +} + +static void disconnect_watch_destroy(void *user_data) +{ + struct hfp_gw *hfp = user_data; + + if (hfp->disconnect_destroy) + hfp->disconnect_destroy(hfp->disconnect_data); + + if (hfp->destroyed) + free(hfp); +} + +static bool io_disconnected(struct io *io, void *user_data) +{ + struct hfp_gw *hfp = user_data; + + hfp->in_disconnect = true; + + if (hfp->disconnect_callback) + hfp->disconnect_callback(hfp->disconnect_data); + + hfp->in_disconnect = false; + + return false; +} + +bool hfp_gw_set_disconnect_handler(struct hfp_gw *hfp, + hfp_disconnect_func_t callback, + void *user_data, + hfp_destroy_func_t destroy) +{ + if (!hfp) + return false; + + if (hfp->disconnect_destroy) + hfp->disconnect_destroy(hfp->disconnect_data); + + if (!io_set_disconnect_handler(hfp->io, io_disconnected, hfp, + disconnect_watch_destroy)) { + hfp->disconnect_callback = NULL; + hfp->disconnect_destroy = NULL; + hfp->disconnect_data = NULL; + return false; + } + + hfp->disconnect_callback = callback; + hfp->disconnect_destroy = destroy; + hfp->disconnect_data = user_data; + + return true; +} + +bool hfp_gw_disconnect(struct hfp_gw *hfp) +{ + if (!hfp) + return false; + + return io_shutdown(hfp->io); +} diff --git a/src/shared/hfp.h b/src/shared/hfp.h new file mode 100644 index 0000000..743db65 --- /dev/null +++ b/src/shared/hfp.h @@ -0,0 +1,126 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 + +enum hfp_result { + HFP_RESULT_OK = 0, + HFP_RESULT_CONNECT = 1, + HFP_RESULT_RING = 2, + HFP_RESULT_NO_CARRIER = 3, + HFP_RESULT_ERROR = 4, + HFP_RESULT_NO_DIALTONE = 6, + HFP_RESULT_BUSY = 7, + HFP_RESULT_NO_ANSWER = 8, +}; + +enum hfp_error { + HFP_ERROR_AG_FAILURE = 0, + HFP_ERROR_NO_CONNECTION_TO_PHONE = 1, + HFP_ERROR_OPERATION_NOT_ALLOWED = 3, + HFP_ERROR_OPERATION_NOT_SUPPORTED = 4, + HFP_ERROR_PH_SIM_PIN_REQUIRED = 5, + HFP_ERROR_SIM_NOT_INSERTED = 10, + HFP_ERROR_SIM_PIN_REQUIRED = 11, + HFP_ERROR_SIM_PUK_REQUIRED = 12, + HFP_ERROR_SIM_FAILURE = 13, + HFP_ERROR_SIM_BUSY = 14, + HFP_ERROR_INCORRECT_PASSWORD = 16, + HFP_ERROR_SIM_PIN2_REQUIRED = 17, + HFP_ERROR_SIM_PUK2_REQUIRED = 18, + HFP_ERROR_MEMORY_FULL = 20, + HFP_ERROR_INVALID_INDEX = 21, + HFP_ERROR_MEMORY_FAILURE = 23, + HFP_ERROR_TEXT_STRING_TOO_LONG = 24, + HFP_ERROR_INVALID_CHARS_IN_TEXT_STRING = 25, + HFP_ERROR_DIAL_STRING_TO_LONG = 26, + HFP_ERROR_INVALID_CHARS_IN_DIAL_STRING = 27, + HFP_ERROR_NO_NETWORK_SERVICE = 30, + HFP_ERROR_NETWORK_TIMEOUT = 31, + HFP_ERROR_NETWORK_NOT_ALLOWED = 32, +}; + +enum hfp_gw_cmd_type { + HFP_GW_CMD_TYPE_READ, + HFP_GW_CMD_TYPE_SET, + HFP_GW_CMD_TYPE_TEST, + HFP_GW_CMD_TYPE_COMMAND +}; + +struct hfp_gw_result; + +typedef void (*hfp_result_func_t)(struct hfp_gw_result *result, + enum hfp_gw_cmd_type type, void *user_data); + +typedef void (*hfp_destroy_func_t)(void *user_data); +typedef void (*hfp_debug_func_t)(const char *str, void *user_data); + +typedef void (*hfp_command_func_t)(const char *command, void *user_data); +typedef void (*hfp_disconnect_func_t)(void *user_data); + +struct hfp_gw; + +struct hfp_gw *hfp_gw_new(int fd); + +struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp); +void hfp_gw_unref(struct hfp_gw *hfp); + +bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback, + void *user_data, hfp_destroy_func_t destroy); + +bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close); +bool hfp_gw_set_permissive_syntax(struct hfp_gw *hfp, bool permissive); + +bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result); +bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error); +bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...) + __attribute__((format(printf, 2, 3))); + +bool hfp_gw_set_command_handler(struct hfp_gw *hfp, + hfp_command_func_t callback, + void *user_data, hfp_destroy_func_t destroy); + +bool hfp_gw_set_disconnect_handler(struct hfp_gw *hfp, + hfp_disconnect_func_t callback, + void *user_data, + hfp_destroy_func_t destroy); + +bool hfp_gw_disconnect(struct hfp_gw *hfp); + +bool hfp_gw_register(struct hfp_gw *hfp, hfp_result_func_t callback, + const char *prefix, + void *user_data, + hfp_destroy_func_t destroy); +bool hfp_gw_unregister(struct hfp_gw *hfp, const char *prefix); + +bool hfp_gw_result_get_number(struct hfp_gw_result *result, unsigned int *val); +bool hfp_gw_result_get_number_default(struct hfp_gw_result *result, + unsigned int *val, + unsigned int default_val); +bool hfp_gw_result_open_container(struct hfp_gw_result *result); +bool hfp_gw_result_close_container(struct hfp_gw_result *result); +bool hfp_gw_result_get_string(struct hfp_gw_result *result, char *buf, + uint8_t len); +bool hfp_gw_result_get_unquoted_string(struct hfp_gw_result *result, char *buf, + uint8_t len); +bool hfp_gw_result_has_next(struct hfp_gw_result *result); diff --git a/src/shared/io-glib.c b/src/shared/io-glib.c new file mode 100644 index 0000000..6316037 --- /dev/null +++ b/src/shared/io-glib.c @@ -0,0 +1,334 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include + +#include "src/shared/io.h" + +struct io { + int ref_count; + GIOChannel *channel; + guint read_watch; + io_callback_func_t read_callback; + io_destroy_func_t read_destroy; + void *read_data; + guint write_watch; + io_callback_func_t write_callback; + io_destroy_func_t write_destroy; + void *write_data; + guint disconnect_watch; + io_callback_func_t disconnect_callback; + io_destroy_func_t disconnect_destroy; + void *disconnect_data; +}; + +static struct io *io_ref(struct io *io) +{ + if (!io) + return NULL; + + __sync_fetch_and_add(&io->ref_count, 1); + + return io; +} + +static void io_unref(struct io *io) +{ + if (!io) + return; + + if (__sync_sub_and_fetch(&io->ref_count, 1)) + return; + + g_free(io); +} + +struct io *io_new(int fd) +{ + struct io *io; + + if (fd < 0) + return NULL; + + io = g_try_new0(struct io, 1); + if (!io) + return NULL; + + io->channel = g_io_channel_unix_new(fd); + + g_io_channel_set_encoding(io->channel, NULL, NULL); + g_io_channel_set_buffered(io->channel, FALSE); + + g_io_channel_set_close_on_unref(io->channel, FALSE); + + io->read_watch = 0; + io->read_callback = NULL; + io->read_destroy = NULL; + io->read_data = NULL; + + io->write_watch = 0; + io->write_callback = NULL; + io->write_destroy = NULL; + io->write_data = NULL; + + return io_ref(io); +} + +void io_destroy(struct io *io) +{ + if (!io) + return; + + if (io->read_watch > 0) { + g_source_remove(io->read_watch); + io->read_watch = 0; + } + + if (io->write_watch > 0) { + g_source_remove(io->write_watch); + io->write_watch = 0; + } + + g_io_channel_unref(io->channel); + io->channel = NULL; + + io_unref(io); +} + +int io_get_fd(struct io *io) +{ + if (!io) + return -1; + + return g_io_channel_unix_get_fd(io->channel); +} + +bool io_set_close_on_destroy(struct io *io, bool do_close) +{ + if (!io) + return false; + + if (do_close) + g_io_channel_set_close_on_unref(io->channel, TRUE); + else + g_io_channel_set_close_on_unref(io->channel, FALSE); + + return true; +} + +static void read_watch_destroy(gpointer user_data) +{ + struct io *io = user_data; + + if (io->read_destroy) + io->read_destroy(io->read_data); + + io->read_watch = 0; + io->read_callback = NULL; + io->read_destroy = NULL; + io->read_data = NULL; + + io_unref(io); +} + +static gboolean read_callback(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct io *io = user_data; + bool result; + + if (cond & (G_IO_ERR | G_IO_NVAL)) + return FALSE; + + if (io->read_callback) + result = io->read_callback(io, io->read_data); + else + result = false; + + return result ? TRUE : FALSE; +} + +bool io_set_read_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy) +{ + if (!io) + return false; + + if (io->read_watch > 0) { + g_source_remove(io->read_watch); + io->read_watch = 0; + } + + if (!callback) + goto done; + + io->read_watch = g_io_add_watch_full(io->channel, G_PRIORITY_DEFAULT, + G_IO_IN | G_IO_ERR | G_IO_NVAL, + read_callback, io_ref(io), + read_watch_destroy); + if (io->read_watch == 0) + return false; + + io->read_destroy = destroy; + io->read_data = user_data; + +done: + io->read_callback = callback; + + return true; +} + +static void write_watch_destroy(gpointer user_data) +{ + struct io *io = user_data; + + if (io->write_destroy) + io->write_destroy(io->write_data); + + io->write_watch = 0; + io->write_callback = NULL; + io->write_destroy = NULL; + io->write_data = NULL; + + io_unref(io); +} + +static gboolean write_callback(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct io *io = user_data; + bool result; + + if (cond & (G_IO_ERR | G_IO_NVAL)) + return FALSE; + + if (io->write_callback) + result = io->write_callback(io, io->write_data); + else + result = false; + + return result ? TRUE : FALSE; +} + +bool io_set_write_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy) +{ + if (!io) + return false; + + if (io->write_watch > 0) { + g_source_remove(io->write_watch); + io->write_watch = 0; + } + + if (!callback) + goto done; + + io->write_watch = g_io_add_watch_full(io->channel, G_PRIORITY_DEFAULT, + G_IO_OUT | G_IO_ERR | G_IO_NVAL, + write_callback, io_ref(io), + write_watch_destroy); + if (io->write_watch == 0) + return false; + + io->write_destroy = destroy; + io->write_data = user_data; + +done: + io->write_callback = callback; + + return true; +} + +static void disconnect_watch_destroy(gpointer user_data) +{ + struct io *io = user_data; + + if (io->disconnect_destroy) + io->disconnect_destroy(io->disconnect_data); + + io->disconnect_watch = 0; + io->disconnect_callback = NULL; + io->disconnect_destroy = NULL; + io->disconnect_data = NULL; + + io_unref(io); +} + +static gboolean disconnect_callback(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct io *io = user_data; + bool result; + + if (io->disconnect_callback) + result = io->disconnect_callback(io, io->disconnect_data); + else + result = false; + + return result ? TRUE : FALSE; +} + +bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy) +{ + if (!io) + return false; + + if (io->disconnect_watch > 0) { + g_source_remove(io->disconnect_watch); + io->disconnect_watch = 0; + } + + if (!callback) + goto done; + + io->disconnect_watch = g_io_add_watch_full(io->channel, + G_PRIORITY_DEFAULT, + G_IO_HUP | G_IO_ERR | G_IO_NVAL, + disconnect_callback, io_ref(io), + disconnect_watch_destroy); + if (io->disconnect_watch == 0) + return false; + + io->disconnect_destroy = destroy; + io->disconnect_data = user_data; + +done: + io->disconnect_callback = callback; + + return true; +} + +bool io_shutdown(struct io *io) +{ + if (!io || !io->channel) + return false; + + return g_io_channel_shutdown(io->channel, TRUE, NULL) + == G_IO_STATUS_NORMAL; +} diff --git a/src/shared/io-mainloop.c b/src/shared/io-mainloop.c new file mode 100644 index 0000000..3e33d88 --- /dev/null +++ b/src/shared/io-mainloop.c @@ -0,0 +1,304 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include + +#include "monitor/mainloop.h" +#include "src/shared/util.h" +#include "src/shared/io.h" + +struct io { + int ref_count; + int fd; + uint32_t events; + bool close_on_destroy; + io_callback_func_t read_callback; + io_destroy_func_t read_destroy; + void *read_data; + io_callback_func_t write_callback; + io_destroy_func_t write_destroy; + void *write_data; + io_callback_func_t disconnect_callback; + io_destroy_func_t disconnect_destroy; + void *disconnect_data; +}; + +static struct io *io_ref(struct io *io) +{ + if (!io) + return NULL; + + __sync_fetch_and_add(&io->ref_count, 1); + + return io; +} + +static void io_unref(struct io *io) +{ + if (!io) + return; + + if (__sync_sub_and_fetch(&io->ref_count, 1)) + return; + + free(io); +} + +static void io_cleanup(void *user_data) +{ + struct io *io = user_data; + + if (io->write_destroy) + io->write_destroy(io->write_data); + + if (io->read_destroy) + io->read_destroy(io->read_data); + + if (io->disconnect_destroy) + io->disconnect_destroy(io->disconnect_data); + + if (io->close_on_destroy) + close(io->fd); + + io->fd = -1; +} + +static void io_callback(int fd, uint32_t events, void *user_data) +{ + struct io *io = user_data; + + if ((events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))) { + io->read_callback = NULL; + io->write_callback = NULL; + + if (!io->disconnect_callback) { + mainloop_remove_fd(io->fd); + return; + } + + if (!io->disconnect_callback(io, io->disconnect_data)) { + if (io->disconnect_destroy) + io->disconnect_destroy(io->disconnect_data); + + io->disconnect_callback = NULL; + io->disconnect_destroy = NULL; + io->disconnect_data = NULL; + + io->events &= ~EPOLLRDHUP; + + mainloop_modify_fd(io->fd, io->events); + } + } + + if ((events & EPOLLIN) && io->read_callback) { + if (!io->read_callback(io, io->read_data)) { + if (io->read_destroy) + io->read_destroy(io->read_data); + + io->read_callback = NULL; + io->read_destroy = NULL; + io->read_data = NULL; + + io->events &= ~EPOLLIN; + + mainloop_modify_fd(io->fd, io->events); + } + } + + if ((events & EPOLLOUT) && io->write_callback) { + if (!io->write_callback(io, io->write_data)) { + if (io->write_destroy) + io->write_destroy(io->write_data); + + io->write_callback = NULL; + io->write_destroy = NULL; + io->write_data = NULL; + + io->events &= ~EPOLLOUT; + + mainloop_modify_fd(io->fd, io->events); + } + } +} + +struct io *io_new(int fd) +{ + struct io *io; + + if (fd < 0) + return NULL; + + io = new0(struct io, 1); + if (!io) + return NULL; + + io->fd = fd; + io->events = 0; + io->close_on_destroy = false; + + if (mainloop_add_fd(io->fd, io->events, io_callback, + io, io_cleanup) < 0) { + free(io); + return NULL; + } + + return io_ref(io); +} + +void io_destroy(struct io *io) +{ + if (!io) + return; + + io->read_callback = NULL; + io->write_callback = NULL; + io->disconnect_callback = NULL; + + mainloop_remove_fd(io->fd); + + io_unref(io); +} + +int io_get_fd(struct io *io) +{ + if (!io) + return -1; + + return io->fd; +} + +bool io_set_close_on_destroy(struct io *io, bool do_close) +{ + if (!io) + return false; + + io->close_on_destroy = do_close; + + return true; +} + +bool io_set_read_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy) +{ + uint32_t events; + + if (!io || io->fd < 0) + return false; + + if (io->read_destroy) + io->read_destroy(io->read_data); + + if (callback) + events = io->events | EPOLLIN; + else + events = io->events & ~EPOLLIN; + + io->read_callback = callback; + io->read_destroy = destroy; + io->read_data = user_data; + + if (events == io->events) + return true; + + if (mainloop_modify_fd(io->fd, events) < 0) + return false; + + io->events = events; + + return true; +} + +bool io_set_write_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy) +{ + uint32_t events; + + if (!io || io->fd < 0) + return false; + + if (io->write_destroy) + io->write_destroy(io->write_data); + + if (callback) + events = io->events | EPOLLOUT; + else + events = io->events & ~EPOLLOUT; + + io->write_callback = callback; + io->write_destroy = destroy; + io->write_data = user_data; + + if (events == io->events) + return true; + + if (mainloop_modify_fd(io->fd, events) < 0) + return false; + + io->events = events; + + return true; +} + +bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy) +{ + uint32_t events; + + if (!io || io->fd < 0) + return false; + + if (io->disconnect_destroy) + io->disconnect_destroy(io->disconnect_data); + + if (callback) + events = io->events | EPOLLRDHUP; + else + events = io->events & ~EPOLLRDHUP; + + io->disconnect_callback = callback; + io->disconnect_destroy = destroy; + io->disconnect_data = user_data; + + if (events == io->events) + return true; + + if (mainloop_modify_fd(io->fd, events) < 0) + return false; + + io->events = events; + + return true; +} + +bool io_shutdown(struct io *io) +{ + if (!io || io->fd < 0) + return false; + + return shutdown(io->fd, SHUT_RDWR) == 0; +} diff --git a/src/shared/io.h b/src/shared/io.h new file mode 100644 index 0000000..8897964 --- /dev/null +++ b/src/shared/io.h @@ -0,0 +1,45 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 + +typedef void (*io_destroy_func_t)(void *data); + +struct io; + +struct io *io_new(int fd); +void io_destroy(struct io *io); + +int io_get_fd(struct io *io); +bool io_set_close_on_destroy(struct io *io, bool do_close); + +bool io_shutdown(struct io *io); + +typedef bool (*io_callback_func_t)(struct io *io, void *user_data); + +bool io_set_read_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy); +bool io_set_write_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy); +bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback, + void *user_data, io_destroy_func_t destroy); diff --git a/src/shared/mgmt.c b/src/shared/mgmt.c index 2c79886..ae90b89 100644 --- a/src/shared/mgmt.c +++ b/src/shared/mgmt.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or @@ -30,12 +30,12 @@ #include #include -#include - #include "lib/bluetooth.h" #include "lib/mgmt.h" #include "lib/hci.h" +#include "src/shared/io.h" +#include "src/shared/queue.h" #include "src/shared/util.h" #include "src/shared/mgmt.h" @@ -43,16 +43,15 @@ struct mgmt { int ref_count; int fd; bool close_on_unref; - GIOChannel *io; - guint read_watch; - guint write_watch; - GQueue *request_queue; - GQueue *reply_queue; - GList *pending_list; - GList *notify_list; - GList *notify_destroyed; + struct io *io; + bool writer_active; + struct queue *request_queue; + struct queue *reply_queue; + struct queue *pending_list; + struct queue *notify_list; unsigned int next_request_id; unsigned int next_notify_id; + bool need_notify_cleanup; bool in_notify; bool destroyed; void *buf; @@ -77,75 +76,103 @@ struct mgmt_notify { unsigned int id; uint16_t event; uint16_t index; - bool destroyed; + bool removed; mgmt_notify_func_t callback; mgmt_destroy_func_t destroy; void *user_data; }; -static void destroy_request(gpointer data, gpointer user_data) +static void destroy_request(void *data) { struct mgmt_request *request = data; if (request->destroy) request->destroy(request->user_data); - g_free(request->buf); - g_free(request); + free(request->buf); + free(request); } -static int compare_request_id(gconstpointer a, gconstpointer b) +static bool match_request_id(const void *a, const void *b) { const struct mgmt_request *request = a; - unsigned int id = GPOINTER_TO_UINT(b); + unsigned int id = PTR_TO_UINT(b); - return request->id - id; + return request->id == id; } -static void destroy_notify(gpointer data, gpointer user_data) +static bool match_request_index(const void *a, const void *b) +{ + const struct mgmt_request *request = a; + uint16_t index = PTR_TO_UINT(b); + + return request->index == index; +} + +static void destroy_notify(void *data) { struct mgmt_notify *notify = data; if (notify->destroy) notify->destroy(notify->user_data); - g_free(notify); + free(notify); +} + +static bool match_notify_id(const void *a, const void *b) +{ + const struct mgmt_notify *notify = a; + unsigned int id = PTR_TO_UINT(b); + + return notify->id == id; +} + +static bool match_notify_index(const void *a, const void *b) +{ + const struct mgmt_notify *notify = a; + uint16_t index = PTR_TO_UINT(b); + + return notify->index == index; } -static int compare_notify_id(gconstpointer a, gconstpointer b) +static bool match_notify_removed(const void *a, const void *b) { const struct mgmt_notify *notify = a; - unsigned int id = GPOINTER_TO_UINT(b); - return notify->id - id; + return notify->removed; } -static void write_watch_destroy(gpointer user_data) +static void mark_notify_removed(void *data , void *user_data) +{ + struct mgmt_notify *notify = data; + uint16_t index = PTR_TO_UINT(user_data); + + if (notify->index == index || index == MGMT_INDEX_NONE) + notify->removed = true; +} + +static void write_watch_destroy(void *user_data) { struct mgmt *mgmt = user_data; - mgmt->write_watch = 0; + mgmt->writer_active = false; } -static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, - gpointer user_data) +static bool can_write_data(struct io *io, void *user_data) { struct mgmt *mgmt = user_data; struct mgmt_request *request; ssize_t bytes_written; - if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) - return FALSE; - - request = g_queue_pop_head(mgmt->reply_queue); + request = queue_pop_head(mgmt->reply_queue); if (!request) { /* only reply commands can jump the queue */ - if (mgmt->pending_list) - return FALSE; + if (!queue_isempty(mgmt->pending_list)) + return false; - request = g_queue_pop_head(mgmt->request_queue); + request = queue_pop_head(mgmt->request_queue); if (!request) - return FALSE; + return false; } bytes_written = write(mgmt->fd, request->buf, request->len); @@ -155,8 +182,8 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, if (request->callback) request->callback(MGMT_STATUS_FAILED, 0, NULL, request->user_data); - destroy_request(request, NULL); - return TRUE; + destroy_request(request); + return true; } util_debug(mgmt->debug_callback, mgmt->debug_data, @@ -166,61 +193,56 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, util_hexdump('<', request->buf, bytes_written, mgmt->debug_callback, mgmt->debug_data); - mgmt->pending_list = g_list_append(mgmt->pending_list, request); + queue_push_tail(mgmt->pending_list, request); - return FALSE; + return false; } static void wakeup_writer(struct mgmt *mgmt) { - if (mgmt->pending_list) { + if (!queue_isempty(mgmt->pending_list)) { /* only queued reply commands trigger wakeup */ - if (g_queue_get_length(mgmt->reply_queue) == 0) + if (queue_isempty(mgmt->reply_queue)) return; } - if (mgmt->write_watch > 0) + if (mgmt->writer_active) return; - mgmt->write_watch = g_io_add_watch_full(mgmt->io, G_PRIORITY_HIGH, - G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - can_write_data, mgmt, write_watch_destroy); + io_set_write_handler(mgmt->io, can_write_data, mgmt, + write_watch_destroy); } -static GList *lookup_pending(struct mgmt *mgmt, uint16_t opcode, uint16_t index) -{ - GList *list; - - for (list = g_list_first(mgmt->pending_list); list; - list = g_list_next(list)) { - struct mgmt_request *request = list->data; +struct opcode_index { + uint16_t opcode; + uint16_t index; +}; - if (request->opcode == opcode && request->index == index) - return list; - } +static bool match_request_opcode_index(const void *a, const void *b) +{ + const struct mgmt_request *request = a; + const struct opcode_index *match = b; - return NULL; + return request->opcode == match->opcode && + request->index == match->index; } static void request_complete(struct mgmt *mgmt, uint8_t status, uint16_t opcode, uint16_t index, uint16_t length, const void *param) { + struct opcode_index match = { .opcode = opcode, .index = index }; struct mgmt_request *request; - GList *list; - - list = lookup_pending(mgmt, opcode, index); - if (!list) - return; - request = list->data; - - mgmt->pending_list = g_list_delete_link(mgmt->pending_list, list); - - if (request->callback) - request->callback(status, length, param, request->user_data); + request = queue_remove_if(mgmt->pending_list, + match_request_opcode_index, &match); + if (request) { + if (request->callback) + request->callback(status, length, param, + request->user_data); - destroy_request(request, NULL); + destroy_request(request); + } if (mgmt->destroyed) return; @@ -228,56 +250,63 @@ static void request_complete(struct mgmt *mgmt, uint8_t status, wakeup_writer(mgmt); } -static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index, - uint16_t length, const void *param) +struct event_index { + uint16_t event; + uint16_t index; + uint16_t length; + const void *param; +}; + +static void notify_handler(void *data, void *user_data) { - GList *list; + struct mgmt_notify *notify = data; + struct event_index *match = user_data; - mgmt->in_notify = true; + if (notify->removed) + return; - for (list = g_list_first(mgmt->notify_list); list; - list = g_list_next(list)) { - struct mgmt_notify *notify = list->data; + if (notify->event != match->event) + return; - if (notify->destroyed) - continue; + if (notify->index != match->index && notify->index != MGMT_INDEX_NONE) + return; - if (notify->event != event) - continue; + if (notify->callback) + notify->callback(match->index, match->length, match->param, + notify->user_data); +} - if (notify->index != index && notify->index != MGMT_INDEX_NONE) - continue; +static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index, + uint16_t length, const void *param) +{ + struct event_index match = { .event = event, .index = index, + .length = length, .param = param }; - if (notify->callback) - notify->callback(index, length, param, - notify->user_data); + mgmt->in_notify = true; - if (mgmt->destroyed) - break; - } + queue_foreach(mgmt->notify_list, notify_handler, &match); mgmt->in_notify = false; - g_list_foreach(mgmt->notify_destroyed, destroy_notify, NULL); - g_list_free(mgmt->notify_destroyed); - - mgmt->notify_destroyed = NULL; + if (mgmt->need_notify_cleanup) { + queue_remove_all(mgmt->notify_list, match_notify_removed, + NULL, destroy_notify); + mgmt->need_notify_cleanup = false; + } } -static void read_watch_destroy(gpointer user_data) +static void read_watch_destroy(void *user_data) { struct mgmt *mgmt = user_data; if (mgmt->destroyed) { - g_free(mgmt); - return; + queue_destroy(mgmt->notify_list, NULL); + queue_destroy(mgmt->pending_list, NULL); + free(mgmt); } - - mgmt->read_watch = 0; } -static gboolean received_data(GIOChannel *channel, GIOCondition cond, - gpointer user_data) +static bool can_read_data(struct io *io, void *user_data) { struct mgmt *mgmt = user_data; struct mgmt_hdr *hdr; @@ -286,18 +315,15 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, ssize_t bytes_read; uint16_t opcode, event, index, length; - if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) - return FALSE; - bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len); if (bytes_read < 0) - return TRUE; + return false; util_hexdump('>', mgmt->buf, bytes_read, mgmt->debug_callback, mgmt->debug_data); if (bytes_read < MGMT_HDR_SIZE) - return TRUE; + return true; hdr = mgmt->buf; event = btohs(hdr->opcode); @@ -305,7 +331,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, length = btohs(hdr->len); if (bytes_read < length + MGMT_HDR_SIZE) - return TRUE; + return true; switch (event) { case MGMT_EV_CMD_COMPLETE: @@ -339,9 +365,9 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, } if (mgmt->destroyed) - return FALSE; + return false; - return TRUE; + return true; } struct mgmt *mgmt_new(int fd) @@ -351,7 +377,7 @@ struct mgmt *mgmt_new(int fd) if (fd < 0) return NULL; - mgmt = g_try_new0(struct mgmt, 1); + mgmt = new0(struct mgmt, 1); if (!mgmt) return NULL; @@ -359,23 +385,70 @@ struct mgmt *mgmt_new(int fd) mgmt->close_on_unref = false; mgmt->len = 512; - mgmt->buf = g_try_malloc(mgmt->len); + mgmt->buf = malloc(mgmt->len); if (!mgmt->buf) { - g_free(mgmt); + free(mgmt); return NULL; } - mgmt->io = g_io_channel_unix_new(mgmt->fd); + mgmt->io = io_new(fd); + if (!mgmt->io) { + free(mgmt->buf); + free(mgmt); + return NULL; + } - g_io_channel_set_encoding(mgmt->io, NULL, NULL); - g_io_channel_set_buffered(mgmt->io, FALSE); + mgmt->request_queue = queue_new(); + if (!mgmt->request_queue) { + io_destroy(mgmt->io); + free(mgmt->buf); + free(mgmt); + return NULL; + } - mgmt->request_queue = g_queue_new(); - mgmt->reply_queue = g_queue_new(); + mgmt->reply_queue = queue_new(); + if (!mgmt->reply_queue) { + queue_destroy(mgmt->request_queue, NULL); + io_destroy(mgmt->io); + free(mgmt->buf); + free(mgmt); + return NULL; + } - mgmt->read_watch = g_io_add_watch_full(mgmt->io, G_PRIORITY_DEFAULT, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - received_data, mgmt, read_watch_destroy); + mgmt->pending_list = queue_new(); + if (!mgmt->pending_list) { + queue_destroy(mgmt->reply_queue, NULL); + queue_destroy(mgmt->request_queue, NULL); + io_destroy(mgmt->io); + free(mgmt->buf); + free(mgmt); + return NULL; + } + + mgmt->notify_list = queue_new(); + if (!mgmt->notify_list) { + queue_destroy(mgmt->pending_list, NULL); + queue_destroy(mgmt->reply_queue, NULL); + queue_destroy(mgmt->request_queue, NULL); + io_destroy(mgmt->io); + free(mgmt->buf); + free(mgmt); + return NULL; + } + + if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, + read_watch_destroy)) { + queue_destroy(mgmt->notify_list, NULL); + queue_destroy(mgmt->pending_list, NULL); + queue_destroy(mgmt->reply_queue, NULL); + queue_destroy(mgmt->request_queue, NULL); + io_destroy(mgmt->io); + free(mgmt->buf); + free(mgmt); + return NULL; + } + + mgmt->writer_active = false; return mgmt_ref(mgmt); } @@ -383,7 +456,10 @@ struct mgmt *mgmt_new(int fd) struct mgmt *mgmt_new_default(void) { struct mgmt *mgmt; - struct sockaddr_hci addr; + union { + struct sockaddr common; + struct sockaddr_hci hci; + } addr; int fd; fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, @@ -392,11 +468,11 @@ struct mgmt *mgmt_new_default(void) return NULL; memset(&addr, 0, sizeof(addr)); - addr.hci_family = AF_BLUETOOTH; - addr.hci_dev = HCI_DEV_NONE; - addr.hci_channel = HCI_CHANNEL_CONTROL; + addr.hci.hci_family = AF_BLUETOOTH; + addr.hci.hci_dev = HCI_DEV_NONE; + addr.hci.hci_channel = HCI_CHANNEL_CONTROL; - if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) { close(fd); return NULL; } @@ -433,16 +509,13 @@ void mgmt_unref(struct mgmt *mgmt) mgmt_unregister_all(mgmt); mgmt_cancel_all(mgmt); - g_queue_free(mgmt->reply_queue); - g_queue_free(mgmt->request_queue); + queue_destroy(mgmt->reply_queue, NULL); + queue_destroy(mgmt->request_queue, NULL); - if (mgmt->write_watch > 0) - g_source_remove(mgmt->write_watch); + io_set_write_handler(mgmt->io, NULL, NULL, NULL); + io_set_read_handler(mgmt->io, NULL, NULL, NULL); - if (mgmt->read_watch > 0) - g_source_remove(mgmt->read_watch); - - g_io_channel_unref(mgmt->io); + io_destroy(mgmt->io); mgmt->io = NULL; if (mgmt->close_on_unref) @@ -451,11 +524,13 @@ void mgmt_unref(struct mgmt *mgmt) if (mgmt->debug_destroy) mgmt->debug_destroy(mgmt->debug_data); - g_free(mgmt->buf); + free(mgmt->buf); mgmt->buf = NULL; if (!mgmt->in_notify) { - g_free(mgmt); + queue_destroy(mgmt->notify_list, NULL); + queue_destroy(mgmt->pending_list, NULL); + free(mgmt); return; } @@ -502,14 +577,14 @@ static struct mgmt_request *create_request(uint16_t opcode, uint16_t index, if (length > 0 && !param) return NULL; - request = g_try_new0(struct mgmt_request, 1); + request = new0(struct mgmt_request, 1); if (!request) return NULL; request->len = length + MGMT_HDR_SIZE; - request->buf = g_try_malloc(request->len); + request->buf = malloc(request->len); if (!request->buf) { - g_free(request); + free(request); return NULL; } @@ -551,7 +626,11 @@ unsigned int mgmt_send(struct mgmt *mgmt, uint16_t opcode, uint16_t index, request->id = mgmt->next_request_id++; - g_queue_push_tail(mgmt->request_queue, request); + if (!queue_push_tail(mgmt->request_queue, request)) { + free(request->buf); + free(request); + return 0; + } wakeup_writer(mgmt); @@ -578,7 +657,11 @@ unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index, request->id = mgmt->next_request_id++; - g_queue_push_tail(mgmt->reply_queue, request); + if (!queue_push_tail(mgmt->reply_queue, request)) { + free(request->buf); + free(request); + return 0; + } wakeup_writer(mgmt); @@ -588,38 +671,27 @@ unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index, bool mgmt_cancel(struct mgmt *mgmt, unsigned int id) { struct mgmt_request *request; - GList *list; if (!mgmt || !id) return false; - list = g_queue_find_custom(mgmt->request_queue, GUINT_TO_POINTER(id), - compare_request_id); - if (list) { - request = list->data; - g_queue_delete_link(mgmt->request_queue, list); + request = queue_remove_if(mgmt->request_queue, match_request_id, + UINT_TO_PTR(id)); + if (request) goto done; - } - list = g_queue_find_custom(mgmt->reply_queue, GUINT_TO_POINTER(id), - compare_request_id); - if (list) { - request = list->data; - g_queue_delete_link(mgmt->reply_queue, list); + request = queue_remove_if(mgmt->reply_queue, match_request_id, + UINT_TO_PTR(id)); + if (request) goto done; - } - list = g_list_find_custom(mgmt->pending_list, GUINT_TO_POINTER(id), - compare_request_id); - if (!list) + request = queue_remove_if(mgmt->pending_list, match_request_id, + UINT_TO_PTR(id)); + if (!request) return false; - request = list->data; - - mgmt->pending_list = g_list_delete_link(mgmt->pending_list, list); - done: - destroy_request(request, NULL); + destroy_request(request); wakeup_writer(mgmt); @@ -628,52 +700,15 @@ done: bool mgmt_cancel_index(struct mgmt *mgmt, uint16_t index) { - GList *list, *next; - if (!mgmt) return false; - for (list = g_queue_peek_head_link(mgmt->request_queue); list; - list = next) { - struct mgmt_request *request = list->data; - - next = g_list_next(list); - - if (request->index != index) - continue; - - g_queue_delete_link(mgmt->request_queue, list); - - destroy_request(request, NULL); - } - - for (list = g_queue_peek_head_link(mgmt->reply_queue); list; - list = next) { - struct mgmt_request *request = list->data; - - next = g_list_next(list); - - if (request->index != index) - continue; - - g_queue_delete_link(mgmt->reply_queue, list); - - destroy_request(request, NULL); - } - - for (list = g_list_first(mgmt->pending_list); list; list = next) { - struct mgmt_request *request = list->data; - - next = g_list_next(list); - - if (request->index != index) - continue; - - mgmt->pending_list = g_list_delete_link(mgmt->pending_list, - list); - - destroy_request(request, NULL); - } + queue_remove_all(mgmt->request_queue, match_request_index, + UINT_TO_PTR(index), destroy_request); + queue_remove_all(mgmt->reply_queue, match_request_index, + UINT_TO_PTR(index), destroy_request); + queue_remove_all(mgmt->pending_list, match_request_index, + UINT_TO_PTR(index), destroy_request); return true; } @@ -683,15 +718,9 @@ bool mgmt_cancel_all(struct mgmt *mgmt) if (!mgmt) return false; - g_list_foreach(mgmt->pending_list, destroy_request, NULL); - g_list_free(mgmt->pending_list); - mgmt->pending_list = NULL; - - g_queue_foreach(mgmt->reply_queue, destroy_request, NULL); - g_queue_clear(mgmt->reply_queue); - - g_queue_foreach(mgmt->request_queue, destroy_request, NULL); - g_queue_clear(mgmt->request_queue); + queue_remove_all(mgmt->pending_list, NULL, NULL, destroy_request); + queue_remove_all(mgmt->reply_queue, NULL, NULL, destroy_request); + queue_remove_all(mgmt->request_queue, NULL, NULL, destroy_request); return true; } @@ -705,7 +734,7 @@ unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index, if (!mgmt || !event) return 0; - notify = g_try_new0(struct mgmt_notify, 1); + notify = new0(struct mgmt_notify, 1); if (!notify) return 0; @@ -721,7 +750,10 @@ unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index, notify->id = mgmt->next_notify_id++; - mgmt->notify_list = g_list_append(mgmt->notify_list, notify); + if (!queue_push_tail(mgmt->notify_list, notify)) { + free(notify); + return 0; + } return notify->id; } @@ -729,87 +761,53 @@ unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index, bool mgmt_unregister(struct mgmt *mgmt, unsigned int id) { struct mgmt_notify *notify; - GList *list; if (!mgmt || !id) return false; - list = g_list_find_custom(mgmt->notify_list, - GUINT_TO_POINTER(id), compare_notify_id); - if (!list) + notify = queue_remove_if(mgmt->notify_list, match_notify_id, + UINT_TO_PTR(id)); + if (!notify) return false; - notify = list->data; - - mgmt->notify_list = g_list_remove_link(mgmt->notify_list, list); - if (!mgmt->in_notify) { - g_list_free_1(list); - destroy_notify(notify, NULL); + destroy_notify(notify); return true; } - notify->destroyed = true; - - mgmt->notify_destroyed = g_list_concat(mgmt->notify_destroyed, list); + notify->removed = true; + mgmt->need_notify_cleanup = true; return true; } bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index) { - GList *list, *next; - if (!mgmt) return false; - for (list = g_list_first(mgmt->notify_list); list; list = next) { - struct mgmt_notify *notify = list->data; - - next = g_list_next(list); - - if (notify->index != index) - continue; - - mgmt->notify_list = g_list_remove_link(mgmt->notify_list, list); - - if (!mgmt->in_notify) { - g_list_free_1(list); - destroy_notify(notify, NULL); - continue; - } - - notify->destroyed = true; - - mgmt->notify_destroyed = g_list_concat(mgmt->notify_destroyed, - list); - } + if (mgmt->in_notify) { + queue_foreach(mgmt->notify_list, mark_notify_removed, + UINT_TO_PTR(index)); + mgmt->need_notify_cleanup = true; + } else + queue_remove_all(mgmt->notify_list, match_notify_index, + UINT_TO_PTR(index), destroy_notify); return true; } -static void mark_notify(gpointer data, gpointer user_data) -{ - struct mgmt_notify *notify = data; - - notify->destroyed = true; -} - bool mgmt_unregister_all(struct mgmt *mgmt) { if (!mgmt) return false; - if (!mgmt->in_notify) { - g_list_foreach(mgmt->notify_list, destroy_notify, NULL); - g_list_free(mgmt->notify_list); - } else { - g_list_foreach(mgmt->notify_list, mark_notify, NULL); - mgmt->notify_destroyed = g_list_concat(mgmt->notify_destroyed, - mgmt->notify_list); - } - - mgmt->notify_list = NULL; + if (mgmt->in_notify) { + queue_foreach(mgmt->notify_list, mark_notify_removed, + UINT_TO_PTR(MGMT_INDEX_NONE)); + mgmt->need_notify_cleanup = true; + } else + queue_remove_all(mgmt->notify_list, NULL, NULL, destroy_notify); return true; } diff --git a/src/shared/mgmt.h b/src/shared/mgmt.h index e23cc7d..626a699 100644 --- a/src/shared/mgmt.h +++ b/src/shared/mgmt.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or diff --git a/src/shared/pcap.c b/src/shared/pcap.c index c722db2..bd7675f 100644 --- a/src/shared/pcap.c +++ b/src/shared/pcap.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or @@ -29,12 +29,8 @@ #include #include -#include "pcap.h" - -#define le16_to_cpu(val) (val) -#define le32_to_cpu(val) (val) -#define cpu_to_le16(val) (val) -#define cpu_to_le32(val) (val) +#include "src/shared/util.h" +#include "src/shared/pcap.h" struct pcap_hdr { uint32_t magic_number; /* magic number */ diff --git a/src/shared/pcap.h b/src/shared/pcap.h index f333bfc..b47de62 100644 --- a/src/shared/pcap.h +++ b/src/shared/pcap.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or diff --git a/src/shared/queue.c b/src/shared/queue.c new file mode 100644 index 0000000..8a69729 --- /dev/null +++ b/src/shared/queue.c @@ -0,0 +1,346 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include "src/shared/util.h" +#include "src/shared/queue.h" + +struct queue_entry { + void *data; + struct queue_entry *next; +}; + +struct queue { + struct queue_entry *head; + struct queue_entry *tail; + unsigned int entries; +}; + +struct queue *queue_new(void) +{ + struct queue *queue; + + queue = new0(struct queue, 1); + if (!queue) + return NULL; + + queue->head = NULL; + queue->tail = NULL; + queue->entries = 0; + + return queue; +} + +void queue_destroy(struct queue *queue, queue_destroy_func_t destroy) +{ + struct queue_entry *entry; + + if (!queue) + return; + + entry = queue->head; + + while (entry) { + struct queue_entry *tmp = entry; + + if (destroy) + destroy(entry->data); + + entry = entry->next; + + free(tmp); + } + + free(queue); +} + +bool queue_push_tail(struct queue *queue, void *data) +{ + struct queue_entry *entry; + + if (!queue) + return false; + + entry = new0(struct queue_entry, 1); + if (!entry) + return false; + + entry->data = data; + entry->next = NULL; + + if (queue->tail) + queue->tail->next = entry; + + queue->tail = entry; + + if (!queue->head) + queue->head = entry; + + queue->entries++; + + return true; +} + +bool queue_push_head(struct queue *queue, void *data) +{ + struct queue_entry *entry; + + if (!queue) + return false; + + entry = new0(struct queue_entry, 1); + if (!entry) + return false; + + entry->data = data; + entry->next = queue->head; + + queue->head = entry; + + if (!queue->tail) + queue->tail = entry; + + queue->entries++; + + return true; +} + +void *queue_pop_head(struct queue *queue) +{ + struct queue_entry *entry; + void *data; + + if (!queue || !queue->head) + return NULL; + + entry = queue->head; + + if (!queue->head->next) { + queue->head = NULL; + queue->tail = NULL; + } else + queue->head = queue->head->next; + + data = entry->data; + + free(entry); + queue->entries--; + + return data; +} + +void *queue_peek_head(struct queue *queue) +{ + if (!queue || !queue->head) + return NULL; + + return queue->head->data; +} + +void *queue_peek_tail(struct queue *queue) +{ + if (!queue || !queue->tail) + return NULL; + + return queue->tail->data; +} + +void queue_foreach(struct queue *queue, queue_foreach_func_t function, + void *user_data) +{ + struct queue_entry *entry; + + if (!queue || !function) + return; + + entry = queue->head; + + while (entry) { + struct queue_entry *tmp = entry; + + entry = tmp->next; + + function(tmp->data, user_data); + } +} + +void *queue_find(struct queue *queue, queue_match_func_t function, + void *user_data) +{ + struct queue_entry *entry; + + if (!queue || !function) + return NULL; + + for (entry = queue->head; entry; entry = entry->next) + if (function(entry->data, user_data)) + return entry->data; + + return NULL; +} + +bool queue_remove(struct queue *queue, void *data) +{ + struct queue_entry *entry, *prev; + + if (!queue || !data) + return false; + + for (entry = queue->head, prev = NULL; entry; + prev = entry, entry = entry->next) { + if (entry->data != data) + continue; + + if (prev) + prev->next = entry->next; + else + queue->head = entry->next; + + if (!entry->next) + queue->tail = prev; + + free(entry); + queue->entries--; + + return true; + } + + return false; +} + +void *queue_remove_if(struct queue *queue, queue_match_func_t function, + void *user_data) +{ + struct queue_entry *entry, *prev = NULL; + + if (!queue || !function) + return NULL; + + entry = queue->head; + + while (entry) { + if (function(entry->data, user_data)) { + void *data; + + if (prev) + prev->next = entry->next; + else + queue->head = entry->next; + + if (!entry->next) + queue->tail = prev; + + data = entry->data; + + free(entry); + queue->entries--; + + return data; + } else { + prev = entry; + entry = entry->next; + } + } + + return NULL; +} + +unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function, + void *user_data, queue_destroy_func_t destroy) +{ + struct queue_entry *entry; + unsigned int count = 0; + + if (!queue) + return 0; + + entry = queue->head; + + if (function) { + struct queue_entry *prev = NULL; + + while (entry) { + if (function(entry->data, user_data)) { + struct queue_entry *tmp = entry; + + if (prev) + prev->next = entry->next; + else + queue->head = entry->next; + + if (!entry->next) + queue->tail = prev; + + entry = entry->next; + + if (destroy) + destroy(tmp->data); + + free(tmp); + count++; + } else { + prev = entry; + entry = entry->next; + } + } + + queue->entries -= count; + } else { + while (entry) { + struct queue_entry *tmp = entry; + + entry = entry->next; + + if (destroy) + destroy(tmp->data); + + free(tmp); + count++; + } + + queue->head = NULL; + queue->tail = NULL; + queue->entries = 0; + } + + return count; +} + +unsigned int queue_length(struct queue *queue) +{ + if (!queue) + return 0; + + return queue->entries; +} + +bool queue_isempty(struct queue *queue) +{ + if (!queue) + return true; + + return queue->entries == 0; +} diff --git a/src/shared/queue.h b/src/shared/queue.h new file mode 100644 index 0000000..8201ff8 --- /dev/null +++ b/src/shared/queue.h @@ -0,0 +1,56 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 + +typedef void (*queue_destroy_func_t)(void *data); + +struct queue; + +struct queue *queue_new(void); +void queue_destroy(struct queue *queue, queue_destroy_func_t destroy); + +bool queue_push_tail(struct queue *queue, void *data); +bool queue_push_head(struct queue *queue, void *data); +void *queue_pop_head(struct queue *queue); +void *queue_peek_head(struct queue *queue); +void *queue_peek_tail(struct queue *queue); + +typedef void (*queue_foreach_func_t)(void *data, void *user_data); + +void queue_foreach(struct queue *queue, queue_foreach_func_t function, + void *user_data); + +typedef bool (*queue_match_func_t)(const void *a, const void *b); + +void *queue_find(struct queue *queue, queue_match_func_t function, + void *user_data); + +bool queue_remove(struct queue *queue, void *data); +void *queue_remove_if(struct queue *queue, queue_match_func_t function, + void *user_data); +unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function, + void *user_data, queue_destroy_func_t destroy); + +unsigned int queue_length(struct queue *queue); +bool queue_isempty(struct queue *queue); diff --git a/src/shared/ringbuf.c b/src/shared/ringbuf.c new file mode 100644 index 0000000..a11d2dc --- /dev/null +++ b/src/shared/ringbuf.c @@ -0,0 +1,312 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#endif + +#include +#include +#include +#include + +#include "src/shared/util.h" +#include "src/shared/ringbuf.h" + +#ifndef MIN +#define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +struct ringbuf { + void *buffer; + size_t size; + size_t in; + size_t out; + ringbuf_tracing_func_t in_tracing; + void *in_data; +}; + +#define RINGBUF_RESET 0 + +/* Find last (most siginificant) set bit */ +static inline unsigned int fls(unsigned int x) +{ + return x ? sizeof(x) * 8 - __builtin_clz(x) : 0; +} + +/* Round up to nearest power of two */ +static inline unsigned int align_power2(unsigned int u) +{ + return 1 << fls(u - 1); +} + +struct ringbuf *ringbuf_new(size_t size) +{ + struct ringbuf *ringbuf; + size_t real_size; + + if (size < 2 || size > UINT_MAX) + return NULL; + + /* Find the next power of two for size */ + real_size = align_power2(size); + + ringbuf = new0(struct ringbuf, 1); + if (!ringbuf) + return NULL; + + ringbuf->buffer = malloc(real_size); + if (!ringbuf->buffer) { + free(ringbuf); + return NULL; + } + + ringbuf->size = real_size; + ringbuf->in = RINGBUF_RESET; + ringbuf->out = RINGBUF_RESET; + + return ringbuf; +} + +void ringbuf_free(struct ringbuf *ringbuf) +{ + if (!ringbuf) + return; + + free(ringbuf->buffer); + free(ringbuf); +} + +bool ringbuf_set_input_tracing(struct ringbuf *ringbuf, + ringbuf_tracing_func_t callback, void *user_data) +{ + if (!ringbuf) + return false; + + ringbuf->in_tracing = callback; + ringbuf->in_data = user_data; + + return true; +} + +size_t ringbuf_capacity(struct ringbuf *ringbuf) +{ + if (!ringbuf) + return 0; + + return ringbuf->size; +} + +size_t ringbuf_len(struct ringbuf *ringbuf) +{ + if (!ringbuf) + return 0; + + return ringbuf->in - ringbuf->out; +} + +size_t ringbuf_drain(struct ringbuf *ringbuf, size_t count) +{ + size_t len; + + if (!ringbuf) + return 0; + + len = MIN(count, ringbuf->in - ringbuf->out); + if (!len) + return 0; + + ringbuf->out += len; + + if (ringbuf->out == ringbuf->in) { + ringbuf->in = RINGBUF_RESET; + ringbuf->out = RINGBUF_RESET; + } + + return len; +} + +void *ringbuf_peek(struct ringbuf *ringbuf, size_t offset, size_t *len_nowrap) +{ + if (!ringbuf) + return NULL; + + offset = (ringbuf->out + offset) & (ringbuf->size - 1); + + if (len_nowrap) { + size_t len = ringbuf->in - ringbuf->out; + *len_nowrap = MIN(len, ringbuf->size - offset); + } + + return ringbuf->buffer + offset; +} + +ssize_t ringbuf_write(struct ringbuf *ringbuf, int fd) +{ + size_t len, offset, end; + struct iovec iov[2]; + ssize_t consumed; + + if (!ringbuf || fd < 0) + return -1; + + /* Determine how much data is available */ + len = ringbuf->in - ringbuf->out; + if (!len) + return 0; + + /* Grab data from buffer starting at offset until the end */ + offset = ringbuf->out & (ringbuf->size - 1); + end = MIN(len, ringbuf->size - offset); + + iov[0].iov_base = ringbuf->buffer + offset; + iov[0].iov_len = end; + + /* Use second vector for remainder from the beginning */ + iov[1].iov_base = ringbuf->buffer; + iov[1].iov_len = len - end; + + consumed = writev(fd, iov, 2); + if (consumed < 0) + return -1; + + ringbuf->out += consumed; + + if (ringbuf->out == ringbuf->in) { + ringbuf->in = RINGBUF_RESET; + ringbuf->out = RINGBUF_RESET; + } + + return consumed; +} + +size_t ringbuf_avail(struct ringbuf *ringbuf) +{ + if (!ringbuf) + return 0; + + return ringbuf->size - ringbuf->in + ringbuf->out; +} + +int ringbuf_printf(struct ringbuf *ringbuf, const char *format, ...) +{ + va_list ap; + int len; + + va_start(ap, format); + len = ringbuf_vprintf(ringbuf, format, ap); + va_end(ap); + + return len; +} + +int ringbuf_vprintf(struct ringbuf *ringbuf, const char *format, va_list ap) +{ + size_t avail, offset, end; + char *str; + int len; + + if (!ringbuf || !format) + return -1; + + /* Determine maximum length available for string */ + avail = ringbuf->size - ringbuf->in + ringbuf->out; + if (!avail) + return -1; + + len = vasprintf(&str, format, ap); + if (len < 0) + return -1; + + if ((size_t) len > avail) { + free(str); + return -1; + } + + /* Determine possible length of string before wrapping */ + offset = ringbuf->in & (ringbuf->size - 1); + end = MIN((size_t) len, ringbuf->size - offset); + memcpy(ringbuf->buffer + offset, str, end); + + if (ringbuf->in_tracing) + ringbuf->in_tracing(ringbuf->buffer + offset, end, + ringbuf->in_data); + + if (len - end > 0) { + /* Put the remainder of string at the beginning */ + memcpy(ringbuf->buffer, str + end, len - end); + + if (ringbuf->in_tracing) + ringbuf->in_tracing(ringbuf->buffer, len - end, + ringbuf->in_data); + } + + free(str); + + ringbuf->in += len; + + return len; +} + +ssize_t ringbuf_read(struct ringbuf *ringbuf, int fd) +{ + size_t avail, offset, end; + struct iovec iov[2]; + ssize_t consumed; + + if (!ringbuf || fd < 0) + return -1; + + /* Determine how much can actually be consumed */ + avail = ringbuf->size - ringbuf->in + ringbuf->out; + if (!avail) + return -1; + + /* Determine how much to consume before wrapping */ + offset = ringbuf->in & (ringbuf->size - 1); + end = MIN(avail, ringbuf->size - offset); + + iov[0].iov_base = ringbuf->buffer + offset; + iov[0].iov_len = end; + + /* Now put the remainder into the second vector */ + iov[1].iov_base = ringbuf->buffer; + iov[1].iov_len = avail - end; + + consumed = readv(fd, iov, 2); + if (consumed < 0) + return -1; + + if (ringbuf->in_tracing) { + size_t len = MIN((size_t) consumed, end); + ringbuf->in_tracing(ringbuf->buffer + offset, len, + ringbuf->in_data); + if (consumed - len > 0) + ringbuf->in_tracing(ringbuf->buffer, consumed - len, + ringbuf->in_data); + } + + ringbuf->in += consumed; + + return consumed; +} diff --git a/src/shared/ringbuf.h b/src/shared/ringbuf.h new file mode 100644 index 0000000..adf471a --- /dev/null +++ b/src/shared/ringbuf.h @@ -0,0 +1,50 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * + * 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 +#include +#include + +typedef void (*ringbuf_tracing_func_t)(const void *buf, size_t count, + void *user_data); + +struct ringbuf; + +struct ringbuf *ringbuf_new(size_t size); +void ringbuf_free(struct ringbuf *ringbuf); + +bool ringbuf_set_input_tracing(struct ringbuf *ringbuf, + ringbuf_tracing_func_t callback, void *user_data); + +size_t ringbuf_capacity(struct ringbuf *ringbuf); + +size_t ringbuf_len(struct ringbuf *ringbuf); +size_t ringbuf_drain(struct ringbuf *ringbuf, size_t count); +void *ringbuf_peek(struct ringbuf *ringbuf, size_t offset, size_t *len_nowrap); +ssize_t ringbuf_write(struct ringbuf *ringbuf, int fd); + +size_t ringbuf_avail(struct ringbuf *ringbuf); +int ringbuf_printf(struct ringbuf *ringbuf, const char *format, ...) + __attribute__((format(printf, 2, 3))); +int ringbuf_vprintf(struct ringbuf *ringbuf, const char *format, va_list ap); +ssize_t ringbuf_read(struct ringbuf *ringbuf, int fd); diff --git a/src/shared/tester.c b/src/shared/tester.c index f3edd74..56e5696 100644 --- a/src/shared/tester.c +++ b/src/shared/tester.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or @@ -29,11 +29,13 @@ #include #include #include +#include #include #include #include +#include "src/shared/util.h" #include "src/shared/tester.h" #define COLOR_OFF "\x1B[0m" @@ -114,8 +116,8 @@ static void test_destroy(gpointer data) if (test->destroy) test->destroy(test->user_data); - g_free(test->name); - g_free(test); + free(test->name); + free(test); } void tester_print(const char *format, ...) @@ -190,9 +192,14 @@ void tester_add_full(const char *name, const void *test_data, return; } - test = g_new0(struct test_case, 1); + test = new0(struct test_case, 1); + if (!test) { + if (destroy) + destroy(user_data); + return; + } - test->name = g_strdup(name); + test->name = strdup(name); test->result = TEST_RESULT_NOT_RUN; test->stage = TEST_STAGE_INVALID; @@ -460,6 +467,11 @@ void tester_setup_failed(void) if (test->stage != TEST_STAGE_SETUP) return; + if (test->timeout_id > 0) { + g_source_remove(test->timeout_id); + test->timeout_id = 0; + } + print_progress(test->name, COLOR_RED, "setup failed"); g_idle_add(done_callback, test); @@ -612,7 +624,7 @@ static gboolean wait_callback(gpointer user_data) wait->func(wait->user_data); - g_free(wait); + free(wait); return FALSE; } @@ -631,9 +643,9 @@ void tester_wait(unsigned int seconds, tester_wait_func_t func, test = test_current->data; - print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds); - - wait = g_new0(struct wait_data, 1); + wait = new0(struct wait_data, 1); + if (!wait) + return; wait->seconds = seconds; wait->test = test; @@ -641,6 +653,8 @@ void tester_wait(unsigned int seconds, tester_wait_func_t func, wait->user_data = user_data; g_timeout_add(1000, wait_callback, wait); + + print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds); } static gboolean signal_handler(GIOChannel *channel, GIOCondition condition, diff --git a/src/shared/tester.h b/src/shared/tester.h index 775ed1e..85d5e95 100644 --- a/src/shared/tester.h +++ b/src/shared/tester.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or diff --git a/src/shared/timeout-glib.c b/src/shared/timeout-glib.c new file mode 100644 index 0000000..4163bce --- /dev/null +++ b/src/shared/timeout-glib.c @@ -0,0 +1,78 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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. + * + */ + +#include "timeout.h" + +#include + +struct timeout_data { + timeout_func_t func; + timeout_destroy_func_t destroy; + void *user_data; +}; + +static gboolean timeout_callback(gpointer user_data) +{ + struct timeout_data *data = user_data; + + if (data->func(data->user_data)) + return TRUE; + + return FALSE; +} + +static void timeout_destroy(gpointer user_data) +{ + struct timeout_data *data = user_data; + + if (data->destroy) + data->destroy(data->user_data); + + g_free(data); +} + +unsigned int timeout_add(unsigned int timeout, timeout_func_t func, + void *user_data, timeout_destroy_func_t destroy) +{ + struct timeout_data *data; + guint id; + + data = g_try_new0(struct timeout_data, 1); + if (!data) + return 0; + + data->func = func; + data->destroy = destroy; + data->user_data = user_data; + + id = g_timeout_add_full(G_PRIORITY_DEFAULT, timeout, timeout_callback, + data, timeout_destroy); + if (!id) + g_free(data); + + return id; +} + +void timeout_remove(unsigned int id) +{ + GSource *source = g_main_context_find_source_by_id(NULL, id); + + if (source) + g_source_destroy(source); +} diff --git a/src/shared/timeout-mainloop.c b/src/shared/timeout-mainloop.c new file mode 100644 index 0000000..77aaa63 --- /dev/null +++ b/src/shared/timeout-mainloop.c @@ -0,0 +1,86 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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. + * + */ + +#include + +#include "monitor/mainloop.h" + +#include "util.h" +#include "timeout.h" + +struct timeout_data { + int id; + timeout_func_t func; + timeout_destroy_func_t destroy; + unsigned int timeout; + void *user_data; +}; + +static void timeout_callback(int id, void *user_data) +{ + struct timeout_data *data = user_data; + + if (data->func(data->user_data) && + !mainloop_modify_timeout(data->id, data->timeout)) + return; + + mainloop_remove_timeout(data->id); +} + +static void timeout_destroy(void *user_data) +{ + struct timeout_data *data = user_data; + + if (data->destroy) + data->destroy(data->user_data); + + free(data); +} + +unsigned int timeout_add(unsigned int timeout, timeout_func_t func, + void *user_data, timeout_destroy_func_t destroy) +{ + struct timeout_data *data; + + data = new0(struct timeout_data, 1); + if (!data) + return 0; + + data->func = func; + data->user_data = user_data; + data->timeout = timeout; + data->destroy = destroy; + + data->id = mainloop_add_timeout(timeout, timeout_callback, data, + timeout_destroy); + if (data->id < 0) { + free(data); + return 0; + } + + return (unsigned int) data->id; +} + +void timeout_remove(unsigned int id) +{ + if (!id) + return; + + mainloop_remove_timeout((int) id); +} diff --git a/src/shared/timeout.h b/src/shared/timeout.h new file mode 100644 index 0000000..4930ce1 --- /dev/null +++ b/src/shared/timeout.h @@ -0,0 +1,27 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * 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. + * + */ + +#include + +typedef bool (*timeout_func_t)(void *user_data); +typedef void (*timeout_destroy_func_t)(void *user_data); + +unsigned int timeout_add(unsigned int timeout, timeout_func_t func, + void *user_data, timeout_destroy_func_t destroy); +void timeout_remove(unsigned int id); diff --git a/src/shared/util.c b/src/shared/util.c index 5aee69d..eb90ecb 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or diff --git a/src/shared/util.h b/src/shared/util.h index 88e8954..4bc77a1 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. * * * This library is free software; you can redistribute it and/or @@ -21,6 +21,67 @@ * */ +#include +#include +#include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define le16_to_cpu(val) (val) +#define le32_to_cpu(val) (val) +#define le64_to_cpu(val) (val) +#define cpu_to_le16(val) (val) +#define cpu_to_le32(val) (val) +#define cpu_to_le64(val) (val) +#define be16_to_cpu(val) bswap_16(val) +#define be32_to_cpu(val) bswap_32(val) +#define be64_to_cpu(val) bswap_64(val) +#define cpu_to_be16(val) bswap_16(val) +#define cpu_to_be32(val) bswap_32(val) +#define cpu_to_be64(val) bswap_64(val) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define le16_to_cpu(val) bswap_16(val) +#define le32_to_cpu(val) bswap_32(val) +#define le64_to_cpu(val) bswap_64(val) +#define cpu_to_le16(val) bswap_16(val) +#define cpu_to_le32(val) bswap_32(val) +#define cpu_to_le64(val) bswap_64(val) +#define be16_to_cpu(val) (val) +#define be32_to_cpu(val) (val) +#define be64_to_cpu(val) (val) +#define cpu_to_be16(val) (val) +#define cpu_to_be32(val) (val) +#define cpu_to_be64(val) (val) +#else +#error "Unknown byte order" +#endif + +#define get_unaligned(ptr) \ +({ \ + struct __attribute__((packed)) { \ + typeof(*(ptr)) __v; \ + } *__p = (typeof(__p)) (ptr); \ + __p->__v; \ +}) + +#define put_unaligned(val, ptr) \ +do { \ + struct __attribute__((packed)) { \ + typeof(*(ptr)) __v; \ + } *__p = (typeof(__p)) (ptr); \ + __p->__v = (val); \ +} while (0) + +#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) +#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) + +#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) +#define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) + +#define new0(t, n) ((t*) calloc((n), sizeof(t))) +#define newa(t, n) ((t*) alloca(sizeof(t)*(n))) +#define malloc0(n) (calloc((n), 1)) + typedef void (*util_debug_func_t)(const char *str, void *user_data); void util_debug(util_debug_func_t function, void *user_data, @@ -29,3 +90,73 @@ void util_debug(util_debug_func_t function, void *user_data, void util_hexdump(const char dir, const unsigned char *buf, size_t len, util_debug_func_t function, void *user_data); + +static inline void bswap_128(const void *src, void *dst) +{ + const uint8_t *s = src; + uint8_t *d = dst; + int i; + + for (i = 0; i < 16; i++) + d[15 - i] = s[i]; +} + +static inline uint16_t get_le16(const void *ptr) +{ + return le16_to_cpu(get_unaligned((const uint16_t *) ptr)); +} + +static inline uint16_t get_be16(const void *ptr) +{ + return be16_to_cpu(get_unaligned((const uint16_t *) ptr)); +} + +static inline uint32_t get_le32(const void *ptr) +{ + return le32_to_cpu(get_unaligned((const uint32_t *) ptr)); +} + +static inline uint32_t get_be32(const void *ptr) +{ + return be32_to_cpu(get_unaligned((const uint32_t *) ptr)); +} + +static inline uint64_t get_le64(const void *ptr) +{ + return le64_to_cpu(get_unaligned((const uint64_t *) ptr)); +} + +static inline uint64_t get_be64(const void *ptr) +{ + return be64_to_cpu(get_unaligned((const uint64_t *) ptr)); +} + +static inline void put_le16(uint16_t val, void *dst) +{ + put_unaligned(cpu_to_le16(val), (uint16_t *) dst); +} + +static inline void put_be16(uint16_t val, const void *ptr) +{ + put_unaligned(cpu_to_be16(val), (uint16_t *) ptr); +} + +static inline void put_le32(uint32_t val, void *dst) +{ + put_unaligned(cpu_to_le32(val), (uint32_t *) dst); +} + +static inline void put_be32(uint32_t val, void *dst) +{ + put_unaligned(cpu_to_be32(val), (uint32_t *) dst); +} + +static inline void put_le64(uint64_t val, void *dst) +{ + put_unaligned(cpu_to_le64(val), (uint64_t *) dst); +} + +static inline void put_be64(uint64_t val, void *dst) +{ + put_unaligned(cpu_to_be64(val), (uint64_t *) dst); +} diff --git a/src/storage.c b/src/storage.c index f7e4db6..b230e1e 100644 --- a/src/storage.c +++ b/src/storage.c @@ -44,7 +44,7 @@ #include "lib/uuid.h" #include "textfile.h" -#include "glib-helper.h" +#include "uuid-helper.h" #include "storage.h" /* When all services should trust a remote device */ diff --git a/src/storage.h b/src/storage.h index 4c5ab50..1c0ad57 100644 --- a/src/storage.h +++ b/src/storage.h @@ -21,8 +21,6 @@ * */ -#include "textfile.h" - int read_discoverable_timeout(const char *src, int *timeout); int read_pairable_timeout(const char *src, int *timeout); int read_on_mode(const char *src, char *mode, int length); diff --git a/src/textfile.h b/src/textfile.h index b779bd2..f01629e 100644 --- a/src/textfile.h +++ b/src/textfile.h @@ -21,9 +21,6 @@ * */ -#ifndef __TEXTFILE_H -#define __TEXTFILE_H - 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); @@ -35,5 +32,3 @@ char *textfile_get(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/glib-helper.c b/src/uuid-helper.c similarity index 87% rename from src/glib-helper.c rename to src/uuid-helper.c index 4a020e9..bce36b0 100644 --- a/src/glib-helper.c +++ b/src/uuid-helper.c @@ -26,29 +26,40 @@ #endif #include +#include +#include #include +#include #include #include #include -#include - -#include "glib-helper.h" +#include "uuid-helper.h" char *bt_modalias(uint16_t source, uint16_t vendor, uint16_t product, uint16_t version) { + char *str; + int err; + switch (source) { case 0x0001: - return g_strdup_printf("%s:v%04Xp%04Xd%04X", + err = asprintf(&str, "%s:v%04Xp%04Xd%04X", "bluetooth", vendor, product, version); + break; case 0x0002: - return g_strdup_printf("%s:v%04Xp%04Xd%04X", + err = asprintf(&str, "%s:v%04Xp%04Xd%04X", "usb", vendor, product, version); + break; + default: + return NULL; } - return NULL; + if (err < 0) + return NULL; + + return str; } char *bt_uuid2string(uuid_t *uuid) @@ -61,6 +72,7 @@ char *bt_uuid2string(uuid_t *uuid) unsigned short data3; unsigned int data4; unsigned short data5; + int err; if (!uuid) return NULL; @@ -87,15 +99,13 @@ char *bt_uuid2string(uuid_t *uuid) 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) + err = asprintf(&str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", + ntohl(data0), ntohs(data1), + ntohs(data2), ntohs(data3), + ntohl(data4), ntohs(data5)); + if (err < 0) 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; } @@ -141,7 +151,7 @@ static uint16_t name2class(const char *pattern) return 0; } -static inline gboolean is_uuid128(const char *string) +static inline bool is_uuid128(const char *string) { return (strlen(string) == 36 && string[8] == '-' && @@ -176,7 +186,7 @@ char *bt_name2string(const char *pattern) /* UUID 128 string format */ if (is_uuid128(pattern)) - return g_strdup(pattern); + return strdup(pattern); /* Friendly service name format */ uuid16 = name2class(pattern); @@ -208,12 +218,12 @@ int bt_string2uuid(uuid_t *uuid, const char *string) &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); + 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); diff --git a/src/glib-helper.h b/src/uuid-helper.h similarity index 100% rename from src/glib-helper.h rename to src/uuid-helper.h diff --git a/test/ftp-client b/test/ftp-client new file mode 100755 index 0000000..78c32b3 --- /dev/null +++ b/test/ftp-client @@ -0,0 +1,178 @@ +#!/usr/bin/python + +from __future__ import absolute_import, print_function, unicode_literals + +from optparse import OptionParser +import os.path +import sys +import dbus +import dbus.service +import dbus.mainloop.glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject + +BUS_NAME='org.bluez.obex' +PATH = '/org/bluez/obex' +CLIENT_INTERFACE='org.bluez.obex.Client1' +SESSION_INTERFACE='org.bluez.obex.Session1' +FILE_TRASNFER_INTERFACE='org.bluez.obex.FileTransfer1' +TRANSFER_INTERFACE='org.bluez.obex.Transfer1' + +def parse_options(): + parser.add_option("-d", "--device", dest="device", + help="Device to connect", metavar="DEVICE") + parser.add_option("-c", "--chdir", dest="new_dir", + help="Change current directory to DIR", metavar="DIR") + parser.add_option("-l", "--list", action="store_true", dest="list_dir", + help="List the current directory") + parser.add_option("-g", "--get", dest="get_file", + help="Get FILE", metavar="FILE") + parser.add_option("-p", "--put", dest="put_file", + help="Put FILE", metavar="FILE") + parser.add_option("-y", "--copy", dest="copy_file", + help="Copy FILE", metavar="FILE") + parser.add_option("-m", "--move", dest="move_file", + help="Move FILE", metavar="FILE") + parser.add_option("-n", "--destname", dest="dest_file", + help="Destination FILE", metavar="FILE") + parser.add_option("-r", "--remove", dest="remove_file", + help="Remove FILE", metavar="FILE") + parser.add_option("-v", "--verbose", action="store_true", + dest="verbose") + + return parser.parse_args() + +class FtpClient: + def __init__(self, session_path, verbose=False): + self.transferred = 0 + self.transfer_path = None + self.transfer_size = 0 + self.verbose = verbose + bus = dbus.SessionBus() + obj = bus.get_object(BUS_NAME, session_path) + self.session = dbus.Interface(obj, SESSION_INTERFACE) + self.ftp = dbus.Interface(obj, FILE_TRASNFER_INTERFACE) + bus.add_signal_receiver(self.properties_changed, + dbus_interface="org.freedesktop.DBus.Properties", + signal_name="PropertiesChanged", + path_keyword="path") + + def create_transfer_reply(self, path, properties): + self.transfer_path = path + self.transfer_size = properties["Size"] + if self.verbose: + print("Transfer created: %s" % path) + + def generic_reply(self): + if self.verbose: + print("Operation succeeded") + + def error(self, err): + print(err) + mainloop.quit() + + def properties_changed(self, interface, properties, invalidated, path): + if path != self.transfer_path: + return + + if properties['Status'] == 'complete' or \ + properties['Status'] == 'error': + if self.verbose: + print("Transfer %s" % properties['Status']) + mainloop.quit() + return + + if properties["Transferred"] == None: + return + + speed = (value - self.transferred) / 1000 + print("Transfer progress %d/%d at %d kBps" % (value, + self.transfer_size, + speed)) + self.transferred = value + + def change_folder(self, new_dir): + for node in new_dir.split("/"): + self.ftp.ChangeFolder(node) + + def list_folder(self): + for i in self.ftp.ListFolder(): + if i["Type"] == "folder": + print("%s/" % (i["Name"])) + else: + print("%s" % (i["Name"])) + + def put_file(self, filename): + self.ftp.PutFile(os.path.abspath(filename), + os.path.basename(filename), + reply_handler=self.create_transfer_reply, + error_handler=self.error) + + def get_file(self, filename): + self.ftp.GetFile(os.path.abspath(filename), + os.path.basename(filename), + reply_handler=self.create_transfer_reply, + error_handler=self.error) + + def remove_file(self, filename): + self.ftp.Delete(filename, + reply_handler=self.generic_reply, + error_handler=self.error) + + def move_file(self, filename, destname): + self.ftp.MoveFile(filename, destname, + reply_handler=self.generic_reply, + error_handler=self.error) + + def copy_file(self, filename, destname): + self.ftp.CopyFile(filename, destname, + reply_handler=self.generic_reply, + error_handler=self.error) + +if __name__ == '__main__': + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + parser = OptionParser() + + (options, args) = parse_options() + + if not options.device: + parser.print_help() + sys.exit(0) + + bus = dbus.SessionBus() + mainloop = GObject.MainLoop() + + client = dbus.Interface(bus.get_object(BUS_NAME, PATH,), + CLIENT_INTERFACE) + + print("Creating Session") + path = client.CreateSession(options.device, { "Target": "ftp" }) + + ftp_client = FtpClient(path, options.verbose) + + if options.new_dir: + ftp_client.change_folder(options.new_dir) + + if options.list_dir: + ftp_client.list_folder() + + if options.get_file: + ftp_client.get_file(options.get_file) + + if options.put_file: + ftp_client.put_file(options.put_file) + + if options.move_file: + ftp_client.move_file(options.move_file, options.dest_file) + + if options.copy_file: + ftp_client.copy_file(options.copy_file, options.dest_file) + + if options.remove_file: + ftp_client.remove_file(options.remove_file) + + mainloop.run() diff --git a/test/map-client b/test/map-client new file mode 100755 index 0000000..b9695da --- /dev/null +++ b/test/map-client @@ -0,0 +1,232 @@ +#!/usr/bin/python + +from __future__ import absolute_import, print_function, unicode_literals + +from optparse import OptionParser +import os +from pprint import pformat +import sys +import dbus +import dbus.mainloop.glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject + +BUS_NAME='org.bluez.obex' +PATH = '/org/bluez/obex' +CLIENT_INTERFACE = 'org.bluez.obex.Client1' +SESSION_INTERFACE = 'org.bluez.obex.Session1' +MESSAGE_ACCESS_INTERFACE = 'org.bluez.obex.MessageAccess1' +MESSAGE_INTERFACE = 'org.bluez.obex.Message1' +TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1' + +def unwrap(x): + """Hack to unwrap D-Bus values, so that they're easier to read when + printed. Taken from d-feet """ + + if isinstance(x, list): + return map(unwrap, x) + + if isinstance(x, tuple): + return tuple(map(unwrap, x)) + + if isinstance(x, dict): + return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()]) + + for t in [unicode, str, long, int, float, bool]: + if isinstance(x, t): + return t(x) + + return x + +def parse_options(): + parser.add_option("-d", "--device", dest="device", + help="Device to connect", metavar="DEVICE") + parser.add_option("-c", "--chdir", dest="new_dir", + help="Change current directory to DIR", metavar="DIR") + parser.add_option("-l", "--lsdir", action="store_true", dest="ls_dir", + help="List folders in current directory") + parser.add_option("-v", "--verbose", action="store_true", dest="verbose") + parser.add_option("-L", "--lsmsg", action="store", dest="ls_msg", + help="List messages in supplied CWD subdir") + parser.add_option("-g", "--get", action="store", dest="get_msg", + help="Get message contents") + parser.add_option("-p", "--push", action="store", dest="push_msg", + help="Push message") + parser.add_option("--get-properties", action="store", dest="get_msg_properties", + help="Get message properties") + parser.add_option("--mark-read", action="store", dest="mark_msg_read", + help="Marks the messages as read") + parser.add_option("--mark-unread", action="store", dest="mark_msg_unread", + help="Marks the messages as unread") + parser.add_option("--mark-deleted", action="store", dest="mark_msg_deleted", + help="Deletes the message from the folder") + parser.add_option("--mark-undeleted", action="store", dest="mark_msg_undeleted", + help="Undeletes the message") + parser.add_option("-u", "--update-inbox", action="store_true", dest="update_inbox", + help="Checks for new mails") + + return parser.parse_args() + +def set_folder(session, new_dir): + session.SetFolder(new_dir) + +class MapClient: + def __init__(self, session_path, verbose=False): + self.progress = 0 + self.transfer_path = None + self.props = dict() + self.verbose = verbose + self.path = session_path + bus = dbus.SessionBus() + obj = bus.get_object(BUS_NAME, session_path) + self.session = dbus.Interface(obj, SESSION_INTERFACE) + self.map = dbus.Interface(obj, MESSAGE_ACCESS_INTERFACE) + bus.add_signal_receiver(self.properties_changed, + dbus_interface="org.freedesktop.DBus.Properties", + signal_name="PropertiesChanged", + path_keyword="path") + + def create_transfer_reply(self, path, properties): + self.transfer_path = path + self.props[path] = properties + if self.verbose: + print("Transfer created: %s (file %s)" % (path, + properties["Filename"])) + + def generic_reply(self): + if self.verbose: + print("Operation succeeded") + + def error(self, err): + print(err) + mainloop.quit() + + def transfer_complete(self, path): + if self.verbose: + print("Transfer finished") + properties = self.props.get(path) + if properties == None: + return + f = open(properties["Filename"], "r") + os.remove(properties["Filename"]) + print(f.readlines()) + + def transfer_error(self, path): + print("Transfer %s error" % path) + mainloop.quit() + + def properties_changed(self, interface, properties, invalidated, path): + req = self.props.get(path) + if req == None: + return + + if properties['Status'] == 'complete': + self.transfer_complete(path) + return + + if properties['Status'] == 'error': + self.transfer_error(path) + return + + def set_folder(self, new_dir): + self.map.SetFolder(new_dir) + + def list_folders(self): + for i in self.map.ListFolders(dict()): + print("%s/" % (i["Name"])) + + def list_messages(self, folder): + ret = self.map.ListMessages(folder, dict()) + print(pformat(unwrap(ret))) + + def get_message(self, handle): + self.map.ListMessages("", dict()) + path = self.path + "/message" + handle + obj = bus.get_object(BUS_NAME, path) + msg = dbus.Interface(obj, MESSAGE_INTERFACE) + msg.Get("", True, reply_handler=self.create_transfer_reply, + error_handler=self.error) + + def push_message(self, filename): + self.map.PushMessage(filename, "telecom/msg/outbox", dict(), + reply_handler=self.create_transfer_reply, + error_handler=self.error) + + def get_message_properties(self, handle): + self.map.ListMessages("", dict()) + path = self.path + "/message" + handle + obj = bus.get_object(BUS_NAME, path) + msg = dbus.Interface(obj, "org.freedesktop.DBus.Properties") + ret = msg.GetAll(MESSAGE_INTERFACE) + print(pformat(unwrap(ret))) + + def set_message_property(self, handle, prop, flag): + self.map.ListMessages("", dict()) + path = self.path + "/message" + handle + obj = bus.get_object(BUS_NAME, path) + msg = dbus.Interface(obj, MESSAGE_INTERFACE) + msg.SetProperty (prop, flag); + + def update_inbox(self): + self.map.UpdateInbox() + + +if __name__ == '__main__': + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + parser = OptionParser() + + (options, args) = parse_options() + + if not options.device: + parser.print_help() + exit(0) + + bus = dbus.SessionBus() + mainloop = GObject.MainLoop() + + client = dbus.Interface(bus.get_object(BUS_NAME, PATH), + CLIENT_INTERFACE) + + print("Creating Session") + path = client.CreateSession(options.device, { "Target": "map" }) + + map_client = MapClient(path, options.verbose) + + if options.new_dir: + map_client.set_folder(options.new_dir) + + if options.ls_dir: + map_client.list_folders() + + if options.ls_msg is not None: + map_client.list_messages(options.ls_msg) + + if options.get_msg is not None: + map_client.get_message(options.get_msg) + + if options.push_msg is not None: + map_client.push_message(options.push_msg) + + if options.get_msg_properties is not None: + map_client.get_message_properties(options.get_msg_properties) + + if options.mark_msg_read is not None: + map_client.set_message_property(options.mark_msg_read, "Read", True) + + if options.mark_msg_unread is not None: + map_client.set_message_property(options.mark_msg_unread, "Read", False) + + if options.mark_msg_deleted is not None: + map_client.set_message_property(options.mark_msg_deleted, "Deleted", True) + + if options.mark_msg_undeleted is not None: + map_client.set_message_property(options.mark_msg_undeleted, "Deleted", False) + + if options.update_inbox: + map_client.update_inbox() + + mainloop.run() diff --git a/test/monitor-bluetooth b/test/monitor-bluetooth index bc5ddaf..d9b5472 100755 --- a/test/monitor-bluetooth +++ b/test/monitor-bluetooth @@ -2,10 +2,12 @@ from __future__ import absolute_import, print_function, unicode_literals -import gobject - import dbus import dbus.mainloop.glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject relevant_ifaces = [ "org.bluez.Adapter1", "org.bluez.Device1" ] @@ -48,5 +50,5 @@ if __name__ == '__main__': dbus_interface="org.freedesktop.DBus.ObjectManager", signal_name="InterfacesRemoved") - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() mainloop.run() diff --git a/test/opp-client b/test/opp-client new file mode 100755 index 0000000..62d5b84 --- /dev/null +++ b/test/opp-client @@ -0,0 +1,119 @@ +#!/usr/bin/python + +from __future__ import absolute_import, print_function, unicode_literals + +from optparse import OptionParser +import os.path +import sys +import dbus +import dbus.mainloop.glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject + +BUS_NAME='org.bluez.obex' +PATH = '/org/bluez/obex' +CLIENT_INTERFACE='org.bluez.obex.Client1' +SESSION_INTERFACE='org.bluez.obex.Session1' +OBJECT_PUSH_INTERFACE='org.bluez.obex.ObjectPush1' +TRANSFER_INTERFACE='org.bluez.obex.Transfer1' + +def parse_options(): + parser.add_option("-d", "--device", dest="device", + help="Device to connect", metavar="DEVICE") + parser.add_option("-p", "--pull", dest="pull_to_file", + help="Pull vcard and store in FILE", metavar="FILE") + parser.add_option("-s", "--send", dest="send_file", + help="Send FILE", metavar="FILE") + parser.add_option("-v", "--verbose", action="store_true", + dest="verbose") + + return parser.parse_args() + +class OppClient: + def __init__(self, session_path, verbose=False): + self.transferred = 0 + self.transfer_path = None + self.verbose = verbose + bus = dbus.SessionBus() + obj = bus.get_object(BUS_NAME, session_path) + self.session = dbus.Interface(obj, SESSION_INTERFACE) + self.opp = dbus.Interface(obj, OBJECT_PUSH_INTERFACE) + bus.add_signal_receiver(self.properties_changed, + dbus_interface="org.freedesktop.DBus.Properties", + signal_name="PropertiesChanged", + path_keyword="path") + + def create_transfer_reply(self, path, properties): + self.transfer_path = path + self.transfer_size = properties["Size"] + if self.verbose: + print("Transfer created: %s" % path) + + def error(self, err): + print(err) + mainloop.quit() + + def properties_changed(self, interface, properties, invalidated, path): + if path != self.transfer_path: + return + + if "Status" in properties and \ + (properties["Status"] == "complete" or \ + properties["Status"] == "error"): + if self.verbose: + print("Transfer %s" % properties["Status"]) + mainloop.quit() + return + + if "Transferred" not in properties: + return + + value = properties["Transferred"] + speed = (value - self.transferred) / 1000 + print("Transfer progress %d/%d at %d kBps" % (value, + self.transfer_size, + speed)) + self.transferred = value + + def pull_business_card(self, filename): + self.opp.PullBusinessCard(os.path.abspath(filename), + reply_handler=self.create_transfer_reply, + error_handler=self.error) + + def send_file(self, filename): + self.opp.SendFile(os.path.abspath(filename), + reply_handler=self.create_transfer_reply, + error_handler=self.error) + +if __name__ == '__main__': + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + parser = OptionParser() + + (options, args) = parse_options() + + if not options.device: + parser.print_help() + sys.exit(0) + + bus = dbus.SessionBus() + mainloop = GObject.MainLoop() + + client = dbus.Interface(bus.get_object(BUS_NAME, PATH), + CLIENT_INTERFACE) + + print("Creating Session") + path = client.CreateSession(options.device, { "Target": "OPP" }) + + opp_client = OppClient(path, options.verbose) + + if options.pull_to_file: + opp_client.pull_business_card(options.pull_to_file) + + if options.send_file: + opp_client.send_file(options.send_file) + + mainloop.run() diff --git a/test/pbap-client b/test/pbap-client new file mode 100755 index 0000000..51e26eb --- /dev/null +++ b/test/pbap-client @@ -0,0 +1,175 @@ +#!/usr/bin/python + +from __future__ import absolute_import, print_function, unicode_literals + +import os +import sys +import dbus +import dbus.service +import dbus.mainloop.glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject + +BUS_NAME='org.bluez.obex' +PATH = '/org/bluez/obex' +CLIENT_INTERFACE = 'org.bluez.obex.Client1' +SESSION_INTERFACE = 'org.bluez.obex.Session1' +PHONEBOOK_ACCESS_INTERFACE = 'org.bluez.obex.PhonebookAccess1' +TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1' + +class Transfer: + def __init__(self, callback_func): + self.callback_func = callback_func + self.path = None + self.filename = None + +class PbapClient: + def __init__(self, session_path): + self.transfers = 0 + self.props = dict() + self.flush_func = None + bus = dbus.SessionBus() + obj = bus.get_object(BUS_NAME, session_path) + self.session = dbus.Interface(obj, SESSION_INTERFACE) + self.pbap = dbus.Interface(obj, PHONEBOOK_ACCESS_INTERFACE) + bus.add_signal_receiver(self.properties_changed, + dbus_interface="org.freedesktop.DBus.Properties", + signal_name="PropertiesChanged", + path_keyword="path") + + def register(self, path, properties, transfer): + transfer.path = path + transfer.filename = properties["Filename"] + self.props[path] = transfer + print("Transfer created: %s (file %s)" % (path, + transfer.filename)) + + def error(self, err): + print(err) + mainloop.quit() + + def transfer_complete(self, path): + req = self.props.get(path) + if req == None: + return + self.transfers -= 1 + print("Transfer %s complete" % path) + try: + f = open(req.filename, "r") + os.remove(req.filename) + lines = f.readlines() + del self.props[path] + req.callback_func(lines) + except: + pass + + if (len(self.props) == 0) and (self.transfers == 0): + if self.flush_func != None: + f = self.flush_func + self.flush_func = None + f() + + def transfer_error(self, path): + print("Transfer %s error" % path) + mainloop.quit() + + def properties_changed(self, interface, properties, invalidated, path): + req = self.props.get(path) + if req == None: + return + + if properties['Status'] == 'complete': + self.transfer_complete(path) + return + + if properties['Status'] == 'error': + self.transfer_error(path) + return + + def pull(self, vcard, params, func): + req = Transfer(func) + self.pbap.Pull(vcard, "", params, + reply_handler=lambda o, p: self.register(o, p, req), + error_handler=self.error) + self.transfers += 1 + + def pull_all(self, params, func): + req = Transfer(func) + self.pbap.PullAll("", params, + reply_handler=lambda o, p: self.register(o, p, req), + error_handler=self.error) + self.transfers += 1 + + def flush_transfers(self, func): + if (len(self.props) == 0) and (self.transfers == 0): + return + self.flush_func = func + + def interface(self): + return self.pbap + +if __name__ == '__main__': + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SessionBus() + mainloop = GObject.MainLoop() + + client = dbus.Interface(bus.get_object(BUS_NAME, PATH), + CLIENT_INTERFACE) + + if (len(sys.argv) < 2): + print("Usage: %s " % (sys.argv[0])) + sys.exit(1) + + print("Creating Session") + session_path = client.CreateSession(sys.argv[1], { "Target": "PBAP" }) + + pbap_client = PbapClient(session_path) + + def process_result(lines, header): + if header != None: + print(header) + for line in lines: + print(line), + print + + def test_paths(paths): + if len(paths) == 0: + print + print("FINISHED") + mainloop.quit() + return + + path = paths[0] + + print("\n--- Select Phonebook %s ---\n" % (path)) + pbap_client.interface().Select("int", path) + + print("\n--- GetSize ---\n") + ret = pbap_client.interface().GetSize() + print("Size = %d\n" % (ret)) + + print("\n--- List vCard ---\n") + try: + ret = pbap_client.interface().List(dbus.Dictionary()) + except: + ret = [] + + params = dbus.Dictionary({ "Format" : "vcard30", + "Fields" : ["PHOTO"] }) + for item in ret: + print("%s : %s" % (item[0], item[1])) + pbap_client.pull(item[0], params, + lambda x: process_result(x, None)) + + pbap_client.pull_all(params, lambda x: process_result(x, + "\n--- PullAll ---\n")) + + pbap_client.flush_transfers(lambda: test_paths(paths[1:])) + + test_paths(["PB", "ICH", "OCH", "MCH", "CCH"]) + + mainloop.run() diff --git a/test/simple-agent b/test/simple-agent index 854e1af..a69299a 100755 --- a/test/simple-agent +++ b/test/simple-agent @@ -2,13 +2,15 @@ from __future__ import absolute_import, print_function, unicode_literals -from gi.repository import GObject - +from optparse import OptionParser import sys import dbus import dbus.service import dbus.mainloop.glib -from optparse import OptionParser +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils BUS_NAME = 'org.bluez' diff --git a/test/simple-endpoint b/test/simple-endpoint index 590f83a..0164cff 100755 --- a/test/simple-endpoint +++ b/test/simple-endpoint @@ -6,7 +6,10 @@ import sys import dbus import dbus.service import dbus.mainloop.glib -import gobject +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils A2DP_SOURCE_UUID = "0000110A-0000-1000-8000-00805F9B34FB" @@ -94,7 +97,7 @@ if __name__ == '__main__': path = "/test/endpoint" endpoint = Endpoint(bus, path) - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID, "Codec" : SBC_CODEC, diff --git a/test/simple-player b/test/simple-player index 01bec06..23e78ad 100755 --- a/test/simple-player +++ b/test/simple-player @@ -1,12 +1,16 @@ #!/usr/bin/python from __future__ import print_function + import os import sys import dbus import dbus.service import dbus.mainloop.glib -import gobject +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils class Player(dbus.service.Object): @@ -56,7 +60,7 @@ class Player(dbus.service.Object): signature="sv") handler = InputHandler(self) - gobject.io_add_watch(sys.stdin, gobject.IO_IN, + GObject.io_add_watch(sys.stdin, GObject.IO_IN, handler.handle) @dbus.service.method("org.freedesktop.DBus.Properties", @@ -136,7 +140,7 @@ if __name__ == '__main__': path = "/test/player" player = Player(bus, path) - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() if len(sys.argv) > 2: player.set_object(sys.argv[2]) diff --git a/test/simple-service b/test/simple-service deleted file mode 100755 index 02d7648..0000000 --- a/test/simple-service +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/python - -from __future__ import absolute_import, print_function, unicode_literals - -import sys -import time -import dbus -import bluezutils - -xml = ' \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -' - -bus = dbus.SystemBus() - -if len(sys.argv) > 1: - path = bluezutils.find_adapter(sys.argv[1]).object_path -else: - path = bluezutils.find_adapter().object_path - -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 index 5deeda4..959a437 100755 --- a/test/test-adapter +++ b/test/test-adapter @@ -2,10 +2,10 @@ from __future__ import absolute_import, print_function, unicode_literals +from optparse import OptionParser, make_option import sys -import dbus import time -from optparse import OptionParser, make_option +import dbus import bluezutils bus = dbus.SystemBus() diff --git a/test/test-alert b/test/test-alert index 066e537..43b3cf3 100755 --- a/test/test-alert +++ b/test/test-alert @@ -1,12 +1,17 @@ #!/usr/bin/python + from __future__ import absolute_import, print_function, unicode_literals + +import optparse +import os +import sys import dbus import dbus.service import dbus.mainloop.glib -import gobject -import optparse -import sys -import os +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject BUS_NAME = 'org.bluez' ALERT_INTERFACE = 'org.bluez.Alert1' @@ -149,7 +154,7 @@ parser.disable_interspersed_args() dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() -mainloop = gobject.MainLoop() +mainloop = GObject.MainLoop() alert = dbus.Interface(bus.get_object(BUS_NAME, BLUEZ_OBJECT_PATH), ALERT_INTERFACE) alert_agent = AlertAgent(bus, TEST_OBJECT_PATH, alert, mainloop) diff --git a/test/test-cyclingspeed b/test/test-cyclingspeed index 75bd7d7..393f79c 100755 --- a/test/test-cyclingspeed +++ b/test/test-cyclingspeed @@ -6,13 +6,16 @@ from __future__ import absolute_import, print_function, unicode_literals Cycling Speed and Cadence test script ''' -import gobject - +from optparse import OptionParser, make_option import sys import dbus import dbus.service import dbus.mainloop.glib -from optparse import OptionParser, make_option +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject +import bluezutils BUS_NAME = 'org.bluez' CYCLINGSPEED_MANAGER_INTERFACE = 'org.bluez.CyclingSpeedManager1' @@ -141,7 +144,7 @@ if __name__ == "__main__": device_path = device.object_path cscmanager = dbus.Interface(bus.get_object(BUS_NAME, adapter_path), - CYCLINGSPEED_WATCHER_INTERFACE) + CYCLINGSPEED_MANAGER_INTERFACE) watcher_path = "/test/watcher" watcher = Watcher(bus, watcher_path) @@ -190,5 +193,5 @@ if __name__ == "__main__": print("Unknown command") sys.exit(1) - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() mainloop.run() diff --git a/test/test-device b/test/test-device index 3d7b852..b490d53 100755 --- a/test/test-device +++ b/test/test-device @@ -2,13 +2,15 @@ from __future__ import absolute_import, print_function, unicode_literals -from gi.repository import GObject - +from optparse import OptionParser, make_option +import re import sys import dbus import dbus.mainloop.glib -import re -from optparse import OptionParser, make_option +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) diff --git a/test/test-discovery b/test/test-discovery index c13bfac..73b8161 100755 --- a/test/test-discovery +++ b/test/test-discovery @@ -2,11 +2,13 @@ from __future__ import absolute_import, print_function, unicode_literals -from gi.repository import GObject - +from optparse import OptionParser, make_option import dbus import dbus.mainloop.glib -from optparse import OptionParser, make_option +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils compact = False diff --git a/test/test-health b/test/test-health index 052a602..343f29c 100755 --- a/test/test-health +++ b/test/test-health @@ -3,11 +3,14 @@ from __future__ import absolute_import, print_function, unicode_literals # -*- coding: utf-8 -*- +import sys import dbus import dbus.service -import gobject from dbus.mainloop.glib import DBusGMainLoop -import sys +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject BUS_NAME = 'org.bluez' PATH = '/org/bluez' @@ -16,7 +19,7 @@ HEALTH_MANAGER_INTERFACE = 'org.bluez.HealthManager1' HEALTH_DEVICE_INTERFACE = 'org.bluez.HealthDevice1' DBusGMainLoop(set_as_default=True) -loop = gobject.MainLoop() +loop = GObject.MainLoop() bus = dbus.SystemBus() @@ -48,7 +51,7 @@ def enter_mainloop(): try: print("Entering main lopp, push Ctrl+C for finish") - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() mainloop.run() except KeyboardInterrupt: pass diff --git a/test/test-health-sink b/test/test-health-sink index 32afd71..52be535 100755 --- a/test/test-health-sink +++ b/test/test-health-sink @@ -3,11 +3,14 @@ from __future__ import absolute_import, print_function, unicode_literals # -*- coding: utf-8 -*- +import sys import dbus import dbus.service -import gobject from dbus.mainloop.glib import DBusGMainLoop -import sys +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject BUS_NAME = 'org.bluez' PATH = '/org/bluez' @@ -16,7 +19,7 @@ HEALTH_MANAGER_INTERFACE = 'org.bluez.HealthManager1' HEALTH_DEVICE_INTERFACE = 'org.bluez.HealthDevice1' DBusGMainLoop(set_as_default=True) -loop = gobject.MainLoop() +loop = GObject.MainLoop() bus = dbus.SystemBus() diff --git a/test/test-heartrate b/test/test-heartrate index f26b3db..5e4e7e5 100755 --- a/test/test-heartrate +++ b/test/test-heartrate @@ -6,13 +6,15 @@ from __future__ import absolute_import, print_function, unicode_literals Heart Rate Monitor test script ''' -import gobject - +from optparse import OptionParser, make_option import sys import dbus import dbus.service import dbus.mainloop.glib -from optparse import OptionParser, make_option +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils BUS_NAME = 'org.bluez' @@ -102,5 +104,5 @@ if __name__ == "__main__": print("unknown command") sys.exit(1) - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() mainloop.run() diff --git a/test/test-hfp b/test/test-hfp index 873de0a..a806043 100755 --- a/test/test-hfp +++ b/test/test-hfp @@ -2,16 +2,18 @@ from __future__ import absolute_import, print_function, unicode_literals -from gi.repository import GObject - +from optparse import OptionParser, make_option import os +from socket import SOCK_SEQPACKET, socket import sys import dbus -import glib import dbus.service import dbus.mainloop.glib -from optparse import OptionParser, make_option -from socket import SOCK_SEQPACKET, socket +import glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject mainloop = None audio_supported = True diff --git a/test/test-manager b/test/test-manager index 1e3882f..4f5994f 100755 --- a/test/test-manager +++ b/test/test-manager @@ -2,10 +2,12 @@ from __future__ import absolute_import, print_function, unicode_literals -from gi.repository import GObject - import dbus import dbus.mainloop.glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils def interfaces_added(path, interfaces): diff --git a/test/test-nap b/test/test-nap index 197e3c2..00a2585 100755 --- a/test/test-nap +++ b/test/test-nap @@ -2,10 +2,10 @@ from __future__ import absolute_import, print_function, unicode_literals +from optparse import OptionParser, make_option import sys import time import dbus -from optparse import OptionParser, make_option import bluezutils bus = dbus.SystemBus() diff --git a/test/test-network b/test/test-network index 3e8713f..6f09486 100755 --- a/test/test-network +++ b/test/test-network @@ -2,10 +2,10 @@ from __future__ import absolute_import, print_function, unicode_literals +from optparse import OptionParser, make_option import sys import time import dbus -from optparse import OptionParser, make_option import bluezutils bus = dbus.SystemBus() diff --git a/test/test-profile b/test/test-profile index b78d00c..2791580 100755 --- a/test/test-profile +++ b/test/test-profile @@ -2,15 +2,17 @@ from __future__ import absolute_import, print_function, unicode_literals -from gi.repository import GObject - +from optparse import OptionParser, make_option import os import sys import uuid import dbus import dbus.service import dbus.mainloop.glib -from optparse import OptionParser, make_option +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject class Profile(dbus.service.Object): fd = -1 diff --git a/test/test-proximity b/test/test-proximity index 2f47824..66b7bc2 100755 --- a/test/test-proximity +++ b/test/test-proximity @@ -6,12 +6,14 @@ from __future__ import absolute_import, print_function, unicode_literals Proximity Monitor test script ''' -import gobject - +from optparse import OptionParser, make_option import sys import dbus import dbus.mainloop.glib -from optparse import OptionParser, make_option +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils BUS_NAME = 'org.bluez' @@ -64,5 +66,5 @@ if __name__ == "__main__": print("Proximity SetProperty('%s', '%s')" % (args[0], args[1])) device_prop.Set(PROXIMITY_MONITOR_INTERFACE, args[0], args[1]) - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() mainloop.run() diff --git a/test/test-thermometer b/test/test-thermometer index 6c143be..7e67c23 100755 --- a/test/test-thermometer +++ b/test/test-thermometer @@ -6,13 +6,15 @@ from __future__ import absolute_import, print_function, unicode_literals Thermometer test script ''' -import gobject - +from optparse import OptionParser, make_option import sys import dbus import dbus.service import dbus.mainloop.glib -from optparse import OptionParser, make_option +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import bluezutils BUS_NAME = 'org.bluez' @@ -93,5 +95,5 @@ if __name__ == "__main__": print("unknown command") sys.exit(1) - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() mainloop.run() diff --git a/tools/3dsp.c b/tools/3dsp.c new file mode 100644 index 0000000..68dcbb5 --- /dev/null +++ b/tools/3dsp.c @@ -0,0 +1,478 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. 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 +#endif + +#include +#include +#include +#include +#include + +#include "monitor/mainloop.h" +#include "monitor/bt.h" +#include "src/shared/timeout.h" +#include "src/shared/util.h" +#include "src/shared/hci.h" + +#define LT_ADDR 0x01 +#define PKT_TYPE 0x0008 /* 0x0008 = EDR + DM1, 0xff1e = BR only */ +#define SERVICE_DATA LT_ADDR + +static struct bt_hci *hci_dev; + +static bool reset_on_init = false; +static bool reset_on_shutdown = false; + +static bool shutdown_timeout(void *user_data) +{ + mainloop_quit(); + + return false; +} + +static void shutdown_complete(const void *data, uint8_t size, void *user_data) +{ + unsigned int id = PTR_TO_UINT(user_data); + + timeout_remove(id); + mainloop_quit(); +} + +static void shutdown_device(void) +{ + unsigned int id; + + bt_hci_flush(hci_dev); + + if (reset_on_shutdown) { + id = timeout_add(5000, shutdown_timeout, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, + shutdown_complete, UINT_TO_PTR(id), NULL); + } else + mainloop_quit(); +} + +static void slave_broadcast_receive(const void *data, uint8_t size, + void *user_data) +{ + printf("Slave broadcast receiption enabled\n"); +} + +static void sync_train_received(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_evt_sync_train_received *evt = data; + struct bt_hci_cmd_set_slave_broadcast_receive cmd; + + if (evt->status) { + printf("Failed to synchronize with 3D display\n"); + shutdown_device(); + return; + } + + cmd.enable = 0x01; + memcpy(cmd.bdaddr, evt->bdaddr, 6); + cmd.lt_addr = evt->lt_addr; + cmd.interval = evt->interval; + cmd.offset = evt->offset; + cmd.instant = evt->instant; + cmd.timeout = cpu_to_le16(0xfffe); + cmd.accuracy = 250; + cmd.skip = 20; + cmd.pkt_type = cpu_to_le16(PKT_TYPE); + memcpy(cmd.map, evt->map, 10); + + bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE, + &cmd, sizeof(cmd), + slave_broadcast_receive, NULL, NULL); +} + +static void truncated_page_complete(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_evt_truncated_page_complete *evt = data; + struct bt_hci_cmd_receive_sync_train cmd; + + if (evt->status) { + printf("Failed to contact 3D display\n"); + shutdown_device(); + return; + } + + printf("Attempt to synchronize with 3D display\n"); + + bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED, + sync_train_received, NULL, NULL); + + memcpy(cmd.bdaddr, evt->bdaddr, 6); + cmd.timeout = cpu_to_le16(0x4000); + cmd.window = cpu_to_le16(0x0100); + cmd.interval = cpu_to_le16(0x0080); + + bt_hci_send(hci_dev, BT_HCI_CMD_RECEIVE_SYNC_TRAIN, &cmd, sizeof(cmd), + NULL, NULL, NULL); +} + +static void ext_inquiry_result(const void *data, uint8_t size, void *user_data) +{ + const struct bt_hci_evt_ext_inquiry_result *evt = data; + + if (evt->dev_class[0] != 0x3c || evt->dev_class[1] != 0x04 + || evt->dev_class[2] != 0x08) + return; + + if (evt->data[0]) { + struct bt_hci_cmd_truncated_page cmd; + + printf("Found 3D display\n"); + + bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY_CANCEL, NULL, 0, + NULL, NULL, NULL); + + bt_hci_register(hci_dev, BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE, + truncated_page_complete, NULL, NULL); + + memcpy(cmd.bdaddr, evt->bdaddr, 6); + cmd.pscan_rep_mode = evt->pscan_rep_mode; + cmd.clock_offset = evt->clock_offset; + + bt_hci_send(hci_dev, BT_HCI_CMD_TRUNCATED_PAGE, + &cmd, sizeof(cmd), NULL, NULL, NULL); + } +} + +static void inquiry_complete(const void *data, uint8_t size, void *user_data) +{ + printf("No 3D display found\n"); + + shutdown_device(); +} + +static void inquiry_started(const void *data, uint8_t size, void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + printf("Failed to search for 3D display\n"); + shutdown_device(); + return; + } + + printf("Searching for 3D display\n"); +} + +static void start_glasses(void) +{ + struct bt_hci_cmd_inquiry cmd; + uint8_t evtmask1[] = { 0x03, 0xe0, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00 }; + uint8_t evtmask2[] = { 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t inqmode = 0x02; + + if (reset_on_init) { + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, + NULL, NULL, NULL); + bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask1, 8, + NULL, NULL, NULL); + } + + bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, evtmask2, 8, + NULL, NULL, NULL); + bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_INQUIRY_MODE, &inqmode, 1, + NULL, NULL, NULL); + + bt_hci_register(hci_dev, BT_HCI_EVT_INQUIRY_COMPLETE, + inquiry_complete, NULL, NULL); + bt_hci_register(hci_dev, BT_HCI_EVT_EXT_INQUIRY_RESULT, + ext_inquiry_result, NULL, NULL); + + cmd.lap[0] = 0x33; + cmd.lap[1] = 0x8b; + cmd.lap[2] = 0x9e; + cmd.length = 0x08; + cmd.num_resp = 0x00; + + bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY, &cmd, sizeof(cmd), + inquiry_started, NULL, NULL); +} + +static void conn_request(const void *data, uint8_t size, void *user_data) +{ + const struct bt_hci_evt_conn_request *evt = data; + struct bt_hci_cmd_accept_conn_request cmd; + + printf("Incoming connection from 3D glasses\n"); + + memcpy(cmd.bdaddr, evt->bdaddr, 6); + cmd.role = 0x00; + + bt_hci_send(hci_dev, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd, sizeof(cmd), + NULL, NULL, NULL); +} + +static bool sync_train_active = false; + +static void sync_train_complete(const void *data, uint8_t size, + void *user_data) +{ + sync_train_active = false; +} + +static void slave_page_response_timeout(const void *data, uint8_t size, + void *user_data) +{ + struct bt_hci_cmd_write_sync_train_params cmd; + + if (sync_train_active) + return; + + printf("Starting new synchronization train\n"); + + cmd.min_interval = cpu_to_le16(0x0050); + cmd.max_interval = cpu_to_le16(0x00a0); + cmd.timeout = cpu_to_le32(0x0002ee00); /* 120 sec */ + cmd.service_data = SERVICE_DATA; + + bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS, + &cmd, sizeof(cmd), NULL, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, NULL, 0, + NULL, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_START_SYNC_TRAIN, NULL, 0, + NULL, NULL, NULL); + + sync_train_active = true; +} + +static void inquiry_resp_tx_power(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_rsp_read_inquiry_resp_tx_power *rsp = data; + struct bt_hci_cmd_write_ext_inquiry_response cmd; + uint8_t inqdata[] = { 0x03, 0x3d, 0x03, 0x43, 0x02, 0x0a, 0x00, 0x00 }; + uint8_t devclass[] = { 0x3c, 0x04, 0x08 }; + uint8_t scanmode = 0x03; + + inqdata[6] = (uint8_t) rsp->level; + + cmd.fec = 0x00; + memset(cmd.data, 0, sizeof(cmd.data)); + memcpy(cmd.data, inqdata, sizeof(inqdata)); + + bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE, + &cmd, sizeof(cmd), NULL, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_CLASS_OF_DEV, devclass, 3, + NULL, NULL, NULL); + bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SCAN_ENABLE, &scanmode, 1, + NULL, NULL, NULL); +} + +static void start_display(void) +{ + struct bt_hci_cmd_set_slave_broadcast cmd; + uint8_t bcastdata[20] = { LT_ADDR, 0x03, 0x11, 0x23, 0x42, }; + uint8_t evtmask1[] = { 0x1c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t evtmask2[] = { 0x00, 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t sspmode = 0x01; + uint8_t ltaddr = LT_ADDR; + + if (reset_on_init) { + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, + NULL, NULL, NULL); + bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask1, 8, + NULL, NULL, NULL); + } + + bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, evtmask2, 8, + NULL, NULL, NULL); + bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &sspmode, 1, + NULL, NULL, NULL); + bt_hci_send(hci_dev, BT_HCI_CMD_SET_RESERVED_LT_ADDR, <addr, 1, + NULL, NULL, NULL); + bt_hci_send(hci_dev, BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, NULL, 0, + NULL, NULL, NULL); + + bt_hci_register(hci_dev, BT_HCI_EVT_CONN_REQUEST, + conn_request, NULL, NULL); + + bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_PAGE_RESPONSE_TIMEOUT, + slave_page_response_timeout, NULL, NULL); + bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_COMPLETE, + sync_train_complete, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER, NULL, 0, + inquiry_resp_tx_power, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA, + bcastdata, sizeof(bcastdata), NULL, NULL, NULL); + + cmd.enable = 0x01; + cmd.lt_addr = LT_ADDR; + cmd.lpo_allowed = 0x01; + cmd.pkt_type = cpu_to_le16(PKT_TYPE); + cmd.min_interval = cpu_to_le16(0x0050); /* 50 ms */ + cmd.max_interval = cpu_to_le16(0x00a0); /* 100 ms */ + cmd.timeout = cpu_to_le16(0xfffe); + + bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST, &cmd, sizeof(cmd), + NULL, NULL, NULL); +} + +static void signal_callback(int signum, void *user_data) +{ + static bool terminated = false; + + switch (signum) { + case SIGINT: + case SIGTERM: + if (!terminated) { + shutdown_device(); + terminated = true; + } + break; + } +} + +static void usage(void) +{ + printf("3dsp - 3D Synchronization Profile testing\n" + "Usage:\n"); + printf("\t3dsp [options]\n"); + printf("options:\n" + "\t-D, --display Use display role\n" + "\t-G, --glasses Use glasses role\n" + "\t-i, --index Use specified controller\n" + "\t-h, --help Show help options\n"); +} + +static const struct option main_options[] = { + { "display", no_argument, NULL, 'D' }, + { "glasses", no_argument, NULL, 'G' }, + { "index", required_argument, NULL, 'i' }, + { "raw", no_argument, NULL, 'r' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } +}; + +int main(int argc, char *argv[]) +{ + bool display_role = false, glasses_role = false; + uint16_t index = 0; + const char *str; + bool use_raw = false; + sigset_t mask; + int exit_status; + + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "DGi:rvh", main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'D': + display_role = true; + break; + case 'G': + glasses_role = true; + break; + case 'i': + if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3)) + str = optarg + 3; + else + str = optarg; + if (!isdigit(*str)) { + usage(); + return EXIT_FAILURE; + } + index = atoi(str); + break; + case 'r': + use_raw = true; + break; + case 'v': + printf("%s\n", VERSION); + return EXIT_SUCCESS; + case 'h': + usage(); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } + } + + if (argc - optind > 0) { + fprintf(stderr, "Invalid command line parameters\n"); + return EXIT_FAILURE; + } + + if (display_role == glasses_role) { + fprintf(stderr, "Specify either display or glasses role\n"); + return EXIT_FAILURE; + } + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + printf("3D Synchronization Profile testing ver %s\n", VERSION); + + if (use_raw) { + hci_dev = bt_hci_new_raw_device(index); + if (!hci_dev) { + fprintf(stderr, "Failed to open HCI raw device\n"); + return EXIT_FAILURE; + } + } else { + hci_dev = bt_hci_new_user_channel(index); + if (!hci_dev) { + fprintf(stderr, "Failed to open HCI user channel\n"); + return EXIT_FAILURE; + } + + reset_on_init = true; + reset_on_shutdown = true; + } + + if (display_role) + start_display(); + else if (glasses_role) + start_glasses(); + + exit_status = mainloop_run(); + + bt_hci_unref(hci_dev); + + return exit_status; +} diff --git a/tools/amptest.c b/tools/amptest.c index 16f15bc..6192f7e 100644 --- a/tools/amptest.c +++ b/tools/amptest.c @@ -496,6 +496,7 @@ static bool find_amp_controller(void) struct hci_dev_list_req *dl; struct hci_dev_req *dr; int fd, i; + bool result; fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (fd < 0) { @@ -515,8 +516,8 @@ static bool find_amp_controller(void) if (ioctl(fd, HCIGETDEVLIST, (void *) dl) < 0) { perror("Failed to get HCI device list"); - close(fd); - return false; + result = false; + goto done; } for (i = 0; i< dl->dev_num; i++) { @@ -541,9 +542,12 @@ static bool find_amp_controller(void) } } - close(fd); + result = true; - return true; +done: + free(dl); + close(fd); + return result; } int main(int argc ,char *argv[]) diff --git a/tools/avinfo.c b/tools/avinfo.c index d237742..a4deaac 100644 --- a/tools/avinfo.c +++ b/tools/avinfo.c @@ -170,6 +170,10 @@ static void print_vendor(a2dp_vendor_codec_t *vendor) vendor->codec_id[0], vendor->codec_id[1]); } +static void print_mpeg24(a2dp_mpeg_t *mpeg) +{ + printf("\tMedia Codec: MPEG24\n"); +} static void print_mpeg12(a2dp_mpeg_t *mpeg) { @@ -304,6 +308,9 @@ static void print_media_codec(struct avdtp_media_codec_capability *cap) case A2DP_CODEC_MPEG12: print_mpeg12((void *) cap->data); break; + case A2DP_CODEC_MPEG24: + print_mpeg24((void *) cap->data); + break; case A2DP_CODEC_VENDOR: print_vendor((void *) cap->data); break; diff --git a/tools/bccmd.c b/tools/bccmd.c index ff1b307..4649ad5 100644 --- a/tools/bccmd.c +++ b/tools/bccmd.c @@ -389,7 +389,7 @@ static int cmd_chiprev(int transport, int argc, char *argv[]) static int cmd_buildname(int transport, int argc, char *argv[]) { - uint8_t array[130]; + uint8_t array[131]; char name[64]; unsigned int i; int err; diff --git a/tools/bdaddr.c b/tools/bdaddr.c index 73dffce..8356a8d 100644 --- a/tools/bdaddr.c +++ b/tools/bdaddr.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -36,7 +37,7 @@ #include #include -#include "oui.h" +#include "src/oui.h" static int transient = 0; @@ -56,7 +57,6 @@ static int generic_reset_device(int dd) 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) { @@ -70,7 +70,7 @@ static int ericsson_write_bd_addr(int dd, bdaddr_t *bdaddr) rq.ogf = OGF_VENDOR_CMD; rq.ocf = OCF_ERICSSON_WRITE_BD_ADDR; rq.cparam = &cp; - rq.clen = ERICSSON_WRITE_BD_ADDR_CP_SIZE; + rq.clen = sizeof(cp); rq.rparam = NULL; rq.rlen = 0; @@ -86,7 +86,6 @@ typedef struct { 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) { @@ -103,7 +102,7 @@ static int ericsson_store_in_flash(int dd, uint8_t user_id, uint8_t flash_length rq.ogf = OGF_VENDOR_CMD; rq.ocf = OCF_ERICSSON_STORE_IN_FLASH; rq.cparam = &cp; - rq.clen = ERICSSON_STORE_IN_FLASH_CP_SIZE; + rq.clen = sizeof(cp); rq.rparam = NULL; rq.rlen = 0; @@ -198,7 +197,6 @@ static int csr_reset_device(int dd) 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) { @@ -212,7 +210,7 @@ static int ti_write_bd_addr(int dd, bdaddr_t *bdaddr) rq.ogf = OGF_VENDOR_CMD; rq.ocf = OCF_TI_WRITE_BD_ADDR; rq.cparam = &cp; - rq.clen = TI_WRITE_BD_ADDR_CP_SIZE; + rq.clen = sizeof(cp); rq.rparam = NULL; rq.rlen = 0; @@ -226,7 +224,6 @@ static int ti_write_bd_addr(int dd, bdaddr_t *bdaddr) 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) { @@ -240,7 +237,7 @@ static int bcm_write_bd_addr(int dd, bdaddr_t *bdaddr) rq.ogf = OGF_VENDOR_CMD; rq.ocf = OCF_BCM_WRITE_BD_ADDR; rq.cparam = &cp; - rq.clen = BCM_WRITE_BD_ADDR_CP_SIZE; + rq.clen = sizeof(cp); rq.rparam = NULL; rq.rlen = 0; @@ -254,7 +251,6 @@ static int bcm_write_bd_addr(int dd, bdaddr_t *bdaddr) 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) { @@ -268,7 +264,7 @@ static int zeevo_write_bd_addr(int dd, bdaddr_t *bdaddr) rq.ogf = OGF_VENDOR_CMD; rq.ocf = OCF_ZEEVO_WRITE_BD_ADDR; rq.cparam = &cp; - rq.clen = ZEEVO_WRITE_BD_ADDR_CP_SIZE; + rq.clen = sizeof(cp); rq.rparam = NULL; rq.rlen = 0; @@ -278,6 +274,30 @@ static int zeevo_write_bd_addr(int dd, bdaddr_t *bdaddr) return 0; } +#define OCF_MRVL_WRITE_BD_ADDR 0x0022 +typedef struct { + uint8_t parameter_id; + uint8_t bdaddr_len; + bdaddr_t bdaddr; +} __attribute__ ((packed)) mrvl_write_bd_addr_cp; + +static int mrvl_write_bd_addr(int dd, bdaddr_t *bdaddr) +{ + mrvl_write_bd_addr_cp cp; + + memset(&cp, 0, sizeof(cp)); + cp.parameter_id = 0xFE; + cp.bdaddr_len = 6; + bacpy(&cp.bdaddr, bdaddr); + + if (hci_send_cmd(dd, OGF_VENDOR_CMD, OCF_MRVL_WRITE_BD_ADDR, + sizeof(cp), &cp) < 0) + return -1; + + sleep(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); @@ -295,6 +315,7 @@ static struct { { 18, zeevo_write_bd_addr, NULL }, { 48, st_write_bd_addr, generic_reset_device }, { 57, ericsson_write_bd_addr, generic_reset_device }, + { 72, mrvl_write_bd_addr, generic_reset_device }, { 65535, NULL, NULL }, }; diff --git a/tools/bluemoon.c b/tools/bluemoon.c new file mode 100644 index 0000000..67d6d25 --- /dev/null +++ b/tools/bluemoon.c @@ -0,0 +1,541 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. 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 +#endif + +#include +#include +#include +#include +#include + +#include "monitor/mainloop.h" +#include "monitor/bt.h" +#include "src/shared/util.h" +#include "src/shared/hci.h" + +#define CMD_READ_VERSION 0xfc05 +struct rsp_read_version { + uint8_t status; + uint8_t hw_platform; + uint8_t hw_variant; + uint8_t hw_revision; + uint8_t fw_variant; + uint8_t fw_revision; + uint8_t fw_build_nn; + uint8_t fw_build_cw; + uint8_t fw_build_yy; + uint8_t fw_patch; +} __attribute__ ((packed)); + +#define CMD_MANUFACTURER_MODE 0xfc11 +struct cmd_manufacturer_mode { + uint8_t mode_switch; + uint8_t reset; +} __attribute__ ((packed)); + +#define CMD_WRITE_BD_DATA 0xfc2f +struct cmd_write_bd_data { + uint8_t bdaddr[6]; + uint8_t reserved1[6]; + uint8_t features[8]; + uint8_t le_features; + uint8_t reserved2[32]; + uint8_t lmp_version; + uint8_t reserved3[26]; +} __attribute__ ((packed)); + +#define CMD_READ_BD_DATA 0xfc30 +struct rsp_read_bd_data { + uint8_t status; + uint8_t bdaddr[6]; + uint8_t reserved1[6]; + uint8_t features[8]; + uint8_t le_features; + uint8_t reserved2[32]; + uint8_t lmp_version; + uint8_t reserved3[26]; +} __attribute__ ((packed)); + +#define CMD_WRITE_BD_ADDRESS 0xfc31 +struct cmd_write_bd_address { + uint8_t bdaddr[6]; +} __attribute__ ((packed)); + +#define CMD_ACT_DEACT_TRACES 0xfc43 +struct cmd_act_deact_traces { + uint8_t tx_trace; + uint8_t tx_arq; + uint8_t rx_trace; +} __attribute__ ((packed)); + +static struct bt_hci *hci_dev; +static uint16_t hci_index = 0; + +static bool set_bdaddr = false; +static const char *set_bdaddr_value = NULL; + +static bool reset_on_exit = false; +static bool use_manufacturer_mode = false; +static bool get_bddata = false; +static bool set_traces = false; + +static void reset_complete(const void *data, uint8_t size, void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + fprintf(stderr, "Failed to reset (0x%02x)\n", status); + mainloop_quit(); + return; + } + + mainloop_quit(); +} + +static void leave_manufacturer_mode_complete(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + fprintf(stderr, "Failed to leave manufacturer mode (0x%02x)\n", + status); + mainloop_quit(); + return; + } + + if (reset_on_exit) { + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, + reset_complete, NULL, NULL); + return; + } + + mainloop_quit(); +} + +static void shutdown_device(void) +{ + bt_hci_flush(hci_dev); + + if (use_manufacturer_mode) { + struct cmd_manufacturer_mode cmd; + + cmd.mode_switch = 0x00; + cmd.reset = 0x00; + + bt_hci_send(hci_dev, CMD_MANUFACTURER_MODE, &cmd, sizeof(cmd), + leave_manufacturer_mode_complete, NULL, NULL); + return; + } + + if (reset_on_exit) { + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, + reset_complete, NULL, NULL); + return; + } + + mainloop_quit(); +} + +static void write_bd_address_complete(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + fprintf(stderr, "Failed to write address (0x%02x)\n", status); + mainloop_quit(); + return; + } + + shutdown_device(); +} + +static void read_bd_addr_complete(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_rsp_read_bd_addr *rsp = data; + struct cmd_write_bd_address cmd; + + if (rsp->status) { + fprintf(stderr, "Failed to read address (0x%02x)\n", + rsp->status); + mainloop_quit(); + shutdown_device(); + return; + } + + if (set_bdaddr_value) { + fprintf(stderr, "Setting address is not supported\n"); + mainloop_quit(); + return; + } + + printf("Controller Address\n"); + printf("\tOld BD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", + rsp->bdaddr[5], rsp->bdaddr[4], + rsp->bdaddr[3], rsp->bdaddr[2], + rsp->bdaddr[1], rsp->bdaddr[0]); + + memcpy(cmd.bdaddr, rsp->bdaddr, 6); + cmd.bdaddr[0] = (hci_index & 0xff); + + printf("\tNew BD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", + cmd.bdaddr[5], cmd.bdaddr[4], + cmd.bdaddr[3], cmd.bdaddr[2], + cmd.bdaddr[1], cmd.bdaddr[0]); + + bt_hci_send(hci_dev, CMD_WRITE_BD_ADDRESS, &cmd, sizeof(cmd), + write_bd_address_complete, NULL, NULL); +} + +static void act_deact_traces_complete(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + fprintf(stderr, "Failed to activate traces (0x%02x)\n", status); + shutdown_device(); + return; + } + + shutdown_device(); +} + +static void act_deact_traces(void) +{ + struct cmd_act_deact_traces cmd; + + cmd.tx_trace = 0x03; + cmd.tx_arq = 0x03; + cmd.rx_trace = 0x03; + + bt_hci_send(hci_dev, CMD_ACT_DEACT_TRACES, &cmd, sizeof(cmd), + act_deact_traces_complete, NULL, NULL); +} + +static void write_bd_data_complete(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + fprintf(stderr, "Failed to write data (0x%02x)\n", status); + shutdown_device(); + return; + } + + if (set_traces) { + act_deact_traces(); + return; + } + + shutdown_device(); +} + +static void read_bd_data_complete(const void *data, uint8_t size, + void *user_data) +{ + const struct rsp_read_bd_data *rsp = data; + + if (rsp->status) { + fprintf(stderr, "Failed to read data (0x%02x)\n", rsp->status); + shutdown_device(); + return; + } + + printf("Controller Data\n"); + printf("\tBD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", + rsp->bdaddr[5], rsp->bdaddr[4], + rsp->bdaddr[3], rsp->bdaddr[2], + rsp->bdaddr[1], rsp->bdaddr[0]); + + printf("\tLMP Version: %u\n", rsp->lmp_version); + printf("\tLMP Features: 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", + rsp->features[0], rsp->features[1], + rsp->features[2], rsp->features[3], + rsp->features[4], rsp->features[5], + rsp->features[6], rsp->features[7]); + printf("\tLE Features: 0x%2.2x\n", rsp->le_features); + + if (set_bdaddr) { + struct cmd_write_bd_data cmd; + + memcpy(cmd.bdaddr, rsp->bdaddr, 6); + cmd.bdaddr[0] = (hci_index & 0xff); + cmd.lmp_version = 0x07; + memcpy(cmd.features, rsp->features, 8); + cmd.le_features = rsp->le_features; + cmd.le_features |= 0x1e; + memcpy(cmd.reserved1, rsp->reserved1, sizeof(cmd.reserved1)); + memcpy(cmd.reserved2, rsp->reserved2, sizeof(cmd.reserved2)); + memcpy(cmd.reserved3, rsp->reserved3, sizeof(cmd.reserved3)); + + bt_hci_send(hci_dev, CMD_WRITE_BD_DATA, &cmd, sizeof(cmd), + write_bd_data_complete, NULL, NULL); + return; + } + + shutdown_device(); +} + +static void enter_manufacturer_mode_complete(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + fprintf(stderr, "Failed to enter manufacturer mode (0x%02x)\n", + status); + mainloop_quit(); + return; + } + + if (get_bddata || set_bdaddr) { + bt_hci_send(hci_dev, CMD_READ_BD_DATA, NULL, 0, + read_bd_data_complete, NULL, NULL); + return; + } + + if (set_traces) { + act_deact_traces(); + return; + } + + shutdown_device(); +} + +static void read_version_complete(const void *data, uint8_t size, + void *user_data) +{ + const struct rsp_read_version *rsp = data; + const char *str; + + if (rsp->status) { + fprintf(stderr, "Failed to read version (0x%02x)\n", + rsp->status); + mainloop_quit(); + return; + } + + if (use_manufacturer_mode) { + struct cmd_manufacturer_mode cmd; + + cmd.mode_switch = 0x01; + cmd.reset = 0x00; + + bt_hci_send(hci_dev, CMD_MANUFACTURER_MODE, &cmd, sizeof(cmd), + enter_manufacturer_mode_complete, NULL, NULL); + return; + } + + if (set_bdaddr) { + bt_hci_send(hci_dev, BT_HCI_CMD_READ_BD_ADDR, NULL, 0, + read_bd_addr_complete, NULL, NULL); + return; + } + + printf("Controller Version Information\n"); + printf("\tHardware Platform:\t%u\n", rsp->hw_platform); + + switch (rsp->hw_variant) { + case 0x07: + str = "iBT 2.0"; + break; + default: + str = "Reserved"; + break; + } + + printf("\tHardware Variant:\t%s (0x%02x)\n", str, rsp->hw_variant); + printf("\tHardware Revision:\t%u.%u\n", rsp->hw_revision >> 4, + rsp->hw_revision & 0x0f); + + switch (rsp->fw_variant) { + case 0x01: + str = "BT IP 4.0"; + break; + case 0x06: + str = "iBT Bootloader"; + break; + default: + str = "Reserved"; + break; + } + + printf("\tFirmware Variant:\t%s (0x%02x)\n", str, rsp->fw_variant); + printf("\tFirmware Revision:\t%u.%u\n", rsp->fw_revision >> 4, + rsp->fw_revision & 0x0f); + printf("\tFirmware Build Number:\t%u-%u.%u\n", rsp->fw_build_nn, + rsp->fw_build_cw, 2000 + rsp->fw_build_yy); + printf("\tFirmware Patch Number:\t%u\n", rsp->fw_patch); + + mainloop_quit(); +} + +static void read_local_version_complete(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_rsp_read_local_version *rsp = data; + uint16_t manufacturer; + + if (rsp->status) { + fprintf(stderr, "Failed to read local version (0x%02x)\n", + rsp->status); + mainloop_quit(); + return; + } + + manufacturer = le16_to_cpu(rsp->manufacturer); + + if (manufacturer != 2) { + fprintf(stderr, "Unsupported manufacturer (%u)\n", + manufacturer); + mainloop_quit(); + return; + } + + bt_hci_send(hci_dev, CMD_READ_VERSION, NULL, 0, + read_version_complete, NULL, NULL); +} + +static void signal_callback(int signum, void *user_data) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + mainloop_quit(); + break; + } +} + +static void usage(void) +{ + printf("bluemoon - Bluemoon configuration utility\n" + "Usage:\n"); + printf("\tbluemoon [options]\n"); + printf("Options:\n" + "\t-B, --bdaddr [addr] Set Bluetooth address\n" + "\t-R, --reset Reset controller\n" + "\t-i, --index Use specified controller\n" + "\t-h, --help Show help options\n"); +} + +static const struct option main_options[] = { + { "bdaddr", optional_argument, NULL, 'A' }, + { "bddata", no_argument, NULL, 'D' }, + { "traces", no_argument, NULL, 'T' }, + { "reset", no_argument, NULL, 'R' }, + { "index", required_argument, NULL, 'i' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } +}; + +int main(int argc, char *argv[]) +{ + const char *str; + sigset_t mask; + int exit_status; + + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "A::DTRi:vh", main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'A': + if (optarg) + set_bdaddr_value = optarg; + set_bdaddr = true; + break; + case 'D': + use_manufacturer_mode = true; + get_bddata = true; + break; + case 'T': + use_manufacturer_mode = true; + set_traces = true; + break; + case 'R': + reset_on_exit = true; + break; + case 'i': + if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3)) + str = optarg + 3; + else + str = optarg; + if (!isdigit(*str)) { + usage(); + return EXIT_FAILURE; + } + hci_index = atoi(str); + break; + case 'v': + printf("%s\n", VERSION); + return EXIT_SUCCESS; + case 'h': + usage(); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } + } + + if (argc - optind > 0) { + fprintf(stderr, "Invalid command line parameters\n"); + return EXIT_FAILURE; + } + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + printf("Bluemoon configuration utility ver %s\n", VERSION); + + hci_dev = bt_hci_new_user_channel(hci_index); + if (!hci_dev) { + fprintf(stderr, "Failed to open HCI user channel\n"); + return EXIT_FAILURE; + } + + bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0, + read_local_version_complete, NULL, NULL); + + exit_status = mainloop_run(); + + bt_hci_unref(hci_dev); + + return exit_status; +} diff --git a/tools/bluetooth-player.c b/tools/bluetooth-player.c index 622d391..f10d9be 100644 --- a/tools/bluetooth-player.c +++ b/tools/bluetooth-player.c @@ -738,6 +738,11 @@ static void cmd_change_folder(int argc, char *argv[]) return; } + if (dbus_validate_path(argv[1], NULL) == FALSE) { + rl_printf("Not a valid path\n"); + return; + } + if (check_default_player() == FALSE) return; @@ -1080,6 +1085,7 @@ static void rl_handler(char *input) if (!strlen(input)) goto done; + g_strstrip(input); add_history(input); argv = g_strsplit(input, " ", -1); diff --git a/tools/btinfo.c b/tools/btinfo.c index ed434e6..b838c25 100644 --- a/tools/btinfo.c +++ b/tools/btinfo.c @@ -26,189 +26,150 @@ #include #endif +#include +#include #include #include -#include +#include #include -#include -#include - -#include -#include +#include +#include +#include "monitor/mainloop.h" #include "monitor/bt.h" +#include "src/shared/timeout.h" +#include "src/shared/util.h" +#include "src/shared/hci.h" + +#define BTPROTO_HCI 1 + +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; +}; -#define le16_to_cpu(val) (val) -#define le32_to_cpu(val) (val) -#define cpu_to_le16(val) (val) -#define cpu_to_le32(val) (val) - -struct bt_h4_pkt { - uint8_t type; - union { - struct { - uint16_t opcode; - uint8_t plen; - union { - uint8_t data; - }; - } cmd; - struct { - uint8_t event; - uint8_t plen; - union { - uint8_t data; - struct bt_hci_evt_cmd_complete cmd_complete; - struct bt_hci_evt_cmd_status cmd_status; - }; - } evt; - }; -} __attribute__ ((packed)); - -static bool hci_request(int fd, uint16_t opcode, - const void *cmd_data, uint8_t cmd_len, - void *rsp_data, uint8_t rsp_size, uint8_t *rsp_len) -{ - struct bt_h4_pkt *cmd = alloca(4 + cmd_len); - struct bt_h4_pkt *rsp = alloca(2048); - ssize_t len; - - cmd->type = BT_H4_CMD_PKT; - cmd->cmd.opcode = cpu_to_le16(opcode); - cmd->cmd.plen = cpu_to_le16(cmd_len); - if (cmd_len > 0) - memcpy(&cmd->cmd.data, cmd_data, cmd_len); - - if (write(fd, cmd, 4 + cmd_len) < 0) { - perror("Failed to write command"); - return false; - } - - len = read(fd, rsp, 2048); - if (len < 0) { - perror("Failed to read event"); - return false; - } - - if (rsp->type != BT_H4_EVT_PKT) { - fprintf(stderr, "Unexpected packet type %d\n", rsp->type); - return false; - } - - if (rsp->evt.event == BT_HCI_EVT_CMD_COMPLETE) { - if (opcode != le16_to_cpu(rsp->evt.cmd_complete.opcode)) - return false; +struct hci_dev_info { + uint16_t dev_id; + char name[8]; + uint8_t bdaddr[6]; + 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; +}; - if (rsp_data) - memcpy(rsp_data, (&rsp->evt.data) + 3, rsp->evt.plen - 3); +#define HCIDEVUP _IOW('H', 201, int) +#define HCIDEVDOWN _IOW('H', 202, int) +#define HCIGETDEVINFO _IOR('H', 211, int) - if (rsp_len) - *rsp_len = rsp->evt.plen - 3; +#define HCI_UP (1 << 0) - return true; - } else if (rsp->evt.event == BT_HCI_EVT_CMD_STATUS) { - if (opcode == le16_to_cpu(rsp->evt.cmd_status.opcode)) - return false; +#define HCI_BREDR 0x00 +#define HCI_AMP 0x01 - if (rsp->evt.cmd_status.status != BT_HCI_ERR_SUCCESS) - return false; +static struct hci_dev_info hci_info; +static uint8_t hci_type; +static struct bt_hci *hci_dev; - if (rsp_len) - *rsp_len = 0; +static bool reset_on_init = false; +static bool reset_on_shutdown = false; - return true; - } +static bool shutdown_timeout(void *user_data) +{ + mainloop_quit(); return false; } -static int cmd_local(int fd, int argc, char *argv[]) +static void shutdown_complete(const void *data, uint8_t size, void *user_data) { - struct bt_hci_rsp_read_local_features lf; - struct bt_hci_rsp_read_local_version lv; - struct bt_hci_rsp_read_local_commands lc; - struct bt_hci_cmd_read_local_ext_features lef_cmd; - struct bt_hci_rsp_read_local_ext_features lef; - uint8_t len; - - if (!hci_request(fd, BT_HCI_CMD_RESET, NULL, 0, NULL, 0, &len)) - return EXIT_FAILURE; - - if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0, - &lf, sizeof(lf), &len)) - return EXIT_FAILURE; - - if (lf.status != BT_HCI_ERR_SUCCESS) - return EXIT_FAILURE; - - printf("Features: 0x%02x 0x%02x 0x%02x 0x%02x " - "0x%02x 0x%02x 0x%02x 0x%02x\n", - lf.features[0], lf.features[1], - lf.features[2], lf.features[3], - lf.features[4], lf.features[5], - lf.features[6], lf.features[7]); - - if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0, - &lv, sizeof(lv), &len)) - return EXIT_FAILURE; - - if (lv.status != BT_HCI_ERR_SUCCESS) - return EXIT_FAILURE; + unsigned int id = PTR_TO_UINT(user_data); - printf("Version: %d\n", lv.hci_ver); - printf("Manufacturer: %d\n", le16_to_cpu(lv.manufacturer)); - - if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_COMMANDS, NULL, 0, - &lc, sizeof(lc), &len)) - return EXIT_FAILURE; + timeout_remove(id); + mainloop_quit(); +} - if (lc.status != BT_HCI_ERR_SUCCESS) - return EXIT_FAILURE; +static void shutdown_device(void) +{ + unsigned int id; - if (!(lf.features[7] & 0x80)) - return EXIT_SUCCESS; + bt_hci_flush(hci_dev); - lef_cmd.page = 0x01; + if (reset_on_shutdown) { + id = timeout_add(5000, shutdown_timeout, NULL, NULL); - if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES, - &lef_cmd, sizeof(lef_cmd), - &lef, sizeof(lef), &len)) - return EXIT_FAILURE; + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, + shutdown_complete, UINT_TO_PTR(id), NULL); + } else + mainloop_quit(); +} - if (lef.status != BT_HCI_ERR_SUCCESS) - return EXIT_FAILURE; +static void local_version_callback(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_rsp_read_local_version *rsp = data; + + printf("HCI version: %u\n", rsp->hci_ver); + printf("HCI revision: %u\n", le16_to_cpu(rsp->hci_rev)); + + switch (hci_type) { + case HCI_BREDR: + printf("LMP version: %u\n", rsp->lmp_ver); + printf("LMP subversion: %u\n", le16_to_cpu(rsp->lmp_subver)); + break; + case HCI_AMP: + printf("PAL version: %u\n", rsp->lmp_ver); + printf("PAL subversion: %u\n", le16_to_cpu(rsp->lmp_subver)); + break; + } - printf("Host features: 0x%02x 0x%02x 0x%02x 0x%02x " - "0x%02x 0x%02x 0x%02x 0x%02x\n", - lef.features[0], lef.features[1], - lef.features[2], lef.features[3], - lef.features[4], lef.features[5], - lef.features[6], lef.features[7]); + printf("Manufacturer: %u\n", le16_to_cpu(rsp->manufacturer)); +} - if (lef.max_page < 0x02) - return EXIT_SUCCESS; +static void local_commands_callback(const void *data, uint8_t size, + void *user_data) +{ + shutdown_device(); +} - lef_cmd.page = 0x02; +static void local_features_callback(const void *data, uint8_t size, + void *user_data) +{ + bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_COMMANDS, NULL, 0, + local_commands_callback, NULL, NULL); +} - if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES, - &lef_cmd, sizeof(lef_cmd), - &lef, sizeof(lef), &len)) - return EXIT_FAILURE; +static bool cmd_local(int argc, char *argv[]) +{ + if (reset_on_init) + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, + NULL, NULL, NULL); - if (lef.status != BT_HCI_ERR_SUCCESS) - return EXIT_FAILURE; + bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0, + local_version_callback, NULL, NULL); - printf("Extended features: 0x%02x 0x%02x 0x%02x 0x%02x " - "0x%02x 0x%02x 0x%02x 0x%02x\n", - lef.features[0], lef.features[1], - lef.features[2], lef.features[3], - lef.features[4], lef.features[5], - lef.features[6], lef.features[7]); + bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0, + local_features_callback, NULL, NULL); - return EXIT_SUCCESS; + return true; } -typedef int (*cmd_func_t)(int fd, int argc, char *argv[]); +typedef bool (*cmd_func_t)(int argc, char *argv[]); static const struct { const char *name; @@ -219,6 +180,21 @@ static const struct { { } }; +static void signal_callback(int signum, void *user_data) +{ + static bool terminated = false; + + switch (signum) { + case SIGINT: + case SIGTERM: + if (!terminated) { + shutdown_device(); + terminated = true; + } + break; + } +} + static void usage(void) { int i; @@ -227,15 +203,17 @@ static void usage(void) "Usage:\n"); printf("\tbtinfo [options] \n"); printf("options:\n" - "\t-i, --device Use local HCI device\n" - "\t-h, --help Show help options\n"); + "\t-i, --index Use specified controller\n" + "\t-h, --help Show help options\n"); printf("commands:\n"); for (i = 0; cmd_table[i].name; i++) printf("\t%-25s%s\n", cmd_table[i].name, cmd_table[i].help); } static const struct option main_options[] = { - { "device", required_argument, NULL, 'i' }, + { "index", required_argument, NULL, 'i' }, + { "reset", no_argument, NULL, 'r' }, + { "raw", no_argument, NULL, 'R' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } @@ -243,22 +221,38 @@ static const struct option main_options[] = { int main(int argc, char *argv[]) { - const char *device = NULL; cmd_func_t func = NULL; - struct sockaddr_hci addr; - int result, fd, i; + uint16_t index = 0; + const char *str; + bool use_raw = false; + bool power_down = false; + sigset_t mask; + int fd, i, exit_status; for (;;) { int opt; - opt = getopt_long(argc, argv, "i:vh", - main_options, NULL); + opt = getopt_long(argc, argv, "i:rRvh", main_options, NULL); if (opt < 0) break; switch (opt) { case 'i': - device = optarg; + if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3)) + str = optarg + 3; + else + str = optarg; + if (!isdigit(*str)) { + usage(); + return EXIT_FAILURE; + } + index = atoi(str); + break; + case 'r': + reset_on_init = true; + break; + case 'R': + use_raw = true; break; case 'v': printf("%s\n", VERSION); @@ -288,30 +282,84 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + printf("Bluetooth information utility ver %s\n", VERSION); + fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); if (fd < 0) { - perror("Failed to open channel"); + perror("Failed to open HCI raw socket"); return EXIT_FAILURE; } - memset(&addr, 0, sizeof(addr)); - addr.hci_family = AF_BLUETOOTH; - addr.hci_channel = HCI_CHANNEL_USER; + memset(&hci_info, 0, sizeof(hci_info)); + hci_info.dev_id = index; - if (device) - addr.hci_dev = atoi(device); - else - addr.hci_dev = 0; - - if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("Failed to bind channel"); + if (ioctl(fd, HCIGETDEVINFO, (void *) &hci_info) < 0) { + perror("Failed to get HCI device information"); close(fd); return EXIT_FAILURE; } - result = func(fd, argc - optind - 1, argv + optind + 1); + if (use_raw && !(hci_info.flags & HCI_UP)) { + printf("Powering on controller\n"); + + if (ioctl(fd, HCIDEVUP, hci_info.dev_id) < 0) { + perror("Failed to power on controller"); + close(fd); + return EXIT_FAILURE; + } + + power_down = true; + } close(fd); - return result; + hci_type = (hci_info.type & 0x30) >> 4; + + if (use_raw) { + hci_dev = bt_hci_new_raw_device(index); + if (!hci_dev) { + fprintf(stderr, "Failed to open HCI raw device\n"); + return EXIT_FAILURE; + } + } else { + hci_dev = bt_hci_new_user_channel(index); + if (!hci_dev) { + fprintf(stderr, "Failed to open HCI user channel\n"); + return EXIT_FAILURE; + } + + reset_on_init = true; + reset_on_shutdown = true; + } + + if (!func(argc - optind - 1, argv + optind + 1)) { + bt_hci_unref(hci_dev); + return EXIT_FAILURE; + } + + exit_status = mainloop_run(); + + bt_hci_unref(hci_dev); + + if (use_raw && power_down) { + fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); + if (fd >= 0) { + printf("Powering down controller\n"); + + if (ioctl(fd, HCIDEVDOWN, hci_info.dev_id) < 0) + perror("Failed to power down controller"); + + close(fd); + } + } + + return exit_status; } diff --git a/tools/btiotest.c b/tools/btiotest.c index a77eba1..6a87ffd 100644 --- a/tools/btiotest.c +++ b/tools/btiotest.c @@ -35,7 +35,9 @@ #include -#include +#include + +#include "btio/btio.h" #define DEFAULT_ACCEPT_TIMEOUT 2 static int opt_update_sec = 0; @@ -274,16 +276,23 @@ static void l2cap_connect(const char *src, const char *dst, uint8_t addr_type, { struct io_data *data; GError *err = NULL; + uint8_t src_type; printf("Connecting to %s L2CAP PSM %u\n", dst, psm); data = io_data_new(NULL, -1, disconn, -1); + if (addr_type != BDADDR_BREDR) + src_type = BDADDR_LE_PUBLIC; + else + src_type = BDADDR_BREDR; + if (src) data->io = bt_io_connect(connect_cb, data, (GDestroyNotify) io_data_unref, &err, BT_IO_OPT_SOURCE, src, + BT_IO_OPT_SOURCE_TYPE, src_type, BT_IO_OPT_DEST, dst, BT_IO_OPT_DEST_TYPE, addr_type, BT_IO_OPT_PSM, psm, @@ -295,6 +304,7 @@ static void l2cap_connect(const char *src, const char *dst, uint8_t addr_type, data->io = bt_io_connect(connect_cb, data, (GDestroyNotify) io_data_unref, &err, + BT_IO_OPT_SOURCE_TYPE, src_type, BT_IO_OPT_DEST, dst, BT_IO_OPT_DEST_TYPE, addr_type, BT_IO_OPT_PSM, psm, diff --git a/tools/btmgmt.c b/tools/btmgmt.c index 1d71d74..0618de1 100644 --- a/tools/btmgmt.c +++ b/tools/btmgmt.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include #include @@ -40,14 +42,12 @@ #include #include -#include - -#include "src/shared/mgmt.h" -#include "glib-helper.h" +#include "src/uuid-helper.h" #include "lib/mgmt.h" -#include "eir.h" -static GMainLoop *event_loop = NULL; +#include "monitor/mainloop.h" +#include "src/shared/util.h" +#include "src/shared/mgmt.h" static bool monitor = false; static bool discovery = false; @@ -96,6 +96,9 @@ static const char *settings_str[] = { "hs", "le", "advertising", + "secure-conn", + "debug-keys", + "privacy", }; static void print_settings(uint32_t settings) @@ -120,7 +123,7 @@ static void new_settings(uint16_t index, uint16_t len, if (monitor) { printf("hci%u new_settings: ", index); - print_settings(bt_get_le32(ev)); + print_settings(get_le32(ev)); printf("\n"); } } @@ -137,7 +140,7 @@ static void discovering(uint16_t index, uint16_t len, const void *param, } if (ev->discovering == 0 && discovery) { - g_main_loop_quit(event_loop); + mainloop_quit(); return; } @@ -188,7 +191,7 @@ static void connected(uint16_t index, uint16_t len, const void *param, return; } - eir_len = bt_get_le16(&ev->eir_len); + eir_len = get_le16(&ev->eir_len); if (len != sizeof(*ev) + eir_len) { fprintf(stderr, "Invalid connected event length " "(%u bytes, eir_len %u bytes)\n", len, eir_len); @@ -311,14 +314,69 @@ static void confirm_name_rsp(uint8_t status, uint16_t len, printf("confirm_name succeeded for %s\n", addr); } +static char *eir_get_name(const uint8_t *eir, uint16_t eir_len) +{ + uint8_t parsed = 0; + + if (eir_len < 2) + return NULL; + + while (parsed < eir_len - 1) { + uint8_t field_len = eir[0]; + + if (field_len == 0) + break; + + parsed += field_len + 1; + + if (parsed > eir_len) + break; + + /* Check for short of complete name */ + if (eir[1] == 0x09 || eir[1] == 0x08) + return strndup((char *) &eir[2], field_len - 1); + + eir += field_len + 1; + } + + return NULL; +} + +static unsigned int eir_get_flags(const uint8_t *eir, uint16_t eir_len) +{ + uint8_t parsed = 0; + + if (eir_len < 2) + return 0; + + while (parsed < eir_len - 1) { + uint8_t field_len = eir[0]; + + if (field_len == 0) + break; + + parsed += field_len + 1; + + if (parsed > eir_len) + break; + + /* Check for flags */ + if (eir[1] == 0x01) + return eir[2]; + + eir += field_len + 1; + } + + return 0; +} + static void device_found(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_device_found *ev = param; struct mgmt *mgmt = user_data; - uint32_t flags; uint16_t eir_len; - struct eir_data eir; + uint32_t flags; if (len < sizeof(*ev)) { fprintf(stderr, @@ -328,30 +386,33 @@ static void device_found(uint16_t index, uint16_t len, const void *param, flags = btohl(ev->flags); - eir_len = bt_get_le16(&ev->eir_len); + eir_len = get_le16(&ev->eir_len); if (len != sizeof(*ev) + eir_len) { fprintf(stderr, "dev_found: expected %zu bytes, got %u bytes\n", sizeof(*ev) + eir_len, len); return; } - memset(&eir, 0, sizeof(eir)); - eir_parse(&eir, ev->eir, eir_len); - if (monitor || discovery) { - char addr[18]; + char addr[18], *name; + ba2str(&ev->addr.bdaddr, addr); printf("hci%u dev_found: %s type %s rssi %d " "flags 0x%04x ", index, addr, typestr(ev->addr.type), ev->rssi, flags); - if (eir.name) - printf("name %s\n", eir.name); + if (ev->addr.type != BDADDR_BREDR) + printf("AD flags 0x%02x ", + eir_get_flags(ev->eir, eir_len)); + + name = eir_get_name(ev->eir, eir_len); + if (name) + printf("name %s\n", name); else printf("eir_len %u\n", eir_len); - } - eir_data_free(&eir); + free(name); + } if (discovery && (flags & MGMT_DEV_FOUND_CONFIRM_NAME)) { struct mgmt_cp_confirm_name cp; @@ -375,7 +436,7 @@ static void pin_rsp(uint8_t status, uint16_t len, const void *param, fprintf(stderr, "PIN Code reply failed with status 0x%02x (%s)\n", status, mgmt_errstr(status)); - g_main_loop_quit(event_loop); + mainloop_quit(); return; } @@ -404,7 +465,7 @@ static void pin_neg_rsp(uint8_t status, uint16_t len, const void *param, fprintf(stderr, "PIN Neg reply failed with status 0x%02x (%s)\n", status, mgmt_errstr(status)); - g_main_loop_quit(event_loop); + mainloop_quit(); return; } @@ -469,7 +530,7 @@ static void confirm_rsp(uint8_t status, uint16_t len, const void *param, fprintf(stderr, "User Confirm reply failed. status 0x%02x (%s)\n", status, mgmt_errstr(status)); - g_main_loop_quit(event_loop); + mainloop_quit(); return; } @@ -495,7 +556,7 @@ static void confirm_neg_rsp(uint8_t status, uint16_t len, const void *param, fprintf(stderr, "Confirm Neg reply failed. status 0x%02x (%s)\n", status, mgmt_errstr(status)); - g_main_loop_quit(event_loop); + mainloop_quit(); return; } @@ -532,7 +593,7 @@ static void user_confirm(uint16_t index, uint16_t len, const void *param, } ba2str(&ev->addr.bdaddr, addr); - val = bt_get_le32(&ev->value); + val = get_le32(&ev->value); if (monitor) printf("hci%u %s User Confirm %06u hint %u\n", index, addr, @@ -553,10 +614,8 @@ static void user_confirm(uint16_t index, uint16_t len, const void *param, } rsp_len = strlen(rsp); - if (rsp[rsp_len - 1] == '\n') { + if (rsp[rsp_len - 1] == '\n') rsp[rsp_len - 1] = '\0'; - rsp_len--; - } if (rsp[0] == 'y' || rsp[0] == 'Y') mgmt_confirm_reply(mgmt, index, &ev->addr.bdaddr); @@ -588,10 +647,10 @@ static void version_rsp(uint8_t status, uint16_t len, const void *param, } printf("MGMT Version %u, revision %u\n", rp->version, - bt_get_le16(&rp->revision)); + get_le16(&rp->revision)); done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cmd_version(struct mgmt *mgmt, uint16_t index, int argc, @@ -624,8 +683,8 @@ static void commands_rsp(uint8_t status, uint16_t len, const void *param, goto done; } - num_commands = bt_get_le16(&rp->num_commands); - num_events = bt_get_le16(&rp->num_events); + num_commands = get_le16(&rp->num_commands); + num_events = get_le16(&rp->num_events); expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) + num_events * sizeof(uint16_t); @@ -640,18 +699,18 @@ static void commands_rsp(uint8_t status, uint16_t len, const void *param, printf("%u commands:\n", num_commands); for (i = 0; i < num_commands; i++) { - uint16_t op = bt_get_le16(opcode++); + uint16_t op = get_le16(opcode++); printf("\t%s (0x%04x)\n", mgmt_opstr(op), op); } printf("%u events:\n", num_events); for (i = 0; i < num_events; i++) { - uint16_t ev = bt_get_le16(opcode++); + uint16_t ev = get_le16(opcode++); printf("\t%s (0x%04x)\n", mgmt_evstr(ev), ev); } done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc, @@ -668,7 +727,7 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_info *rp = param; - int id = GPOINTER_TO_INT(user_data); + uint16_t index = PTR_TO_UINT(user_data); char addr[18]; pending--; @@ -676,7 +735,7 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param, if (status != 0) { fprintf(stderr, "Reading hci%u info failed with status 0x%02x (%s)\n", - id, status, mgmt_errstr(status)); + index, status, mgmt_errstr(status)); goto done; } @@ -687,15 +746,15 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param, ba2str(&rp->bdaddr, addr); printf("hci%u:\taddr %s version %u manufacturer %u" - " class 0x%02x%02x%02x\n", - id, addr, rp->version, bt_get_le16(&rp->manufacturer), + " class 0x%02x%02x%02x\n", index, + addr, rp->version, get_le16(&rp->manufacturer), rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); printf("\tsupported settings: "); - print_settings(bt_get_le32(&rp->supported_settings)); + print_settings(get_le32(&rp->supported_settings)); printf("\n\tcurrent settings: "); - print_settings(bt_get_le32(&rp->current_settings)); + print_settings(get_le32(&rp->current_settings)); printf("\n\tname %s\n", rp->name); printf("\tshort name %s\n", rp->short_name); @@ -704,7 +763,7 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param, return; done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void index_rsp(uint8_t status, uint16_t len, const void *param, @@ -728,7 +787,7 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param, goto done; } - count = bt_get_le16(&rp->num_controllers); + count = get_le16(&rp->num_controllers); if (len < sizeof(*rp) + count * sizeof(uint16_t)) { fprintf(stderr, @@ -751,12 +810,12 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param, uint16_t index; void *data; - index = bt_get_le16(&rp->index[i]); + index = get_le16(&rp->index[i]); if (monitor) printf("hci%u ", index); - data = GINT_TO_POINTER((int) index); + data = UINT_TO_PTR(index); if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp, data, NULL) == 0) { @@ -773,7 +832,7 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param, return; done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -791,7 +850,7 @@ static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv) return; } - data = GINT_TO_POINTER((int) index); + data = UINT_TO_PTR(index); if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp, data, NULL) == 0) { @@ -825,14 +884,17 @@ static unsigned int send_cmd(struct mgmt *mgmt, uint16_t op, uint16_t id, struct command_data *data; unsigned int send_id; - data = g_new0(struct command_data, 1); + data = new0(struct command_data, 1); + if (!data) + return 0; + data->id = id; data->op = op; data->callback = cb; - send_id = mgmt_send(mgmt, op, id, len, param, cmd_rsp, data, g_free); + send_id = mgmt_send(mgmt, op, id, len, param, cmd_rsp, data, free); if (send_id == 0) - g_free(data); + free(data); return send_id; } @@ -856,11 +918,11 @@ static void setting_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, } printf("hci%u %s complete, settings: ", id, mgmt_opstr(op)); - print_settings(bt_get_le32(rp)); + print_settings(get_le32(rp)); printf("\n"); done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op, @@ -957,6 +1019,34 @@ static void cmd_ssp(struct mgmt *mgmt, uint16_t index, int argc, char **argv) cmd_setting(mgmt, index, MGMT_OP_SET_SSP, argc, argv); } +static void cmd_sc(struct mgmt *mgmt, uint16_t index, int argc, char **argv) +{ + uint8_t val; + + if (argc < 2) { + printf("Specify \"on\" or \"off\" or \"only\"\n"); + exit(EXIT_FAILURE); + } + + if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) + val = 1; + else if (strcasecmp(argv[1], "off") == 0) + val = 0; + else if (strcasecmp(argv[1], "only") == 0) + val = 2; + else + val = atoi(argv[1]); + + if (index == MGMT_INDEX_NONE) + index = 0; + + if (send_cmd(mgmt, MGMT_OP_SET_SECURE_CONN, index, + sizeof(val), &val, setting_rsp) == 0) { + fprintf(stderr, "Unable to send set_secure_conn cmd\n"); + exit(EXIT_FAILURE); + } +} + static void cmd_hs(struct mgmt *mgmt, uint16_t index, int argc, char **argv) { cmd_setting(mgmt, index, MGMT_OP_SET_HS, argc, argv); @@ -978,6 +1068,48 @@ static void cmd_bredr(struct mgmt *mgmt, uint16_t index, int argc, char **argv) cmd_setting(mgmt, index, MGMT_OP_SET_BREDR, argc, argv); } +static void cmd_privacy(struct mgmt *mgmt, uint16_t index, int argc, + char **argv) +{ + struct mgmt_cp_set_privacy cp; + int fd; + + if (argc < 2) { + printf("Specify \"on\" or \"off\"\n"); + exit(EXIT_FAILURE); + } + + if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) + cp.privacy = 0x01; + else if (strcasecmp(argv[1], "off") == 0) + cp.privacy = 0x00; + else + cp.privacy = atoi(argv[1]); + + if (index == MGMT_INDEX_NONE) + index = 0; + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "open(/dev/urandom): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + if (read(fd, cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) { + fprintf(stderr, "Reading from urandom failed\n"); + close(fd); + exit(EXIT_FAILURE); + } + + close(fd); + + if (send_cmd(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp, + setting_rsp) == 0) { + fprintf(stderr, "Unable to send Set Privacy command\n"); + exit(EXIT_FAILURE); + } +} + static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, const void *param) { @@ -998,7 +1130,7 @@ static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]); done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cmd_class(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1051,24 +1183,56 @@ static void disconnect_rsp(uint8_t status, uint16_t len, const void *param, addr, status, mgmt_errstr(status)); done: - g_main_loop_quit(event_loop); + mainloop_quit(); +} + +static void disconnect_usage(void) +{ + printf("Usage: btmgmt disconnect [-t type] \n"); } +static struct option disconnect_options[] = { + { "help", 0, 0, 'h' }, + { "type", 1, 0, 't' }, + { 0, 0, 0, 0 } +}; + static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc, char **argv) { struct mgmt_cp_disconnect cp; + uint8_t type = BDADDR_BREDR; + int opt; - if (argc < 2) { - printf("Usage: btmgmt %s
\n", argv[0]); - exit(EXIT_FAILURE); + while ((opt = getopt_long(argc, argv, "+t:h", disconnect_options, + NULL)) != -1) { + switch (opt) { + case 't': + type = strtol(optarg, NULL, 0); + break; + case 'h': + default: + disconnect_usage(); + exit(EXIT_SUCCESS); + } } - str2ba(argv[1], &cp.addr.bdaddr); + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + disconnect_usage(); + exit(EXIT_FAILURE); + } if (index == MGMT_INDEX_NONE) index = 0; + memset(&cp, 0, sizeof(cp)); + str2ba(argv[0], &cp.addr.bdaddr); + cp.addr.type = type; + if (mgmt_send(mgmt, MGMT_OP_DISCONNECT, index, sizeof(cp), &cp, disconnect_rsp, NULL, NULL) == 0) { fprintf(stderr, "Unable to send disconnect cmd\n"); @@ -1088,7 +1252,7 @@ static void con_rsp(uint8_t status, uint16_t len, const void *param, goto done; } - count = bt_get_le16(&rp->conn_count); + count = get_le16(&rp->conn_count); if (len != sizeof(*rp) + count * sizeof(struct mgmt_addr_info)) { fprintf(stderr, "Invalid get_connections length " " (count=%u, len=%u)\n", count, len); @@ -1104,7 +1268,7 @@ static void con_rsp(uint8_t status, uint16_t len, const void *param, } done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cmd_con(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1126,7 +1290,7 @@ static void find_rsp(uint8_t status, uint16_t len, const void *param, fprintf(stderr, "Unable to start discovery. status 0x%02x (%s)\n", status, mgmt_errstr(status)); - g_main_loop_quit(event_loop); + mainloop_quit(); return; } @@ -1202,7 +1366,7 @@ static void name_rsp(uint8_t status, uint16_t len, const void *param, "with status 0x%02x (%s)\n", status, mgmt_errstr(status)); - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cmd_name(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1257,10 +1421,10 @@ static void pair_rsp(uint8_t status, uint16_t len, const void *param, goto done; } - printf("Paired with %s\n", addr); + printf("Paired with %s (%s)\n", addr, typestr(rp->addr.type)); done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void pair_usage(void) @@ -1280,6 +1444,7 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv) struct mgmt_cp_pair_device cp; uint8_t cap = 0x01; uint8_t type = BDADDR_BREDR; + char addr[18]; int opt; while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options, @@ -1315,6 +1480,9 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv) cp.addr.type = type; cp.io_cap = cap; + ba2str(&cp.addr.bdaddr, addr); + printf("Pairing with %s (%s)\n", addr, typestr(cp.addr.type)); + if (mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp, pair_rsp, NULL, NULL) == 0) { fprintf(stderr, "Unable to send pair_device cmd\n"); @@ -1352,7 +1520,7 @@ static void cancel_pair_rsp(uint8_t status, uint16_t len, const void *param, printf("Pairing Cancelled with %s\n", addr); done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cancel_pair_usage(void) @@ -1438,16 +1606,46 @@ static void unpair_rsp(uint8_t status, uint16_t len, const void *param, printf("%s unpaired\n", addr); done: - g_main_loop_quit(event_loop); + mainloop_quit(); } +static void unpair_usage(void) +{ + printf("Usage: btmgmt unpair [-t type] \n"); +} + +static struct option unpair_options[] = { + { "help", 0, 0, 'h' }, + { "type", 1, 0, 't' }, + { 0, 0, 0, 0 } +}; + static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc, char **argv) { struct mgmt_cp_unpair_device cp; + uint8_t type = BDADDR_BREDR; + int opt; - if (argc < 2) { - printf("Usage: btmgmt %s \n", argv[0]); + while ((opt = getopt_long(argc, argv, "+t:h", unpair_options, + NULL)) != -1) { + switch (opt) { + case 't': + type = strtol(optarg, NULL, 0); + break; + case 'h': + default: + unpair_usage(); + exit(EXIT_SUCCESS); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + unpair_usage(); exit(EXIT_FAILURE); } @@ -1455,7 +1653,8 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc, index = 0; memset(&cp, 0, sizeof(cp)); - str2ba(argv[1], &cp.addr.bdaddr); + str2ba(argv[0], &cp.addr.bdaddr); + cp.addr.type = type; cp.disconnect = 1; if (mgmt_send(mgmt, MGMT_OP_UNPAIR_DEVICE, index, sizeof(cp), &cp, @@ -1474,7 +1673,7 @@ static void keys_rsp(uint8_t status, uint16_t len, const void *param, else printf("Keys successfully loaded\n"); - g_main_loop_quit(event_loop); + mainloop_quit(); } static void cmd_keys(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1493,6 +1692,62 @@ static void cmd_keys(struct mgmt *mgmt, uint16_t index, int argc, char **argv) } } +static void ltks_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + if (status != 0) + fprintf(stderr, "Load keys failed with status 0x%02x (%s)\n", + status, mgmt_errstr(status)); + else + printf("Long term keys successfully loaded\n"); + + mainloop_quit(); +} + +static void cmd_ltks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) +{ + struct mgmt_cp_load_long_term_keys cp; + + if (index == MGMT_INDEX_NONE) + index = 0; + + memset(&cp, 0, sizeof(cp)); + + if (mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index, sizeof(cp), &cp, + ltks_rsp, NULL, NULL) == 0) { + fprintf(stderr, "Unable to send load_ltks cmd\n"); + exit(EXIT_FAILURE); + } +} + +static void irks_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + if (status != 0) + fprintf(stderr, "Load IRKs failed with status 0x%02x (%s)\n", + status, mgmt_errstr(status)); + else + printf("Identity Resolving Keys successfully loaded\n"); + + mainloop_quit(); +} + +static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) +{ + struct mgmt_cp_load_irks cp; + + if (index == MGMT_INDEX_NONE) + index = 0; + + memset(&cp, 0, sizeof(cp)); + + if (mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index, sizeof(cp), &cp, + irks_rsp, NULL, NULL) == 0) { + fprintf(stderr, "Unable to send load_irks cmd\n"); + exit(EXIT_FAILURE); + } +} + static void block_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, const void *param) { @@ -1522,7 +1777,7 @@ static void block_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, printf("%s %s succeeded\n", mgmt_opstr(op), addr); done: - g_main_loop_quit(event_loop); + mainloop_quit(); } static void block_usage(void) @@ -1713,6 +1968,66 @@ static void cmd_clr_uuids(struct mgmt *mgmt, uint16_t index, int argc, cmd_remove_uuid(mgmt, index, 2, rm_argv); } +static void local_oob_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_read_local_oob_data *rp = param; + const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param; + int i; + + if (status != 0) { + fprintf(stderr, "Read Local OOB Data failed " + "with status 0x%02x (%s)\n", + status, mgmt_errstr(status)); + goto done; + } + + if (len < sizeof(*rp)) { + fprintf(stderr, "Too small (%u bytes) read_local_oob rsp\n", + len); + goto done; + } + + printf("Hash C from P-192: "); + for (i = 0; i < 16; i++) + printf("%02x", rp->hash[i]); + printf("\n"); + + printf("Randomizer R with P-192: "); + for (i = 0; i < 16; i++) + printf("%02x", rp->randomizer[i]); + printf("\n"); + + if (len < sizeof(*rp_ext)) + goto done; + + printf("Hash C from P-256: "); + for (i = 0; i < 16; i++) + printf("%02x", rp_ext->hash256[i]); + printf("\n"); + + printf("Randomizer R with P-256: "); + for (i = 0; i < 16; i++) + printf("%02x", rp_ext->randomizer256[i]); + printf("\n"); + +done: + mainloop_quit(); +} + +static void cmd_local_oob(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + if (index == MGMT_INDEX_NONE) + index = 0; + + if (mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL, + local_oob_rsp, NULL, NULL) == 0) { + fprintf(stderr, "Unable to send read_local_oob cmd\n"); + exit(EXIT_FAILURE); + } +} + static void did_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { @@ -1723,7 +2038,7 @@ static void did_rsp(uint8_t status, uint16_t len, const void *param, else printf("Device ID successfully set\n"); - g_main_loop_quit(event_loop); + mainloop_quit(); } static void did_usage(void) @@ -1786,7 +2101,7 @@ static void static_addr_rsp(uint8_t status, uint16_t len, const void *param, else printf("Static address successfully set\n"); - g_main_loop_quit(event_loop); + mainloop_quit(); } static void static_addr_usage(void) @@ -1816,6 +2131,100 @@ static void cmd_static_addr(struct mgmt *mgmt, uint16_t index, } } +static void cmd_debug_keys(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + cmd_setting(mgmt, index, MGMT_OP_SET_DEBUG_KEYS, argc, argv); +} + +static void conn_info_rsp(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_get_conn_info *rp = param; char addr[18]; + + if (len == 0 && status != 0) { + fprintf(stderr, "Get Conn Info failed, status 0x%02x (%s)\n", + status, mgmt_errstr(status)); + goto done; + } + + if (len < sizeof(*rp)) { + fprintf(stderr, "Unexpected Get Conn Info len %u\n", len); + goto done; + } + + ba2str(&rp->addr.bdaddr, addr); + + if (status != 0) { + fprintf(stderr, "Get Conn Info for %s (%s) failed. status 0x%02x (%s)\n", + addr, typestr(rp->addr.type), + status, mgmt_errstr(status)); + goto done; + } + + printf("Connection Information for %s (%s)\n", + addr, typestr(rp->addr.type)); + printf("\tRSSI %d\n\tTX power %d\n\tmaximum TX power %d\n", + rp->rssi, rp->tx_power, rp->max_tx_power); + +done: + mainloop_quit(); +} + +static void conn_info_usage(void) +{ + printf("Usage: btmgmt conn-info [-t type] \n"); +} + +static struct option conn_info_options[] = { + { "help", 0, 0, 'h' }, + { "type", 1, 0, 't' }, + { 0, 0, 0, 0 } +}; + +static void cmd_conn_info(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + struct mgmt_cp_get_conn_info cp; + uint8_t type = BDADDR_BREDR; + int opt; + + while ((opt = getopt_long(argc, argv, "+t:h", conn_info_options, + NULL)) != -1) { + switch (opt) { + case 't': + type = strtol(optarg, NULL, 0); + break; + case 'h': + default: + conn_info_usage(); + exit(EXIT_SUCCESS); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + conn_info_usage(); + exit(EXIT_FAILURE); + } + + if (index == MGMT_INDEX_NONE) + index = 0; + + memset(&cp, 0, sizeof(cp)); + str2ba(argv[0], &cp.addr.bdaddr); + cp.addr.type = type; + + if (mgmt_send(mgmt, MGMT_OP_GET_CONN_INFO, index, sizeof(cp), &cp, + conn_info_rsp, NULL, NULL) == 0) { + fprintf(stderr, "Unable to send get_conn_info cmd\n"); + exit(EXIT_FAILURE); + } +} + static struct { char *cmd; void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv); @@ -1832,10 +2241,12 @@ static struct { { "pairable", cmd_pairable, "Toggle pairable state" }, { "linksec", cmd_linksec, "Toggle link level security" }, { "ssp", cmd_ssp, "Toggle SSP mode" }, - { "hs", cmd_hs, "Toggle HS Support" }, - { "le", cmd_le, "Toggle LE Support" }, + { "sc", cmd_sc, "Toogle SC support" }, + { "hs", cmd_hs, "Toggle HS support" }, + { "le", cmd_le, "Toggle LE support" }, { "advertising",cmd_advertising,"Toggle LE advertising", }, { "bredr", cmd_bredr, "Toggle BR/EDR support", }, + { "privacy", cmd_privacy, "Toggle privacy support" }, { "class", cmd_class, "Set device major/minor class" }, { "disconnect", cmd_disconnect, "Disconnect device" }, { "con", cmd_con, "List connections" }, @@ -1844,15 +2255,20 @@ static struct { { "pair", cmd_pair, "Pair with a remote device" }, { "cancelpair", cmd_cancel_pair,"Cancel pairing" }, { "unpair", cmd_unpair, "Unpair device" }, - { "keys", cmd_keys, "Load Keys" }, + { "keys", cmd_keys, "Load Link Keys" }, + { "ltks", cmd_ltks, "Load Long Term Keys" }, + { "irks", cmd_irks, "Load Identity Resolving Keys" }, { "block", cmd_block, "Block Device" }, { "unblock", cmd_unblock, "Unblock Device" }, { "add-uuid", cmd_add_uuid, "Add UUID" }, - { "rm-uuid", cmd_remove_uuid, "Remove UUID" }, - { "clr-uuids", cmd_clr_uuids, "Clear UUIDs", }, - { "did", cmd_did, "Set Device ID", }, - { "static-addr",cmd_static_addr,"Set static address", }, - { NULL, NULL, 0 } + { "rm-uuid", cmd_remove_uuid,"Remove UUID" }, + { "clr-uuids", cmd_clr_uuids, "Clear UUIDs" }, + { "local-oob", cmd_local_oob, "Local OOB data" }, + { "did", cmd_did, "Set Device ID" }, + { "static-addr",cmd_static_addr,"Set static address" }, + { "debug-keys", cmd_debug_keys, "Toogle debug keys" }, + { "conn-info", cmd_conn_info, "Get connection information" }, + { } }; static void usage(void) @@ -1889,6 +2305,7 @@ int main(int argc, char *argv[]) int opt, i; uint16_t index = MGMT_INDEX_NONE; struct mgmt *mgmt; + int exit_status; while ((opt = getopt_long(argc, argv, "+hvi:", main_options, NULL)) != -1) { @@ -1919,13 +2336,12 @@ int main(int argc, char *argv[]) return 0; } - event_loop = g_main_loop_new(NULL, FALSE); + mainloop_init(); mgmt = mgmt_new_default(); if (!mgmt) { fprintf(stderr, "Unable to open mgmt_socket\n"); - g_main_loop_unref(event_loop); - return -1; + return EXIT_FAILURE; } for (i = 0; command[i].cmd; i++) { @@ -1939,8 +2355,7 @@ int main(int argc, char *argv[]) if (command[i].cmd == NULL) { fprintf(stderr, "Unknown command: %s\n", argv[0]); mgmt_unref(mgmt); - g_main_loop_unref(event_loop); - return -1; + return EXIT_FAILURE; } mgmt_register(mgmt, MGMT_EV_CONTROLLER_ERROR, index, controller_error, @@ -1972,13 +2387,11 @@ int main(int argc, char *argv[]) mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm, mgmt, NULL); - g_main_loop_run(event_loop); + exit_status = mainloop_run(); mgmt_cancel_all(mgmt); mgmt_unregister_all(mgmt); mgmt_unref(mgmt); - g_main_loop_unref(event_loop); - - return 0; + return exit_status; } diff --git a/tools/btproxy.c b/tools/btproxy.c new file mode 100644 index 0000000..3503148 --- /dev/null +++ b/tools/btproxy.c @@ -0,0 +1,713 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "src/shared/util.h" +#include "monitor/mainloop.h" +#include "monitor/bt.h" + +#define BTPROTO_HCI 1 +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; +#define HCI_CHANNEL_USER 1 + +static uint16_t hci_index = 0; +static bool client_active = false; +static bool debug_enabled = false; + +static void hexdump_print(const char *str, void *user_data) +{ + printf("%s%s\n", (char *) user_data, str); +} + +struct proxy { + /* Receive commands, ACL and SCO data */ + int host_fd; + uint8_t host_buf[4096]; + uint16_t host_len; + bool host_shutdown; + + /* Receive events, ACL and SCO data */ + int dev_fd; + uint8_t dev_buf[4096]; + uint16_t dev_len; + bool dev_shutdown; +}; + +static bool write_packet(int fd, const void *data, size_t size, + void *user_data) +{ + while (size > 0) { + ssize_t written; + + written = write(fd, data, size); + if (written < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + return false; + } + + if (debug_enabled) + util_hexdump('<', data, written, hexdump_print, + user_data); + + data += written; + size -= written; + } + + return true; +} + +static void host_read_destroy(void *user_data) +{ + struct proxy *proxy = user_data; + + printf("Closing host descriptor\n"); + + if (proxy->host_shutdown) + shutdown(proxy->host_fd, SHUT_RDWR); + + close(proxy->host_fd); + proxy->host_fd = -1; + + if (proxy->dev_fd < 0) { + client_active = false; + free(proxy); + } else + mainloop_remove_fd(proxy->dev_fd); +} + +static void host_read_callback(int fd, uint32_t events, void *user_data) +{ + struct proxy *proxy = user_data; + struct bt_hci_cmd_hdr *cmd_hdr; + struct bt_hci_acl_hdr *acl_hdr; + struct bt_hci_sco_hdr *sco_hdr; + ssize_t len; + uint16_t pktlen; + + if (events & (EPOLLERR | EPOLLHUP)) { + fprintf(stderr, "Error from host descriptor\n"); + mainloop_remove_fd(proxy->host_fd); + return; + } + + if (events & EPOLLRDHUP) { + fprintf(stderr, "Remote hangup of host descriptor\n"); + mainloop_remove_fd(proxy->host_fd); + return; + } + + len = read(proxy->host_fd, proxy->host_buf + proxy->host_len, + sizeof(proxy->host_buf) - proxy->host_len); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return; + + fprintf(stderr, "Read from host descriptor failed\n"); + mainloop_remove_fd(proxy->host_fd); + return; + } + + if (debug_enabled) + util_hexdump('>', proxy->host_buf + proxy->host_len, len, + hexdump_print, "H: "); + + proxy->host_len += len; + +process_packet: + if (proxy->host_len < 1) + return; + + switch (proxy->host_buf[0]) { + case BT_H4_CMD_PKT: + if (proxy->host_len < 1 + sizeof(*cmd_hdr)) + return; + + cmd_hdr = (void *) (proxy->host_buf + 1); + pktlen = 1 + sizeof(*cmd_hdr) + cmd_hdr->plen; + break; + case BT_H4_ACL_PKT: + if (proxy->host_len < 1 + sizeof(*acl_hdr)) + return; + + acl_hdr = (void *) (proxy->host_buf + 1); + pktlen = 1 + sizeof(*acl_hdr) + cpu_to_le16(acl_hdr->dlen); + break; + case BT_H4_SCO_PKT: + if (proxy->host_len < 1 + sizeof(*sco_hdr)) + return; + + sco_hdr = (void *) (proxy->host_buf + 1); + pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen; + break; + case 0xff: + /* Notification packet from /dev/vhci - ignore */ + proxy->host_len = 0; + return; + default: + fprintf(stderr, "Received unknown host packet type 0x%02x\n", + proxy->host_buf[0]); + mainloop_remove_fd(proxy->host_fd); + return; + } + + if (proxy->host_len < pktlen) + return; + + if (!write_packet(proxy->dev_fd, proxy->host_buf, pktlen, "D: ")) { + fprintf(stderr, "Write to device descriptor failed\n"); + mainloop_remove_fd(proxy->dev_fd); + return; + } + + if (proxy->host_len > pktlen) { + memmove(proxy->host_buf, proxy->host_buf + pktlen, + proxy->host_len - pktlen); + proxy->host_len -= pktlen; + goto process_packet; + } + + proxy->host_len = 0; +} + +static void dev_read_destroy(void *user_data) +{ + struct proxy *proxy = user_data; + + printf("Closing device descriptor\n"); + + if (proxy->dev_shutdown) + shutdown(proxy->dev_fd, SHUT_RDWR); + + close(proxy->dev_fd); + proxy->dev_fd = -1; + + if (proxy->host_fd < 0) { + client_active = false; + free(proxy); + } else + mainloop_remove_fd(proxy->host_fd); +} + +static void dev_read_callback(int fd, uint32_t events, void *user_data) +{ + struct proxy *proxy = user_data; + struct bt_hci_evt_hdr *evt_hdr; + struct bt_hci_acl_hdr *acl_hdr; + struct bt_hci_sco_hdr *sco_hdr; + ssize_t len; + uint16_t pktlen; + + if (events & (EPOLLERR | EPOLLHUP)) { + fprintf(stderr, "Error from device descriptor\n"); + mainloop_remove_fd(proxy->dev_fd); + return; + } + + if (events & EPOLLRDHUP) { + fprintf(stderr, "Remote hangup of device descriptor\n"); + mainloop_remove_fd(proxy->host_fd); + return; + } + + len = read(proxy->dev_fd, proxy->dev_buf + proxy->dev_len, + sizeof(proxy->dev_buf) - proxy->dev_len); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return; + + fprintf(stderr, "Read from device descriptor failed\n"); + mainloop_remove_fd(proxy->dev_fd); + return; + } + + if (debug_enabled) + util_hexdump('>', proxy->dev_buf + proxy->dev_len, len, + hexdump_print, "D: "); + + proxy->dev_len += len; + +process_packet: + if (proxy->dev_len < 1) + return; + + switch (proxy->dev_buf[0]) { + case BT_H4_EVT_PKT: + if (proxy->dev_len < 1 + sizeof(*evt_hdr)) + return; + + evt_hdr = (void *) (proxy->dev_buf + 1); + pktlen = 1 + sizeof(*evt_hdr) + evt_hdr->plen; + break; + case BT_H4_ACL_PKT: + if (proxy->dev_len < 1 + sizeof(*acl_hdr)) + return; + + acl_hdr = (void *) (proxy->dev_buf + 1); + pktlen = 1 + sizeof(*acl_hdr) + cpu_to_le16(acl_hdr->dlen); + break; + case BT_H4_SCO_PKT: + if (proxy->dev_len < 1 + sizeof(*sco_hdr)) + return; + + sco_hdr = (void *) (proxy->dev_buf + 1); + pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen; + break; + default: + fprintf(stderr, "Received unknown device packet type 0x%02x\n", + proxy->dev_buf[0]); + mainloop_remove_fd(proxy->dev_fd); + return; + } + + if (proxy->dev_len < pktlen) + return; + + if (!write_packet(proxy->host_fd, proxy->dev_buf, pktlen, "H: ")) { + fprintf(stderr, "Write to host descriptor failed\n"); + mainloop_remove_fd(proxy->host_fd); + return; + } + + if (proxy->dev_len > pktlen) { + memmove(proxy->dev_buf, proxy->dev_buf + pktlen, + proxy->dev_len - pktlen); + proxy->dev_len -= pktlen; + goto process_packet; + } + + proxy->dev_len = 0; +} + +static bool setup_proxy(int host_fd, bool host_shutdown, + int dev_fd, bool dev_shutdown) +{ + struct proxy *proxy; + + proxy = new0(struct proxy, 1); + if (!proxy) + return NULL; + + proxy->host_fd = host_fd; + proxy->host_shutdown = host_shutdown; + + proxy->dev_fd = dev_fd; + proxy->dev_shutdown = dev_shutdown; + + mainloop_add_fd(proxy->host_fd, EPOLLIN | EPOLLRDHUP, + host_read_callback, proxy, host_read_destroy); + + mainloop_add_fd(proxy->dev_fd, EPOLLIN | EPOLLRDHUP, + dev_read_callback, proxy, dev_read_destroy); + + return true; +} + +static int open_channel(uint16_t index) +{ + struct sockaddr_hci addr; + int fd; + + printf("Opening user channel for hci%u\n", hci_index); + + fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); + if (fd < 0) { + perror("Failed to open Bluetooth socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.hci_family = AF_BLUETOOTH; + addr.hci_dev = index; + addr.hci_channel = HCI_CHANNEL_USER; + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(fd); + perror("Failed to bind Bluetooth socket"); + return -1; + } + + return fd; +} + +static void server_callback(int fd, uint32_t events, void *user_data) +{ + union { + struct sockaddr common; + struct sockaddr_un sun; + struct sockaddr_in sin; + } addr; + socklen_t len; + int host_fd, dev_fd; + + if (events & (EPOLLERR | EPOLLHUP)) { + mainloop_quit(); + return; + } + + memset(&addr, 0, sizeof(addr)); + len = sizeof(addr); + + if (getsockname(fd, &addr.common, &len) < 0) { + perror("Failed to get socket name"); + return; + } + + host_fd = accept(fd, &addr.common, &len); + if (host_fd < 0) { + perror("Failed to accept client socket"); + return; + } + + if (client_active) { + fprintf(stderr, "Active client already present\n"); + close(host_fd); + return; + } + + dev_fd = open_channel(hci_index); + if (dev_fd < 0) { + close(host_fd); + return; + } + + printf("New client connected\n"); + + if (!setup_proxy(host_fd, true, dev_fd, false)) { + close(dev_fd); + close(host_fd); + return; + } + + client_active = true; +} + +static int open_unix(const char *path) +{ + struct sockaddr_un addr; + int fd; + + unlink(path); + + fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("Failed to open Unix server socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to bind Unix server socket"); + close(fd); + return -1; + } + + if (listen(fd, 1) < 0) { + perror("Failed to listen Unix server socket"); + close(fd); + return -1; + } + + if (chmod(path, 0666) < 0) + perror("Failed to change mode"); + + return fd; +} + +static int open_tcp(const char *address, unsigned int port) +{ + struct sockaddr_in addr; + int fd, opt = 1; + + fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("Failed to open TCP server socket"); + return -1; + } + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(address); + addr.sin_port = htons(port); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to bind TCP server socket"); + close(fd); + return -1; + } + + if (listen(fd, 1) < 0) { + perror("Failed to listen TCP server socket"); + close(fd); + return -1; + } + + return fd; +} + +static int connect_tcp(const char *address, unsigned int port) +{ + struct sockaddr_in addr; + int fd; + + fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("Failed to open TCP client socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(address); + addr.sin_port = htons(port); + + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to connect TCP client socket"); + close(fd); + return -1; + } + + return fd; +} + +static int open_vhci(uint8_t type) +{ + uint8_t create_req[2] = { 0xff, type }; + ssize_t written; + int fd; + + fd = open("/dev/vhci", O_RDWR | O_CLOEXEC); + if (fd < 0) { + perror("Failed to open /dev/vhci device"); + return -1; + } + + written = write(fd, create_req, sizeof(create_req)); + if (written < 0) { + perror("Failed to set device type"); + close(fd); + return -1; + } + + return fd; +} + +static void signal_callback(int signum, void *user_data) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + mainloop_quit(); + break; + } +} + +static void usage(void) +{ + printf("btproxy - Bluetooth controller proxy\n" + "Usage:\n"); + printf("\tbtproxy [options]\n"); + printf("Options:\n" + "\t-c, --connect
Connect to server\n" + "\t-l, --listen [address] Use TCP server\n" + "\t-u, --unix [path] Use Unix server\n" + "\t-p, --port Use specified TCP port\n" + "\t-i, --index Use specified controller\n" + "\t-d, --debug Enable debugging output\n" + "\t-h, --help Show help options\n"); +} + +static const struct option main_options[] = { + { "connect", required_argument, NULL, 'c' }, + { "listen", optional_argument, NULL, 'l' }, + { "unix", optional_argument, NULL, 'u' }, + { "port", required_argument, NULL, 'p' }, + { "index", required_argument, NULL, 'i' }, + { "debug", no_argument, NULL, 'd' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } +}; + +int main(int argc, char *argv[]) +{ + const char *connect_address = NULL; + const char *server_address = NULL; + const char *unix_path = NULL; + unsigned short tcp_port = 0xb1ee; /* 45550 */ + const char *str; + sigset_t mask; + + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "c:l::u::p:i:dvh", + main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'c': + connect_address = optarg; + break; + case 'l': + if (optarg) + server_address = optarg; + else + server_address = "0.0.0.0"; + break; + case 'u': + if (optarg) + unix_path = optarg; + else + unix_path = "/tmp/bt-server-bredr"; + break; + case 'p': + tcp_port = atoi(optarg); + break; + case 'i': + if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3)) + str = optarg + 3; + else + str = optarg; + if (!isdigit(*str)) { + usage(); + return EXIT_FAILURE; + } + hci_index = atoi(str); + break; + case 'd': + debug_enabled = true; + break; + case 'v': + printf("%s\n", VERSION); + return EXIT_SUCCESS; + case 'h': + usage(); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } + } + + if (argc - optind > 0) { + fprintf(stderr, "Invalid command line parameters\n"); + return EXIT_FAILURE; + } + + if (unix_path && server_address) { + fprintf(stderr, "Invalid to specify TCP and Unix servers\n"); + return EXIT_FAILURE; + } + + if (connect_address && (unix_path || server_address)) { + fprintf(stderr, "Invalid to specify client and server mode\n"); + return EXIT_FAILURE; + } + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + if (connect_address) { + int host_fd, dev_fd; + + printf("Connecting to %s:%u\n", connect_address, tcp_port); + + dev_fd = connect_tcp(connect_address, tcp_port); + if (dev_fd < 0) + return EXIT_FAILURE; + + printf("Opening virtual device\n"); + + host_fd = open_vhci(0x00); + if (host_fd < 0) { + close(dev_fd); + return EXIT_FAILURE; + } + + if (!setup_proxy(host_fd, false, dev_fd, true)) { + close(dev_fd); + close(host_fd); + return EXIT_FAILURE; + } + } else { + int server_fd; + + if (unix_path) { + printf("Listening on %s\n", unix_path); + + server_fd = open_unix(unix_path); + } else if (server_address) { + printf("Listening on %s:%u\n", server_address, + tcp_port); + + server_fd = open_tcp(server_address, tcp_port); + } else { + fprintf(stderr, "Missing emulator device\n"); + return EXIT_FAILURE; + } + + if (server_fd < 0) + return EXIT_FAILURE; + + mainloop_add_fd(server_fd, EPOLLIN, server_callback, + NULL, NULL); + } + + return mainloop_run(); +} diff --git a/tools/btsnoop.c b/tools/btsnoop.c index 306e643..6ca62d2 100644 --- a/tools/btsnoop.c +++ b/tools/btsnoop.c @@ -38,20 +38,7 @@ #include #include -#include "monitor/btsnoop.h" - -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; -} - -#define hton64(x) ntoh64(x) +#include "src/shared/btsnoop.h" struct btsnoop_hdr { uint8_t id[8]; /* Identification Pattern */ @@ -89,8 +76,8 @@ static int create_btsnoop(const char *path) } memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id)); - hdr.version = htonl(btsnoop_version); - hdr.type = htonl(2001); + hdr.version = htobe32(btsnoop_version); + hdr.type = htobe32(2001); written = write(fd, &hdr, BTSNOOP_HDR_SIZE); if (written < 0) { @@ -127,14 +114,14 @@ static int open_btsnoop(const char *path, uint32_t *type) return -1; } - if (ntohl(hdr.version) != btsnoop_version) { + if (be32toh(hdr.version) != btsnoop_version) { fprintf(stderr, "invalid btsnoop version\n"); close(fd); return -1; } if (type) - *type = ntohl(hdr.type); + *type = be32toh(hdr.type); return fd; } @@ -205,17 +192,17 @@ next_packet: continue; } - ts = ntoh64(input_pkt[i].ts); + ts = be64toh(input_pkt[i].ts); - if (ts < ntoh64(input_pkt[select_input].ts)) + if (ts < be64toh(input_pkt[select_input].ts)) select_input = i; } if (select_input < 0) goto close_output; - toread = ntohl(input_pkt[select_input].size); - flags = ntohl(input_pkt[select_input].flags); + toread = be32toh(input_pkt[select_input].size); + flags = be32toh(input_pkt[select_input].flags); len = read(input_fd[select_input], buf, toread); if (len < 0 || len != (ssize_t) toread) { @@ -224,8 +211,8 @@ next_packet: goto next_packet; } - written = input_pkt[select_input].size = htonl(toread - 1); - written = input_pkt[select_input].len = htonl(toread - 1); + written = input_pkt[select_input].size = htobe32(toread - 1); + written = input_pkt[select_input].len = htobe32(toread - 1); switch (buf[0]) { case 0x01: @@ -251,7 +238,7 @@ next_packet: } index = select_input; - input_pkt[select_input].flags = htonl((index << 16) | opcode); + input_pkt[select_input].flags = htobe32((index << 16) | opcode); written = write(output_fd, &input_pkt[select_input], BTSNOOP_PKT_SIZE); if (written != BTSNOOP_PKT_SIZE) { @@ -307,8 +294,8 @@ next_packet: if (len < 0 || len != BTSNOOP_PKT_SIZE) goto close_input; - toread = ntohl(pkt.size); - flags = ntohl(pkt.flags); + toread = be32toh(pkt.size); + flags = be32toh(pkt.flags); opcode = flags & 0x00ff; @@ -380,8 +367,8 @@ next_packet: if (len < 0 || len != BTSNOOP_PKT_SIZE) goto close_input; - toread = ntohl(pkt.size); - flags = ntohl(pkt.flags); + toread = be32toh(pkt.size); + flags = be32toh(pkt.flags); opcode = flags & 0x00ff; @@ -457,7 +444,7 @@ next_packet: if (len < 0 || len != BTSNOOP_PKT_SIZE) goto close_input; - toread = ntohl(pkt.size); + toread = be32toh(pkt.size); len = read(fd, buf, toread); if (len < 0 || len != (ssize_t) toread) { diff --git a/tools/cltest.c b/tools/cltest.c index 16b7553..4ddb98a 100644 --- a/tools/cltest.c +++ b/tools/cltest.c @@ -205,8 +205,8 @@ static bool find_controllers(void) dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)); if (!dl) { perror("Failed allocate HCI device request memory"); - result = false; - goto done; + close(fd); + return false; } dl->dev_num = HCI_MAX_DEV; @@ -243,6 +243,7 @@ static bool find_controllers(void) } done: + free(dl); close(fd); return result; } diff --git a/tools/csr_usb.c b/tools/csr_usb.c index a483bc1..5fb6bdc 100644 --- a/tools/csr_usb.c +++ b/tools/csr_usb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "csr.h" diff --git a/tools/gatt-service.c b/tools/gatt-service.c new file mode 100644 index 0000000..6bca404 --- /dev/null +++ b/tools/gatt-service.c @@ -0,0 +1,538 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 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 +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "src/error.h" + +#define GATT_MGR_IFACE "org.bluez.GattManager1" +#define GATT_SERVICE_IFACE "org.bluez.GattService1" +#define GATT_CHR_IFACE "org.bluez.GattCharacteristic1" +#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1" + +/* Immediate Alert Service UUID */ +#define IAS_UUID "00001802-0000-1000-8000-00805f9b34fb" +#define ALERT_LEVEL_CHR_UUID "00002a06-0000-1000-8000-00805f9b34fb" + +/* Random UUID for testing purpose */ +#define READ_WRITE_DESCRIPTOR_UUID "8260c653-1a54-426b-9e36-e84c238bc669" + +static GMainLoop *main_loop; +static GSList *services; +static DBusConnection *connection; + +struct characteristic { + char *uuid; + char *path; + uint8_t *value; + int vlen; + const char **props; +}; + +struct descriptor { + char *uuid; + char *path; + uint8_t *value; + int vlen; +}; + +/* + * Alert Level support Write Without Response only. Supported + * properties are defined at doc/gatt-api.txt. See "Flags" + * property of the GattCharacteristic1. + */ +static const char const *ias_alert_level_props[] = { + "write-without-response", + NULL }; + +static gboolean desc_get_uuid(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct descriptor *desc = user_data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid); + + return TRUE; +} + +static gboolean desc_get_value(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct descriptor *desc = user_data; + DBusMessageIter array; + + printf("Descriptor(%s): Get(\"Value\")\n", desc->uuid); + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, &array); + + if (desc->vlen && desc->value) + dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, + &desc->value, desc->vlen); + + dbus_message_iter_close_container(iter, &array); + + return TRUE; +} + +static void desc_set_value(const GDBusPropertyTable *property, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *user_data) +{ + struct descriptor *desc = user_data; + DBusMessageIter array; + const uint8_t *value; + int vlen; + + printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid); + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &value, &vlen); + + g_free(desc->value); + desc->value = g_memdup(value, vlen); + desc->vlen = vlen; + + g_dbus_pending_property_success(id); + + g_dbus_emit_property_changed(connection, desc->path, + GATT_DESCRIPTOR_IFACE, "Value"); + +} + +static const GDBusPropertyTable desc_properties[] = { + { "UUID", "s", desc_get_uuid }, + { "Value", "ay", desc_get_value, desc_set_value, NULL }, + { } +}; + +static gboolean chr_get_uuid(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct characteristic *chr = user_data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid); + + return TRUE; +} + +static gboolean chr_get_value(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct characteristic *chr = user_data; + DBusMessageIter array; + + printf("Characteristic(%s): Get(\"Value\")\n", chr->uuid); + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, &array); + + dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, + &chr->value, chr->vlen); + + dbus_message_iter_close_container(iter, &array); + + return TRUE; +} + +static gboolean chr_get_props(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct characteristic *chr = data; + DBusMessageIter array; + int i; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array); + + for (i = 0; chr->props[i]; i++) + dbus_message_iter_append_basic(&array, + DBUS_TYPE_STRING, &chr->props[i]); + + dbus_message_iter_close_container(iter, &array); + + return TRUE; +} + +static void chr_set_value(const GDBusPropertyTable *property, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *user_data) +{ + struct characteristic *chr = user_data; + DBusMessageIter array; + uint8_t *value; + int len; + + printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { + printf("Invalid value for Set('Value'...)\n"); + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &value, &len); + + g_free(chr->value); + chr->value = g_memdup(value, len); + chr->vlen = len; + + g_dbus_pending_property_success(id); + g_dbus_emit_property_changed(connection, chr->path, + GATT_CHR_IFACE, "Value"); +} + +static const GDBusPropertyTable chr_properties[] = { + { "UUID", "s", chr_get_uuid }, + { "Value", "ay", chr_get_value, chr_set_value, NULL }, + { "Flags", "as", chr_get_props, NULL, NULL }, + { } +}; + +static gboolean service_get_uuid(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + const char *uuid = user_data; + + printf("Get UUID: %s\n", uuid); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); + + return TRUE; +} + +static gboolean service_get_includes(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + const char *uuid = user_data; + + printf("Get Includes: %s\n", uuid); + + return TRUE; +} + +static gboolean service_exist_includes(const GDBusPropertyTable *property, + void *user_data) +{ + const char *uuid = user_data; + + printf("Exist Includes: %s\n", uuid); + + return FALSE; +} + +static const GDBusPropertyTable service_properties[] = { + { "UUID", "s", service_get_uuid }, + { "Includes", "ao", service_get_includes, NULL, + service_exist_includes }, + { } +}; + +static void chr_iface_destroy(gpointer user_data) +{ + struct characteristic *chr = user_data; + + g_free(chr->uuid); + g_free(chr->value); + g_free(chr->path); + g_free(chr); +} + +static void desc_iface_destroy(gpointer user_data) +{ + struct descriptor *desc = user_data; + + g_free(desc->uuid); + g_free(desc->value); + g_free(desc->path); + g_free(desc); +} + +static gboolean register_characteristic(const char *chr_uuid, + const uint8_t *value, int vlen, + const char **props, + const char *desc_uuid, + const char *service_path) +{ + struct characteristic *chr; + struct descriptor *desc; + static int id = 1; + + chr = g_new0(struct characteristic, 1); + chr->uuid = g_strdup(chr_uuid); + chr->value = g_memdup(value, vlen); + chr->vlen = vlen; + chr->props = props; + chr->path = g_strdup_printf("%s/characteristic%d", service_path, id++); + + if (!g_dbus_register_interface(connection, chr->path, GATT_CHR_IFACE, + NULL, NULL, chr_properties, + chr, chr_iface_destroy)) { + printf("Couldn't register characteristic interface\n"); + chr_iface_destroy(chr); + return FALSE; + } + + if (!desc_uuid) + return TRUE; + + desc = g_new0(struct descriptor, 1); + desc->uuid = g_strdup(desc_uuid); + desc->path = g_strdup_printf("%s/descriptor%d", chr->path, id++); + + if (!g_dbus_register_interface(connection, desc->path, + GATT_DESCRIPTOR_IFACE, + NULL, NULL, desc_properties, + desc, desc_iface_destroy)) { + printf("Couldn't register descriptor interface\n"); + g_dbus_unregister_interface(connection, chr->path, + GATT_CHR_IFACE); + + desc_iface_destroy(desc); + return FALSE; + } + + return TRUE; +} + +static char *register_service(const char *uuid) +{ + static int id = 1; + char *path; + + path = g_strdup_printf("/service%d", id++); + if (!g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE, + NULL, NULL, service_properties, + g_strdup(uuid), g_free)) { + printf("Couldn't register service interface\n"); + g_free(path); + return NULL; + } + + return path; +} + +static void create_services() +{ + char *service_path; + uint8_t level = 0; + + service_path = register_service(IAS_UUID); + if (!service_path) + return; + + /* Add Alert Level Characteristic to Immediate Alert Service */ + if (!register_characteristic(ALERT_LEVEL_CHR_UUID, + &level, sizeof(level), + ias_alert_level_props, + READ_WRITE_DESCRIPTOR_UUID, + service_path)) { + printf("Couldn't register Alert Level characteristic (IAS)\n"); + g_dbus_unregister_interface(connection, service_path, + GATT_SERVICE_IFACE); + g_free(service_path); + return; + } + + services = g_slist_prepend(services, service_path); + printf("Registered service: %s\n", service_path); +} + +static void register_external_service_reply(DBusPendingCall *call, + void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError derr; + + dbus_error_init(&derr); + dbus_set_error_from_message(&derr, reply); + + if (dbus_error_is_set(&derr)) + printf("RegisterService: %s\n", derr.message); + else + printf("RegisterService: OK\n"); + + dbus_message_unref(reply); + dbus_error_free(&derr); +} + +static void register_external_service(gpointer a, gpointer b) +{ + DBusConnection *conn = b; + const char *path = a; + DBusMessage *msg; + DBusPendingCall *call; + DBusMessageIter iter, dict; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + GATT_MGR_IFACE, "RegisterService"); + if (!msg) { + printf("Couldn't allocate D-Bus message\n"); + return; + } + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); + + /* TODO: Add options dictionary */ + + dbus_message_iter_close_container(&iter, &dict); + + if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) { + dbus_message_unref(msg); + return; + } + + dbus_pending_call_set_notify(call, register_external_service_reply, + NULL, NULL); + + dbus_pending_call_unref(call); +} + +static void connect_handler(DBusConnection *conn, void *user_data) +{ + g_slist_foreach(services, register_external_service, conn); +} + +static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + static bool __terminated = false; + struct signalfd_siginfo si; + ssize_t result; + int fd; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) + return FALSE; + + fd = g_io_channel_unix_get_fd(channel); + + result = read(fd, &si, sizeof(si)); + if (result != sizeof(si)) + return FALSE; + + switch (si.ssi_signo) { + case SIGINT: + case SIGTERM: + if (!__terminated) { + printf("Terminating\n"); + g_main_loop_quit(main_loop); + } + + __terminated = true; + break; + } + + return TRUE; +} + +static guint setup_signalfd(void) +{ + GIOChannel *channel; + guint source; + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { + perror("Failed to set signal mask"); + return 0; + } + + fd = signalfd(-1, &mask, 0); + if (fd < 0) { + perror("Failed to create signal descriptor"); + return 0; + } + + channel = g_io_channel_unix_new(fd); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +int main(int argc, char *argv[]) +{ + GDBusClient *client; + guint signal; + + signal = setup_signalfd(); + if (signal == 0) + return -errno; + + connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); + + main_loop = g_main_loop_new(NULL, FALSE); + + g_dbus_attach_object_manager(connection); + + printf("gatt-service unique name: %s\n", + dbus_bus_get_unique_name(connection)); + + create_services(); + + client = g_dbus_client_new(connection, "org.bluez", "/org/bluez"); + + g_dbus_client_set_connect_watch(client, connect_handler, NULL); + + g_main_loop_run(main_loop); + + g_dbus_client_unref(client); + + g_source_remove(signal); + + g_slist_free_full(services, g_free); + dbus_connection_unref(connection); + + return 0; +} diff --git a/tools/hci-tester.c b/tools/hci-tester.c new file mode 100644 index 0000000..a5dbde2 --- /dev/null +++ b/tools/hci-tester.c @@ -0,0 +1,673 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. 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 +#endif + +#include +#include + +#include "monitor/bt.h" +#include "src/shared/hci.h" +#include "src/shared/util.h" +#include "src/shared/tester.h" + +struct user_data { + const void *test_data; + uint16_t index_ut; + uint16_t index_lt; + struct bt_hci *hci_ut; /* Upper Tester / IUT */ + struct bt_hci *hci_lt; /* Lower Tester / Reference */ + + uint8_t bdaddr_ut[6]; + uint8_t bdaddr_lt[6]; + uint16_t handle_ut; +}; + +static void test_pre_setup_lt_address(const void *data, uint8_t size, + void *user_data) +{ + struct user_data *user = tester_get_data(); + const struct bt_hci_rsp_read_bd_addr *rsp = data; + + if (rsp->status) { + tester_warn("Read lower tester address failed (0x%02x)", + rsp->status); + tester_pre_setup_failed(); + return; + } + + memcpy(user->bdaddr_lt, rsp->bdaddr, 6); + + tester_pre_setup_complete(); +} + +static void test_pre_setup_lt_complete(const void *data, uint8_t size, + void *user_data) +{ + struct user_data *user = tester_get_data(); + uint8_t status = *((uint8_t *) data); + + if (status) { + tester_warn("Reset lower tester failed (0x%02x)", status); + tester_pre_setup_failed(); + return; + } + + if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_READ_BD_ADDR, NULL, 0, + test_pre_setup_lt_address, NULL, NULL)) { + tester_warn("Failed to read lower tester address"); + tester_pre_setup_failed(); + return; + } +} + +static void test_pre_setup_ut_address(const void *data, uint8_t size, + void *user_data) +{ + struct user_data *user = tester_get_data(); + const struct bt_hci_rsp_read_bd_addr *rsp = data; + + if (rsp->status) { + tester_warn("Read upper tester address failed (0x%02x)", + rsp->status); + tester_pre_setup_failed(); + return; + } + + memcpy(user->bdaddr_ut, rsp->bdaddr, 6); + + user->hci_lt = bt_hci_new_user_channel(user->index_lt); + if (!user->hci_lt) { + tester_warn("Failed to setup lower tester user channel"); + tester_pre_setup_failed(); + return; + } + + if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_RESET, NULL, 0, + test_pre_setup_lt_complete, NULL, NULL)) { + tester_warn("Failed to reset lower tester"); + tester_pre_setup_failed(); + return; + } +} + +static void test_pre_setup_ut_complete(const void *data, uint8_t size, + void *user_data) +{ + struct user_data *user = tester_get_data(); + uint8_t status = *((uint8_t *) data); + + if (status) { + tester_warn("Reset upper tester failed (0x%02x)", status); + tester_pre_setup_failed(); + return; + } + + if (user->index_lt == 0xffff) { + tester_pre_setup_complete(); + return; + } + + if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_BD_ADDR, NULL, 0, + test_pre_setup_ut_address, NULL, NULL)) { + tester_warn("Failed to read upper tester address"); + tester_pre_setup_failed(); + return; + } +} + +static void test_pre_setup(const void *test_data) +{ + struct user_data *user = tester_get_data(); + + user->hci_ut = bt_hci_new_user_channel(user->index_ut); + if (!user->hci_ut) { + tester_warn("Failed to setup upper tester user channel"); + tester_pre_setup_failed(); + return; + } + + if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_RESET, NULL, 0, + test_pre_setup_ut_complete, NULL, NULL)) { + tester_warn("Failed to reset upper tester"); + tester_pre_setup_failed(); + return; + } +} + +static void test_post_teardown(const void *test_data) +{ + struct user_data *user = tester_get_data(); + + bt_hci_unref(user->hci_lt); + user->hci_lt = NULL; + + bt_hci_unref(user->hci_ut); + user->hci_ut = NULL; + + tester_post_teardown_complete(); +} + +static void user_data_free(void *data) +{ + struct user_data *user = data; + + free(user); +} + +#define test_hci(name, data, setup, func, teardown) \ + do { \ + struct user_data *user; \ + user = calloc(1, sizeof(struct user_data)); \ + if (!user) \ + break; \ + user->test_data = data; \ + user->index_ut = 0; \ + user->index_lt = 1; \ + tester_add_full(name, data, \ + test_pre_setup, setup, func, teardown, \ + test_post_teardown, 30, user, user_data_free); \ + } while (0) + +#define test_hci_local(name, data, setup, func) \ + do { \ + struct user_data *user; \ + user = calloc(1, sizeof(struct user_data)); \ + if (!user) \ + break; \ + user->test_data = data; \ + user->index_ut = 0; \ + user->index_lt = 0xffff; \ + tester_add_full(name, data, \ + test_pre_setup, setup, func, NULL, \ + test_post_teardown, 30, user, user_data_free); \ + } while (0) + +static void setup_features_complete(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_rsp_read_local_features *rsp = data; + + if (rsp->status) { + tester_warn("Failed to get HCI features (0x%02x)", rsp->status); + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_features(const void *test_data) +{ + struct user_data *user = tester_get_data(); + + if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0, + setup_features_complete, NULL, NULL)) { + tester_warn("Failed to send HCI features command"); + tester_setup_failed(); + return; + } +} + +static void test_reset(const void *test_data) +{ + tester_test_passed(); +} + +static void test_command_complete(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + tester_warn("HCI command failed (0x%02x)", status); + tester_test_failed(); + return; + } + + tester_test_passed(); +} + +static void test_command(uint16_t opcode) +{ + struct user_data *user = tester_get_data(); + + if (!bt_hci_send(user->hci_ut, opcode, NULL, 0, + test_command_complete, NULL, NULL)) { + tester_warn("Failed to send HCI command 0x%04x", opcode); + tester_test_failed(); + return; + } +} + +static void test_read_local_version_information(const void *test_data) +{ + test_command(BT_HCI_CMD_READ_LOCAL_VERSION); +} + +static void test_read_local_supported_commands(const void *test_data) +{ + test_command(BT_HCI_CMD_READ_LOCAL_COMMANDS); +} + +static void test_read_local_supported_features(const void *test_data) +{ + test_command(BT_HCI_CMD_READ_LOCAL_FEATURES); +} + +static void test_local_extended_features_complete(const void *data, + uint8_t size, void *user_data) +{ + const struct bt_hci_rsp_read_local_ext_features *rsp = data; + + if (rsp->status) { + tester_warn("Failed to get HCI extended features (0x%02x)", + rsp->status); + tester_test_failed(); + return; + } + + tester_test_passed(); +} + +static void test_read_local_extended_features(const void *test_data) +{ + struct user_data *user = tester_get_data(); + struct bt_hci_cmd_read_local_ext_features cmd; + + cmd.page = 0x00; + + if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES, + &cmd, sizeof(cmd), + test_local_extended_features_complete, + NULL, NULL)) { + tester_warn("Failed to send HCI extended features command"); + tester_test_failed(); + return; + } +} + +static void test_read_buffer_size(const void *test_data) +{ + test_command(BT_HCI_CMD_READ_BUFFER_SIZE); +} + +static void test_read_country_code(const void *test_data) +{ + test_command(BT_HCI_CMD_READ_COUNTRY_CODE); +} + +static void test_read_bd_addr(const void *test_data) +{ + test_command(BT_HCI_CMD_READ_BD_ADDR); +} + +static void test_read_local_supported_codecs(const void *test_data) +{ + test_command(BT_HCI_CMD_READ_LOCAL_CODECS); +} + +static void test_le_read_white_list_size(const void *test_data) +{ + test_command(BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE); +} + +static void test_le_clear_white_list(const void *test_data) +{ + test_command(BT_HCI_CMD_LE_CLEAR_WHITE_LIST); +} + +static void test_inquiry_complete(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_evt_inquiry_complete *evt = data; + + if (evt->status) { + tester_warn("HCI inquiry complete failed (0x%02x)", + evt->status); + tester_test_failed(); + return; + } + + tester_test_passed(); +} + +static void test_inquiry_status(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + tester_warn("HCI inquiry command failed (0x%02x)", status); + tester_test_failed(); + return; + } +} + +static void test_inquiry_liac(const void *test_data) +{ + struct user_data *user = tester_get_data(); + struct bt_hci_cmd_inquiry cmd; + + bt_hci_register(user->hci_ut, BT_HCI_EVT_INQUIRY_COMPLETE, + test_inquiry_complete, NULL, NULL); + + cmd.lap[0] = 0x00; + cmd.lap[1] = 0x8b; + cmd.lap[2] = 0x9e; + cmd.length = 0x08; + cmd.num_resp = 0x00; + + if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_INQUIRY, &cmd, sizeof(cmd), + test_inquiry_status, NULL, NULL)) { + tester_warn("Failed to send HCI inquiry command"); + tester_test_failed(); + return; + } +} + +static void setup_lt_connectable_complete(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + tester_warn("Failed to set HCI scan enable (0x%02x)", status); + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_lt_connect_request_accept(const void *data, uint8_t size, + void *user_data) +{ + struct user_data *user = tester_get_data(); + const struct bt_hci_evt_conn_request *evt = data; + struct bt_hci_cmd_accept_conn_request cmd; + + memcpy(cmd.bdaddr, evt->bdaddr, 6); + cmd.role = 0x01; + + if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_ACCEPT_CONN_REQUEST, + &cmd, sizeof(cmd), NULL, NULL, NULL)) { + tester_warn("Failed to send HCI accept connection command"); + return; + } +} + +static void setup_lt_connectable(const void *test_data) +{ + struct user_data *user = tester_get_data(); + struct bt_hci_cmd_write_scan_enable cmd; + + bt_hci_register(user->hci_lt, BT_HCI_EVT_CONN_REQUEST, + setup_lt_connect_request_accept, NULL, NULL); + + cmd.enable = 0x02; + + if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_WRITE_SCAN_ENABLE, + &cmd, sizeof(cmd), + setup_lt_connectable_complete, NULL, NULL)) { + tester_warn("Failed to send HCI scan enable command"); + tester_setup_failed(); + return; + } +} + +static void test_create_connection_disconnect(void *user_data) +{ + tester_test_passed(); +} + +static void test_create_connection_complete(const void *data, uint8_t size, + void *user_data) +{ + struct user_data *user = tester_get_data(); + const struct bt_hci_evt_conn_complete *evt = data; + + if (evt->status) { + tester_warn("HCI create connection complete failed (0x%02x)", + evt->status); + tester_test_failed(); + return; + } + + user->handle_ut = le16_to_cpu(evt->handle); + + tester_wait(2, test_create_connection_disconnect, NULL); +} + +static void test_create_connection_status(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + tester_warn("HCI create connection command failed (0x%02x)", + status); + tester_test_failed(); + return; + } +} + +static void test_create_connection(const void *test_data) +{ + struct user_data *user = tester_get_data(); + struct bt_hci_cmd_create_conn cmd; + + bt_hci_register(user->hci_ut, BT_HCI_EVT_CONN_COMPLETE, + test_create_connection_complete, NULL, NULL); + + memcpy(cmd.bdaddr, user->bdaddr_lt, 6); + cmd.pkt_type = cpu_to_le16(0x0008); + cmd.pscan_rep_mode = 0x02; + cmd.pscan_mode = 0x00; + cmd.clock_offset = cpu_to_le16(0x0000); + cmd.role_switch = 0x01; + + if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_CREATE_CONN, + &cmd, sizeof(cmd), + test_create_connection_status, + NULL, NULL)) { + tester_warn("Failed to send HCI create connection command"); + tester_test_failed(); + return; + } +} + +static void teardown_timeout(void *user_data) +{ + tester_teardown_complete(); +} + +static void teardown_disconnect_status(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + tester_warn("HCI disconnect failed (0x%02x)", status); + tester_teardown_failed(); + return; + } + + tester_wait(1, teardown_timeout, NULL); +} + +static void teardown_connection(const void *test_data) +{ + struct user_data *user = tester_get_data(); + struct bt_hci_cmd_disconnect cmd; + + cmd.handle = cpu_to_le16(user->handle_ut); + cmd.reason = 0x13; + + if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_DISCONNECT, + &cmd, sizeof(cmd), + teardown_disconnect_status, + NULL, NULL)) { + tester_warn("Failed to send HCI disconnect command"); + tester_test_failed(); + return; + } +} + +static void test_adv_report(const void *data, uint8_t size, void *user_data) +{ + struct user_data *user = tester_get_data(); + uint8_t subevent = *((uint8_t *) data); + const struct bt_hci_evt_le_adv_report *lar = data + 1; + + switch (subevent) { + case BT_HCI_EVT_LE_ADV_REPORT: + if (!memcmp(lar->addr, user->bdaddr_ut, 6)) + tester_setup_complete(); + break; + } +} + +static void setup_advertising_initiated(const void *test_data) +{ + struct user_data *user = tester_get_data(); + struct bt_hci_cmd_set_event_mask sem; + struct bt_hci_cmd_le_set_event_mask lsem; + struct bt_hci_cmd_le_set_scan_enable lsse; + struct bt_hci_cmd_le_set_adv_parameters lsap; + struct bt_hci_cmd_le_set_adv_enable lsae; + + bt_hci_register(user->hci_lt, BT_HCI_EVT_LE_META_EVENT, + test_adv_report, NULL, NULL); + + memset(sem.mask, 0, 8); + sem.mask[1] |= 0x20; /* Command Complete */ + sem.mask[1] |= 0x40; /* Command Status */ + sem.mask[7] |= 0x20; /* LE Meta */ + + bt_hci_send(user->hci_lt, BT_HCI_CMD_SET_EVENT_MASK, + &sem, sizeof(sem), NULL, NULL, NULL); + + memset(lsem.mask, 0, 8); + lsem.mask[0] |= 0x02; /* LE Advertising Report */ + + bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_EVENT_MASK, + &lsem, sizeof(lsem), NULL, NULL, NULL); + + lsse.enable = 0x01; + lsse.filter_dup = 0x00; + + bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_SCAN_ENABLE, + &lsse, sizeof(lsse), NULL, NULL, NULL); + + lsap.min_interval = cpu_to_le16(0x0800); + lsap.max_interval = cpu_to_le16(0x0800); + lsap.type = 0x03; + lsap.own_addr_type = 0x00; + lsap.direct_addr_type = 0x00; + memset(lsap.direct_addr, 0, 6); + lsap.channel_map = 0x07; + lsap.filter_policy = 0x00; + + bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + &lsap, sizeof(lsap), NULL, NULL, NULL); + + lsae.enable = 0x01; + + bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_ENABLE, + &lsae, sizeof(lsae), NULL, NULL, NULL); +} + +static void test_reset_in_advertising_state_timeout(void *user_data) +{ + struct user_data *user = tester_get_data(); + struct bt_hci_cmd_le_set_adv_enable lsae; + struct bt_hci_cmd_le_set_scan_enable lsse; + + lsae.enable = 0x00; + + bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_ENABLE, + &lsae, sizeof(lsae), NULL, NULL, NULL); + + lsse.enable = 0x00; + lsse.filter_dup = 0x00; + + bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_SCAN_ENABLE, + &lsse, sizeof(lsse), NULL, NULL, NULL); + + tester_test_passed(); +} + +static void test_reset_in_advertising_state(const void *test_data) +{ + struct user_data *user = tester_get_data(); + + bt_hci_send(user->hci_ut, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL); + + tester_wait(5, test_reset_in_advertising_state_timeout, NULL); +} + +int main(int argc, char *argv[]) +{ + tester_init(&argc, &argv); + + test_hci_local("Reset", NULL, NULL, test_reset); + + test_hci_local("Read Local Version Information", NULL, NULL, + test_read_local_version_information); + test_hci_local("Read Local Supported Commands", NULL, NULL, + test_read_local_supported_commands); + test_hci_local("Read Local Supported Features", NULL, NULL, + test_read_local_supported_features); + test_hci_local("Read Local Extended Features", NULL, + setup_features, + test_read_local_extended_features); + test_hci_local("Read Buffer Size", NULL, NULL, + test_read_buffer_size); + test_hci_local("Read Country Code", NULL, NULL, + test_read_country_code); + test_hci_local("Read BD_ADDR", NULL, NULL, + test_read_bd_addr); + test_hci_local("Read Local Supported Codecs", NULL, NULL, + test_read_local_supported_codecs); + + test_hci_local("LE Read White List Size", NULL, NULL, + test_le_read_white_list_size); + test_hci_local("LE Clear White List", NULL, NULL, + test_le_clear_white_list); + + test_hci_local("Inquiry (LIAC)", NULL, NULL, test_inquiry_liac); + + test_hci("Create Connection", NULL, + setup_lt_connectable, + test_create_connection, + teardown_connection); + + test_hci("TP/DSU/BV-02-C Reset in Advertising State", NULL, + setup_advertising_initiated, + test_reset_in_advertising_state, NULL); + + return tester_run(); +} diff --git a/tools/hciattach.c b/tools/hciattach.c index db01b85..1904ac5 100644 --- a/tools/hciattach.c +++ b/tools/hciattach.c @@ -84,7 +84,7 @@ static void sig_alarm(int sig) exit(1); } -static int uart_speed(int s) +int uart_speed(int s) { switch (s) { case 9600: @@ -327,6 +327,11 @@ static int intel(int fd, struct uart_t *u, struct termios *ti) return intel_init(fd, u->init_speed, &u->speed, ti); } +static int bcm43xx(int fd, struct uart_t *u, struct termios *ti) +{ + return bcm43xx_init(fd, u->speed, ti, u->bdaddr); +} + static int read_check(int fd, void *buf, int count) { int res; @@ -1135,6 +1140,10 @@ struct uart_t uart[] = { { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800, FLOW_CTL, DISABLE_PM, NULL, bcm2035 }, + /* Broadcom BCM43XX */ + { "bcm43xx", 0x0000, 0x0000, HCI_UART_H4, 115200, 3000000, + FLOW_CTL, DISABLE_PM, NULL, bcm43xx, NULL }, + { "ath3k", 0x0000, 0x0000, HCI_UART_ATH3K, 115200, 115200, FLOW_CTL, DISABLE_PM, NULL, ath3k_ps, ath3k_pm }, diff --git a/tools/hciattach.h b/tools/hciattach.h index 1b23ad7..4810a09 100644 --- a/tools/hciattach.h +++ b/tools/hciattach.h @@ -46,6 +46,7 @@ int read_hci_event(int fd, unsigned char *buf, int size); int set_speed(int fd, struct termios *ti, int speed); +int uart_speed(int speed); int texas_init(int fd, int *speed, struct termios *ti); int texas_post(int fd, struct termios *ti); @@ -57,3 +58,4 @@ int ath3k_init(int fd, int speed, int init_speed, char *bdaddr, int ath3k_post(int fd, int pm); int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr); int intel_init(int fd, int init_speed, int *speed, struct termios *ti); +int bcm43xx_init(int fd, int speed, struct termios *ti, const char *bdaddr); diff --git a/tools/hciattach_bcm43xx.c b/tools/hciattach_bcm43xx.c new file mode 100644 index 0000000..ad9b239 --- /dev/null +++ b/tools/hciattach_bcm43xx.c @@ -0,0 +1,378 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hciattach.h" + +#ifndef FIRMWARE_DIR +#define FIRMWARE_DIR "/etc/firmware" +#endif + +#define FW_EXT ".hcd" + +#define BCM43XX_CLOCK_48 1 +#define BCM43XX_CLOCK_24 2 + +#define CMD_SUCCESS 0x00 + +#define CC_MIN_SIZE 7 + +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +static int bcm43xx_read_local_name(int fd, char *name, size_t size) +{ + unsigned char cmd[] = { HCI_COMMAND_PKT, 0x14, 0x0C, 0x00 }; + unsigned char *resp; + unsigned int name_len; + + resp = malloc(size + CC_MIN_SIZE); + if (!resp) + return -1; + + tcflush(fd, TCIOFLUSH); + + if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { + fprintf(stderr, "Failed to write read local name command\n"); + goto fail; + } + + if (read_hci_event(fd, resp, size) < CC_MIN_SIZE) { + fprintf(stderr, "Failed to read local name, invalid HCI event\n"); + goto fail; + } + + if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { + fprintf(stderr, "Failed to read local name, command failure\n"); + goto fail; + } + + name_len = resp[2] - 1; + + strncpy(name, (char *) &resp[7], MIN(name_len, size)); + name[size - 1] = 0; + + free(resp); + return 0; + +fail: + free(resp); + return -1; +} + +static int bcm43xx_reset(int fd) +{ + unsigned char cmd[] = { HCI_COMMAND_PKT, 0x03, 0x0C, 0x00 }; + unsigned char resp[CC_MIN_SIZE]; + + if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { + fprintf(stderr, "Failed to write reset command\n"); + return -1; + } + + if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { + fprintf(stderr, "Failed to reset chip, invalid HCI event\n"); + return -1; + } + + if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { + fprintf(stderr, "Failed to reset chip, command failure\n"); + return -1; + } + + return 0; +} + +static int bcm43xx_set_bdaddr(int fd, const char *bdaddr) +{ + unsigned char cmd[] = + { HCI_COMMAND_PKT, 0x01, 0xfc, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + unsigned char resp[CC_MIN_SIZE]; + + printf("Set BDADDR UART: %s\n", bdaddr); + + if (str2ba(bdaddr, (bdaddr_t *) (&cmd[4])) < 0) { + fprintf(stderr, "Incorrect bdaddr\n"); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { + fprintf(stderr, "Failed to write set bdaddr command\n"); + return -1; + } + + if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { + fprintf(stderr, "Failed to set bdaddr, invalid HCI event\n"); + return -1; + } + + if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { + fprintf(stderr, "Failed to set bdaddr, command failure\n"); + return -1; + } + + return 0; +} + +static int bcm43xx_set_speed(int fd, uint32_t speed) +{ + unsigned char cmd[] = + { HCI_COMMAND_PKT, 0x18, 0xfc, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + unsigned char resp[CC_MIN_SIZE]; + + printf("Set Controller UART speed to %d bit/s\n", speed); + + cmd[6] = (uint8_t) (speed); + cmd[7] = (uint8_t) (speed >> 8); + cmd[8] = (uint8_t) (speed >> 16); + cmd[9] = (uint8_t) (speed >> 24); + + tcflush(fd, TCIOFLUSH); + + if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { + fprintf(stderr, "Failed to write update baudrate command\n"); + return -1; + } + + if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { + fprintf(stderr, "Failed to update baudrate, invalid HCI event\n"); + return -1; + } + + if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { + fprintf(stderr, "Failed to update baudrate, command failure\n"); + return -1; + } + + return 0; +} + +static int bcm43xx_set_clock(int fd, unsigned char clock) +{ + unsigned char cmd[] = { HCI_COMMAND_PKT, 0x45, 0xfc, 0x01, 0x00 }; + unsigned char resp[CC_MIN_SIZE]; + + printf("Set Controller clock (%d)\n", clock); + + cmd[4] = clock; + + tcflush(fd, TCIOFLUSH); + + if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { + fprintf(stderr, "Failed to write update clock command\n"); + return -1; + } + + if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { + fprintf(stderr, "Failed to update clock, invalid HCI event\n"); + return -1; + } + + if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { + fprintf(stderr, "Failed to update clock, command failure\n"); + return -1; + } + + return 0; +} + +static int bcm43xx_load_firmware(int fd, const char *fw) +{ + unsigned char cmd[] = { HCI_COMMAND_PKT, 0x2e, 0xfc, 0x00 }; + struct timespec tm_mode = { 0, 50000 }; + struct timespec tm_ready = { 0, 2000000 }; + unsigned char resp[CC_MIN_SIZE]; + unsigned char tx_buf[1024]; + int len, fd_fw, n; + + printf("Flash firmware %s\n", fw); + + fd_fw = open(fw, O_RDONLY); + if (fd_fw < 0) { + fprintf(stderr, "Unable to open firmware (%s)\n", fw); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) { + fprintf(stderr, "Failed to write download mode command\n"); + goto fail; + } + + if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) { + fprintf(stderr, "Failed to load firmware, invalid HCI event\n"); + goto fail; + } + + if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) { + fprintf(stderr, "Failed to load firmware, command failure\n"); + goto fail; + } + + /* Wait 50ms to let the firmware placed in download mode */ + nanosleep(&tm_mode, NULL); + + tcflush(fd, TCIOFLUSH); + + while ((n = read(fd_fw, &tx_buf[1], 3))) { + if (n < 0) { + fprintf(stderr, "Failed to read firmware\n"); + goto fail; + } + + tx_buf[0] = HCI_COMMAND_PKT; + + len = tx_buf[3]; + + if (read(fd_fw, &tx_buf[4], len) < 0) { + fprintf(stderr, "Failed to read firmware\n"); + goto fail; + } + + if (write(fd, tx_buf, len + 4) != (len + 4)) { + fprintf(stderr, "Failed to write firmware\n"); + goto fail; + } + + read_hci_event(fd, resp, sizeof(resp)); + tcflush(fd, TCIOFLUSH); + } + + /* Wait for firmware ready */ + nanosleep(&tm_ready, NULL); + + close(fd_fw); + return 0; + +fail: + close(fd_fw); + return -1; +} + +static int bcm43xx_locate_patch(const char *dir_name, + const char *chip_name, char *location) +{ + DIR *dir; + int ret = -1; + + dir = opendir(dir_name); + if (!dir) { + fprintf(stderr, "Cannot open directory '%s': %s\n", + dir_name, strerror(errno)); + return -1; + } + + /* Recursively look for a BCM43XX*.hcd */ + while (1) { + struct dirent *entry = readdir(dir); + if (!entry) + break; + + if (entry->d_type & DT_DIR) { + char path[PATH_MAX]; + + if (!strcmp(entry->d_name, "..") || !strcmp(entry->d_name, ".")) + continue; + + snprintf(path, PATH_MAX, "%s/%s", dir_name, entry->d_name); + + ret = bcm43xx_locate_patch(path, chip_name, location); + if (!ret) + break; + } else if (!strncmp(chip_name, entry->d_name, strlen(chip_name))) { + unsigned int name_len = strlen(entry->d_name); + size_t curs_ext = name_len - sizeof(FW_EXT) + 1; + + if (curs_ext > name_len) + break; + + if (strncmp(FW_EXT, &entry->d_name[curs_ext], sizeof(FW_EXT))) + break; + + /* found */ + snprintf(location, PATH_MAX, "%s/%s", dir_name, entry->d_name); + ret = 0; + break; + } + } + + closedir(dir); + + return ret; +} + +int bcm43xx_init(int fd, int speed, struct termios *ti, const char *bdaddr) +{ + char chip_name[20]; + char fw_path[PATH_MAX]; + + printf("bcm43xx_init\n"); + + if (bcm43xx_reset(fd)) + return -1; + + if (bcm43xx_read_local_name(fd, chip_name, sizeof(chip_name))) + return -1; + + if (bcm43xx_locate_patch(FIRMWARE_DIR, chip_name, fw_path)) { + fprintf(stderr, "Patch not found, continue anyway\n"); + } else { + if (bcm43xx_load_firmware(fd, fw_path)) + return -1; + + if (bcm43xx_reset(fd)) + return -1; + } + + if (bdaddr) + bcm43xx_set_bdaddr(fd, bdaddr); + + if (speed > 3000000 && bcm43xx_set_clock(fd, BCM43XX_CLOCK_48)) + return -1; + + if (bcm43xx_set_speed(fd, speed)) + return -1; + + return 0; +} diff --git a/tools/hciattach_qualcomm.c b/tools/hciattach_qualcomm.c index 0e02e1e..eb72a0f 100644 --- a/tools/hciattach_qualcomm.c +++ b/tools/hciattach_qualcomm.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include diff --git a/tools/hciattach_tialt.c b/tools/hciattach_tialt.c index c3caa49..5c7f3a5 100644 --- a/tools/hciattach_tialt.c +++ b/tools/hciattach_tialt.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include diff --git a/tools/hciconfig.c b/tools/hciconfig.c index fe45167..765d980 100644 --- a/tools/hciconfig.c +++ b/tools/hciconfig.c @@ -37,13 +37,15 @@ #include #include #include +#include #include #include #include -#include "textfile.h" -#include "csr.h" +#include "src/textfile.h" +#include "src/shared/util.h" +#include "tools/csr.h" static struct hci_dev_info di; static int all; @@ -824,25 +826,32 @@ static char *get_minor_device_name(int major, int minor) case 0: break; case 1: - strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Joystick", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 2: - strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Gamepad", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 3: - strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Remote control", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 4: - strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Sensing device", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 5: - strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Digitizer tablet", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 6: - strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Card reader", + sizeof(cls_str) - strlen(cls_str) - 1); break; default: - strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "(reserved)", + sizeof(cls_str) - strlen(cls_str) - 1); break; } if (strlen(cls_str) > 0) @@ -1308,7 +1317,7 @@ static void cmd_inq_data(int ctl, int hdev, char *opt) printf("\t%s service classes:", type == 0x02 ? "Shortened" : "Complete"); for (i = 0; i < (len - 1) / 2; i++) { - uint16_t val = bt_get_le16((ptr + (i * 2))); + uint16_t val = get_le16((ptr + (i * 2))); printf(" 0x%4.4x", val); } printf("\n"); diff --git a/tools/hcidump.c b/tools/hcidump.c index 055c8fa..37a9f00 100644 --- a/tools/hcidump.c +++ b/tools/hcidump.c @@ -59,7 +59,6 @@ enum { /* Default options */ static int snap_len = SNAP_LEN; static int mode = PARSE; -static int permcheck = 1; static char *dump_file = NULL; static char *pppdump_file = NULL; static char *audio_file = NULL; @@ -252,15 +251,15 @@ static int process_frames(int dev, int sock, int fd, unsigned long flags) if (flags & DUMP_BTSNOOP) { uint64_t ts; uint8_t pkt_type = ((uint8_t *) frm.data)[0]; - dp->size = htonl(frm.data_len); + dp->size = htobe32(frm.data_len); dp->len = dp->size; - dp->flags = ntohl(frm.in & 0x01); + dp->flags = be32toh(frm.in & 0x01); dp->drops = 0; ts = (frm.ts.tv_sec - 946684800ll) * 1000000ll + frm.ts.tv_usec; - dp->ts = hton64(ts + 0x00E03AB44A676000ll); + dp->ts = htobe64(ts + 0x00E03AB44A676000ll); if (pkt_type == HCI_COMMAND_PKT || pkt_type == HCI_EVENT_PKT) - dp->flags |= ntohl(0x02); + dp->flags |= be32toh(0x02); } else { dh->len = htobs(frm.data_len); dh->in = frm.in; @@ -309,7 +308,7 @@ static void read_dump(int fd) if (err < 0) goto failed; if (!err) - return; + goto done; if (parser.flags & DUMP_PKTLOG) { switch (ph.type) { @@ -330,11 +329,11 @@ static void read_dump(int fd) frm.in = 1; break; default: - lseek(fd, ntohl(ph.len) - 9, SEEK_CUR); + lseek(fd, be32toh(ph.len) - 9, SEEK_CUR); continue; } - frm.data_len = ntohl(ph.len) - 8; + frm.data_len = be32toh(ph.len) - 8; err = read_n(fd, frm.data + 1, frm.data_len - 1); } else if (parser.flags & DUMP_BTSNOOP) { uint32_t opcode; @@ -342,8 +341,8 @@ static void read_dump(int fd) switch (btsnoop_type) { case 1001: - if (ntohl(dp.flags) & 0x02) { - if (ntohl(dp.flags) & 0x01) + if (be32toh(dp.flags) & 0x02) { + if (be32toh(dp.flags) & 0x01) pkt_type = HCI_EVENT_PKT; else pkt_type = HCI_COMMAND_PKT; @@ -352,17 +351,17 @@ static void read_dump(int fd) ((uint8_t *) frm.data)[0] = pkt_type; - frm.data_len = ntohl(dp.len) + 1; + frm.data_len = be32toh(dp.len) + 1; err = read_n(fd, frm.data + 1, frm.data_len - 1); break; case 1002: - frm.data_len = ntohl(dp.len); + frm.data_len = be32toh(dp.len); err = read_n(fd, frm.data, frm.data_len); break; case 2001: - opcode = ntohl(dp.flags) & 0xffff; + opcode = be32toh(dp.flags) & 0xffff; switch (opcode) { case 2: @@ -396,7 +395,7 @@ static void read_dump(int fd) ((uint8_t *) frm.data)[0] = pkt_type; - frm.data_len = ntohl(dp.len) + 1; + frm.data_len = be32toh(dp.len) + 1; err = read_n(fd, frm.data + 1, frm.data_len - 1); } } else { @@ -407,20 +406,20 @@ static void read_dump(int fd) if (err < 0) goto failed; if (!err) - return; + goto done; frm.ptr = frm.data; frm.len = frm.data_len; if (parser.flags & DUMP_PKTLOG) { uint64_t ts; - ts = ntoh64(ph.ts); + ts = be64toh(ph.ts); frm.ts.tv_sec = ts >> 32; frm.ts.tv_usec = ts & 0xffffffff; } else if (parser.flags & DUMP_BTSNOOP) { uint64_t ts; - frm.in = ntohl(dp.flags) & 0x01; - ts = ntoh64(dp.ts) - 0x00E03AB44A676000ll; + frm.in = be32toh(dp.flags) & 0x01; + ts = be64toh(dp.ts) - 0x00E03AB44A676000ll; frm.ts.tv_sec = (ts / 1000000ll) + 946684800ll; frm.ts.tv_usec = ts % 1000000ll; } else { @@ -432,8 +431,13 @@ static void read_dump(int fd) parse(&frm); } +done: + free(frm.data); + return; + failed: perror("Read failed"); + free(frm.data); exit(1); } @@ -464,8 +468,8 @@ static int open_file(char *file, int mode, unsigned long flags) if (!memcmp(hdr->id, btsnoop_id, sizeof(btsnoop_id))) { parser.flags |= DUMP_BTSNOOP; - btsnoop_version = ntohl(hdr->version); - btsnoop_type = ntohl(hdr->type); + btsnoop_version = be32toh(hdr->version); + btsnoop_type = be32toh(hdr->type); printf("btsnoop version: %d datalink type: %d\n", btsnoop_version, btsnoop_type); @@ -496,8 +500,8 @@ static int open_file(char *file, int mode, unsigned long flags) btsnoop_type = 1002; memcpy(hdr->id, btsnoop_id, sizeof(btsnoop_id)); - hdr->version = htonl(btsnoop_version); - hdr->type = htonl(btsnoop_type); + hdr->version = htobe32(btsnoop_version); + hdr->type = htobe32(btsnoop_type); printf("btsnoop version: %d datalink type: %d\n", btsnoop_version, btsnoop_type); @@ -522,31 +526,7 @@ static int open_socket(int dev, unsigned long flags) { struct sockaddr_hci addr; struct hci_filter flt; - struct hci_dev_info di; - int sk, dd, opt; - - if (permcheck && dev != HCI_DEV_NONE) { - dd = hci_open_dev(dev); - if (dd < 0) { - perror("Can't open device"); - return -1; - } - - if (hci_devinfo(dev, &di) < 0) { - perror("Can't get device info"); - return -1; - } - - opt = hci_test_bit(HCI_RAW, &di.flags); - if (ioctl(dd, HCISETRAW, opt) < 0) { - if (errno == EACCES) { - perror("Can't access device"); - return -1; - } - } - - hci_close_dev(dd); - } + int sk, opt; /* Create HCI socket */ sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); @@ -683,7 +663,6 @@ static struct option main_options[] = { { "pppdump", 1, 0, 'D' }, { "audio", 1, 0, 'A' }, { "novendor", 0, 0, 'Y' }, - { "nopermcheck", 0, 0, 'Z' }, { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { 0 } @@ -700,7 +679,7 @@ int main(int argc, char *argv[]) uint16_t obex_port; while ((opt = getopt_long(argc, argv, - "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:YZhv", + "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Yhv", main_options, NULL)) != -1) { switch(opt) { case 'i': @@ -788,10 +767,6 @@ int main(int argc, char *argv[]) flags |= DUMP_NOVENDOR; break; - case 'Z': - permcheck = 0; - break; - case 'v': printf("%s\n", VERSION); exit(0); diff --git a/tools/hcitool.c b/tools/hcitool.c index f2e4fa4..ffaf953 100644 --- a/tools/hcitool.c +++ b/tools/hcitool.c @@ -40,14 +40,11 @@ #include #include -#include - #include #include #include -#include "textfile.h" -#include "oui.h" +#include "src/oui.h" /* Unofficial value, might still change */ #define LE_LINK 0x03 @@ -336,25 +333,32 @@ static char *get_minor_device_name(int major, int minor) case 0: break; case 1: - strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Joystick", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 2: - strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Gamepad", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 3: - strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Remote control", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 4: - strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Sensing device", + sizeof(cls_str) - strlen(cls_str) - 1); break; case 5: - strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str)); - break; + strncat(cls_str, "Digitizer tablet", + sizeof(cls_str) - strlen(cls_str) - 1); + break; case 6: - strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "Card reader", + sizeof(cls_str) - strlen(cls_str) - 1); break; default: - strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str)); + strncat(cls_str, "(reserved)", + sizeof(cls_str) - strlen(cls_str) - 1); break; } if (strlen(cls_str) > 0) @@ -409,36 +413,6 @@ static char *major_classes[] = { "Audio/Video", "Peripheral", "Imaging", "Uncategorized" }; -static char *get_device_name(const bdaddr_t *local, const bdaddr_t *peer) -{ - char filename[PATH_MAX + 1]; - char local_addr[18], peer_addr[18]; - GKeyFile *key_file; - char *str = NULL; - int len; - - ba2str(local, local_addr); - ba2str(peer, peer_addr); - - snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local_addr, - peer_addr); - filename[PATH_MAX] = '\0'; - key_file = g_key_file_new(); - - if (g_key_file_load_from_file(key_file, filename, 0, NULL)) { - str = g_key_file_get_string(key_file, "General", "Name", NULL); - if (str) { - len = strlen(str); - if (len > HCI_MAX_NAME_LENGTH) - str[HCI_MAX_NAME_LENGTH] = '\0'; - } - } - - g_key_file_free(key_file); - - return str; -} - /* Display local devices */ static struct option dev_options[] = { @@ -562,7 +536,6 @@ static struct option scan_options[] = { { "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' }, @@ -581,12 +554,12 @@ static void cmd_scan(int dev_id, int argc, char **argv) uint8_t lap[3] = { 0x33, 0x8b, 0x9e }; int num_rsp, length, flags; uint8_t cls[3], features[8]; - char addr[18], name[249], *comp, *tmp; + char addr[18], name[249], *comp; 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; + int extcls = 0, extinf = 0, extoui = 0; + int i, n, l, opt, dd, cc; length = 8; /* ~10 seconds */ num_rsp = 0; @@ -621,10 +594,6 @@ static void cmd_scan(int dev_id, int argc, char **argv) flags |= IREQ_CACHE_FLUSH; break; - case 'r': - refresh = 1; - break; - case 'C': extcls = 1; break; @@ -683,26 +652,9 @@ static void cmd_scan(int dev_id, int argc, char **argv) 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, @@ -763,27 +715,22 @@ static void cmd_scan(int dev_id, int argc, char **argv) } } - if (handle > 0 || !nc) { - if (hci_read_remote_name_with_clock_offset(dd, + 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; + } else { + for (n = 0; n < 248 && name[n]; n++) { + if ((unsigned char) name[i] < 32 || name[i] == 127) + name[i] = '.'; } + + name[248] = '\0'; } if (strlen(name) > 0) - printf("Device name:\t%s%s\n", name, nc ? " [cached]" : ""); + printf("Device name:\t%s\n", name); if (extcls) { memcpy(cls, (info+i)->dev_class, 3); @@ -954,6 +901,7 @@ static void cmd_info(int dev_id, int argc, char **argv) htobs(di.pkt_type & ACL_PTYPE_MASK), 0, 0x01, &handle, 25000) < 0) { perror("Can't create connection"); + free(cr); close(dd); exit(1); } @@ -962,6 +910,8 @@ static void cmd_info(int dev_id, int argc, char **argv) } else handle = htobs(cr->conn_info->handle); + free(cr); + printf("\tBD Address: %s\n", argv[0]); comp = batocomp(&bdaddr); diff --git a/tools/hex2hcd.c b/tools/hex2hcd.c new file mode 100644 index 0000000..d9b5d3b --- /dev/null +++ b/tools/hex2hcd.c @@ -0,0 +1,143 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Canonical + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have 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 +#endif + +#include +#include +#include + +#define RBUF_SIZE 640 + +static unsigned int asc_to_int(char a) +{ + if (a >= 'A') + return (a - 'A') + 10; + else + return a - '0'; +} + +static unsigned int hex_to_int(const char *h) +{ + return asc_to_int(*h) * 0x10 + asc_to_int(*(h + 1)); +} + +static unsigned int lhex_to_int(const char *h) +{ + return hex_to_int(h) * 0x100 + hex_to_int(h + 2); +} + +static int check_sum(const char *str, int len) +{ + unsigned int sum, cal; + int i; + sum = hex_to_int(str + len - 2); + for (cal = 0, i = 1; i < len - 2; i += 2) + cal += hex_to_int(str + i); + cal = 0x100 - (cal & 0xFF); + return sum == cal; +} + +static int check_hex_line(const char *str, unsigned int len) +{ + if ((str[0] != ':') || (len < 11) || !check_sum(str, len) || + (hex_to_int(str + 1) * 2 + 11 != len)) + return 0; + return 1; +} + +int main(int argc, char *argv[]) +{ + unsigned int i, addr = 0; + FILE *ifp, *ofp; + char *rbuf; + ssize_t len; + size_t buflen; + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return 0; + } + + ifp = fopen(argv[1], "r"); + ofp = fopen(argv[2], "w"); + if ((ifp == NULL) || (ofp == NULL)) { + puts("failed to open file."); + return -EIO; + } + + rbuf = NULL; + while ((len = getline(&rbuf, &buflen, ifp)) > 0) { + int type; + char obuf[7]; + unsigned int dest_addr; + while ((rbuf[len - 1] == '\r') || (rbuf[len - 1] == '\n')) + len--; + printf("%d, %s\n", (int)len, rbuf); + if (!check_hex_line(rbuf, len)) + break; + type = hex_to_int(rbuf + 7); + switch (type) { + case 4: + addr = lhex_to_int(rbuf + 9) * 0x10000; + printf("bump addr to 0x%08X\n", addr); + break; + case 0: + dest_addr = addr + lhex_to_int(rbuf + 3); + obuf[0] = 0x4c; + obuf[1] = 0xfc; + obuf[2] = hex_to_int(rbuf + 1) + 4; + obuf[3] = dest_addr; + obuf[4] = dest_addr >> 8; + obuf[5] = dest_addr >> 16; + obuf[6] = dest_addr >> 24; + if (fwrite(obuf, 7, 1, ofp) != 1) + goto output_err; + for (i = 0; i < hex_to_int(rbuf + 1); i++) { + obuf[0] = hex_to_int(rbuf + 9 + i * 2); + if (fwrite(obuf, 1, 1, ofp) != 1) + goto output_err; + } + break; + case 1: + if (fwrite("\x4e\xfc\x04\xff\xff\xff\xff", 7, 1, ofp) != 1) + goto output_err; + goto end; + default: + return -EINVAL; + } + } + + puts("hex file formatting error"); + return -EINVAL; + +output_err: + puts("error on writing output file"); + return -EIO; + +end: + return 0; +} + diff --git a/tools/hid2hci.c b/tools/hid2hci.c index 95b4abf..2dbfca7 100644 --- a/tools/hid2hci.c +++ b/tools/hid2hci.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/tools/ibeacon.c b/tools/ibeacon.c new file mode 100644 index 0000000..28967de --- /dev/null +++ b/tools/ibeacon.c @@ -0,0 +1,312 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "monitor/mainloop.h" +#include "monitor/bt.h" +#include "src/shared/timeout.h" +#include "src/shared/util.h" +#include "src/shared/hci.h" + +static int urandom_fd; +static struct bt_hci *hci_dev; + +static bool shutdown_timeout(void *user_data) +{ + mainloop_quit(); + + return false; +} + +static void shutdown_complete(const void *data, uint8_t size, void *user_data) +{ + unsigned int id = PTR_TO_UINT(user_data); + + timeout_remove(id); + mainloop_quit(); +} + +static void shutdown_device(void) +{ + uint8_t enable = 0x00; + unsigned int id; + + bt_hci_flush(hci_dev); + + id = timeout_add(5000, shutdown_timeout, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE, + &enable, 1, NULL, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, + shutdown_complete, UINT_TO_PTR(id), NULL); +} + +static void set_random_address(void) +{ + struct bt_hci_cmd_le_set_random_address cmd; + ssize_t len; + + len = read(urandom_fd, cmd.addr, sizeof(cmd.addr)); + if (len < 0 || len != sizeof(cmd.addr)) { + fprintf(stderr, "Failed to read random data\n"); + return; + } + + /* Clear top most significant bits */ + cmd.addr[5] &= 0x3f; + + bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, + &cmd, sizeof(cmd), NULL, NULL, NULL); +} + +static void set_adv_parameters(void) +{ + struct bt_hci_cmd_le_set_adv_parameters cmd; + + cmd.min_interval = cpu_to_le16(0x0800); + cmd.max_interval = cpu_to_le16(0x0800); + cmd.type = 0x03; /* Non-connectable advertising */ + cmd.own_addr_type = 0x01; /* Use random address */ + cmd.direct_addr_type = 0x00; + memset(cmd.direct_addr, 0, 6); + cmd.channel_map = 0x07; + cmd.filter_policy = 0x00; + + bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + &cmd, sizeof(cmd), NULL, NULL, NULL); +} + +static void set_adv_enable(void) +{ + uint8_t enable = 0x01; + + bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE, + &enable, 1, NULL, NULL, NULL); +} + +static void adv_data_callback(const void *data, uint8_t size, + void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + fprintf(stderr, "Failed to set advertising data\n"); + shutdown_device(); + return; + } + + set_random_address(); + set_adv_parameters(); + set_adv_enable(); +} + +static void adv_tx_power_callback(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data; + struct bt_hci_cmd_le_set_adv_data cmd; + + if (rsp->status) { + fprintf(stderr, "Failed to read advertising TX power\n"); + shutdown_device(); + return; + } + + cmd.data[0] = 0x02; /* Field length */ + cmd.data[1] = 0x01; /* Flags */ + cmd.data[2] = 0x02; /* LE General Discoverable Mode */ + cmd.data[2] |= 0x04; /* BR/EDR Not Supported */ + + cmd.data[3] = 0x1a; /* Field length */ + cmd.data[4] = 0xff; /* Vendor field */ + cmd.data[5] = 0x4c; /* Apple (76) - LSB */ + cmd.data[6] = 0x00; /* Apple (76) - MSB */ + cmd.data[7] = 0x02; /* iBeacon type */ + cmd.data[8] = 0x15; /* Length */ + memset(cmd.data + 9, 0, 16); /* UUID */ + cmd.data[25] = 0x00; /* Major - LSB */ + cmd.data[26] = 0x00; /* Major - MSB */ + cmd.data[27] = 0x00; /* Minor - LSB */ + cmd.data[28] = 0x00; /* Minor - MSB */ + cmd.data[29] = 0xc5; /* TX power level */ + + cmd.data[30] = 0x00; /* Field terminator */ + + cmd.len = 1 + cmd.data[0] + 1 + cmd.data[3]; + + bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd), + adv_data_callback, NULL, NULL); +} + +static void local_features_callback(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_rsp_read_local_features *rsp = data; + + if (rsp->status) { + fprintf(stderr, "Failed to read local features\n"); + shutdown_device(); + return; + } + + if (!(rsp->features[4] & 0x40)) { + fprintf(stderr, "Controller without Low Energy support\n"); + shutdown_device(); + return; + } + + bt_hci_send(hci_dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, NULL, 0, + adv_tx_power_callback, NULL, NULL); +} + +static void start_ibeacon(void) +{ + bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL); + + bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0, + local_features_callback, NULL, NULL); +} + +static void signal_callback(int signum, void *user_data) +{ + static bool terminated = false; + + switch (signum) { + case SIGINT: + case SIGTERM: + if (!terminated) { + shutdown_device(); + terminated = true; + } + break; + } +} + +static void usage(void) +{ + printf("ibeacon - Low Energy iBeacon testing tool\n" + "Usage:\n"); + printf("\tibeacon [options]\n"); + printf("Options:\n" + "\t-i, --index Use specified controller\n" + "\t-h, --help Show help options\n"); +} + +static const struct option main_options[] = { + { "index", required_argument, NULL, 'i' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } +}; + +int main(int argc, char *argv[]) +{ + uint16_t index = 0; + const char *str; + sigset_t mask; + int exit_status; + + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "i:vh", main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'i': + if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3)) + str = optarg + 3; + else + str = optarg; + if (!isdigit(*str)) { + usage(); + return EXIT_FAILURE; + } + index = atoi(str); + break; + case 'v': + printf("%s\n", VERSION); + return EXIT_SUCCESS; + case 'h': + usage(); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } + } + + if (argc - optind > 0) { + fprintf(stderr, "Invalid command line parameters\n"); + return EXIT_FAILURE; + } + + urandom_fd = open("/dev/urandom", O_RDONLY); + if (urandom_fd < 0) { + fprintf(stderr, "Failed to open /dev/urandom device\n"); + return EXIT_FAILURE; + } + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + printf("Low Energy iBeacon utility ver %s\n", VERSION); + + hci_dev = bt_hci_new_user_channel(index); + if (!hci_dev) { + fprintf(stderr, "Failed to open HCI user channel\n"); + exit_status = EXIT_FAILURE; + goto done; + } + + start_ibeacon(); + + exit_status = mainloop_run(); + + bt_hci_unref(hci_dev); + +done: + close(urandom_fd); + + return exit_status; +} diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c index 505ac79..79362b2 100644 --- a/tools/l2cap-tester.c +++ b/tools/l2cap-tester.c @@ -50,22 +50,37 @@ struct test_data { struct hciemu *hciemu; enum hciemu_type hciemu_type; unsigned int io_id; + uint16_t handle; + uint16_t scid; + uint16_t dcid; }; -struct l2cap_client_data { +struct l2cap_data { uint16_t client_psm; uint16_t server_psm; + uint16_t cid; int expect_err; -}; -struct l2cap_server_data { - uint16_t server_psm; - uint8_t send_req_code; - const void *send_req; - uint16_t send_req_len; - uint8_t expect_rsp_code; - const void *expect_rsp; - uint16_t expect_rsp_len; + uint8_t send_cmd_code; + const void *send_cmd; + uint16_t send_cmd_len; + uint8_t expect_cmd_code; + const void *expect_cmd; + uint16_t expect_cmd_len; + + uint16_t data_len; + const void *read_data; + const void *write_data; + + bool enable_ssp; + int sec_level; + bool reject_ssp; + + bool expect_pin; + uint8_t pin_len; + const void *pin; + uint8_t client_pin_len; + const void *client_pin; }; static void mgmt_debug(const char *str, void *user_data) @@ -242,24 +257,113 @@ static void test_data_free(void *test_data) test_post_teardown, 2, user, test_data_free); \ } while (0) -static const struct l2cap_client_data client_connect_success_test = { +static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; /* "0000" */ + +static const struct l2cap_data client_connect_success_test = { + .client_psm = 0x1001, + .server_psm = 0x1001, +}; + +static const struct l2cap_data client_connect_ssp_success_test_1 = { + .client_psm = 0x1001, + .server_psm = 0x1001, + .enable_ssp = true, +}; + +static const struct l2cap_data client_connect_ssp_success_test_2 = { .client_psm = 0x1001, .server_psm = 0x1001, + .enable_ssp = true, + .sec_level = BT_SECURITY_HIGH, }; -static const struct l2cap_client_data client_connect_nval_psm_test = { +static const struct l2cap_data client_connect_pin_success_test = { .client_psm = 0x1001, + .server_psm = 0x1001, + .sec_level = BT_SECURITY_MEDIUM, + .pin = pair_device_pin, + .pin_len = sizeof(pair_device_pin), + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), +}; + +static uint8_t l2_data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + +static const struct l2cap_data client_connect_read_success_test = { + .client_psm = 0x1001, + .server_psm = 0x1001, + .read_data = l2_data, + .data_len = sizeof(l2_data), +}; + +static const struct l2cap_data client_connect_write_success_test = { + .client_psm = 0x1001, + .server_psm = 0x1001, + .write_data = l2_data, + .data_len = sizeof(l2_data), +}; + +static const struct l2cap_data client_connect_nval_psm_test_1 = { + .client_psm = 0x1001, + .expect_err = ECONNREFUSED, +}; + +static const struct l2cap_data client_connect_nval_psm_test_2 = { + .client_psm = 0x0001, .expect_err = ECONNREFUSED, }; +static const struct l2cap_data client_connect_nval_psm_test_3 = { + .client_psm = 0x0001, + .expect_err = ECONNREFUSED, + .enable_ssp = true, +}; + static const uint8_t l2cap_connect_req[] = { 0x01, 0x10, 0x41, 0x00 }; -static const struct l2cap_server_data l2cap_server_success_test = { +static const struct l2cap_data l2cap_server_success_test = { + .server_psm = 0x1001, + .send_cmd_code = BT_L2CAP_PDU_CONN_REQ, + .send_cmd = l2cap_connect_req, + .send_cmd_len = sizeof(l2cap_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP, +}; + +static const struct l2cap_data l2cap_server_read_success_test = { + .server_psm = 0x1001, + .send_cmd_code = BT_L2CAP_PDU_CONN_REQ, + .send_cmd = l2cap_connect_req, + .send_cmd_len = sizeof(l2cap_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP, + .read_data = l2_data, + .data_len = sizeof(l2_data), +}; + +static const struct l2cap_data l2cap_server_write_success_test = { .server_psm = 0x1001, - .send_req_code = BT_L2CAP_PDU_CONN_REQ, - .send_req = l2cap_connect_req, - .send_req_len = sizeof(l2cap_connect_req), - .expect_rsp_code = BT_L2CAP_PDU_CONN_RSP, + .send_cmd_code = BT_L2CAP_PDU_CONN_REQ, + .send_cmd = l2cap_connect_req, + .send_cmd_len = sizeof(l2cap_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP, + .write_data = l2_data, + .data_len = sizeof(l2_data), +}; + +static const uint8_t l2cap_sec_block_rsp[] = { 0x00, 0x00, /* dcid */ + 0x41, 0x00, /* scid */ + 0x03, 0x00, /* Sec Block */ + 0x00, 0x00 /* status */ + }; + +static const struct l2cap_data l2cap_server_sec_block_test = { + .server_psm = 0x1001, + .send_cmd_code = BT_L2CAP_PDU_CONN_REQ, + .send_cmd = l2cap_connect_req, + .send_cmd_len = sizeof(l2cap_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP, + .expect_cmd = l2cap_sec_block_rsp, + .expect_cmd_len = sizeof(l2cap_sec_block_rsp), + .enable_ssp = true, }; static const uint8_t l2cap_nval_psm_rsp[] = { 0x00, 0x00, /* dcid */ @@ -268,59 +372,74 @@ static const uint8_t l2cap_nval_psm_rsp[] = { 0x00, 0x00, /* dcid */ 0x00, 0x00 /* status */ }; -static const struct l2cap_server_data l2cap_server_nval_psm_test = { - .send_req_code = BT_L2CAP_PDU_CONN_REQ, - .send_req = l2cap_connect_req, - .send_req_len = sizeof(l2cap_connect_req), - .expect_rsp_code = BT_L2CAP_PDU_CONN_RSP, - .expect_rsp = l2cap_nval_psm_rsp, - .expect_rsp_len = sizeof(l2cap_nval_psm_rsp), +static const struct l2cap_data l2cap_server_nval_psm_test = { + .send_cmd_code = BT_L2CAP_PDU_CONN_REQ, + .send_cmd = l2cap_connect_req, + .send_cmd_len = sizeof(l2cap_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP, + .expect_cmd = l2cap_nval_psm_rsp, + .expect_cmd_len = sizeof(l2cap_nval_psm_rsp), }; static const uint8_t l2cap_nval_conn_req[] = { 0x00 }; static const uint8_t l2cap_nval_pdu_rsp[] = { 0x00, 0x00 }; -static const struct l2cap_server_data l2cap_server_nval_pdu_test1 = { - .send_req_code = BT_L2CAP_PDU_CONN_REQ, - .send_req = l2cap_nval_conn_req, - .send_req_len = sizeof(l2cap_nval_conn_req), - .expect_rsp_code = BT_L2CAP_PDU_CMD_REJECT, - .expect_rsp = l2cap_nval_pdu_rsp, - .expect_rsp_len = sizeof(l2cap_nval_pdu_rsp), +static const struct l2cap_data l2cap_server_nval_pdu_test1 = { + .send_cmd_code = BT_L2CAP_PDU_CONN_REQ, + .send_cmd = l2cap_nval_conn_req, + .send_cmd_len = sizeof(l2cap_nval_conn_req), + .expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT, + .expect_cmd = l2cap_nval_pdu_rsp, + .expect_cmd_len = sizeof(l2cap_nval_pdu_rsp), }; static const uint8_t l2cap_nval_dc_req[] = { 0x12, 0x34, 0x56, 0x78 }; static const uint8_t l2cap_nval_cid_rsp[] = { 0x02, 0x00, 0x12, 0x34, 0x56, 0x78 }; -static const struct l2cap_server_data l2cap_server_nval_cid_test1 = { - .send_req_code = BT_L2CAP_PDU_DISCONN_REQ, - .send_req = l2cap_nval_dc_req, - .send_req_len = sizeof(l2cap_nval_dc_req), - .expect_rsp_code = BT_L2CAP_PDU_CMD_REJECT, - .expect_rsp = l2cap_nval_cid_rsp, - .expect_rsp_len = sizeof(l2cap_nval_cid_rsp), +static const struct l2cap_data l2cap_server_nval_cid_test1 = { + .send_cmd_code = BT_L2CAP_PDU_DISCONN_REQ, + .send_cmd = l2cap_nval_dc_req, + .send_cmd_len = sizeof(l2cap_nval_dc_req), + .expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT, + .expect_cmd = l2cap_nval_cid_rsp, + .expect_cmd_len = sizeof(l2cap_nval_cid_rsp), }; static const uint8_t l2cap_nval_cfg_req[] = { 0x12, 0x34, 0x00, 0x00 }; static const uint8_t l2cap_nval_cfg_rsp[] = { 0x02, 0x00, 0x12, 0x34, 0x00, 0x00 }; -static const struct l2cap_server_data l2cap_server_nval_cid_test2 = { - .send_req_code = BT_L2CAP_PDU_CONFIG_REQ, - .send_req = l2cap_nval_cfg_req, - .send_req_len = sizeof(l2cap_nval_cfg_req), - .expect_rsp_code = BT_L2CAP_PDU_CMD_REJECT, - .expect_rsp = l2cap_nval_cfg_rsp, - .expect_rsp_len = sizeof(l2cap_nval_cfg_rsp), +static const struct l2cap_data l2cap_server_nval_cid_test2 = { + .send_cmd_code = BT_L2CAP_PDU_CONFIG_REQ, + .send_cmd = l2cap_nval_cfg_req, + .send_cmd_len = sizeof(l2cap_nval_cfg_req), + .expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT, + .expect_cmd = l2cap_nval_cfg_rsp, + .expect_cmd_len = sizeof(l2cap_nval_cfg_rsp), }; -static const struct l2cap_client_data le_client_connect_success_test = { +static const struct l2cap_data le_client_connect_success_test_1 = { .client_psm = 0x0080, .server_psm = 0x0080, }; -static const struct l2cap_client_data le_client_connect_nval_psm_test = { +static const struct l2cap_data le_client_connect_success_test_2 = { + .client_psm = 0x0080, + .server_psm = 0x0080, + .sec_level = BT_SECURITY_MEDIUM, +}; + +static const uint8_t cmd_reject_rsp[] = { 0x01, 0x01, 0x02, 0x00, 0x00, 0x00 }; + +static const struct l2cap_data le_client_connect_reject_test_1 = { + .client_psm = 0x0080, + .send_cmd = cmd_reject_rsp, + .send_cmd_len = sizeof(cmd_reject_rsp), + .expect_err = ECONNREFUSED, +}; + +static const struct l2cap_data le_client_connect_nval_psm_test = { .client_psm = 0x0080, .expect_err = ECONNREFUSED, }; @@ -332,27 +451,67 @@ static const uint8_t le_connect_req[] = { 0x80, 0x00, /* PSM */ 0x05, 0x00, /* Credits */ }; -static const struct l2cap_server_data le_server_success_test = { +static const struct l2cap_data le_server_success_test = { .server_psm = 0x0080, - .send_req_code = BT_L2CAP_PDU_LE_CONN_REQ, - .send_req = le_connect_req, - .send_req_len = sizeof(le_connect_req), - .expect_rsp_code = BT_L2CAP_PDU_LE_CONN_RSP, + .send_cmd_code = BT_L2CAP_PDU_LE_CONN_REQ, + .send_cmd = le_connect_req, + .send_cmd_len = sizeof(le_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_LE_CONN_RSP, +}; + +static const struct l2cap_data le_att_client_connect_success_test_1 = { + .cid = 0x0004, + .sec_level = BT_SECURITY_LOW, +}; + +static const struct l2cap_data le_att_server_success_test_1 = { + .cid = 0x0004, }; -static void client_connectable_complete(uint16_t opcode, uint8_t status, +static void client_cmd_complete(uint16_t opcode, uint8_t status, const void *param, uint8_t len, void *user_data) { + struct test_data *data = tester_get_data(); + const struct l2cap_data *test = data->test_data; + struct bthost *bthost; + + bthost = hciemu_client_get_host(data->hciemu); + switch (opcode) { case BT_HCI_CMD_WRITE_SCAN_ENABLE: case BT_HCI_CMD_LE_SET_ADV_ENABLE: + tester_print("Client set connectable status 0x%02x", status); + if (!status && test && test->enable_ssp) { + bthost_write_ssp_mode(bthost, 0x01); + return; + } + break; + case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE: + tester_print("Client enable SSP status 0x%02x", status); break; default: return; } - tester_print("Client set connectable status 0x%02x", status); + + if (status) + tester_setup_failed(); + else + tester_setup_complete(); +} + +static void server_cmd_complete(uint16_t opcode, uint8_t status, + const void *param, uint8_t len, + void *user_data) +{ + switch (opcode) { + case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE: + tester_print("Server enable SSP status 0x%02x", status); + break; + default: + return; + } if (status) tester_setup_failed(); @@ -374,7 +533,7 @@ static void setup_powered_client_callback(uint8_t status, uint16_t length, tester_print("Controller powered on"); bthost = hciemu_client_get_host(data->hciemu); - bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data); + bthost_set_cmd_complete_cb(bthost, client_cmd_complete, user_data); if (data->hciemu_type == HCIEMU_TYPE_LE) bthost_set_adv_enable(bthost, 0x01); else @@ -384,6 +543,10 @@ static void setup_powered_client_callback(uint8_t status, uint16_t length, static void setup_powered_server_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { + struct test_data *data = tester_get_data(); + const struct l2cap_data *test = data->test_data; + struct bthost *bthost; + if (status != MGMT_STATUS_SUCCESS) { tester_setup_failed(); return; @@ -391,22 +554,155 @@ static void setup_powered_server_callback(uint8_t status, uint16_t length, tester_print("Controller powered on"); - tester_setup_complete(); + if (!test->enable_ssp) { + tester_setup_complete(); + return; + } + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_cmd_complete_cb(bthost, server_cmd_complete, user_data); + bthost_write_ssp_mode(bthost, 0x01); } -static void setup_powered_client(const void *test_data) +static void user_confirm_request_callback(uint16_t index, uint16_t length, + const void *param, + void *user_data) { + const struct mgmt_ev_user_confirm_request *ev = param; struct test_data *data = tester_get_data(); + const struct l2cap_data *test = data->test_data; + struct mgmt_cp_user_confirm_reply cp; + uint16_t opcode; + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.addr, &ev->addr, sizeof(cp.addr)); + + if (test->reject_ssp) + opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY; + else + opcode = MGMT_OP_USER_CONFIRM_REPLY; + + mgmt_reply(data->mgmt, opcode, data->mgmt_index, sizeof(cp), &cp, + NULL, NULL, NULL); +} + +static void pin_code_request_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_pin_code_request *ev = param; + struct test_data *data = user_data; + const struct l2cap_data *test = data->test_data; + struct mgmt_cp_pin_code_reply cp; + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.addr, &ev->addr, sizeof(cp.addr)); + + if (!test->pin) { + mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY, + data->mgmt_index, sizeof(cp.addr), &cp.addr, + NULL, NULL, NULL); + return; + } + + cp.pin_len = test->pin_len; + memcpy(cp.pin_code, test->pin, test->pin_len); + + mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_REPLY, data->mgmt_index, + sizeof(cp), &cp, NULL, NULL, NULL); +} + +static void bthost_send_rsp(const void *buf, uint16_t len, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *l2data = data->test_data; + struct bthost *bthost; + + if (l2data->expect_cmd_len && len != l2data->expect_cmd_len) { + tester_test_failed(); + return; + } + + if (l2data->expect_cmd && memcmp(buf, l2data->expect_cmd, + l2data->expect_cmd_len)) { + tester_test_failed(); + return; + } + + if (!l2data->send_cmd) + return; + + bthost = hciemu_client_get_host(data->hciemu); + bthost_send_cid(bthost, data->handle, data->dcid, + l2data->send_cmd, l2data->send_cmd_len); +} + +static void send_rsp_new_conn(uint16_t handle, void *user_data) +{ + struct test_data *data = user_data; + struct bthost *bthost; + + tester_print("New connection with handle 0x%04x", handle); + + data->handle = handle; + + if (data->hciemu_type == HCIEMU_TYPE_LE) + data->dcid = 0x0005; + else + data->dcid = 0x0001; + + bthost = hciemu_client_get_host(data->hciemu); + bthost_add_cid_hook(bthost, data->handle, data->dcid, + bthost_send_rsp, NULL); +} + +static void setup_powered_common(void) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *test = data->test_data; + struct bthost *bthost = hciemu_client_get_host(data->hciemu); unsigned char param[] = { 0x01 }; - tester_print("Powering on controller"); + mgmt_register(data->mgmt, MGMT_EV_USER_CONFIRM_REQUEST, + data->mgmt_index, user_confirm_request_callback, + NULL, NULL); - if (data->hciemu_type == HCIEMU_TYPE_BREDR) + if (test && (test->pin || test->expect_pin)) + mgmt_register(data->mgmt, MGMT_EV_PIN_CODE_REQUEST, + data->mgmt_index, pin_code_request_callback, + data, NULL); + + if (test && test->client_pin) + bthost_set_pin_code(bthost, test->client_pin, + test->client_pin_len); + if (test && test->reject_ssp) + bthost_set_reject_user_confirm(bthost, true); + + if (data->hciemu_type == HCIEMU_TYPE_LE) + mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, + sizeof(param), param, NULL, NULL, NULL); + + if (test && test->enable_ssp) mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); - else - mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, + + mgmt_send(data->mgmt, MGMT_OP_SET_PAIRABLE, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); +} + +static void setup_powered_client(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *test = data->test_data; + unsigned char param[] = { 0x01 }; + + setup_powered_common(); + + tester_print("Powering on controller"); + + if (test && (test->expect_cmd || test->send_cmd)) { + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + bthost_set_connect_cb(bthost, send_rsp_new_conn, data); + } mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, sizeof(param), param, setup_powered_client_callback, @@ -418,20 +714,17 @@ static void setup_powered_server(const void *test_data) struct test_data *data = tester_get_data(); unsigned char param[] = { 0x01 }; + setup_powered_common(); + tester_print("Powering on controller"); - if (data->hciemu_type == HCIEMU_TYPE_BREDR) { - mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, - sizeof(param), param, - NULL, NULL, NULL); - mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index, - sizeof(param), param, NULL, NULL, NULL); - } else { - mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, - sizeof(param), param, NULL, NULL, NULL); - mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index, + mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); - } + + if (data->hciemu_type != HCIEMU_TYPE_BREDR) + mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, + data->mgmt_index, sizeof(param), param, NULL, + NULL, NULL); mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, sizeof(param), param, setup_powered_server_callback, @@ -455,11 +748,133 @@ static void test_basic(const void *test_data) tester_test_passed(); } +static gboolean client_received_data(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *l2data = data->test_data; + char buf[1024]; + int sk; + + sk = g_io_channel_unix_get_fd(io); + if (read(sk, buf, l2data->data_len) != l2data->data_len) { + tester_warn("Unable to read %u bytes", l2data->data_len); + tester_test_failed(); + return FALSE; + } + + if (memcmp(buf, l2data->read_data, l2data->data_len)) + tester_test_failed(); + else + tester_test_passed(); + + return FALSE; +} + +static gboolean server_received_data(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *l2data = data->test_data; + char buf[1024]; + int sk; + + sk = g_io_channel_unix_get_fd(io); + if (read(sk, buf, l2data->data_len) != l2data->data_len) { + tester_warn("Unable to read %u bytes", l2data->data_len); + tester_test_failed(); + return FALSE; + } + + if (memcmp(buf, l2data->read_data, l2data->data_len)) + tester_test_failed(); + else + tester_test_passed(); + + return FALSE; +} + +static void bthost_received_data(const void *buf, uint16_t len, + void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *l2data = data->test_data; + + if (len != l2data->data_len) { + tester_test_failed(); + return; + } + + if (memcmp(buf, l2data->write_data, l2data->data_len)) + tester_test_failed(); + else + tester_test_passed(); +} + +static void server_bthost_received_data(const void *buf, uint16_t len, + void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *l2data = data->test_data; + + if (len != l2data->data_len) { + tester_test_failed(); + return; + } + + if (memcmp(buf, l2data->write_data, l2data->data_len)) + tester_test_failed(); + else + tester_test_passed(); +} + +static bool check_mtu(struct test_data *data, int sk) +{ + const struct l2cap_data *l2data = data->test_data; + struct l2cap_options l2o; + socklen_t len; + + memset(&l2o, 0, sizeof(l2o)); + + if (data->hciemu_type == HCIEMU_TYPE_LE && + (l2data->client_psm || l2data->server_psm)) { + /* LE CoC enabled kernels should support BT_RCVMTU and + * BT_SNDMTU. + */ + len = sizeof(l2o.imtu); + if (getsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, + &l2o.imtu, &len) < 0) { + tester_warn("getsockopt(BT_RCVMTU): %s (%d)", + strerror(errno), errno); + return false; + } + + len = sizeof(l2o.omtu); + if (getsockopt(sk, SOL_BLUETOOTH, BT_SNDMTU, + &l2o.omtu, &len) < 0) { + tester_warn("getsockopt(BT_SNDMTU): %s (%d)", + strerror(errno), errno); + return false; + } + } else { + /* For non-LE CoC enabled kernels we need to fall back to + * L2CAP_OPTIONS, so test support for it as well */ + len = sizeof(l2o); + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { + tester_warn("getsockopt(L2CAP_OPTIONS): %s (%d)", + strerror(errno), errno); + return false; + } + } + + return true; +} + static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct test_data *data = tester_get_data(); - const struct l2cap_client_data *l2data = data->test_data; + const struct l2cap_data *l2data = data->test_data; int err, sk_err, sk; socklen_t len = sizeof(sk_err); @@ -472,11 +887,46 @@ static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond, else err = -sk_err; - if (err < 0) + if (err < 0) { tester_warn("Connect failed: %s (%d)", strerror(-err), -err); - else - tester_print("Successfully connected"); + goto failed; + } + tester_print("Successfully connected"); + + if (!check_mtu(data, sk)) { + tester_test_failed(); + return FALSE; + } + + if (l2data->read_data) { + struct bthost *bthost; + + bthost = hciemu_client_get_host(data->hciemu); + g_io_add_watch(io, G_IO_IN, client_received_data, NULL); + + bthost_send_cid(bthost, data->handle, data->dcid, + l2data->read_data, l2data->data_len); + + return FALSE; + } else if (l2data->write_data) { + struct bthost *bthost; + ssize_t ret; + + bthost = hciemu_client_get_host(data->hciemu); + bthost_add_cid_hook(bthost, data->handle, data->dcid, + bthost_received_data, NULL); + + ret = write(sk, l2data->write_data, l2data->data_len); + if (ret != l2data->data_len) { + tester_warn("Unable to write all data"); + tester_test_failed(); + } + + return FALSE; + } + +failed: if (-err != l2data->expect_err) tester_test_failed(); else @@ -485,7 +935,8 @@ static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond, return FALSE; } -static int create_l2cap_sock(struct test_data *data, uint16_t psm) +static int create_l2cap_sock(struct test_data *data, uint16_t psm, + uint16_t cid, int sec_level) { const uint8_t *master_bdaddr; struct sockaddr_l2 addr; @@ -503,12 +954,14 @@ static int create_l2cap_sock(struct test_data *data, uint16_t psm) master_bdaddr = hciemu_get_master_bdaddr(data->hciemu); if (!master_bdaddr) { tester_warn("No master bdaddr"); + close(sk); return -ENODEV; } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; addr.l2_psm = htobs(psm); + addr.l2_cid = htobs(cid); bacpy(&addr.l2_bdaddr, (void *) master_bdaddr); if (data->hciemu_type == HCIEMU_TYPE_LE) addr.l2_bdaddr_type = BDADDR_LE_PUBLIC; @@ -523,10 +976,27 @@ static int create_l2cap_sock(struct test_data *data, uint16_t psm) return err; } + if (sec_level) { + struct bt_security sec; + + memset(&sec, 0, sizeof(sec)); + sec.level = sec_level; + + if (setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &sec, + sizeof(sec)) < 0) { + err = -errno; + tester_warn("Can't set security level: %s (%d)", + strerror(errno), errno); + close(sk); + return err; + } + } + return sk; } -static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm) +static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm, + uint16_t cid) { const uint8_t *client_bdaddr; struct sockaddr_l2 addr; @@ -542,6 +1012,7 @@ static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm) addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, (void *) client_bdaddr); addr.l2_psm = htobs(psm); + addr.l2_cid = htobs(cid); if (data->hciemu_type == HCIEMU_TYPE_LE) addr.l2_bdaddr_type = BDADDR_LE_PUBLIC; else @@ -558,25 +1029,41 @@ static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm) return 0; } +static void client_l2cap_connect_cb(uint16_t handle, uint16_t cid, + void *user_data) +{ + struct test_data *data = user_data; + + data->dcid = cid; + data->handle = handle; +} + static void test_connect(const void *test_data) { struct test_data *data = tester_get_data(); - const struct l2cap_client_data *l2data = data->test_data; + const struct l2cap_data *l2data = data->test_data; GIOChannel *io; int sk; if (l2data->server_psm) { struct bthost *bthost = hciemu_client_get_host(data->hciemu); - bthost_set_server_psm(bthost, l2data->server_psm); + + if (!l2data->data_len) + bthost_add_l2cap_server(bthost, l2data->server_psm, + NULL, NULL); + else + bthost_add_l2cap_server(bthost, l2data->server_psm, + client_l2cap_connect_cb, data); } - sk = create_l2cap_sock(data, 0); + sk = create_l2cap_sock(data, 0, l2data->cid, l2data->sec_level); if (sk < 0) { tester_test_failed(); return; } - if (connect_l2cap_sock(data, sk, l2data->client_psm) < 0) { + if (connect_l2cap_sock(data, sk, l2data->client_psm, + l2data->cid) < 0) { close(sk); tester_test_failed(); return; @@ -596,6 +1083,7 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct test_data *data = tester_get_data(); + const struct l2cap_data *l2data = data->test_data; int sk, new_sk; data->io_id = 0; @@ -609,6 +1097,45 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond, return FALSE; } + if (!check_mtu(data, new_sk)) { + tester_test_failed(); + return FALSE; + } + + if (l2data->read_data) { + struct bthost *bthost; + GIOChannel *new_io; + + new_io = g_io_channel_unix_new(new_sk); + g_io_channel_set_close_on_unref(new_io, TRUE); + + bthost = hciemu_client_get_host(data->hciemu); + g_io_add_watch(new_io, G_IO_IN, server_received_data, NULL); + bthost_send_cid(bthost, data->handle, data->dcid, + l2data->read_data, l2data->data_len); + + g_io_channel_unref(new_io); + + return FALSE; + } else if (l2data->write_data) { + struct bthost *bthost; + ssize_t ret; + + bthost = hciemu_client_get_host(data->hciemu); + bthost_add_cid_hook(bthost, data->handle, data->scid, + server_bthost_received_data, NULL); + + ret = write(new_sk, l2data->write_data, l2data->data_len); + close(new_sk); + + if (ret != l2data->data_len) { + tester_warn("Unable to write all data"); + tester_test_failed(); + } + + return FALSE; + } + tester_print("Successfully connected"); close(new_sk); @@ -622,28 +1149,41 @@ static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len, void *user_data) { struct test_data *test_data = user_data; - const struct l2cap_server_data *l2data = test_data->test_data; + const struct l2cap_data *l2data = test_data->test_data; tester_print("Client received response code 0x%02x", code); - if (code != l2data->expect_rsp_code) { + if (code != l2data->expect_cmd_code) { tester_warn("Unexpected L2CAP response code (expected 0x%02x)", - l2data->expect_rsp_code); + l2data->expect_cmd_code); goto failed; } - if (!l2data->expect_rsp) { + if (code == BT_L2CAP_PDU_CONN_RSP) { + + const struct bt_l2cap_pdu_conn_rsp *rsp = data; + if (len == sizeof(rsp) && !rsp->result && !rsp->status) + return; + + test_data->dcid = rsp->dcid; + test_data->scid = rsp->scid; + + if (l2data->data_len) + return; + } + + if (!l2data->expect_cmd) { tester_test_passed(); return; } - if (l2data->expect_rsp_len != len) { + if (l2data->expect_cmd_len != len) { tester_warn("Unexpected L2CAP response length (%u != %u)", - len, l2data->expect_rsp_len); + len, l2data->expect_cmd_len); goto failed; } - if (memcmp(l2data->expect_rsp, data, len) != 0) { + if (memcmp(l2data->expect_cmd, data, len) != 0) { tester_warn("Unexpected L2CAP response"); goto failed; } @@ -655,18 +1195,20 @@ failed: tester_test_failed(); } -static void client_new_conn(uint16_t handle, void *user_data) +static void send_req_new_conn(uint16_t handle, void *user_data) { struct test_data *data = user_data; - const struct l2cap_server_data *l2data = data->test_data; + const struct l2cap_data *l2data = data->test_data; struct bthost *bthost; tester_print("New client connection with handle 0x%04x", handle); - if (l2data->send_req) { + data->handle = handle; + + if (l2data->send_cmd) { bthost_l2cap_rsp_cb cb; - if (l2data->expect_rsp_code) + if (l2data->expect_cmd_code) cb = client_l2cap_rsp; else cb = NULL; @@ -674,8 +1216,8 @@ static void client_new_conn(uint16_t handle, void *user_data) tester_print("Sending L2CAP Request from client"); bthost = hciemu_client_get_host(data->hciemu); - bthost_l2cap_req(bthost, handle, l2data->send_req_code, - l2data->send_req, l2data->send_req_len, + bthost_l2cap_req(bthost, handle, l2data->send_cmd_code, + l2data->send_cmd, l2data->send_cmd_len, cb, data); } } @@ -683,15 +1225,16 @@ static void client_new_conn(uint16_t handle, void *user_data) static void test_server(const void *test_data) { struct test_data *data = tester_get_data(); - const struct l2cap_server_data *l2data = data->test_data; + const struct l2cap_data *l2data = data->test_data; const uint8_t *master_bdaddr; uint8_t addr_type; struct bthost *bthost; GIOChannel *io; int sk; - if (l2data->server_psm) { - sk = create_l2cap_sock(data, l2data->server_psm); + if (l2data->server_psm || l2data->cid) { + sk = create_l2cap_sock(data, l2data->server_psm, + l2data->cid, l2data->sec_level); if (sk < 0) { tester_test_failed(); return; @@ -701,6 +1244,7 @@ static void test_server(const void *test_data) tester_warn("listening on socket failed: %s (%u)", strerror(errno), errno); tester_test_failed(); + close(sk); return; } @@ -722,7 +1266,7 @@ static void test_server(const void *test_data) } bthost = hciemu_client_get_host(data->hciemu); - bthost_set_connect_cb(bthost, client_new_conn, data); + bthost_set_connect_cb(bthost, send_req_new_conn, data); if (data->hciemu_type == HCIEMU_TYPE_BREDR) addr_type = BDADDR_BREDR; @@ -732,23 +1276,99 @@ static void test_server(const void *test_data) bthost_hci_connect(bthost, master_bdaddr, addr_type); } +static void test_getpeername_not_connected(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct sockaddr_l2 addr; + socklen_t len; + int sk; + + sk = create_l2cap_sock(data, 0, 0, 0); + if (sk < 0) { + tester_test_failed(); + return; + } + + len = sizeof(addr); + if (getpeername(sk, (struct sockaddr *) &addr, &len) == 0) { + tester_warn("getpeername succeeded on non-connected socket"); + tester_test_failed(); + goto done; + } + + if (errno != ENOTCONN) { + tester_warn("Unexpexted getpeername error: %s (%d)", + strerror(errno), errno); + tester_test_failed(); + goto done; + } + + tester_test_passed(); + +done: + close(sk); +} + int main(int argc, char *argv[]) { tester_init(&argc, &argv); test_l2cap_bredr("Basic L2CAP Socket - Success", NULL, setup_powered_client, test_basic); + test_l2cap_bredr("Non-connected getpeername - Failure", NULL, + setup_powered_client, + test_getpeername_not_connected); test_l2cap_bredr("L2CAP BR/EDR Client - Success", &client_connect_success_test, setup_powered_client, test_connect); - test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM", - &client_connect_nval_psm_test, + + test_l2cap_bredr("L2CAP BR/EDR Client SSP - Success 1", + &client_connect_ssp_success_test_1, + setup_powered_client, test_connect); + test_l2cap_bredr("L2CAP BR/EDR Client SSP - Success 2", + &client_connect_ssp_success_test_2, + setup_powered_client, test_connect); + test_l2cap_bredr("L2CAP BR/EDR Client PIN Code - Success", + &client_connect_pin_success_test, + setup_powered_client, test_connect); + + test_l2cap_bredr("L2CAP BR/EDR Client - Read Success", + &client_connect_read_success_test, + setup_powered_client, test_connect); + + test_l2cap_bredr("L2CAP BR/EDR Client - Write Success", + &client_connect_write_success_test, + setup_powered_client, test_connect); + + test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 1", + &client_connect_nval_psm_test_1, + setup_powered_client, test_connect); + + test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 2", + &client_connect_nval_psm_test_2, + setup_powered_client, test_connect); + + test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 3", + &client_connect_nval_psm_test_3, setup_powered_client, test_connect); test_l2cap_bredr("L2CAP BR/EDR Server - Success", &l2cap_server_success_test, setup_powered_server, test_server); + + test_l2cap_bredr("L2CAP BR/EDR Server - Read Success", + &l2cap_server_read_success_test, + setup_powered_server, test_server); + + test_l2cap_bredr("L2CAP BR/EDR Server - Write Success", + &l2cap_server_write_success_test, + setup_powered_server, test_server); + + test_l2cap_bredr("L2CAP BR/EDR Server - Security Block", + &l2cap_server_sec_block_test, + setup_powered_server, test_server); + test_l2cap_bredr("L2CAP BR/EDR Server - Invalid PSM", &l2cap_server_nval_psm_test, setup_powered_server, test_server); @@ -763,13 +1383,27 @@ int main(int argc, char *argv[]) setup_powered_server, test_server); test_l2cap_le("L2CAP LE Client - Success", - &le_client_connect_success_test, + &le_client_connect_success_test_1, + setup_powered_client, test_connect); + test_l2cap_le("L2CAP LE Client SMP - Success", + &le_client_connect_success_test_2, setup_powered_client, test_connect); + test_l2cap_le("L2CAP LE Client - Command Reject", + &le_client_connect_reject_test_1, + setup_powered_client, test_connect); test_l2cap_le("L2CAP LE Client - Invalid PSM", &le_client_connect_nval_psm_test, setup_powered_client, test_connect); test_l2cap_le("L2CAP LE Server - Success", &le_server_success_test, setup_powered_server, test_server); + + test_l2cap_le("L2CAP LE ATT Client - Success", + &le_att_client_connect_success_test_1, + setup_powered_client, test_connect); + test_l2cap_le("L2CAP LE ATT Server - Success", + &le_att_server_success_test_1, + setup_powered_server, test_server); + return tester_run(); } diff --git a/tools/l2test.c b/tools/l2test.c index 9a7c809..c70bac0 100644 --- a/tools/l2test.c +++ b/tools/l2test.c @@ -47,6 +47,8 @@ #include #include +#include "src/shared/util.h" + #define NIBBLE_TO_ASCII(c) ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57) #define BREDR_DEFAULT_PSM 0x1011 @@ -287,7 +289,7 @@ static int setopts(int sk, struct l2cap_options *opts) { if (bdaddr_type == BDADDR_BREDR || cid) return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, - sizeof(opts)); + sizeof(*opts)); return setsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu, sizeof(opts->imtu)); @@ -782,7 +784,7 @@ static void dump_mode(int sk) data_size = imtu; if (defer_setup) { - len = read(sk, buf, sizeof(buf)); + len = read(sk, buf, data_size); if (len < 0) syslog(LOG_ERR, "Initial read error: %s (%d)", strerror(errno), errno); @@ -842,7 +844,7 @@ static void recv_mode(int sk) data_size = imtu; if (defer_setup) { - len = read(sk, buf, sizeof(buf)); + len = read(sk, buf, data_size); if (len < 0) syslog(LOG_ERR, "Initial read error: %s (%d)", strerror(errno), errno); @@ -909,7 +911,7 @@ static void recv_mode(int sk) } /* Check sequence */ - sq = bt_get_le32(buf); + sq = get_le32(buf); if (seq != sq) { syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq); seq = sq; @@ -917,7 +919,7 @@ static void recv_mode(int sk) seq++; /* Check length */ - l = bt_get_le16(buf + 4); + l = get_le16(buf + 4); if (len != l) { syslog(LOG_INFO, "size missmatch: %d -> %d", len, l); continue; @@ -976,8 +978,8 @@ static void do_send(int sk) seq = 0; while ((num_frames == -1) || (num_frames-- > 0)) { - bt_put_le32(seq, buf); - bt_put_le16(data_size, buf + 4); + put_le32(seq, buf); + put_le16(data_size, buf + 4); seq++; diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c index e2fe73d..4cc7ba4 100644 --- a/tools/mgmt-tester.c +++ b/tools/mgmt-tester.c @@ -26,6 +26,7 @@ #endif #include +#include #include @@ -33,7 +34,9 @@ #include "lib/mgmt.h" #include "monitor/bt.h" +#include "emulator/bthost.h" +#include "src/shared/util.h" #include "src/shared/tester.h" #include "src/shared/mgmt.h" #include "src/shared/hciemu.h" @@ -302,7 +305,7 @@ static void test_condition_complete(struct test_data *data) user->test_data = data; \ user->expected_version = 0x06; \ user->expected_manufacturer = 0x003f; \ - user->expected_supported_settings = 0x000007ff; \ + user->expected_supported_settings = 0x00003fff; \ user->initial_settings = 0x00000080; \ user->unmet_conditions = 0; \ tester_add_full(name, data, \ @@ -321,7 +324,7 @@ static void test_condition_complete(struct test_data *data) user->test_data = data; \ user->expected_version = 0x05; \ user->expected_manufacturer = 0x003f; \ - user->expected_supported_settings = 0x000001ff; \ + user->expected_supported_settings = 0x000011ff; \ user->initial_settings = 0x00000080; \ user->unmet_conditions = 0; \ tester_add_full(name, data, \ @@ -340,7 +343,7 @@ static void test_condition_complete(struct test_data *data) user->test_data = data; \ user->expected_version = 0x06; \ user->expected_manufacturer = 0x003f; \ - user->expected_supported_settings = 0x00000611; \ + user->expected_supported_settings = 0x00003611; \ user->initial_settings = 0x00000200; \ user->unmet_conditions = 0; \ tester_add_full(name, data, \ @@ -360,13 +363,18 @@ struct generic_data { uint16_t setup_expect_hci_command; const void *setup_expect_hci_param; uint8_t setup_expect_hci_len; + uint16_t setup_send_opcode; + const void *setup_send_param; + uint16_t setup_send_len; bool send_index_none; uint16_t send_opcode; const void *send_param; uint16_t send_len; + const void * (*send_func)(uint16_t *len); uint8_t expect_status; const void *expect_param; uint16_t expect_len; + const void * (*expect_func)(uint16_t *len); uint32_t expect_settings_set; uint32_t expect_settings_unset; uint16_t expect_alt_ev; @@ -375,6 +383,18 @@ struct generic_data { uint16_t expect_hci_command; const void *expect_hci_param; uint8_t expect_hci_len; + const void * (*expect_hci_func)(uint8_t *len); + bool expect_pin; + uint8_t pin_len; + const void *pin; + uint8_t client_pin_len; + const void *client_pin; + bool client_enable_ssp; + uint8_t io_cap; + uint8_t client_io_cap; + uint8_t client_auth_req; + bool reject_ssp; + bool client_reject_ssp; }; static const char dummy_data[] = { 0x00 }; @@ -728,7 +748,7 @@ static uint8_t set_connectable_off_adv_param[] = { 0x00, 0x08, /* min_interval */ 0x00, 0x08, /* max_interval */ 0x03, /* type */ - 0x00, /* own_addr_type */ + 0x01, /* own_addr_type */ 0x00, /* direct_addr_type */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* direct_addr */ 0x07, /* channel_map */ @@ -1235,6 +1255,114 @@ static const struct generic_data set_ssp_on_invalid_index_test = { .expect_status = MGMT_STATUS_INVALID_INDEX, }; +static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP, + MGMT_OP_SET_POWERED, 0 }; + +static const char set_sc_on_param[] = { 0x01 }; +static const char set_sc_only_on_param[] = { 0x02 }; +static const char set_sc_invalid_param[] = { 0x03 }; +static const char set_sc_garbage_param[] = { 0x01, 0x00 }; +static const char set_sc_settings_param_1[] = { 0xc0, 0x08, 0x00, 0x00 }; +static const char set_sc_settings_param_2[] = { 0xc1, 0x08, 0x00, 0x00 }; +static const char set_sc_on_write_sc_support_param[] = { 0x01 }; + +static const struct generic_data set_sc_on_success_test_1 = { + .setup_settings = settings_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_on_param, + .send_len = sizeof(set_sc_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_sc_settings_param_1, + .expect_len = sizeof(set_sc_settings_param_1), + .expect_settings_set = MGMT_SETTING_SECURE_CONN, +}; + +static const struct generic_data set_sc_on_success_test_2 = { + .setup_settings = settings_powered_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_on_param, + .send_len = sizeof(set_sc_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_sc_settings_param_2, + .expect_len = sizeof(set_sc_settings_param_2), + .expect_settings_set = MGMT_SETTING_SECURE_CONN, + .expect_hci_command = BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT, + .expect_hci_param = set_sc_on_write_sc_support_param, + .expect_hci_len = sizeof(set_sc_on_write_sc_support_param), +}; + +static const struct generic_data set_sc_on_invalid_param_test_1 = { + .setup_settings = settings_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data set_sc_on_invalid_param_test_2 = { + .setup_settings = settings_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_invalid_param, + .send_len = sizeof(set_sc_invalid_param), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data set_sc_on_invalid_param_test_3 = { + .setup_settings = settings_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_garbage_param, + .send_len = sizeof(set_sc_garbage_param), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data set_sc_on_invalid_index_test = { + .setup_settings = settings_ssp, + .send_index_none = true, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_on_param, + .send_len = sizeof(set_sc_on_param), + .expect_status = MGMT_STATUS_INVALID_INDEX, +}; + +static const struct generic_data set_sc_on_not_supported_test_1 = { + .setup_settings = settings_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_on_param, + .send_len = sizeof(set_sc_on_param), + .expect_status = MGMT_STATUS_NOT_SUPPORTED, +}; + +static const struct generic_data set_sc_on_not_supported_test_2 = { + .setup_settings = settings_powered_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_on_param, + .send_len = sizeof(set_sc_on_param), + .expect_status = MGMT_STATUS_NOT_SUPPORTED, +}; + +static const struct generic_data set_sc_only_on_success_test_1 = { + .setup_settings = settings_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_only_on_param, + .send_len = sizeof(set_sc_only_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_sc_settings_param_1, + .expect_len = sizeof(set_sc_settings_param_1), + .expect_settings_set = MGMT_SETTING_SECURE_CONN, +}; + +static const struct generic_data set_sc_only_on_success_test_2 = { + .setup_settings = settings_powered_ssp, + .send_opcode = MGMT_OP_SET_SECURE_CONN, + .send_param = set_sc_only_on_param, + .send_len = sizeof(set_sc_only_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_sc_settings_param_2, + .expect_len = sizeof(set_sc_settings_param_2), + .expect_settings_set = MGMT_SETTING_SECURE_CONN, + .expect_hci_command = BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT, + .expect_hci_param = set_sc_on_write_sc_support_param, + .expect_hci_len = sizeof(set_sc_on_write_sc_support_param), +}; + static const char set_hs_on_param[] = { 0x01 }; static const char set_hs_invalid_param[] = { 0x02 }; static const char set_hs_garbage_param[] = { 0x01, 0x00 }; @@ -1470,9 +1598,6 @@ static const struct generic_data set_bredr_off_failure_test_3 = { .expect_status = MGMT_STATUS_INVALID_PARAMS, }; -static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP, - MGMT_OP_SET_POWERED, 0 }; - static const char set_local_name_param[260] = { 'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e' }; static const char write_local_name_hci[248] = { 'T', 'e', 's', 't', ' ', @@ -1598,6 +1723,9 @@ static const char stop_discovery_inq_param[] = { 0x33, 0x8b, 0x9e, 0x08, 0x00 }; static const struct generic_data stop_discovery_success_test_1 = { .setup_settings = settings_powered_le, + .setup_send_opcode = MGMT_OP_START_DISCOVERY, + .setup_send_param = start_discovery_bredrle_param, + .setup_send_len = sizeof(start_discovery_bredrle_param), .send_opcode = MGMT_OP_STOP_DISCOVERY, .send_param = stop_discovery_bredrle_param, .send_len = sizeof(stop_discovery_bredrle_param), @@ -1613,10 +1741,10 @@ static const struct generic_data stop_discovery_success_test_1 = { }; static const struct generic_data stop_discovery_bredr_success_test_1 = { - .setup_settings = settings_powered_le, - .setup_expect_hci_command = BT_HCI_CMD_INQUIRY, - .setup_expect_hci_param = stop_discovery_inq_param, - .setup_expect_hci_len = sizeof(stop_discovery_inq_param), + .setup_settings = settings_powered, + .setup_send_opcode = MGMT_OP_START_DISCOVERY, + .setup_send_param = start_discovery_bredr_param, + .setup_send_len = sizeof(start_discovery_bredr_param), .send_opcode = MGMT_OP_STOP_DISCOVERY, .send_param = stop_discovery_bredr_param, .send_len = sizeof(stop_discovery_bredr_param), @@ -1641,6 +1769,9 @@ static const struct generic_data stop_discovery_rejected_test_1 = { static const struct generic_data stop_discovery_invalid_param_test_1 = { .setup_settings = settings_powered_le, + .setup_send_opcode = MGMT_OP_START_DISCOVERY, + .setup_send_param = start_discovery_bredrle_param, + .setup_send_len = sizeof(start_discovery_bredrle_param), .send_opcode = MGMT_OP_STOP_DISCOVERY, .send_param = stop_discovery_bredrle_invalid_param, .send_len = sizeof(stop_discovery_bredrle_invalid_param), @@ -2037,33 +2168,20 @@ static const char load_ltks_invalid_param_2[] = { 0x00, 0x00, /* diversifier */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* rand */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (1/2) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2 */ -}; -/* Invalid authenticated value */ -static const char load_ltks_invalid_param_3[] = { - 0x01, 0x00, /* count */ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* bdaddr */ - 0x01, /* addr type */ - 0x02, /* authenticated */ - 0x00, /* master */ - 0x00, /* encryption size */ - 0x00, 0x00, /* diversifier */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* rand */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (1/2) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2) */ }; /* Invalid master value */ -static const char load_ltks_invalid_param_4[] = { +static const char load_ltks_invalid_param_3[] = { 0x01, 0x00, /* count */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, /* bdaddr */ 0x01, /* addr type */ - 0x00, /* authunticated */ + 0x00, /* authenticated */ 0x02, /* master */ 0x00, /* encryption size */ 0x00, 0x00, /* diversifier */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* rand */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (1/2) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2) */ }; static const struct generic_data load_ltks_success_test_1 = { @@ -2094,13 +2212,6 @@ static const struct generic_data load_ltks_invalid_params_test_3 = { .expect_status = MGMT_STATUS_INVALID_PARAMS, }; -static const struct generic_data load_ltks_invalid_params_test_4 = { - .send_opcode = MGMT_OP_LOAD_LONG_TERM_KEYS, - .send_param = load_ltks_invalid_param_4, - .send_len = sizeof(load_ltks_invalid_param_4), - .expect_status = MGMT_STATUS_INVALID_PARAMS, -}; - static const char pair_device_param[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 }; static const char pair_device_rsp[] = { @@ -2128,6 +2239,301 @@ static const struct generic_data pair_device_invalid_param_test_1 = { .expect_len = sizeof(pair_device_invalid_param_rsp_1), }; +static const void *pair_device_send_param_func(uint16_t *len) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + static uint8_t param[8]; + + memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6); + param[6] = 0x00; /* Address type */ + param[7] = test->io_cap; + + *len = sizeof(param); + + return param; +} + +static const void *pair_device_expect_param_func(uint16_t *len) +{ + struct test_data *data = tester_get_data(); + static uint8_t param[7]; + + memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6); + param[6] = 0x00; /* Address type */ + + *len = sizeof(param); + + return param; +} + +static uint16_t settings_powered_pairable[] = { MGMT_OP_SET_PAIRABLE, + MGMT_OP_SET_POWERED, 0 }; +static uint8_t auth_req_param[] = { 0x2a, 0x00 }; +static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; /* "0000" */ + +static const struct generic_data pair_device_success_test_1 = { + .setup_settings = settings_powered_pairable, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_SUCCESS, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, + .expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED, + .expect_hci_param = auth_req_param, + .expect_hci_len = sizeof(auth_req_param), + .pin = pair_device_pin, + .pin_len = sizeof(pair_device_pin), + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), +}; + +static uint16_t settings_powered_pairable_linksec[] = { MGMT_OP_SET_PAIRABLE, + MGMT_OP_SET_POWERED, + MGMT_OP_SET_LINK_SECURITY, + 0 }; + +static const struct generic_data pair_device_success_test_2 = { + .setup_settings = settings_powered_pairable_linksec, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_SUCCESS, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, + .expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED, + .expect_hci_param = auth_req_param, + .expect_hci_len = sizeof(auth_req_param), + .pin = pair_device_pin, + .pin_len = sizeof(pair_device_pin), + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), +}; + +static const void *client_bdaddr_param_func(uint8_t *len) +{ + struct test_data *data = tester_get_data(); + static uint8_t bdaddr[6]; + + memcpy(bdaddr, hciemu_get_client_bdaddr(data->hciemu), 6); + + *len = sizeof(bdaddr); + + return bdaddr; +} + +static const struct generic_data pair_device_reject_test_1 = { + .setup_settings = settings_powered_pairable, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_AUTH_FAILED, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_AUTH_FAILED, + .expect_alt_ev_len = 8, + .expect_hci_command = BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY, + .expect_hci_func = client_bdaddr_param_func, + .expect_pin = true, + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), +}; + +static const struct generic_data pair_device_reject_test_2 = { + .setup_settings = settings_powered_pairable, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_AUTH_FAILED, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_AUTH_FAILED, + .expect_alt_ev_len = 8, + .expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED, + .expect_hci_param = auth_req_param, + .expect_hci_len = sizeof(auth_req_param), + .pin = pair_device_pin, + .pin_len = sizeof(pair_device_pin), +}; + +static const struct generic_data pair_device_reject_test_3 = { + .setup_settings = settings_powered_pairable_linksec, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_AUTH_FAILED, + .expect_func = pair_device_expect_param_func, + .expect_hci_command = BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY, + .expect_hci_func = client_bdaddr_param_func, + .expect_pin = true, + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), +}; + +static const struct generic_data pair_device_reject_test_4 = { + .setup_settings = settings_powered_pairable_linksec, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_AUTH_FAILED, + .expect_func = pair_device_expect_param_func, + .pin = pair_device_pin, + .pin_len = sizeof(pair_device_pin), +}; + +static uint16_t settings_powered_pairable_ssp[] = { MGMT_OP_SET_PAIRABLE, + MGMT_OP_SET_SSP, + MGMT_OP_SET_POWERED, + 0 }; + +static const struct generic_data pair_device_ssp_test_1 = { + .setup_settings = settings_powered_pairable_ssp, + .client_enable_ssp = true, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_SUCCESS, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, + .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, + .expect_hci_func = client_bdaddr_param_func, + .io_cap = 0x03, /* NoInputNoOutput */ + .client_io_cap = 0x03, /* NoInputNoOutput */ +}; + +static const struct generic_data pair_device_ssp_test_2 = { + .setup_settings = settings_powered_pairable_ssp, + .client_enable_ssp = true, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_SUCCESS, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, + .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, + .expect_hci_func = client_bdaddr_param_func, + .io_cap = 0x01, /* DisplayYesNo */ + .client_io_cap = 0x01, /* DisplayYesNo */ +}; + +static const struct generic_data pair_device_ssp_reject_1 = { + .setup_settings = settings_powered_pairable_ssp, + .client_enable_ssp = true, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_AUTH_FAILED, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_AUTH_FAILED, + .expect_alt_ev_len = 8, + .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY, + .expect_hci_func = client_bdaddr_param_func, + .io_cap = 0x01, /* DisplayYesNo */ + .client_io_cap = 0x01, /* DisplayYesNo */ + .client_auth_req = 0x01, /* No Bonding - MITM */ + .reject_ssp = true, +}; + +static const struct generic_data pair_device_ssp_reject_2 = { + .setup_settings = settings_powered_pairable_ssp, + .client_enable_ssp = true, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_AUTH_FAILED, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_AUTH_FAILED, + .expect_alt_ev_len = 8, + .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, + .expect_hci_func = client_bdaddr_param_func, + .io_cap = 0x01, /* DisplayYesNo */ + .client_io_cap = 0x01, /* DisplayYesNo */ + .client_reject_ssp = true, +}; + +static const struct generic_data pair_device_ssp_nonpairable_1 = { + .setup_settings = settings_powered_ssp, + .client_enable_ssp = true, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_AUTH_FAILED, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_AUTH_FAILED, + .expect_alt_ev_len = 8, + .io_cap = 0x01, /* DisplayYesNo */ + .client_io_cap = 0x01, /* DisplayYesNo */ +}; + +static uint16_t settings_powered_connectable_pairable[] = { + MGMT_OP_SET_PAIRABLE, + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_POWERED, 0 }; + +static const struct generic_data pairing_acceptor_legacy_1 = { + .setup_settings = settings_powered_connectable_pairable, + .pin = pair_device_pin, + .pin_len = sizeof(pair_device_pin), + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, +}; + +static const struct generic_data pairing_acceptor_legacy_2 = { + .setup_settings = settings_powered_connectable_pairable, + .expect_pin = true, + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), + .expect_alt_ev = MGMT_EV_AUTH_FAILED, + .expect_alt_ev_len = 8, +}; + +static uint16_t settings_powered_connectable_pairable_linksec[] = { + MGMT_OP_SET_PAIRABLE, + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_LINK_SECURITY, + MGMT_OP_SET_POWERED, 0 }; + +static const struct generic_data pairing_acceptor_linksec_1 = { + .setup_settings = settings_powered_connectable_pairable_linksec, + .pin = pair_device_pin, + .pin_len = sizeof(pair_device_pin), + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, +}; + +static const struct generic_data pairing_acceptor_linksec_2 = { + .setup_settings = settings_powered_connectable_pairable_linksec, + .expect_pin = true, + .client_pin = pair_device_pin, + .client_pin_len = sizeof(pair_device_pin), + .expect_alt_ev = MGMT_EV_CONNECT_FAILED, + .expect_alt_ev_len = 8, +}; + +static uint16_t settings_powered_connectable_pairable_ssp[] = { + MGMT_OP_SET_PAIRABLE, + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_SSP, + MGMT_OP_SET_POWERED, 0 }; + +static const struct generic_data pairing_acceptor_ssp_1 = { + .setup_settings = settings_powered_connectable_pairable_ssp, + .client_enable_ssp = true, + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, + .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, + .expect_hci_func = client_bdaddr_param_func, + .io_cap = 0x03, /* NoInputNoOutput */ + .client_io_cap = 0x03, /* NoInputNoOutput */ +}; + +static const struct generic_data pairing_acceptor_ssp_2 = { + .setup_settings = settings_powered_connectable_pairable_ssp, + .client_enable_ssp = true, + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, + .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY, + .expect_hci_func = client_bdaddr_param_func, + .io_cap = 0x01, /* DisplayYesNo */ + .client_io_cap = 0x01, /* DisplayYesNo */ +}; + static const char unpair_device_param[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 }; static const char unpair_device_rsp[] = { @@ -2237,6 +2643,160 @@ static const struct generic_data set_scan_params_success_test = { .expect_status = MGMT_STATUS_SUCCESS, }; +static const char load_irks_empty_list[] = { 0x00, 0x00 }; + +static const struct generic_data load_irks_success1_test = { + .send_opcode = MGMT_OP_LOAD_IRKS, + .send_param = load_irks_empty_list, + .send_len = sizeof(load_irks_empty_list), + .expect_status = MGMT_STATUS_SUCCESS, +}; + +static const char load_irks_one_irk[] = { 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x01, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + +static const struct generic_data load_irks_success2_test = { + .send_opcode = MGMT_OP_LOAD_IRKS, + .send_param = load_irks_one_irk, + .send_len = sizeof(load_irks_one_irk), + .expect_status = MGMT_STATUS_SUCCESS, +}; + +static const char load_irks_nval_addr_type[] = { 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + +static const struct generic_data load_irks_nval_param1_test = { + .send_opcode = MGMT_OP_LOAD_IRKS, + .send_param = load_irks_nval_addr_type, + .send_len = sizeof(load_irks_nval_addr_type), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const char load_irks_nval_rand_addr[] = { 0x01, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x02, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + +static const struct generic_data load_irks_nval_param2_test = { + .send_opcode = MGMT_OP_LOAD_IRKS, + .send_param = load_irks_nval_rand_addr, + .send_len = sizeof(load_irks_nval_rand_addr), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const char load_irks_nval_len[] = { 0x02, 0x00, 0xff, 0xff }; + +static const struct generic_data load_irks_nval_param3_test = { + .send_opcode = MGMT_OP_LOAD_IRKS, + .send_param = load_irks_nval_len, + .send_len = sizeof(load_irks_nval_len), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static const struct generic_data load_irks_not_supported_test = { + .send_opcode = MGMT_OP_LOAD_IRKS, + .send_param = load_irks_empty_list, + .send_len = sizeof(load_irks_empty_list), + .expect_status = MGMT_STATUS_NOT_SUPPORTED, +}; + +static const char set_privacy_valid_param[] = { 0x01, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; +static const char set_privacy_settings_param[] = { 0x80, 0x20, 0x00, 0x00 }; + +static const struct generic_data set_privacy_success_test = { + .send_opcode = MGMT_OP_SET_PRIVACY, + .send_param = set_privacy_valid_param, + .send_len = sizeof(set_privacy_valid_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_privacy_settings_param, + .expect_len = sizeof(set_privacy_settings_param), + .expect_settings_set = MGMT_SETTING_PRIVACY, +}; + +static const struct generic_data set_privacy_powered_test = { + .setup_settings = settings_powered, + .send_opcode = MGMT_OP_SET_PRIVACY, + .send_param = set_privacy_valid_param, + .send_len = sizeof(set_privacy_valid_param), + .expect_status = MGMT_STATUS_REJECTED, +}; + +static const char set_privacy_nval_param[] = { 0xff, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; +static const struct generic_data set_privacy_nval_param_test = { + .send_opcode = MGMT_OP_SET_PRIVACY, + .send_param = set_privacy_nval_param, + .send_len = sizeof(set_privacy_nval_param), + .expect_status = MGMT_STATUS_INVALID_PARAMS, +}; + +static void client_cmd_complete(uint16_t opcode, uint8_t status, + const void *param, uint8_t len, + void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + struct bthost *bthost; + + bthost = hciemu_client_get_host(data->hciemu); + + switch (opcode) { + case BT_HCI_CMD_WRITE_SCAN_ENABLE: + case BT_HCI_CMD_LE_SET_ADV_ENABLE: + tester_print("Client set connectable status 0x%02x", status); + if (!status && test->client_enable_ssp) { + bthost_write_ssp_mode(bthost, 0x01); + return; + } + break; + case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE: + tester_print("Client enable SSP status 0x%02x", status); + break; + default: + return; + } + + if (status) + tester_setup_failed(); + else + tester_setup_complete(); +} + +static void setup_bthost(void) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_cmd_complete_cb(bthost, client_cmd_complete, data); + if (data->hciemu_type == HCIEMU_TYPE_LE) + bthost_set_adv_enable(bthost, 0x01); + else + bthost_write_scan_enable(bthost, 0x03); +} + +static void setup_ssp_acceptor(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + + if (!test->io_cap) + return; + + mgmt_send(data->mgmt, MGMT_OP_SET_IO_CAPABILITY, data->mgmt_index, + sizeof(test->io_cap), &test->io_cap, + NULL, NULL, NULL); + + setup_bthost(); +} + static void setup_powered_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -2247,7 +2807,7 @@ static void setup_powered_callback(uint8_t status, uint16_t length, tester_print("Controller powered on"); - tester_setup_complete(); + setup_bthost(); } static void setup_class(const void *test_data) @@ -2279,57 +2839,16 @@ static void setup_discovery_callback(uint8_t status, uint16_t length, tester_setup_complete(); } -static bool setup_command_hci_callback(const void *data, uint16_t len, - void *user_data) -{ - struct test_data *tdata = tester_get_data(); - const struct generic_data *test = tdata->test_data; - - tester_print("HCI Command 0x%04x length %u (setup)", - test->setup_expect_hci_command, len); - - if (len != test->setup_expect_hci_len) { - tester_warn("Invalid parameter size for HCI command (setup)"); - tester_setup_failed(); - goto done; - } - - if (memcmp(data, test->setup_expect_hci_param, len) != 0) { - tester_warn("Unexpected HCI command parameter value (setup)"); - tester_setup_failed(); - goto done; - } - - tester_setup_complete(); - -done: - hciemu_del_hook(tdata->hciemu, HCIEMU_HOOK_PRE_EVT, - test->setup_expect_hci_command); - - return false; -} - static void setup_start_discovery(const void *test_data) { struct test_data *data = tester_get_data(); const struct generic_data *test = data->test_data; + const void *send_param = test->setup_send_param; + uint16_t send_len = test->setup_send_len; - if (test->setup_expect_hci_command) { - tester_print("Registering HCI command callback (setup)"); - hciemu_add_hook(data->hciemu, HCIEMU_HOOK_PRE_EVT, - test->setup_expect_hci_command, - setup_command_hci_callback, - NULL); - mgmt_send(data->mgmt, MGMT_OP_START_DISCOVERY, data->mgmt_index, - test->send_len, test->send_param, - NULL, NULL, NULL); - } else { - unsigned char disc_param[] = { 0x07 }; - - mgmt_send(data->mgmt, MGMT_OP_START_DISCOVERY, data->mgmt_index, - sizeof(disc_param), disc_param, - setup_discovery_callback, NULL, NULL); - } + mgmt_send(data->mgmt, test->setup_send_opcode, data->mgmt_index, + send_len, send_param, setup_discovery_callback, + NULL, NULL); } static void setup_multi_uuid32(const void *test_data) @@ -2531,15 +3050,93 @@ static void setup_complete(uint8_t status, uint16_t length, if (data->test_setup) data->test_setup(data); else - tester_setup_complete(); + setup_bthost(); +} + +static void pin_code_request_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_pin_code_request *ev = param; + struct test_data *data = user_data; + const struct generic_data *test = data->test_data; + struct mgmt_cp_pin_code_reply cp; + + test_condition_complete(data); + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.addr, &ev->addr, sizeof(cp.addr)); + + if (!test->pin) { + mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY, + data->mgmt_index, sizeof(cp.addr), &cp.addr, + NULL, NULL, NULL); + return; + } + + cp.pin_len = test->pin_len; + memcpy(cp.pin_code, test->pin, test->pin_len); + + mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_REPLY, data->mgmt_index, + sizeof(cp), &cp, NULL, NULL, NULL); +} + +static void user_confirm_request_callback(uint16_t index, uint16_t length, + const void *param, + void *user_data) +{ + const struct mgmt_ev_user_confirm_request *ev = param; + struct test_data *data = user_data; + const struct generic_data *test = data->test_data; + struct mgmt_cp_user_confirm_reply cp; + uint16_t opcode; + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.addr, &ev->addr, sizeof(cp.addr)); + + if (test->reject_ssp) + opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY; + else + opcode = MGMT_OP_USER_CONFIRM_REPLY; + + mgmt_reply(data->mgmt, opcode, data->mgmt_index, sizeof(cp), &cp, + NULL, NULL, NULL); } static void test_setup(const void *test_data) { struct test_data *data = tester_get_data(); const struct generic_data *test = data->test_data; + struct bthost *bthost = hciemu_client_get_host(data->hciemu); const uint16_t *cmd; + if (!test) + goto proceed; + + if (test->pin || test->expect_pin) { + mgmt_register(data->mgmt, MGMT_EV_PIN_CODE_REQUEST, + data->mgmt_index, pin_code_request_callback, + data, NULL); + test_add_condition(data); + } + + mgmt_register(data->mgmt, MGMT_EV_USER_CONFIRM_REQUEST, + data->mgmt_index, user_confirm_request_callback, + data, NULL); + + if (test->client_pin) + bthost_set_pin_code(bthost, test->client_pin, + test->client_pin_len); + + if (test->client_io_cap) + bthost_set_io_capability(bthost, test->client_io_cap); + + if (test->client_auth_req) + bthost_set_auth_req(bthost, test->client_auth_req); + + if (test->client_reject_ssp) + bthost_set_reject_user_confirm(bthost, true); + +proceed: if (!test || !test->setup_settings) { if (data->test_setup) data->test_setup(data); @@ -2569,14 +3166,16 @@ static void test_setup(const void *test_data) param = discov_param; } - mgmt_send(data->mgmt, *cmd, data->mgmt_index, - param_size, param, func, data, NULL); - if (*cmd == MGMT_OP_SET_LE && test->setup_nobredr) { unsigned char off[] = { 0x00 }; + mgmt_send(data->mgmt, *cmd, data->mgmt_index, + param_size, param, NULL, NULL, NULL); mgmt_send(data->mgmt, MGMT_OP_SET_BREDR, data->mgmt_index, sizeof(off), off, - NULL, data, NULL); + func, data, NULL); + } else { + mgmt_send(data->mgmt, *cmd, data->mgmt_index, + param_size, param, func, data, NULL); } } } @@ -2606,7 +3205,7 @@ static void command_generic_new_settings_alt(uint16_t index, uint16_t length, return; } - settings = bt_get_le32(param); + settings = get_le32(param); tester_print("New settings 0x%08x received", settings); @@ -2646,7 +3245,8 @@ static void command_generic_event_alt(uint16_t index, uint16_t length, tester_print("New %s event received", mgmt_evstr(test->expect_alt_ev)); - if (memcmp(param, test->expect_alt_ev_param, + if (test->expect_alt_ev_param && + memcmp(param, test->expect_alt_ev_param, test->expect_alt_ev_len) != 0) return; @@ -2663,6 +3263,8 @@ static void command_generic_callback(uint8_t status, uint16_t length, { struct test_data *data = tester_get_data(); const struct generic_data *test = data->test_data; + const void *expect_param = test->expect_param; + uint16_t expect_len = test->expect_len; tester_print("Command 0x%04x finished with status 0x%02x", test->send_opcode, status); @@ -2672,13 +3274,16 @@ static void command_generic_callback(uint8_t status, uint16_t length, return; } - if (length != test->expect_len) { + if (test->expect_func) + expect_param = test->expect_func(&expect_len); + + if (length != expect_len) { tester_test_failed(); return; } - if (test->expect_param && test->expect_len > 0 && - memcmp(param, test->expect_param, length)) { + if (expect_param && expect_len > 0 && + memcmp(param, expect_param, length)) { tester_test_failed(); return; } @@ -2691,19 +3296,24 @@ static void command_hci_callback(uint16_t opcode, const void *param, { struct test_data *data = user_data; const struct generic_data *test = data->test_data; + const void *expect_hci_param = test->expect_hci_param; + uint8_t expect_hci_len = test->expect_hci_len; tester_print("HCI Command 0x%04x length %u", opcode, length); if (opcode != test->expect_hci_command) return; - if (length != test->expect_hci_len) { + if (test->expect_hci_func) + expect_hci_param = test->expect_hci_func(&expect_hci_len); + + if (length != expect_hci_len) { tester_warn("Invalid parameter size for HCI command"); tester_test_failed(); return; } - if (memcmp(param, test->expect_hci_param, length) != 0) { + if (memcmp(param, expect_hci_param, length) != 0) { tester_warn("Unexpected HCI command parameter value"); tester_test_failed(); return; @@ -2716,6 +3326,8 @@ static void test_command_generic(const void *test_data) { struct test_data *data = tester_get_data(); const struct generic_data *test = data->test_data; + const void *send_param = test->send_param; + uint16_t send_len = test->send_len; unsigned int id; uint16_t index; @@ -2752,12 +3364,64 @@ static void test_command_generic(const void *test_data) tester_print("Sending command 0x%04x", test->send_opcode); - mgmt_send(data->mgmt, test->send_opcode, index, - test->send_len, test->send_param, + if (test->send_func) + send_param = test->send_func(&send_len); + + mgmt_send(data->mgmt, test->send_opcode, index, send_len, send_param, command_generic_callback, NULL, NULL); test_add_condition(data); } +static void pairing_new_conn(uint16_t handle, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + + tester_print("New connection with handle 0x%04x", handle); + + bthost = hciemu_client_get_host(data->hciemu); + + bthost_request_auth(bthost, handle); +} + +static void test_pairing_acceptor(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + const uint8_t *master_bdaddr; + struct bthost *bthost; + uint8_t addr_type; + + if (test->expect_alt_ev) { + unsigned int id; + + tester_print("Registering %s notification", + mgmt_evstr(test->expect_alt_ev)); + id = mgmt_register(data->mgmt_alt, test->expect_alt_ev, + data->mgmt_index, + command_generic_event_alt, NULL, NULL); + data->mgmt_alt_ev_id = id; + test_add_condition(data); + } + + master_bdaddr = hciemu_get_master_bdaddr(data->hciemu); + if (!master_bdaddr) { + tester_warn("No master bdaddr"); + tester_test_failed(); + return; + } + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_connect_cb(bthost, pairing_new_conn, data); + + if (data->hciemu_type == HCIEMU_TYPE_BREDRLE) + addr_type = BDADDR_BREDR; + else + addr_type = BDADDR_LE_PUBLIC; + + bthost_hci_connect(bthost, master_bdaddr, addr_type); +} + int main(int argc, char *argv[]) { tester_init(&argc, &argv); @@ -3014,6 +3678,38 @@ int main(int argc, char *argv[]) &set_ssp_on_invalid_index_test, NULL, test_command_generic); + test_bredrle("Set Secure Connections on - Success 1", + &set_sc_on_success_test_1, + NULL, test_command_generic); + test_bredrle("Set Secure Connections on - Success 2", + &set_sc_on_success_test_2, + NULL, test_command_generic); + test_bredrle("Set Secure Connections on - Invalid params 1", + &set_sc_on_invalid_param_test_1, + NULL, test_command_generic); + test_bredrle("Set Secure Connections on - Invalid params 2", + &set_sc_on_invalid_param_test_2, + NULL, test_command_generic); + test_bredrle("Set Secure Connections on - Invalid params 3", + &set_sc_on_invalid_param_test_3, + NULL, test_command_generic); + test_bredrle("Set Secure Connections on - Invalid index", + &set_sc_on_invalid_index_test, + NULL, test_command_generic); + test_bredr("Set Secure Connections on - Not supported 1", + &set_sc_on_not_supported_test_1, + NULL, test_command_generic); + test_bredr("Set Secure Connections on - Not supported 2", + &set_sc_on_not_supported_test_2, + NULL, test_command_generic); + + test_bredrle("Set Secure Connections Only on - Success 1", + &set_sc_only_on_success_test_1, + NULL, test_command_generic); + test_bredrle("Set Secure Connections Only on - Success 2", + &set_sc_only_on_success_test_2, + NULL, test_command_generic); + test_bredrle("Set High Speed on - Success", &set_hs_on_success_test, NULL, test_command_generic); @@ -3195,9 +3891,6 @@ int main(int argc, char *argv[]) test_bredrle("Load Long Term Keys - Invalid Parameters 3", &load_ltks_invalid_params_test_3, NULL, test_command_generic); - test_bredrle("Load Long Term Keys - Invalid Parameters 4", - &load_ltks_invalid_params_test_4, - NULL, test_command_generic); test_bredrle("Pair Device - Not Powered 1", &pair_device_not_powered_test_1, @@ -3205,6 +3898,58 @@ int main(int argc, char *argv[]) test_bredrle("Pair Device - Invalid Parameters 1", &pair_device_invalid_param_test_1, NULL, test_command_generic); + test_bredrle("Pair Device - Legacy Success 1", + &pair_device_success_test_1, + NULL, test_command_generic); + test_bredrle("Pair Device - Sec Mode 3 Success 1", + &pair_device_success_test_2, + NULL, test_command_generic); + test_bredrle("Pair Device - Legacy Reject 1", + &pair_device_reject_test_1, + NULL, test_command_generic); + test_bredrle("Pair Device - Legacy Reject 2", + &pair_device_reject_test_2, + NULL, test_command_generic); + test_bredrle("Pair Device - Sec Mode 3 Reject 1", + &pair_device_reject_test_3, + NULL, test_command_generic); + test_bredrle("Pair Device - Sec Mode 3 Reject 2", + &pair_device_reject_test_4, + NULL, test_command_generic); + test_bredrle("Pair Device - SSP Just-Works Success 1", + &pair_device_ssp_test_1, + NULL, test_command_generic); + test_bredrle("Pair Device - SSP Confirm Success 1", + &pair_device_ssp_test_2, + NULL, test_command_generic); + test_bredrle("Pair Device - SSP Confirm Reject 1", + &pair_device_ssp_reject_1, + NULL, test_command_generic); + test_bredrle("Pair Device - SSP Confirm Reject 2", + &pair_device_ssp_reject_2, + NULL, test_command_generic); + test_bredrle("Pair Device - SSP Non-pairable 1", + &pair_device_ssp_nonpairable_1, + NULL, test_command_generic); + + test_bredrle("Pairing Acceptor - Legacy 1", + &pairing_acceptor_legacy_1, NULL, + test_pairing_acceptor); + test_bredrle("Pairing Acceptor - Legacy 2", + &pairing_acceptor_legacy_2, NULL, + test_pairing_acceptor); + test_bredrle("Pairing Acceptor - Link Sec 1", + &pairing_acceptor_linksec_1, NULL, + test_pairing_acceptor); + test_bredrle("Pairing Acceptor - Link Sec 2", + &pairing_acceptor_linksec_2, NULL, + test_pairing_acceptor); + test_bredrle("Pairing Acceptor - SSP 1", + &pairing_acceptor_ssp_1, setup_ssp_acceptor, + test_pairing_acceptor); + test_bredrle("Pairing Acceptor - SSP 2", + &pairing_acceptor_ssp_2, setup_ssp_acceptor, + test_pairing_acceptor); test_bredrle("Unpair Device - Not Powered 1", &unpair_device_not_powered_test_1, @@ -3239,5 +3984,34 @@ int main(int argc, char *argv[]) &set_scan_params_success_test, NULL, test_command_generic); + test_bredrle("Load IRKs - Success 1", + &load_irks_success1_test, + NULL, test_command_generic); + test_bredrle("Load IRKs - Success 2", + &load_irks_success2_test, + NULL, test_command_generic); + test_bredrle("Load IRKs - Invalid Parameters 1", + &load_irks_nval_param1_test, + NULL, test_command_generic); + test_bredrle("Load IRKs - Invalid Parameters 2", + &load_irks_nval_param2_test, + NULL, test_command_generic); + test_bredrle("Load IRKs - Invalid Parameters 3", + &load_irks_nval_param3_test, + NULL, test_command_generic); + test_bredr("Load IRKs - Not Supported", + &load_irks_not_supported_test, + NULL, test_command_generic); + + test_bredrle("Set Privacy - Success", + &set_privacy_success_test, + NULL, test_command_generic); + test_bredrle("Set Privacy - Rejected", + &set_privacy_powered_test, + NULL, test_command_generic); + test_bredrle("Set Privacy - Invalid Parameters", + &set_privacy_nval_param_test, + NULL, test_command_generic); + return tester_run(); } diff --git a/tools/obex-client-tool.c b/tools/obex-client-tool.c index 54dbcbb..4716a8c 100644 --- a/tools/obex-client-tool.c +++ b/tools/obex-client-tool.c @@ -37,7 +37,7 @@ #include #include -#include +#include "btio/btio.h" static GMainLoop *main_loop = NULL; static GObex *obex = NULL; diff --git a/tools/obex-server-tool.c b/tools/obex-server-tool.c index 86c2271..3fed6dc 100644 --- a/tools/obex-server-tool.c +++ b/tools/obex-server-tool.c @@ -34,7 +34,7 @@ #include #include -#include +#include "btio/btio.h" static GMainLoop *main_loop = NULL; diff --git a/tools/obexctl.c b/tools/obexctl.c index 0491b51..512a145 100644 --- a/tools/obexctl.c +++ b/tools/obexctl.c @@ -431,14 +431,12 @@ static void set_default_session(GDBusProxy *proxy) default_session = proxy; - if (proxy == NULL) { + if (!g_dbus_proxy_get_property(proxy, "Destination", &iter)) { desc = g_strdup(PROMPT_ON); goto done; } - if (g_dbus_proxy_get_property(proxy, "Destination", &iter)) - dbus_message_iter_get_basic(&iter, &desc); - + dbus_message_iter_get_basic(&iter, &desc); desc = g_strdup_printf(COLOR_BLUE "[%s]" COLOR_OFF "# ", desc); done: @@ -598,6 +596,86 @@ static void cmd_cancel(int argc, char *argv[]) g_dbus_proxy_get_path(proxy)); } +static void suspend_reply(DBusMessage *message, void *user_data) +{ + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to suspend: %s\n", error.name); + dbus_error_free(&error); + return; + } + + rl_printf("Suspend successful\n"); +} + +static void cmd_suspend(int argc, char *argv[]) +{ + GDBusProxy *proxy; + + if (argc < 2) { + rl_printf("Missing transfer address argument\n"); + return; + } + + proxy = find_transfer(argv[1]); + if (!proxy) { + rl_printf("Transfer %s not available\n", argv[1]); + return; + } + + if (g_dbus_proxy_method_call(proxy, "Suspend", NULL, suspend_reply, + NULL, NULL) == FALSE) { + rl_printf("Failed to suspend transfer\n"); + return; + } + + rl_printf("Attempting to suspend transfer %s\n", + g_dbus_proxy_get_path(proxy)); +} + +static void resume_reply(DBusMessage *message, void *user_data) +{ + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to resume: %s\n", error.name); + dbus_error_free(&error); + return; + } + + rl_printf("Resume successful\n"); +} + +static void cmd_resume(int argc, char *argv[]) +{ + GDBusProxy *proxy; + + if (argc < 2) { + rl_printf("Missing transfer address argument\n"); + return; + } + + proxy = find_transfer(argv[1]); + if (!proxy) { + rl_printf("Transfer %s not available\n", argv[1]); + return; + } + + if (g_dbus_proxy_method_call(proxy, "Resume", NULL, resume_reply, + NULL, NULL) == FALSE) { + rl_printf("Failed to resume transfer\n"); + return; + } + + rl_printf("Attempting to resume transfer %s\n", + g_dbus_proxy_get_path(proxy)); +} + static GDBusProxy *find_opp(const char *path) { GSList *l; @@ -1203,13 +1281,8 @@ static void list_messages_setup(DBusMessageIter *iter, void *user_data) dbus_message_iter_close_container(iter, &dict); } -static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[]) +static void pbap_list(GDBusProxy *proxy, int argc, char *argv[]) { - if (argc > 1) { - pbap_search(proxy, argc, argv); - return; - } - if (g_dbus_proxy_method_call(proxy, "List", list_setup, list_reply, NULL, NULL) == FALSE) { rl_printf("Failed to List\n"); @@ -1219,6 +1292,51 @@ static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[]) rl_printf("Attempting to List\n"); } +static void get_size_reply(DBusMessage *message, void *user_data) +{ + GDBusProxy *proxy = user_data; + DBusError error; + DBusMessageIter iter; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to GetSize: %s\n", error.name); + dbus_error_free(&error); + return; + } + + dbus_message_iter_init(message, &iter); + + print_iter("\t", "Size", &iter); + + pbap_list(proxy, 0, NULL); +} + +static void pbap_get_size(GDBusProxy *proxy, int argc, char *argv[]) +{ + if (g_dbus_proxy_method_call(proxy, "GetSize", NULL, get_size_reply, + proxy, NULL) == FALSE) { + rl_printf("Failed to GetSize\n"); + return; + } + + rl_printf("Attempting to GetSize\n"); +} + +static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[]) +{ + if (argc > 1) { + if (strcmp("-l", argv[1])) + pbap_search(proxy, argc, argv); + else + pbap_get_size(proxy, argc, argv); + return; + } + + pbap_list(proxy, argc, argv); +} + static void map_ls_messages(GDBusProxy *proxy, int argc, char *argv[]) { if (g_dbus_proxy_method_call(proxy, "ListMessages", list_messages_setup, @@ -1858,9 +1976,11 @@ static const struct { { "select", "", cmd_select, "Select default session" }, { "info", "", cmd_info, "Object information" }, { "cancel", "", cmd_cancel, "Cancel transfer" }, + { "suspend", "", cmd_suspend, "Suspend transfer" }, + { "resume", "", cmd_resume, "Resume transfer" }, { "send", "", cmd_send, "Send file" }, { "cd", "", cmd_cd, "Change current folder" }, - { "ls", NULL, cmd_ls, "List current folder" }, + { "ls", "", cmd_ls, "List current folder" }, { "cp", " ", cmd_cp, "Copy source file to destination file" }, { "mv", " ", cmd_mv, @@ -1925,6 +2045,7 @@ static void rl_handler(char *input) if (!strlen(input)) goto done; + g_strstrip(input); add_history(input); argv = g_strsplit(input, " ", -1); diff --git a/tools/parser/avrcp.c b/tools/parser/avrcp.c index b523533..7c320ea 100644 --- a/tools/parser/avrcp.c +++ b/tools/parser/avrcp.c @@ -1180,12 +1180,16 @@ response: switch (status) { case 0x00: printf("(POWER_ON)\n"); + break; case 0x01: printf("(POWER_OFF)\n"); + break; case 0x02: printf("(UNPLUGGED)\n"); + break; default: printf("(UNKOWN)\n"); + break; } break; case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED: diff --git a/tools/parser/hci.c b/tools/parser/hci.c index 17b776d..351f843 100644 --- a/tools/parser/hci.c +++ b/tools/parser/hci.c @@ -792,7 +792,7 @@ static inline void ext_inquiry_data_dump(int level, struct frame *frm, type == 0x02 ? "Shortened" : "Complete"); for (i = 0; i < len / 2; i++) - printf(" 0x%4.4x", bt_get_le16(data + i * 2)); + printf(" 0x%4.4x", get_le16(data + i * 2)); printf("\n"); break; diff --git a/tools/parser/l2cap.c b/tools/parser/l2cap.c index f0fb78b..a057964 100644 --- a/tools/parser/l2cap.c +++ b/tools/parser/l2cap.c @@ -250,9 +250,9 @@ static uint32_t get_val(uint8_t *ptr, uint8_t len) case 1: return *ptr; case 2: - return bt_get_le16(ptr); + return get_le16(ptr); case 4: - return bt_get_le32(ptr); + return get_le32(ptr); } return 0; } @@ -595,9 +595,9 @@ static void conf_rfc(void *ptr, int len, int in, uint16_t handle, uint16_t rto, mto, mps; txwin = *((uint8_t *) (ptr + 1)); maxtrans = *((uint8_t *) (ptr + 2)); - rto = bt_get_le16(ptr + 3); - mto = bt_get_le16(ptr + 5); - mps = bt_get_le16(ptr + 7); + rto = get_le16(ptr + 3); + mto = get_le16(ptr + 5); + mps = get_le16(ptr + 7); printf(", TxWin %d, MaxTx %d, RTo %d, MTo %d, MPS %d", txwin, maxtrans, rto, mto, mps); } @@ -853,7 +853,7 @@ static void info_opt(int level, int type, void *ptr, int len) } break; case 0x0003: - fc_mask = bt_get_le64(ptr); + fc_mask = get_le64(ptr); printf("Fixed channel list 0x%8.8" PRIx64 "\n", fc_mask); if (parser.flags & DUMP_VERBOSE) for (i=0; l2cap_fix_chan[i].name; i++) @@ -916,7 +916,7 @@ static void l2cap_ctrl_ext_parse(int level, struct frame *frm, uint32_t ctrl) printf(" %s", sar2str(sar)); if (sar == L2CAP_SAR_START) { uint16_t len; - len = bt_get_le16(frm->ptr); + len = get_le16(frm->ptr); frm->ptr += L2CAP_SDULEN_SIZE; frm->len -= L2CAP_SDULEN_SIZE; printf(" (len %d)", len); @@ -949,7 +949,7 @@ static void l2cap_ctrl_parse(int level, struct frame *frm, uint32_t ctrl) printf(" %s", sar2str(sar)); if (sar == L2CAP_SAR_START) { uint16_t len; - len = bt_get_le16(frm->ptr); + len = get_le16(frm->ptr); frm->ptr += L2CAP_SDULEN_SIZE; frm->len -= L2CAP_SDULEN_SIZE; printf(" (len %d)", len); @@ -1062,7 +1062,7 @@ static inline void a2mp_discover_req(int level, struct frame *frm, uint16_t len) do { len -= 2; - mask = bt_get_le16(octet); + mask = get_le16(octet); printf(" 0x%4.4x", mask); extension = octet[1] & 0x80; @@ -1102,7 +1102,7 @@ static inline void a2mp_discover_rsp(int level, struct frame *frm, uint16_t len) do { len -= 2; - mask = bt_get_le16(octet); + mask = get_le16(octet); printf(" 0x%4.4x", mask); extension = octet[1] & 0x80; @@ -1324,7 +1324,7 @@ static void l2cap_parse(int level, struct frame *frm) if (p_filter(FILT_L2CAP)) return; - psm = bt_get_le16(frm->ptr); + psm = get_le16(frm->ptr); frm->ptr += 2; frm->len -= 2; @@ -1433,7 +1433,7 @@ static void l2cap_parse(int level, struct frame *frm) frm->ptr += 2; frm->len -= 4; } - fcs = bt_get_le16(frm->ptr + frm->len); + fcs = get_le16(frm->ptr + frm->len); } if (!p_filter(FILT_L2CAP)) { diff --git a/tools/parser/parser.h b/tools/parser/parser.h index c65b4b5..62b8ac5 100644 --- a/tools/parser/parser.h +++ b/tools/parser/parser.h @@ -30,6 +30,7 @@ #include #include "lib/bluetooth.h" +#include "src/shared/util.h" struct frame { void *data; @@ -173,7 +174,7 @@ static inline uint16_t get_u16(struct frame *frm) uint16_t *u16_ptr = frm->ptr; frm->ptr += 2; frm->len -= 2; - return ntohs(bt_get_unaligned(u16_ptr)); + return get_be16(u16_ptr); } static inline uint32_t get_u32(struct frame *frm) @@ -181,13 +182,13 @@ static inline uint32_t get_u32(struct frame *frm) uint32_t *u32_ptr = frm->ptr; frm->ptr += 4; frm->len -= 4; - return ntohl(bt_get_unaligned(u32_ptr)); + return get_be32(u32_ptr); } static inline uint64_t get_u64(struct frame *frm) { uint64_t *u64_ptr = frm->ptr; - uint64_t u64 = bt_get_unaligned(u64_ptr), tmp; + uint64_t u64 = get_unaligned(u64_ptr), tmp; frm->ptr += 8; frm->len -= 8; tmp = ntohl(u64 & 0xffffffff); diff --git a/tools/parser/ppp.c b/tools/parser/ppp.c index 947ca56..256e172 100644 --- a/tools/parser/ppp.c +++ b/tools/parser/ppp.c @@ -103,7 +103,7 @@ static void hdlc_dump(int level, struct frame *frm) uint8_t ctrl = get_u8(frm); uint16_t fcs, proto; - fcs = bt_get_unaligned((uint16_t *) (frm->ptr + frm->len - 2)); + fcs = get_unaligned((uint16_t *) (frm->ptr + frm->len - 2)); frm->len -= 2; p_indent(level, frm); diff --git a/tools/rctest.c b/tools/rctest.c index 77fa03c..2c7e45b 100644 --- a/tools/rctest.c +++ b/tools/rctest.c @@ -47,6 +47,8 @@ #include #include +#include "src/shared/util.h" + /* Test modes */ enum { SEND, @@ -466,8 +468,11 @@ static void save_mode(int sk) while ((len = read(sk, b, data_size)) > 0) { ret = write(save_fd, b, len); if (ret < 0) - return; + goto done; } + +done: + free(b); } static void recv_mode(int sk) @@ -564,8 +569,8 @@ static void do_send(int sk) seq = 0; while ((num_frames == -1) || (num_frames-- > 0)) { - bt_put_le32(seq, buf); - bt_put_le16(data_size, buf + 4); + put_le32(seq, buf); + put_le16(data_size, buf + 4); seq++; diff --git a/tools/rfcomm-tester.c b/tools/rfcomm-tester.c new file mode 100644 index 0000000..8be6e72 --- /dev/null +++ b/tools/rfcomm-tester.c @@ -0,0 +1,762 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. 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 +#endif + +#include +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/mgmt.h" +#include "bluetooth/rfcomm.h" + +#include "monitor/bt.h" +#include "emulator/bthost.h" + +#include "src/shared/tester.h" +#include "src/shared/mgmt.h" +#include "src/shared/hciemu.h" + +struct test_data { + struct mgmt *mgmt; + uint16_t mgmt_index; + struct hciemu *hciemu; + enum hciemu_type hciemu_type; + const void *test_data; + unsigned int io_id; + uint16_t conn_handle; +}; + +struct rfcomm_client_data { + uint8_t server_channel; + uint8_t client_channel; + int expected_connect_err; + const uint8_t *send_data; + const uint8_t *read_data; + uint16_t data_len; +}; + +struct rfcomm_server_data { + uint8_t server_channel; + uint8_t client_channel; + bool expected_status; + const uint8_t *send_data; + const uint8_t *read_data; + uint16_t data_len; +}; + +static void mgmt_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + tester_print("%s%s", prefix, str); +} + +static void read_info_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct mgmt_rp_read_info *rp = param; + char addr[18]; + uint16_t manufacturer; + uint32_t supported_settings, current_settings; + + tester_print("Read Info callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + ba2str(&rp->bdaddr, addr); + manufacturer = btohs(rp->manufacturer); + supported_settings = btohl(rp->supported_settings); + current_settings = btohl(rp->current_settings); + + tester_print(" Address: %s", addr); + tester_print(" Version: 0x%02x", rp->version); + tester_print(" Manufacturer: 0x%04x", manufacturer); + tester_print(" Supported settings: 0x%08x", supported_settings); + tester_print(" Current settings: 0x%08x", current_settings); + tester_print(" Class: 0x%02x%02x%02x", + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); + tester_print(" Name: %s", rp->name); + tester_print(" Short name: %s", rp->short_name); + + if (strcmp(hciemu_get_address(data->hciemu), addr)) { + tester_pre_setup_failed(); + return; + } + + tester_pre_setup_complete(); +} + +static void index_added_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Added callback"); + tester_print(" Index: 0x%04x", index); + + data->mgmt_index = index; + + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, + read_info_callback, NULL, NULL); +} + +static void index_removed_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Removed callback"); + tester_print(" Index: 0x%04x", index); + + if (index != data->mgmt_index) + return; + + mgmt_unregister_index(data->mgmt, data->mgmt_index); + + mgmt_unref(data->mgmt); + data->mgmt = NULL; + + tester_post_teardown_complete(); +} + +static void read_index_list_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Read Index List callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + index_added_callback, NULL, NULL); + + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + index_removed_callback, NULL, NULL); + + data->hciemu = hciemu_new(data->hciemu_type); + if (!data->hciemu) { + tester_warn("Failed to setup HCI emulation"); + tester_pre_setup_failed(); + } + + tester_print("New hciemu instance created"); +} + +static void test_pre_setup(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + data->mgmt = mgmt_new_default(); + if (!data->mgmt) { + tester_warn("Failed to setup management interface"); + tester_pre_setup_failed(); + return; + } + + if (tester_use_debug()) + mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL); + + mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, + read_index_list_callback, NULL, NULL); +} + +static void test_post_teardown(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + if (data->io_id > 0) { + g_source_remove(data->io_id); + data->io_id = 0; + } + + hciemu_unref(data->hciemu); + data->hciemu = NULL; +} + +static void test_data_free(void *test_data) +{ + struct test_data *data = test_data; + + free(data); +} + +static void client_connectable_complete(uint16_t opcode, uint8_t status, + const void *param, uint8_t len, + void *user_data) +{ + switch (opcode) { + case BT_HCI_CMD_WRITE_SCAN_ENABLE: + break; + default: + return; + } + + tester_print("Client set connectable status 0x%02x", status); + + if (status) + tester_setup_failed(); + else + tester_setup_complete(); +} + +static void setup_powered_client_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + + if (status != MGMT_STATUS_SUCCESS) { + tester_setup_failed(); + return; + } + + tester_print("Controller powered on"); + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data); + bthost_write_scan_enable(bthost, 0x03); +} + +static void setup_powered_client(const void *test_data) +{ + struct test_data *data = tester_get_data(); + unsigned char param[] = { 0x01 }; + + tester_print("Powering on controller"); + + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, + sizeof(param), param, setup_powered_client_callback, + NULL, NULL); +} + +static void setup_powered_server_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + tester_setup_failed(); + return; + } + + tester_print("Controller powered on"); + + tester_setup_complete(); +} + +static void setup_powered_server(const void *test_data) +{ + struct test_data *data = tester_get_data(); + unsigned char param[] = { 0x01 }; + + tester_print("Powering on controller"); + + mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, + sizeof(param), param, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, + sizeof(param), param, setup_powered_server_callback, + NULL, NULL); +} + +const struct rfcomm_client_data connect_success = { + .server_channel = 0x0c, + .client_channel = 0x0c +}; + +const uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + +const struct rfcomm_client_data connect_send_success = { + .server_channel = 0x0c, + .client_channel = 0x0c, + .data_len = sizeof(data), + .send_data = data +}; + +const struct rfcomm_client_data connect_read_success = { + .server_channel = 0x0c, + .client_channel = 0x0c, + .data_len = sizeof(data), + .read_data = data +}; + +const struct rfcomm_client_data connect_nval = { + .server_channel = 0x0c, + .client_channel = 0x0e, + .expected_connect_err = -ECONNREFUSED +}; + +const struct rfcomm_server_data listen_success = { + .server_channel = 0x0c, + .client_channel = 0x0c, + .expected_status = true +}; + +const struct rfcomm_server_data listen_send_success = { + .server_channel = 0x0c, + .client_channel = 0x0c, + .expected_status = true, + .data_len = sizeof(data), + .send_data = data +}; + +const struct rfcomm_server_data listen_read_success = { + .server_channel = 0x0c, + .client_channel = 0x0c, + .expected_status = true, + .data_len = sizeof(data), + .read_data = data +}; + +const struct rfcomm_server_data listen_nval = { + .server_channel = 0x0c, + .client_channel = 0x0e, + .expected_status = false +}; + +static void test_basic(const void *test_data) +{ + int sk; + + sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sk < 0) { + tester_warn("Can't create socket: %s (%d)", strerror(errno), + errno); + tester_test_failed(); + return; + } + + close(sk); + + tester_test_passed(); +} + +static int create_rfcomm_sock(bdaddr_t *address, uint8_t channel) +{ + int sk; + struct sockaddr_rc addr; + + sk = socket(PF_BLUETOOTH, SOCK_STREAM | SOCK_NONBLOCK, BTPROTO_RFCOMM); + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + addr.rc_channel = channel; + bacpy(&addr.rc_bdaddr, address); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(sk); + return -1; + } + + return sk; +} + +static int connect_rfcomm_sock(int sk, const bdaddr_t *bdaddr, uint8_t channel) +{ + struct sockaddr_rc addr; + int err; + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, bdaddr); + addr.rc_channel = htobs(channel); + + err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) + return err; + + return 0; +} + +static gboolean client_received_data(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct rfcomm_client_data *client_data = data->test_data; + int sk; + ssize_t ret; + char buf[248]; + + sk = g_io_channel_unix_get_fd(io); + + ret = read(sk, buf, client_data->data_len); + if (client_data->data_len != ret) { + tester_test_failed(); + return false; + } + + if (memcmp(client_data->read_data, buf, client_data->data_len)) + tester_test_failed(); + else + tester_test_passed(); + + return false; +} + +static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct rfcomm_client_data *client_data = data->test_data; + socklen_t len = sizeof(int); + int sk, err, sk_err; + + data->io_id = 0; + + sk = g_io_channel_unix_get_fd(io); + + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) + err = -errno; + else + err = -sk_err; + + if (client_data->expected_connect_err && + err == client_data->expected_connect_err) { + tester_test_passed(); + return false; + } + + if (client_data->send_data) { + ssize_t ret; + + ret = write(sk, client_data->send_data, client_data->data_len); + if (client_data->data_len != ret) + tester_test_failed(); + + return false; + } else if (client_data->read_data) { + g_io_add_watch(io, G_IO_IN, client_received_data, NULL); + bthost_send_rfcomm_data(hciemu_client_get_host(data->hciemu), + data->conn_handle, + client_data->client_channel, + client_data->read_data, + client_data->data_len); + return false; + } + + if (err < 0) + tester_test_failed(); + else + tester_test_passed(); + + return false; +} + +static void client_hook_func(const void *data, uint16_t len, + void *user_data) +{ + struct test_data *test_data = tester_get_data(); + const struct rfcomm_client_data *client_data = test_data->test_data; + ssize_t ret; + + if (client_data->data_len != len) { + tester_test_failed(); + return; + } + + ret = memcmp(client_data->send_data, data, len); + if (ret) + tester_test_failed(); + else + tester_test_passed(); +} + +static void server_hook_func(const void *data, uint16_t len, + void *user_data) +{ + struct test_data *test_data = tester_get_data(); + const struct rfcomm_server_data *server_data = test_data->test_data; + ssize_t ret; + + if (server_data->data_len != len) { + tester_test_failed(); + return; + } + + ret = memcmp(server_data->send_data, data, len); + if (ret) + tester_test_failed(); + else + tester_test_passed(); +} + +static void rfcomm_connect_cb(uint16_t handle, uint16_t cid, + void *user_data, bool status) +{ + struct test_data *data = tester_get_data(); + const struct rfcomm_client_data *client_data = data->test_data; + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + if (client_data->send_data) + bthost_add_rfcomm_chan_hook(bthost, handle, + client_data->client_channel, + client_hook_func, NULL); + else if (client_data->read_data) + data->conn_handle = handle; +} + +static void test_connect(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + const struct rfcomm_client_data *client_data = data->test_data; + const uint8_t *client_addr, *master_addr; + GIOChannel *io; + int sk; + + bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL); + bthost_add_rfcomm_server(bthost, client_data->server_channel, + rfcomm_connect_cb, NULL); + + master_addr = hciemu_get_master_bdaddr(data->hciemu); + client_addr = hciemu_get_client_bdaddr(data->hciemu); + + sk = create_rfcomm_sock((bdaddr_t *) master_addr, 0); + + if (connect_rfcomm_sock(sk, (const bdaddr_t *) client_addr, + client_data->client_channel) < 0) { + close(sk); + tester_test_failed(); + return; + } + + io = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(io, TRUE); + + data->io_id = g_io_add_watch(io, G_IO_OUT, rc_connect_cb, NULL); + + g_io_channel_unref(io); + + tester_print("Connect in progress %d", sk); +} + +static gboolean server_received_data(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct rfcomm_server_data *server_data = data->test_data; + char buf[1024]; + ssize_t ret; + int sk; + + sk = g_io_channel_unix_get_fd(io); + + ret = read(sk, buf, server_data->data_len); + if (ret != server_data->data_len) { + tester_test_failed(); + return false; + } + + if (memcmp(buf, server_data->read_data, server_data->data_len)) + tester_test_failed(); + else + tester_test_passed(); + + return false; +} + +static gboolean rfcomm_listen_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct rfcomm_server_data *server_data = data->test_data; + int sk, new_sk; + + data->io_id = 0; + + sk = g_io_channel_unix_get_fd(io); + + new_sk = accept(sk, NULL, NULL); + if (new_sk < 0) { + tester_test_failed(); + return false; + } + + if (server_data->send_data) { + ssize_t ret; + + ret = write(new_sk, server_data->send_data, + server_data->data_len); + if (ret != server_data->data_len) + tester_test_failed(); + + close(new_sk); + return false; + } else if (server_data->read_data) { + GIOChannel *new_io; + + new_io = g_io_channel_unix_new(new_sk); + g_io_channel_set_close_on_unref(new_io, TRUE); + + data->io_id = g_io_add_watch(new_io, G_IO_IN, + server_received_data, NULL); + + g_io_channel_unref(new_io); + return false; + } + + close(new_sk); + + tester_test_passed(); + + return false; +} + +static void connection_cb(uint16_t handle, uint16_t cid, void *user_data, + bool status) +{ + struct test_data *data = tester_get_data(); + const struct rfcomm_server_data *server_data = data->test_data; + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + if (server_data->read_data) { + data->conn_handle = handle; + bthost_send_rfcomm_data(bthost, data->conn_handle, + server_data->client_channel, + server_data->read_data, + server_data->data_len); + return; + } else if (server_data->data_len) { + return; + } + + if (server_data->expected_status == status) + tester_test_passed(); + else + tester_test_failed(); +} + +static void client_new_conn(uint16_t handle, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct rfcomm_server_data *server_data = data->test_data; + struct bthost *bthost; + + bthost = hciemu_client_get_host(data->hciemu); + bthost_add_rfcomm_chan_hook(bthost, handle, + server_data->client_channel, + server_hook_func, NULL); + bthost_connect_rfcomm(bthost, handle, server_data->client_channel, + connection_cb, NULL); +} + +static void test_server(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct rfcomm_server_data *server_data = data->test_data; + const uint8_t *master_addr; + struct bthost *bthost; + GIOChannel *io; + int sk; + + master_addr = hciemu_get_master_bdaddr(data->hciemu); + + sk = create_rfcomm_sock((bdaddr_t *) master_addr, + server_data->server_channel); + if (sk < 0) { + tester_test_failed(); + return; + } + + if (listen(sk, 5) < 0) { + tester_warn("listening on socket failed: %s (%u)", + strerror(errno), errno); + tester_test_failed(); + close(sk); + return; + } + + io = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(io, TRUE); + + data->io_id = g_io_add_watch(io, G_IO_IN, rfcomm_listen_cb, NULL); + g_io_channel_unref(io); + + tester_print("Listening for connections"); + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_connect_cb(bthost, client_new_conn, data); + + bthost_hci_connect(bthost, master_addr, BDADDR_BREDR); +} + +#define test_rfcomm(name, data, setup, func) \ + do { \ + struct test_data *user; \ + user = malloc(sizeof(struct test_data)); \ + if (!user) \ + break; \ + user->hciemu_type = HCIEMU_TYPE_BREDR; \ + user->test_data = data; \ + user->io_id = 0; \ + tester_add_full(name, data, \ + test_pre_setup, setup, func, NULL, \ + test_post_teardown, 2, user, test_data_free); \ + } while (0) + +int main(int argc, char *argv[]) +{ + tester_init(&argc, &argv); + + test_rfcomm("Basic RFCOMM Socket - Success", NULL, + setup_powered_client, test_basic); + test_rfcomm("Basic RFCOMM Socket Client - Success", &connect_success, + setup_powered_client, test_connect); + test_rfcomm("Basic RFCOMM Socket Client - Write Success", + &connect_send_success, setup_powered_client, + test_connect); + test_rfcomm("Basic RFCOMM Socket Client - Read Success", + &connect_read_success, setup_powered_client, + test_connect); + test_rfcomm("Basic RFCOMM Socket Client - Conn Refused", + &connect_nval, setup_powered_client, test_connect); + test_rfcomm("Basic RFCOMM Socket Server - Success", &listen_success, + setup_powered_server, test_server); + test_rfcomm("Basic RFCOMM Socket Server - Write Success", + &listen_send_success, setup_powered_server, + test_server); + test_rfcomm("Basic RFCOMM Socket Server - Read Success", + &listen_read_success, setup_powered_server, + test_server); + test_rfcomm("Basic RFCOMM Socket Server - Conn Refused", &listen_nval, + setup_powered_server, test_server); + + return tester_run(); +} diff --git a/tools/sco-tester.c b/tools/sco-tester.c index 1e8351f..db45ef0 100644 --- a/tools/sco-tester.c +++ b/tools/sco-tester.c @@ -415,7 +415,7 @@ end: close(sk); } -static int create_sco_sock(struct test_data *data, uint16_t psm) +static int create_sco_sock(struct test_data *data) { const uint8_t *master_bdaddr; struct sockaddr_sco addr; @@ -514,7 +514,7 @@ static void test_connect(const void *test_data) GIOChannel *io; int sk; - sk = create_sco_sock(data, 0); + sk = create_sco_sock(data); if (sk < 0) { tester_test_failed(); return; @@ -543,7 +543,7 @@ static void test_connect_transp(const void *test_data) int sk, err; struct bt_voice voice; - sk = create_sco_sock(data, 0); + sk = create_sco_sock(data); if (sk < 0) { tester_test_failed(); return; @@ -592,13 +592,13 @@ int main(int argc, char *argv[]) test_sco("eSCO CVSD - Success", &connect_success, setup_powered, test_connect); - test_sco("eSCO MSBC - Success", &connect_success, setup_powered, + test_sco("eSCO mSBC - Success", &connect_success, setup_powered, test_connect_transp); test_sco_11("SCO CVSD 1.1 - Success", &connect_success, setup_powered, test_connect); - test_sco_11("SCO MSBC 1.1 - Failure", &connect_failure, setup_powered, + test_sco_11("SCO mSBC 1.1 - Failure", &connect_failure, setup_powered, test_connect_transp); return tester_run(); diff --git a/tools/scotest.c b/tools/scotest.c index 69150b1..e5530d9 100644 --- a/tools/scotest.c +++ b/tools/scotest.c @@ -40,6 +40,8 @@ #include #include +#include "src/shared/util.h" + /* Test modes */ enum { SEND, @@ -255,7 +257,7 @@ static void dump_mode(int sk) strerror(errno), errno); if (defer_setup) { - len = read(sk, buf, sizeof(buf)); + len = read(sk, buf, data_size); if (len < 0) syslog(LOG_ERR, "Initial read error: %s (%d)", strerror(errno), errno); @@ -283,7 +285,7 @@ static void recv_mode(int sk) strerror(errno), errno); if (defer_setup) { - len = read(sk, buf, sizeof(buf)); + len = read(sk, buf, data_size); if (len < 0) syslog(LOG_ERR, "Initial read error: %s (%d)", strerror(errno), errno); @@ -345,8 +347,8 @@ static void send_mode(char *svr) seq = 0; while (1) { - bt_put_le32(seq, buf); - bt_put_le16(data_size, buf + 4); + put_le32(seq, buf); + put_le16(data_size, buf + 4); seq++; diff --git a/tools/sdptool.c b/tools/sdptool.c index b4b65ec..1600c3e 100644 --- a/tools/sdptool.c +++ b/tools/sdptool.c @@ -45,7 +45,7 @@ #include -#include "sdp-xml.h" +#include "src/sdp-xml.h" #ifndef APPLE_AGENT_SVCLASS_ID #define APPLE_AGENT_SVCLASS_ID 0x2112 @@ -1048,7 +1048,7 @@ static void print_service_desc(void *value, void *user) if (proto == RFCOMM_UUID) printf(" Channel: %d\n", p->val.uint8); else - printf(" uint8: 0x%x\n", p->val.uint8); + printf(" uint8: 0x%02x\n", p->val.uint8); break; case SDP_UINT16: if (proto == L2CAP_UUID) { @@ -1060,9 +1060,9 @@ static void print_service_desc(void *value, void *user) if (i == 1) printf(" Version: 0x%04x\n", p->val.uint16); else - printf(" uint16: 0x%x\n", p->val.uint16); + printf(" uint16: 0x%04x\n", p->val.uint16); else - printf(" uint16: 0x%x\n", p->val.uint16); + printf(" uint16: 0x%04x\n", p->val.uint16); break; case SDP_SEQ16: printf(" SEQ16:"); diff --git a/tools/seq2bseq.c b/tools/seq2bseq.c new file mode 100644 index 0000000..7657a57 --- /dev/null +++ b/tools/seq2bseq.c @@ -0,0 +1,212 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012-2013 Intel 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +static int convert_line(int fd, const char *line) +{ + const char *ptr = line; + char str[3]; + unsigned char val; + + if (line[0] == '*' || line[0] == '\r' || line[0] == '\r') + return 0; + + while (1) { + str[0] = *ptr++; + str[1] = *ptr++; + str[2] = '\0'; + + val = strtol(str, NULL, 16); + + if (write(fd, &val, 1) < 0) + return -errno; + + if (*ptr == '\r' || *ptr == '\n') + break; + + while (*ptr == ' ') + ptr++; + } + + return 0; +} + +static void convert_file(const char *input_path, const char *output_path) +{ + size_t line_size = 1024; + char line_buffer[line_size]; + char *path; + const char *ptr; + FILE *fp; + struct stat st; + off_t cur = 0; + int fd; + + if (output_path) { + path = strdup(output_path); + if (!path) { + perror("Failed to allocate string"); + return; + } + } else { + ptr = strrchr(input_path, '.'); + if (ptr) { + path = malloc(ptr - input_path + 6); + if (!path) { + perror("Failed to allocate string"); + return; + } + strncpy(path, input_path, ptr - input_path); + strcpy(path + (ptr - input_path), ".bseq"); + } else { + if (asprintf(&path, "%s.bseq", input_path) < 0) { + perror("Failed to allocate string"); + return; + } + } + } + + printf("Converting %s to %s\n", input_path, path); + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + free(path); + + if (fd < 0) { + perror("Failed to create output file"); + return; + } + + if (stat(input_path, &st) < 0) { + fprintf(stderr, "Failed get file size\n"); + close(fd); + return; + } + + if (st.st_size == 0) { + fprintf(stderr, "Empty file\n"); + close(fd); + return; + } + + fp = fopen(input_path, "r"); + if (!fp) { + fprintf(stderr, "Failed to open input file\n"); + close(fd); + return; + } + + while (1) { + char *str; + int err; + + str = fgets(line_buffer, line_size - 1, fp); + if (!str) + break; + + cur += strlen(str); + + err = convert_line(fd, str); + if (err < 0) { + fprintf(stderr, "Failed to convert file (%s)\n", + strerror(-err)); + break; + } + } + + fclose(fp); + + close(fd); +} + +static void usage(void) +{ + printf("Intel Bluetooth firmware converter\n" + "Usage:\n"); + printf("\tseq2bseq [options] \n"); + printf("Options:\n" + "\t-o, --output Provide firmware output file\n" + "\t-h, --help Show help options\n"); +} + +static const struct option main_options[] = { + { "output", required_argument, NULL, 'o' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } +}; + +int main(int argc, char *argv[]) +{ + const char *output_path = NULL; + int i; + + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "o:vh", main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'o': + output_path = optarg; + break; + case 'v': + printf("%s\n", VERSION); + return EXIT_SUCCESS; + case 'h': + usage(); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } + } + + if (argc - optind < 1) { + fprintf(stderr, "No input firmware files provided\n"); + return EXIT_FAILURE; + } + + if (output_path && argc - optind > 1) { + fprintf(stderr, "Only single input firmware supported\n"); + return EXIT_FAILURE; + } + + for (i = optind; i < argc; i++) + convert_file(argv[i], output_path); + + return EXIT_SUCCESS; +} diff --git a/tools/smp-tester.c b/tools/smp-tester.c index 05d620a..e09c802 100644 --- a/tools/smp-tester.c +++ b/tools/smp-tester.c @@ -40,44 +40,11 @@ #include "monitor/bt.h" #include "emulator/bthost.h" +#include "src/shared/crypto.h" #include "src/shared/tester.h" #include "src/shared/mgmt.h" #include "src/shared/hciemu.h" -#ifndef SOL_ALG -#define SOL_ALG 279 -#endif - -#ifndef AF_ALG -#define AF_ALG 38 -#define PF_ALG AF_ALG - -#include - -struct sockaddr_alg { - __u16 salg_family; - __u8 salg_type[14]; - __u32 salg_feat; - __u32 salg_mask; - __u8 salg_name[64]; -}; - -struct af_alg_iv { - __u32 ivlen; - __u8 iv[0]; -}; - -#define ALG_SET_KEY 1 -#define ALG_SET_IV 2 -#define ALG_SET_OP 3 - -#define ALG_OP_DECRYPT 0 -#define ALG_OP_ENCRYPT 1 - -#else -#include -#endif - #define SMP_CID 0x0006 struct test_data { @@ -94,14 +61,14 @@ struct test_data { bool out; uint16_t handle; size_t counter; - int alg_sk; - uint8_t smp_tk[16]; - uint8_t smp_prnd[16]; - uint8_t smp_rrnd[16]; - uint8_t smp_pcnf[16]; - uint8_t smp_preq[7]; - uint8_t smp_prsp[7]; - uint8_t smp_ltk[16]; + struct bt_crypto *crypto; + uint8_t tk[16]; + uint8_t prnd[16]; + uint8_t rrnd[16]; + uint8_t pcnf[16]; + uint8_t preq[7]; + uint8_t prsp[7]; + uint8_t ltk[16]; }; struct smp_req_rsp { @@ -116,174 +83,6 @@ struct smp_data { size_t req_count; }; -static int alg_setup(void) -{ - struct sockaddr_alg salg; - int sk; - - sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); - if (sk < 0) { - fprintf(stderr, "socket(AF_ALG): %s\n", strerror(errno)); - return -1; - } - - memset(&salg, 0, sizeof(salg)); - salg.salg_family = AF_ALG; - strcpy((char *) salg.salg_type, "skcipher"); - strcpy((char *) salg.salg_name, "ecb(aes)"); - - if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) { - fprintf(stderr, "bind(AF_ALG): %s\n", strerror(errno)); - close(sk); - return -1; - } - - return sk; -} - -static int alg_new(int alg_sk, const uint8_t *key) -{ - int sk; - - if (setsockopt(alg_sk, SOL_ALG, ALG_SET_KEY, key, 16) < 0) { - tester_warn("setsockopt(ALG_SET_KEY): %s", strerror(errno)); - return -1; - } - - sk = accept4(alg_sk, NULL, 0, SOCK_CLOEXEC); - if (sk < 0) { - tester_warn("accept4(AF_ALG): %s", strerror(errno)); - return -1; - } - - return sk; -} - -static int alg_encrypt(int sk, uint8_t in[16], uint8_t out[16]) -{ - __u32 alg_op = ALG_OP_ENCRYPT; - char cbuf[CMSG_SPACE(sizeof(alg_op))]; - struct cmsghdr *cmsg; - struct msghdr msg; - struct iovec iov; - int ret; - - memset(cbuf, 0, sizeof(cbuf)); - memset(&msg, 0, sizeof(msg)); - - msg.msg_control = cbuf; - msg.msg_controllen = sizeof(cbuf); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_ALG; - cmsg->cmsg_type = ALG_SET_OP; - cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op)); - memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op)); - - iov.iov_base = in; - iov.iov_len = 16; - - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ret = sendmsg(sk, &msg, 0); - if (ret < 0) { - tester_warn("sendmsg(AF_ALG): %s", strerror(errno)); - return ret; - } - - ret = read(sk, out, 16); - if (ret < 0) - tester_warn("read(AF_ALG): %s", strerror(errno)); - - return 0; -} - -static int smp_e(uint8_t key[16], uint8_t in[16], uint8_t out[16]) -{ - struct test_data *data = tester_get_data(); - int sk, err; - - sk = alg_new(data->alg_sk, key); - if (sk < 0) - return sk; - - err = alg_encrypt(sk, in, out); - - close(sk); - - return err; -} - -static inline void swap128(const uint8_t src[16], uint8_t dst[16]) -{ - int i; - for (i = 0; i < 16; i++) - dst[15 - i] = src[i]; -} - -static inline void swap56(const uint8_t src[7], uint8_t dst[7]) -{ - int i; - for (i = 0; i < 7; i++) - dst[6 - i] = src[i]; -} - -typedef struct { - uint64_t a, b; -} u128; - -static inline void u128_xor(u128 *r, const u128 *p, const u128 *q) -{ - r->a = p->a ^ q->a; - r->b = p->b ^ q->b; -} - -static int smp_c1(uint8_t r[16], uint8_t res[16]) -{ - struct test_data *data = tester_get_data(); - uint8_t p1[16], p2[16]; - int err; - - memset(p1, 0, 16); - - /* p1 = pres || preq || _rat || _iat */ - swap56(data->smp_prsp, p1); - swap56(data->smp_preq, p1 + 7); - p1[14] = data->ra_type; - p1[15] = data->ia_type; - - memset(p2, 0, 16); - - /* p2 = padding || ia || ra */ - baswap((bdaddr_t *) (p2 + 4), (bdaddr_t *) data->ia); - baswap((bdaddr_t *) (p2 + 10), (bdaddr_t *) data->ra); - - /* res = r XOR p1 */ - u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); - - /* res = e(k, res) */ - err = smp_e(data->smp_tk, res, res); - if (err) - return err; - - /* res = res XOR p2 */ - u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); - - /* res = e(k, res) */ - return smp_e(data->smp_tk, res, res); -} - -static int smp_s1(uint8_t r1[16], uint8_t r2[16], uint8_t res[16]) -{ - struct test_data *data = tester_get_data(); - - memcpy(res, r1 + 8, 8); - memcpy(res + 8, r2 + 8, 8); - - return smp_e(data->smp_tk, res, res); -} - static void mgmt_debug(const char *str, void *user_data) { const char *prefix = user_data; @@ -396,9 +195,9 @@ static void test_pre_setup(const void *test_data) { struct test_data *data = tester_get_data(); - data->alg_sk = alg_setup(); - if (data->alg_sk < 0) { - tester_warn("Failed to setup AF_ALG socket"); + data->crypto = bt_crypto_new(); + if (!data->crypto) { + tester_warn("Failed to setup crypto"); tester_pre_setup_failed(); return; } @@ -406,6 +205,7 @@ static void test_pre_setup(const void *test_data) data->mgmt = mgmt_new_default(); if (!data->mgmt) { tester_warn("Failed to setup management interface"); + bt_crypto_unref(data->crypto); tester_pre_setup_failed(); return; } @@ -426,9 +226,9 @@ static void test_post_teardown(const void *test_data) data->io_id = 0; } - if (data->alg_sk >= 0) { - close(data->alg_sk); - data->alg_sk = -1; + if (data->crypto) { + bt_crypto_unref(data->crypto); + data->crypto = NULL; } hciemu_unref(data->hciemu); @@ -449,7 +249,6 @@ static void test_data_free(void *test_data) if (!user) \ break; \ user->hciemu_type = HCIEMU_TYPE_LE; \ - user->alg_sk = -1; \ user->test_data = data; \ tester_add_full(name, data, \ test_pre_setup, setup, func, NULL, \ @@ -482,21 +281,34 @@ static const struct smp_data smp_server_nval_req_2_test = { .req_count = G_N_ELEMENTS(srv_nval_req_1), }; +static const uint8_t smp_nval_req_3[] = { 0x01, 0xff }; +static const uint8_t smp_nval_req_3_rsp[] = { 0x05, 0x08 }; + +static const struct smp_req_rsp srv_nval_req_2[] = { + { smp_nval_req_2, sizeof(smp_nval_req_3), + smp_nval_req_3_rsp, sizeof(smp_nval_req_3_rsp) }, +}; + +static const struct smp_data smp_server_nval_req_3_test = { + .req = srv_nval_req_2, + .req_count = G_N_ELEMENTS(srv_nval_req_2), +}; + static const uint8_t smp_basic_req_1[] = { 0x01, /* Pairing Request */ 0x03, /* NoInputNoOutput */ 0x00, /* OOB Flag */ 0x01, /* Bonding - no MITM */ 0x10, /* Max key size */ - 0x00, /* Init. key dist. */ - 0x01, /* Rsp. key dist. */ + 0x05, /* Init. key dist. */ + 0x05, /* Rsp. key dist. */ }; static const uint8_t smp_basic_req_1_rsp[] = { 0x02, /* Pairing Response */ 0x03, /* NoInputNoOutput */ 0x00, /* OOB Flag */ 0x01, /* Bonding - no MITM */ 0x10, /* Max key size */ - 0x00, /* Init. key dist. */ - 0x01, /* Rsp. key dist. */ + 0x05, /* Init. key dist. */ + 0x05, /* Rsp. key dist. */ }; static const uint8_t smp_confirm_req_1[17] = { 0x03 }; @@ -592,61 +404,59 @@ static void pair_device_complete(uint8_t status, uint16_t length, tester_test_passed(); } -static const void *get_pdu(const uint8_t *data) +static const void *get_pdu(const uint8_t *pdu) { - struct test_data *test_data = tester_get_data(); - uint8_t opcode = data[0]; + struct test_data *data = tester_get_data(); + uint8_t opcode = pdu[0]; static uint8_t buf[17]; - uint8_t res[16]; switch (opcode) { case 0x01: /* Pairing Request */ - memcpy(test_data->smp_preq, data, sizeof(test_data->smp_preq)); + memcpy(data->preq, pdu, sizeof(data->preq)); break; case 0x02: /* Pairing Response */ - memcpy(test_data->smp_prsp, data, sizeof(test_data->smp_prsp)); + memcpy(data->prsp, pdu, sizeof(data->prsp)); break; case 0x03: /* Pairing Confirm */ - buf[0] = data[0]; - smp_c1(test_data->smp_prnd, res); - swap128(res, &buf[1]); + buf[0] = pdu[0]; + bt_crypto_c1(data->crypto, data->tk, data->prnd, data->prsp, + data->preq, data->ia_type, data->ia, + data->ra_type, data->ra, &buf[1]); return buf; case 0x04: /* Pairing Random */ - buf[0] = data[0]; - swap128(test_data->smp_prnd, &buf[1]); + buf[0] = pdu[0]; + memcpy(&buf[1], data->prnd, 16); return buf; default: break; } - return data; + return pdu; } static bool verify_random(const uint8_t rnd[16]) { struct test_data *data = tester_get_data(); - uint8_t confirm[16], res[16], key[16]; - int err; + uint8_t confirm[16]; - err = smp_c1(data->smp_rrnd, res); - if (err < 0) + if (!bt_crypto_c1(data->crypto, data->tk, data->rrnd, data->prsp, + data->preq, data->ia_type, data->ia, + data->ra_type, data->ra, confirm)) return false; - swap128(res, confirm); - - if (memcmp(data->smp_pcnf, confirm, sizeof(data->smp_pcnf) != 0)) { + if (memcmp(data->pcnf, confirm, sizeof(data->pcnf) != 0)) { tester_warn("Confirmation values don't match"); return false; } if (data->out) { struct bthost *bthost = hciemu_client_get_host(data->hciemu); - smp_s1(data->smp_rrnd, data->smp_prnd, key); - swap128(key, data->smp_ltk); - bthost_le_start_encrypt(bthost, data->handle, data->smp_ltk); + bt_crypto_s1(data->crypto, data->tk, data->rrnd, data->prnd, + data->ltk); + bthost_le_start_encrypt(bthost, data->handle, data->ltk); } else { - smp_s1(data->smp_prnd, data->smp_rrnd, key); - swap128(key, data->smp_ltk); + bt_crypto_s1(data->crypto, data->tk, data->prnd, data->rrnd, + data->ltk); } return true; @@ -687,16 +497,16 @@ static void smp_server(const void *data, uint16_t len, void *user_data) switch (opcode) { case 0x01: /* Pairing Request */ - memcpy(test_data->smp_preq, data, sizeof(test_data->smp_preq)); + memcpy(test_data->preq, data, sizeof(test_data->preq)); break; case 0x02: /* Pairing Response */ - memcpy(test_data->smp_prsp, data, sizeof(test_data->smp_prsp)); + memcpy(test_data->prsp, data, sizeof(test_data->prsp)); break; case 0x03: /* Pairing Confirm */ - memcpy(test_data->smp_pcnf, data + 1, 16); + memcpy(test_data->pcnf, data + 1, 16); goto next; case 0x04: /* Pairing Random */ - swap128(data + 1, test_data->smp_rrnd); + memcpy(test_data->rrnd, data + 1, 16); if (!verify_random(data + 1)) goto failed; goto next; @@ -833,6 +643,8 @@ static void setup_powered_server(const void *test_data) sizeof(param), param, NULL, NULL, NULL); mgmt_send(data->mgmt, MGMT_OP_SET_PAIRABLE, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); + mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, + sizeof(param), param, NULL, NULL, NULL); mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, @@ -868,6 +680,9 @@ int main(int argc, char *argv[]) test_smp("SMP Server - Invalid Request 2", &smp_server_nval_req_2_test, setup_powered_server, test_server); + test_smp("SMP Server - Invalid Request 3", + &smp_server_nval_req_3_test, + setup_powered_server, test_server); test_smp("SMP Client - Basic Request 1", &smp_client_basic_req_1_test, diff --git a/unit/test-avctp.c b/unit/test-avctp.c new file mode 100644 index 0000000..8f7d5ad --- /dev/null +++ b/unit/test-avctp.c @@ -0,0 +1,339 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. 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 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "src/shared/util.h" +#include "src/log.h" + +#include "android/avctp.h" + +struct test_pdu { + bool valid; + const uint8_t *data; + size_t size; +}; + +struct test_data { + char *test_name; + struct test_pdu *pdu_list; +}; + +struct context { + GMainLoop *main_loop; + struct avctp *session; + guint source; + guint process; + int fd; + unsigned int pdu_offset; + const struct test_data *data; +}; + +#define data(args...) ((const unsigned char[]) { args }) + +#define raw_pdu(args...) \ + { \ + .valid = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + } + +#define define_test(name, function, args...) \ + do { \ + const struct test_pdu pdus[] = { \ + args, { } \ + }; \ + static struct test_data data; \ + data.test_name = g_strdup(name); \ + data.pdu_list = g_malloc(sizeof(pdus)); \ + memcpy(data.pdu_list, pdus, sizeof(pdus)); \ + g_test_add_data_func(name, &data, function); \ + } while (0) + +static void test_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + g_print("%s%s\n", prefix, str); +} + +static void test_free(gconstpointer user_data) +{ + const struct test_data *data = user_data; + + g_free(data->test_name); + g_free(data->pdu_list); +} + +static gboolean context_quit(gpointer user_data) +{ + struct context *context = user_data; + + if (context->process > 0) + g_source_remove(context->process); + + g_main_loop_quit(context->main_loop); + + return FALSE; +} + +static gboolean send_pdu(gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + ssize_t len; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + len = write(context->fd, pdu->data, pdu->size); + + if (g_test_verbose()) + util_hexdump('<', pdu->data, len, test_debug, "AVCTP: "); + + g_assert_cmpint(len, ==, pdu->size); + + context->process = 0; + return FALSE; +} + +static void context_process(struct context *context) +{ + if (!context->data->pdu_list[context->pdu_offset].valid) { + context_quit(context); + return; + } + + context->process = g_idle_add(send_pdu, context); +} + +static gboolean test_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + unsigned char buf[512]; + ssize_t len; + int fd; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + context->source = 0; + g_print("%s: cond %x\n", __func__, cond); + return FALSE; + } + + fd = g_io_channel_unix_get_fd(channel); + + len = read(fd, buf, sizeof(buf)); + + g_assert(len > 0); + + if (g_test_verbose()) + util_hexdump('>', buf, len, test_debug, "AVCTP: "); + + g_assert_cmpint(len, ==, pdu->size); + + g_assert(memcmp(buf, pdu->data, pdu->size) == 0); + + context_process(context); + + return TRUE; +} + +static struct context *create_context(uint16_t version, gconstpointer data) +{ + struct context *context = g_new0(struct context, 1); + GIOChannel *channel; + int err, sv[2]; + + context->main_loop = g_main_loop_new(NULL, FALSE); + g_assert(context->main_loop); + + err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv); + g_assert(err == 0); + + context->session = avctp_new(sv[0], 672, 672, version); + g_assert(context->session != NULL); + + channel = g_io_channel_unix_new(sv[1]); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + context->source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + test_handler, context); + g_assert(context->source > 0); + + g_io_channel_unref(channel); + + context->fd = sv[1]; + context->data = data; + + return context; +} + +static void destroy_context(struct context *context) +{ + if (context->source > 0) + g_source_remove(context->source); + + avctp_shutdown(context->session); + + g_main_loop_unref(context->main_loop); + + test_free(context->data); + g_free(context); +} + +static void execute_context(struct context *context) +{ + g_main_loop_run(context->main_loop); + + destroy_context(context); +} + +static ssize_t handler(struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + DBG("transaction %d code %d subunit %d operand_count %zu", + transaction, *code, *subunit, operand_count); + + g_assert_cmpint(transaction, ==, 0); + g_assert_cmpint(*code, ==, 0); + g_assert_cmpint(*subunit, ==, 0); + g_assert_cmpint(operand_count, ==, 0); + + return operand_count; +} + +static gboolean handler_response(struct avctp *session, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct context *context = user_data; + + DBG("code 0x%02x subunit %d operand_count %zu", code, subunit, + operand_count); + + g_assert_cmpint(code, ==, 0x0a); + g_assert_cmpint(subunit, ==, 0); + g_assert_cmpint(operand_count, ==, 0); + + return context_quit(context); +} + +static void test_client(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + + avctp_send_vendor_req(context->session, AVC_CTYPE_CONTROL, 0, NULL, + 0, handler_response, context); + + execute_context(context); +} + +static void test_server(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + + if (g_str_equal(context->data->test_name, "/TP/NFR/BV-03-C")) { + int ret; + + ret = avctp_register_pdu_handler(context->session, + AVC_OP_VENDORDEP, handler, NULL); + DBG("ret %d", ret); + g_assert_cmpint(ret, !=, 0); + } + + g_idle_add(send_pdu, context); + + execute_context(context); +} + +static void test_dummy(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + + destroy_context(context); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + if (g_test_verbose()) + __btd_log_init("*", 0); + + /* Connection Channel Management tests */ + + /* + * Tests are checking that IUT is able to request establishing + * channels, since we already have connection through socketpair + * the tests are dummy. + */ + define_test("/TP/CCM/BV-01-C", test_dummy, raw_pdu(0x00)); + define_test("/TP/CCM/BV-02-C", test_dummy, raw_pdu(0x00)); + define_test("/TP/CCM/BV-03-C", test_dummy, raw_pdu(0x00)); + define_test("/TP/CCM/BV-04-C", test_dummy, raw_pdu(0x00)); + + /* Non-Fragmented Messages tests */ + + define_test("/TP/NFR/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00)); + + define_test("/TP/NFR/BV-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x00, 0x00)); + + define_test("/TP/NFR/BV-03-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x00, 0x00, 0x00)); + + define_test("/TP/NFR/BV-04-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x00, 0x00)); + + define_test("/TP/NFR/BI-01-C", test_server, + raw_pdu(0x00, 0xff, 0xff, 0x00, 0x00, 0x00), + raw_pdu(0x03, 0xff, 0xff)); + + return g_test_run(); +} diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c index fb555b8..8fe5ce3 100644 --- a/unit/test-avdtp.c +++ b/unit/test-avdtp.c @@ -41,6 +41,7 @@ struct test_pdu { bool valid; + bool fragmented; const uint8_t *data; size_t size; }; @@ -59,10 +60,18 @@ struct test_data { .size = sizeof(data(args)), \ } +#define frg_pdu(args...) \ + { \ + .valid = true, \ + .fragmented = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + } + #define define_test(name, function, args...) \ do { \ const struct test_pdu pdus[] = { \ - args, { }, { } \ + args, { } \ }; \ static struct test_data data; \ data.test_name = g_strdup(name); \ @@ -77,6 +86,7 @@ struct context { struct avdtp_local_sep *sep; struct avdtp_stream *stream; guint source; + guint process; int fd; int mtu; gboolean pending_open; @@ -104,6 +114,9 @@ static gboolean context_quit(gpointer user_data) { struct context *context = user_data; + if (context->process > 0) + g_source_remove(context->process); + g_main_loop_quit(context->main_loop); return FALSE; @@ -122,11 +135,15 @@ static gboolean send_pdu(gpointer user_data) if (g_test_verbose()) util_hexdump('<', pdu->data, len, test_debug, "AVDTP: "); - g_assert(len == (ssize_t) pdu->size); + g_assert_cmpint(len, ==, pdu->size); if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-02-C")) g_timeout_add_seconds(1, context_quit, context); + if (pdu->fragmented) + return send_pdu(user_data); + + context->process = 0; return FALSE; } @@ -137,7 +154,7 @@ static void context_process(struct context *context) return; } - g_idle_add(send_pdu, context); + context->process = g_idle_add(send_pdu, context); } static gboolean transport_open(struct avdtp_stream *stream) @@ -162,8 +179,10 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond, pdu = &context->data->pdu_list[context->pdu_offset++]; - if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + context->source = 0; return FALSE; + } fd = g_io_channel_unix_get_fd(channel); @@ -174,7 +193,7 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond, if (g_test_verbose()) util_hexdump('>', buf, len, test_debug, "AVDTP: "); - g_assert((size_t) len == pdu->size); + g_assert_cmpint(len, ==, pdu->size); g_assert(memcmp(buf, pdu->data, pdu->size) == 0); @@ -191,12 +210,14 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond, g_assert_cmpint(ret, ==, 0); } - context_process(context); + if (!pdu->fragmented) + context_process(context); return TRUE; } -static struct context *create_context(uint16_t version, gconstpointer data) +static struct context *context_new(uint16_t version, uint16_t imtu, + uint16_t omtu, gconstpointer data) { struct context *context = g_new0(struct context, 1); GIOChannel *channel; @@ -208,7 +229,7 @@ static struct context *create_context(uint16_t version, gconstpointer data) err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv); g_assert(err == 0); - context->session = avdtp_new(sv[0], 672, 672, version); + context->session = avdtp_new(sv[0], imtu, omtu, version); g_assert(context->session != NULL); channel = g_io_channel_unix_new(sv[1]); @@ -230,11 +251,17 @@ static struct context *create_context(uint16_t version, gconstpointer data) return context; } +static struct context *create_context(uint16_t version, gconstpointer data) +{ + return context_new(version, 672, 672, data); +} + static void execute_context(struct context *context) { g_main_loop_run(context->main_loop); - g_source_remove(context->source); + if (context->source > 0) + g_source_remove(context->source); avdtp_unref(context->session); g_main_loop_unref(context->main_loop); @@ -245,8 +272,8 @@ static void execute_context(struct context *context) static gboolean sep_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, - gboolean get_all, GSList **caps, - uint8_t *err, void *user_data) + GSList **caps, uint8_t *err, + void *user_data) { struct avdtp_service_capability *media_transport, *media_codec; struct avdtp_media_codec_capability *codec_caps; @@ -500,6 +527,21 @@ static void test_server_1_3(gconstpointer data) avdtp_unregister_sep(sep); } +static void test_server_1_3_sink(gconstpointer data) +{ + struct context *context = create_context(0x0103, data); + struct avdtp_local_sep *sep; + + sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, + 0x00, TRUE, &sep_ind, NULL, context); + + g_idle_add(send_pdu, context); + + execute_context(context); + + avdtp_unregister_sep(sep); +} + static void test_server_0_sep(gconstpointer data) { struct context *context = create_context(0x0100, data); @@ -509,6 +551,62 @@ static void test_server_0_sep(gconstpointer data) execute_context(context); } +static gboolean sep_getcap_ind_frg(struct avdtp *session, + struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err, + void *user_data) +{ + struct avdtp_service_capability *media_transport, *media_codec; + struct avdtp_service_capability *content_protection; + struct avdtp_media_codec_capability *codec_caps; + uint8_t cap[4] = { 0xff, 0xff, 2, 64 }; + uint8_t frg_cap[96] = {}; + + *caps = NULL; + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + *caps = g_slist_append(*caps, media_transport); + + codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap)); + codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO; + codec_caps->media_codec_type = 0x00; + memcpy(codec_caps->data, cap, sizeof(cap)); + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps, + sizeof(*codec_caps) + sizeof(cap)); + + *caps = g_slist_append(*caps, media_codec); + g_free(codec_caps); + + content_protection = avdtp_service_cap_new(AVDTP_CONTENT_PROTECTION, + frg_cap, sizeof(frg_cap)); + *caps = g_slist_append(*caps, content_protection); + + return TRUE; +} + +static struct avdtp_sep_ind sep_ind_frg = { + .get_capability = sep_getcap_ind_frg, +}; + +static void test_server_frg(gconstpointer data) +{ + struct context *context = context_new(0x0100, 48, 48, data); + struct avdtp_local_sep *sep; + + sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO, + 0x00, TRUE, &sep_ind_frg, + NULL, context); + + g_idle_add(send_pdu, context); + + execute_context(context); + + avdtp_unregister_sep(sep); +} + static void discover_cb(struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data) { @@ -544,6 +642,12 @@ static void discover_cb(struct avdtp *session, GSList *seps, g_assert(err == NULL); g_assert_cmpint(g_slist_length(seps), !=, 0); + if (g_str_equal(context->data->test_name, "/TP/SIG/FRA/BV-02-C")) { + g_assert(err == NULL); + context_quit(context); + return; + } + rsep = avdtp_find_remote_sep(session, context->sep); g_assert(rsep != NULL); @@ -604,6 +708,23 @@ static void test_client_1_3(gconstpointer data) avdtp_unregister_sep(sep); } +static void test_client_frg(gconstpointer data) +{ + struct context *context = context_new(0x0100, 48, 48, data); + struct avdtp_local_sep *sep; + + sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, + 0x00, TRUE, NULL, &sep_cfm, + context); + context->sep = sep; + + avdtp_discover(context->session, discover_cb, context); + + execute_context(context); + + avdtp_unregister_sep(sep); +} + int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); @@ -805,7 +926,7 @@ int main(int argc, char *argv[]) raw_pdu(0x02, 0x01, 0x04, 0x00), raw_pdu(0x10, 0x0c, 0x04), raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, - 0xff, 0xff, 0x02, 0x40)); + 0xff, 0xff, 0x02, 0x40, 0x08, 0x00)); define_test("/TP/SIG/SMG/BV-27-C", test_server_1_3, raw_pdu(0x00, 0x01), raw_pdu(0x02, 0x01, 0x04, 0x00), @@ -1086,5 +1207,128 @@ int main(int argc, char *argv[]) raw_pdu(0xa0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, 0x21, 0x02, 0x02, 0x20)); + /* + * Signaling Message Fragmentation Service + * + * verify that the IUT (INT and ACP) fragments the signaling messages + * that cannot fit in a single L2CAP packet. + */ + define_test("/TP/SIG/FRA/BV-01-C", test_server_frg, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + frg_pdu(0x16, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, + 0x00, 0xff, 0xff, 0x02, 0x40, 0x04, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00), + frg_pdu(0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00), + raw_pdu(0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00)); + define_test("/TP/SIG/FRA/BV-02-C", test_client_frg, + raw_pdu(0xb0, 0x01), + raw_pdu(0xb2, 0x01, 0x04, 0x00), + raw_pdu(0xc0, 0x02, 0x04), + frg_pdu(0xc6, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, + 0x00, 0xff, 0xff, 0x02, 0x40, 0x04, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00), + frg_pdu(0xca, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00), + raw_pdu(0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00)); + + /* + * Delay Reporting + * + * Verify that the stream management signaling procedure of delay + * reporting is implemented according to its specification in AVDTP. + */ + define_test("/TP/SIG/SYN/BV-01-C", test_server_1_3_sink, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x08), + raw_pdu(0x10, 0x0c, 0x04), + raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40, 0x08, 0x00)); + define_test("/TP/SIG/SYN/BV-02-C", test_client_1_3, + raw_pdu(0xd0, 0x01), + raw_pdu(0xd2, 0x01, 0x04, 0x00), + raw_pdu(0xe0, 0x0c, 0x04), + raw_pdu(0xe2, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40, 0x0f, 0x00, 0x08, 0x00), + raw_pdu(0xf0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08, + 0x00)); + define_test("/TP/SIG/SYN/BV-03-C", test_server_1_3_sink, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x08), + raw_pdu(0x10, 0x0c, 0x04), + raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40, 0x08, 0x00), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08, + 0x00), + raw_pdu(0x22, 0x03), + raw_pdu(0x00, 0x0d, 0x04, 0x00, 0x00)); + define_test("/TP/SIG/SYN/BV-04-C", test_client_1_3, + raw_pdu(0x10, 0x01), + raw_pdu(0x12, 0x01, 0x04, 0x00), + raw_pdu(0x20, 0x0c, 0x04), + raw_pdu(0x22, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40, 0x0f, 0x00, 0x08, 0x00), + raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08, + 0x00), + raw_pdu(0x32, 0x03), + raw_pdu(0x40, 0x0d, 0x04, 0x00, 0x00)); + define_test("/TP/SIG/SYN/BV-05-C", test_server_1_3, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x0c, 0x04), + raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40, 0x08, 0x00), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08, + 0x00), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x0d, 0x04, 0x00, 0x00), + raw_pdu(0x42, 0x0d)); + define_test("/TP/SIG/SYN/BV-06-C", test_server_1_3, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x0c, 0x04), + raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40, 0x08, 0x00), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08, + 0x00), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x07, 0x04), + raw_pdu(0x42, 0x07), + raw_pdu(0x50, 0x0d, 0x04, 0x00, 0x00), + raw_pdu(0x52, 0x0d)); + return g_test_run(); } diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c new file mode 100644 index 0000000..c41f2e6 --- /dev/null +++ b/unit/test-avrcp.c @@ -0,0 +1,1627 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "src/shared/util.h" +#include "src/log.h" +#include "lib/bluetooth.h" + +#include "android/avctp.h" +#include "android/avrcp-lib.h" + +struct test_pdu { + bool valid; + bool fragmented; + bool browse; + const uint8_t *data; + size_t size; +}; + +struct test_data { + char *test_name; + struct test_pdu *pdu_list; +}; + +struct context { + GMainLoop *main_loop; + struct avrcp *session; + guint source; + guint browse_source; + guint process; + int fd; + int browse_fd; + unsigned int pdu_offset; + const struct test_data *data; +}; + +#define data(args...) ((const unsigned char[]) { args }) + +#define raw_pdu(args...) \ + { \ + .valid = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + } + +#define brs_pdu(args...) \ + { \ + .valid = true, \ + .browse = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + } + +#define frg_pdu(args...) \ + { \ + .valid = true, \ + .fragmented = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + } + +#define define_test(name, function, args...) \ + do { \ + const struct test_pdu pdus[] = { \ + args, { } \ + }; \ + static struct test_data data; \ + data.test_name = g_strdup(name); \ + data.pdu_list = g_malloc(sizeof(pdus)); \ + memcpy(data.pdu_list, pdus, sizeof(pdus)); \ + g_test_add_data_func(name, &data, function); \ + } while (0) + +static void test_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + g_print("%s%s\n", prefix, str); +} + +static void test_free(gconstpointer user_data) +{ + const struct test_data *data = user_data; + + g_free(data->test_name); + g_free(data->pdu_list); +} + +static gboolean context_quit(gpointer user_data) +{ + struct context *context = user_data; + + if (context->process > 0) + g_source_remove(context->process); + + g_main_loop_quit(context->main_loop); + + return FALSE; +} + +static gboolean send_pdu(gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + ssize_t len; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + if (pdu->browse) + len = write(context->browse_fd, pdu->data, pdu->size); + else + len = write(context->fd, pdu->data, pdu->size); + + if (g_test_verbose()) + util_hexdump('<', pdu->data, len, test_debug, "AVRCP: "); + + g_assert_cmpint(len, ==, pdu->size); + + if (pdu->fragmented) + return send_pdu(user_data); + + context->process = 0; + return FALSE; +} + +static void context_process(struct context *context) +{ + if (!context->data->pdu_list[context->pdu_offset].valid) { + context_quit(context); + return; + } + + context->process = g_idle_add(send_pdu, context); +} + +static gboolean test_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + unsigned char buf[512]; + ssize_t len; + int fd; + + DBG(""); + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + context->source = 0; + g_print("%s: cond %x\n", __func__, cond); + return FALSE; + } + + fd = g_io_channel_unix_get_fd(channel); + + len = read(fd, buf, sizeof(buf)); + + g_assert(len > 0); + + if (g_test_verbose()) + util_hexdump('>', buf, len, test_debug, "AVRCP: "); + + g_assert_cmpint(len, ==, pdu->size); + + g_assert(memcmp(buf, pdu->data, pdu->size) == 0); + + if (!pdu->fragmented) + context_process(context); + + return TRUE; +} + +static gboolean browse_test_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + unsigned char buf[512]; + ssize_t len; + int fd; + + DBG(""); + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + context->browse_source = 0; + g_print("%s: cond %x\n", __func__, cond); + return FALSE; + } + + fd = g_io_channel_unix_get_fd(channel); + + len = read(fd, buf, sizeof(buf)); + + g_assert(len > 0); + + if (g_test_verbose()) + util_hexdump('>', buf, len, test_debug, "AVRCP: "); + + g_assert_cmpint(len, ==, pdu->size); + + g_assert(memcmp(buf, pdu->data, pdu->size) == 0); + + if (!pdu->fragmented) + context_process(context); + + return TRUE; +} + +static struct context *create_context(uint16_t version, gconstpointer data) +{ + struct context *context = g_new0(struct context, 1); + GIOChannel *channel; + int err, sv[2]; + + DBG(""); + + context->main_loop = g_main_loop_new(NULL, FALSE); + g_assert(context->main_loop); + + /* Control channel setup */ + + err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv); + g_assert(!err); + + context->session = avrcp_new(sv[0], 672, 672, version); + g_assert(context->session != NULL); + + channel = g_io_channel_unix_new(sv[1]); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + context->source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + test_handler, context); + g_assert(context->source > 0); + + g_io_channel_unref(channel); + + context->fd = sv[1]; + + /* Browsing channel setup */ + + err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv); + g_assert(!err); + + err = avrcp_connect_browsing(context->session, sv[0], 672, 672); + g_assert(!err); + + channel = g_io_channel_unix_new(sv[1]); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + context->browse_source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + browse_test_handler, context); + g_assert(context->browse_source > 0); + + g_io_channel_unref(channel); + + context->browse_fd = sv[1]; + + context->data = data; + + return context; +} + +static void destroy_context(struct context *context) +{ + if (context->source > 0) + g_source_remove(context->source); + + avrcp_shutdown(context->session); + + if (context->browse_source > 0) + g_source_remove(context->browse_source); + + g_main_loop_unref(context->main_loop); + + test_free(context->data); + g_free(context); +} + +static void test_dummy(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + + destroy_context(context); +} + +static void execute_context(struct context *context) +{ + g_main_loop_run(context->main_loop); + + destroy_context(context); +} + +static bool handle_play(struct avrcp *session, bool pressed, void *user_data) +{ + DBG(""); + + return true; +} + +static bool handle_volume_up(struct avrcp *session, bool pressed, + void *user_data) +{ + DBG(""); + + return true; +} + +static bool handle_channel_up(struct avrcp *session, bool pressed, + void *user_data) +{ + DBG(""); + + return true; +} + +static bool handle_select(struct avrcp *session, bool pressed, void *user_data) +{ + DBG(""); + + return true; +} + +static bool handle_vendor_uniq(struct avrcp *session, bool pressed, + void *user_data) +{ + DBG(""); + + return true; +} + +static const struct avrcp_passthrough_handler passthrough_handlers[] = { + { AVC_PLAY, handle_play }, + { AVC_VOLUME_UP, handle_volume_up }, + { AVC_CHANNEL_UP, handle_channel_up }, + { AVC_SELECT, handle_select }, + { AVC_VENDOR_UNIQUE, handle_vendor_uniq }, + { }, +}; + +static int get_capabilities(struct avrcp *session, uint8_t transaction, + void *user_data) +{ + return -EINVAL; +} + +static int list_attributes(struct avrcp *session, uint8_t transaction, + void *user_data) +{ + DBG(""); + + avrcp_list_player_attributes_rsp(session, transaction, 0, NULL); + + return -EAGAIN; +} + +static int get_attribute_text(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, + void *user_data) +{ + const char *text[number]; + + DBG(""); + + if (number) { + memset(text, 0, number); + text[0] = "equalizer"; + } + + avrcp_get_player_attribute_text_rsp(session, transaction, number, attrs, + text); + + return -EAGAIN; +} + +static int list_values(struct avrcp *session, uint8_t transaction, + uint8_t attr, void *user_data) +{ + DBG(""); + + avrcp_list_player_values_rsp(session, transaction, 0, NULL); + + return -EINVAL; +} + +static int get_value_text(struct avrcp *session, uint8_t transaction, + uint8_t attr, uint8_t number, uint8_t *values, + void *user_data) +{ + const char *text[number]; + + DBG(""); + + if (number) { + memset(text, 0, number); + text[0] = "on"; + } + + avrcp_get_player_values_text_rsp(session, transaction, number, + values, text); + + return -EINVAL; +} + +static int get_value(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, void *user_data) +{ + uint8_t values[number]; + + DBG(""); + + memset(values, 0, number); + + avrcp_get_current_player_value_rsp(session, transaction, number, attrs, + values); + + return -EAGAIN; +} + +static int set_value(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, void *user_data) +{ + DBG(""); + + avrcp_set_player_value_rsp(session, transaction); + + return -EAGAIN; +} + +static int get_play_status(struct avrcp *session, uint8_t transaction, + void *user_data) +{ + DBG(""); + + avrcp_get_play_status_rsp(session, transaction, 0xaaaaaaaa, 0xbbbbbbbb, + 0x00); + + return -EAGAIN; +} + +static int get_element_attributes(struct avrcp *session, uint8_t transaction, + uint64_t uid, uint8_t number, + uint32_t *attrs, void *user_data) +{ + DBG(""); + + avrcp_get_element_attrs_rsp(session, transaction, NULL, 0); + + return -EAGAIN; +} + +static int register_notification(struct avrcp *session, uint8_t transaction, + uint8_t event, uint32_t interval, + void *user_data) +{ + struct context *context = user_data; + uint8_t pdu[9]; + size_t pdu_len; + + DBG(""); + + pdu[0] = event; + pdu_len = 1; + + switch (event) { + case AVRCP_EVENT_TRACK_CHANGED: + if (g_str_equal(context->data->test_name, "/TP/NFY/BV-05-C") || + g_str_equal(context->data->test_name, + "/TP/NFY/BV-08-C")) + memset(&pdu[1], 0, 8); + else + memset(&pdu[1], 0xff, 8); + + pdu_len += 8; + break; + case AVRCP_EVENT_SETTINGS_CHANGED: + pdu[1] = 0x01; + pdu[2] = 0x01; + pdu[3] = 0x02; + pdu_len = 4; + break; + default: + return -EINVAL; + } + + avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM, + pdu, pdu_len); + + avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED, + pdu, pdu_len); + + return -EAGAIN; +} + +static int set_volume(struct avrcp *session, uint8_t transaction, + uint8_t volume, void *user_data) +{ + DBG(""); + + avrcp_set_volume_rsp(session, transaction, volume); + + return -EAGAIN; +} + +static int set_addressed(struct avrcp *session, uint8_t transaction, + uint16_t id, void *user_data) +{ + DBG(""); + + + avrcp_set_addressed_player_rsp(session, transaction, + AVRCP_STATUS_SUCCESS); + + return -EAGAIN; +} + +static int get_folder_items(struct avrcp *session, uint8_t transaction, + uint8_t scope, uint32_t start, uint32_t end, + uint16_t number, uint32_t *attrs, + void *user_data) +{ + struct context *context = user_data; + + DBG(""); + + if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BI-02-C")) + return -ERANGE; + + if (start > 1) + return -ERANGE; + + avrcp_get_folder_items_rsp(session, transaction, 0xabcd, 0, NULL, NULL, + NULL); + + return -EAGAIN; +} + +static int change_path(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint8_t direction, + uint64_t uid, void *user_data) +{ + DBG(""); + + if (!uid) + return -ENOTDIR; + + avrcp_change_path_rsp(session, transaction, 0); + + return -EAGAIN; +} + +static int get_item_attributes(struct avrcp *session, uint8_t transaction, + uint8_t scope, uint64_t uid, + uint16_t counter, uint8_t number, + uint32_t *attrs, void *user_data) +{ + DBG(""); + + avrcp_get_item_attributes_rsp(session, transaction, 0, NULL, NULL); + + return -EAGAIN; +} + +static int play_item(struct avrcp *session, uint8_t transaction, uint8_t scope, + uint64_t uid, uint16_t counter, void *user_data) +{ + DBG(""); + + if (!uid) + return -ENOENT; + + avrcp_play_item_rsp(session, transaction); + + return -EAGAIN; +} + +static int search(struct avrcp *session, uint8_t transaction, + const char *string, void *user_data) +{ + DBG(""); + + avrcp_search_rsp(session, transaction, 0xaabb, 0); + + return -EAGAIN; +} + +static int add_to_now_playing(struct avrcp *session, uint8_t transaction, + uint8_t scope, uint64_t uid, uint16_t counter, + void *user_data) +{ + DBG(""); + + if (!uid) + return -ENOENT; + + avrcp_add_to_now_playing_rsp(session, transaction); + + return -EAGAIN; +} + +static const struct avrcp_control_ind control_ind = { + .get_capabilities = get_capabilities, + .list_attributes = list_attributes, + .get_attribute_text = get_attribute_text, + .list_values = list_values, + .get_value_text = get_value_text, + .get_value = get_value, + .set_value = set_value, + .get_play_status = get_play_status, + .get_element_attributes = get_element_attributes, + .register_notification = register_notification, + .set_volume = set_volume, + .set_addressed = set_addressed, + .get_folder_items = get_folder_items, + .change_path = change_path, + .get_item_attributes = get_item_attributes, + .play_item = play_item, + .search = search, + .add_to_now_playing = add_to_now_playing, +}; + +static void test_server(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + + avrcp_set_passthrough_handlers(context->session, passthrough_handlers, + context); + avrcp_register_player(context->session, &control_ind, NULL, context); + + g_idle_add(send_pdu, context); + + execute_context(context); +} + +static void test_client(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + + if (g_str_equal(context->data->test_name, "/TP/MPS/BV-01-C")) + avrcp_set_addressed_player(context->session, 0xabcd); + + if (g_str_equal(context->data->test_name, "/TP/MPS/BV-03-C")) + avrcp_set_browsed_player(context->session, 0xabcd); + + if (g_str_equal(context->data->test_name, "/TP/MPS/BV-08-C")) + avrcp_get_folder_items(context->session, + AVRCP_MEDIA_PLAYER_LIST, 0, 2, 0, NULL); + + if (g_str_equal(context->data->test_name, "/TP/MPS/BV-01-I")) + avrcp_get_folder_items(context->session, + AVRCP_MEDIA_PLAYER_LIST, 0, 2, 0, NULL); + + if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-01-C")) + avrcp_get_folder_items(context->session, + AVRCP_MEDIA_PLAYER_VFS, 0, 2, 0, NULL); + + if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-04-C")) + avrcp_change_path(context->session, 0x01, 0x01, 0xaabb); + + if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-07-C")) + avrcp_get_item_attributes(context->session, + AVRCP_MEDIA_PLAYER_VFS, 0x01, 0xaabb, + 0, NULL); + + if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-01-C")) + avrcp_search(context->session, "Country"); + + if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-03-C")) + avrcp_get_folder_items(context->session, AVRCP_MEDIA_SEARCH, + 0, 2, 0, NULL); + + if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-05-C")) + avrcp_get_item_attributes(context->session, + AVRCP_MEDIA_SEARCH, 0x01, 0xaabb, + 0, NULL); + + if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-01-C")) + avrcp_play_item(context->session, AVRCP_MEDIA_NOW_PLAYING, 1, + 1); + + if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-03-C")) + avrcp_add_to_now_playing(context->session, + AVRCP_MEDIA_NOW_PLAYING, 0x01, 0xaabb); + + if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-05-C")) + avrcp_get_folder_items(context->session, + AVRCP_MEDIA_NOW_PLAYING, 0, 2, 0, NULL); + + if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-08-C")) + avrcp_get_item_attributes(context->session, + AVRCP_MEDIA_NOW_PLAYING, 0x01, 0xaabb, + 0, NULL); + + if (g_str_equal(context->data->test_name, "/TP/CFG/BV-01-C")) + avrcp_get_capabilities(context->session, CAP_EVENTS_SUPPORTED); + + if (g_str_equal(context->data->test_name, "/TP/PAS/BV-01-C")) + avrcp_list_player_attributes(context->session); + + if (g_str_equal(context->data->test_name, "/TP/PAS/BV-03-C")) { + uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER, + AVRCP_ATTRIBUTE_REPEAT_MODE }; + + avrcp_get_player_attribute_text(context->session, sizeof(attrs), + attrs); + } + + if (g_str_equal(context->data->test_name, "/TP/PAS/BV-05-C")) + avrcp_list_player_values(context->session, + AVRCP_ATTRIBUTE_EQUALIZER); + + if (g_str_equal(context->data->test_name, "/TP/PAS/BV-07-C")) { + uint8_t values[2] = { AVRCP_EQUALIZER_OFF, AVRCP_EQUALIZER_ON }; + + avrcp_get_player_value_text(context->session, + AVRCP_ATTRIBUTE_EQUALIZER, + sizeof(values), values); + } + + if (g_str_equal(context->data->test_name, "/TP/PAS/BV-09-C")) { + uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER, + AVRCP_ATTRIBUTE_REPEAT_MODE }; + + avrcp_get_current_player_value(context->session, sizeof(attrs), + attrs); + } + + if (g_str_equal(context->data->test_name, "/TP/PAS/BV-11-C")) { + uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER, + AVRCP_ATTRIBUTE_REPEAT_MODE }; + uint8_t values[2] = { 0xaa, 0xff }; + + avrcp_set_player_value(context->session, sizeof(attrs), attrs, + values); + } + + if (g_str_equal(context->data->test_name, "/TP/MDI/BV-01-C")) + avrcp_get_play_status(context->session); + + if (g_str_equal(context->data->test_name, "/TP/MDI/BV-03-C")) + avrcp_get_element_attributes(context->session); + + if (g_str_equal(context->data->test_name, "/TP/NFY/BV-01-C")) + avrcp_register_notification(context->session, + AVRCP_EVENT_STATUS_CHANGED, 0); + + if (g_str_equal(context->data->test_name, "/TP/BGN/BV-01-I")) + avrcp_send_passthrough(context->session, IEEEID_BTSIG, + AVC_VENDOR_NEXT_GROUP); + + if (g_str_equal(context->data->test_name, "/TP/BGN/BV-02-I")) + avrcp_send_passthrough(context->session, IEEEID_BTSIG, + AVC_VENDOR_PREV_GROUP); + + if (g_str_equal(context->data->test_name, "/TP/VLH/BV-01-C")) + avrcp_set_volume(context->session, 0x00); + + execute_context(context); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + if (g_test_verbose()) + __btd_log_init("*", 0); + + /* Media Player Selection Commands and Notifications */ + + /* SetAddressedPlayer - CT */ + define_test("/TP/MPS/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00, + 0x00, 0x19, 0x58, 0x60, 0x00, 0x00, + 0x02, 0xab, 0xcd)); + + /* SetAddressedPlayer - TG */ + define_test("/TP/MPS/BV-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_SET_ADDRESSED_PLAYER, + 0x00, 0x00, 0x02, 0xab, 0xcd), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_STABLE, + 0x48, 0x00, 0x00, 0x19, 0x58, + AVRCP_SET_ADDRESSED_PLAYER, + 0x00, 0x00, 0x01, 0x04)); + + /* SetBrowsedPlayer - CT */ + define_test("/TP/MPS/BV-03-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x70, 0x00, 0x02, + 0xab, 0xcd)); + + /* GetFolderItems - CT */ + define_test("/TP/MPS/BV-08-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00)); + + /* GetFolderItems - TG */ + define_test("/TP/MPS/BV-09-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00)); + + /* + * Media Content Navigation Commands and Notifications for Content + * Browsing. + */ + + /* GetFolderItems - Virtual FS - CT */ + define_test("/TP/MCN/CB/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00)); + + /* GetFolderItems - Virtual FS - TG */ + define_test("/TP/MCN/CB/BV-02-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00)); + + /* ChangePath - CT */ + define_test("/TP/MCN/CB/BV-04-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH, + 0x00, 0x0b, + 0xaa, 0xbb, /* counter */ + 0x01, /* direction */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01 /* Folder UID */)); + + /* ChangePath - TG */ + define_test("/TP/MCN/CB/BV-05-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH, + 0x00, 0x0b, + 0xaa, 0xbb, /* counter */ + 0x01, /* direction */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01 /* Folder UID */), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH, + 0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00)); + + /* ChangePath - TG */ + define_test("/TP/MCN/CB/BV-06-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH, + 0x00, 0x0b, + 0xaa, 0xbb, /* counter */ + 0x00, /* direction */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01 /* Folder UID */), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH, + 0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00)); + + /* GetItemAttributes - CT */ + define_test("/TP/MCN/CB/BV-07-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x0c, AVRCP_MEDIA_PLAYER_VFS, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, /* uuid */ + 0xaa, 0xbb, /* counter */ + 0x00)); /* num attr */ + + /* GetItemAttributes - TG */ + define_test("/TP/MCN/CB/BV-08-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x0c, AVRCP_MEDIA_PLAYER_VFS, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, /* uuid */ + 0xaa, 0xbb, /* counter */ + 0x00), /* num attr */ + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x02, 0x04, 0x00)); + + /* GetFolderItems - Virtual FS - TG */ + define_test("/TP/MCN/CB/BI-01-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS, + 0x00, 0x00, 0x00, 0x01, /* start */ + 0x00, 0x00, 0x00, 0x00, /* end */ + 0x00), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x01, 0x0b)); + + /* GetFolderItems - Virtual FS - TG */ + define_test("/TP/MCN/CB/BI-02-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x01, /* end */ + 0x00), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x01, 0x0b)); + + /* GetFolderItems - Virtual FS - TG */ + define_test("/TP/MCN/CB/BI-03-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS, + 0x00, 0x00, 0x00, 0x02, /* start */ + 0x00, 0x00, 0x00, 0x03, /* end */ + 0x00), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x01, 0x0b)); + + /* ChangePath - TG */ + define_test("/TP/MCN/CB/BI-04-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH, + 0x00, 0x0b, + 0xaa, 0xbb, /* counter */ + 0x01, /* direction */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 /* Folder UID */), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH, + 0x00, 0x01, 0x08)); + + /* Media Content Navigation Commands and Notifications for Search */ + + /* Search - CT */ + define_test("/TP/MCN/SRC/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_SEARCH, + 0x00, 0x0b, 0x00, 0x6a, + 0x00, 0x07, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79)); + + define_test("/TP/MCN/SRC/BV-02-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_SEARCH, + 0x00, 0x0b, 0x00, 0x6a, + 0x00, 0x07, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_SEARCH, + 0x00, 0x07, 0x04, + 0xaa, 0xbb, /* counter */ + 0x00, 0x00, 0x00, 0x00)); + + /* GetFolderItems - CT */ + define_test("/TP/MCN/SRC/BV-03-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_SEARCH, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00)); + + /* GetFolderItems - NowPlaying - TG */ + define_test("/TP/MCN/SCR/BV-04-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_SEARCH, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00)); + + /* GetItemAttributes - CT */ + define_test("/TP/MCN/SRC/BV-05-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x0c, AVRCP_MEDIA_SEARCH, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, /* uuid */ + 0xaa, 0xbb, /* counter */ + 0x00)); /* num attr */ + + /* GetItemAttributes - TG */ + define_test("/TP/MCN/SRC/BV-06-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x0c, AVRCP_MEDIA_SEARCH, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, /* uid */ + 0xaa, 0xbb, /* counter */ + 0x00), /* num attr */ + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x02, 0x04, 0x00)); + + /* Media Content Navigation Commands and Notifications for NowPlaying */ + + /* PlayItem - NowPlaying - CT */ + define_test("/TP/MCN/NP/BV-01-C", test_client, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM, + 0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01)); + + /* PlayItem - NowPlaying - TG */ + define_test("/TP/MCN/NP/BV-02-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM, + 0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_PLAY_ITEM, + 0x00, 0x01, 0x04)); + + /* AddToNowPlaying - NowPlaying - CT */ + define_test("/TP/MCN/NP/BV-03-C", test_client, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING, + 0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, /* uid */ + 0xaa, 0xbb)); + + /* AddToNowPlaying - NowPlaying - TG */ + define_test("/TP/MCN/NP/BV-04-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING, + 0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, /* uid */ + 0xaa, 0xbb), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING, + 0x00, 0x01, 0x04)); + + /* GetFolderItems - NowPlaying - CT */ + define_test("/TP/MCN/NP/BV-05-C", test_client, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00)); + + /* GetFolderItems - NowPlaying - TG */ + define_test("/TP/MCN/NP/BV-06-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00)); + + /* GetItemAttributes - CT */ + define_test("/TP/MCN/NP/BV-08-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x0c, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, /* uid */ + 0xaa, 0xbb, /* counter */ + 0x00)); /* num attr */ + + /* GetItemAttributes - TG */ + define_test("/TP/MCN/CB/BV-09-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x0c, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, /* uid */ + 0xaa, 0xbb, /* counter */ + 0x00), /* num attr */ + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES, + 0x00, 0x02, 0x04, 0x00)); + + /* PlayItem - NowPlaying - TG */ + define_test("/TP/MCN/NP/BI-01-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM, + 0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /* uid */ + 0xaa, 0xbb), /* counter */ + brs_pdu(0x02, 0x11, 0x0e, AVRCP_PLAY_ITEM, + 0x00, 0x01, 0x09)); + + /* AddToNowPlaying - NowPlaying - TG */ + define_test("/TP/MCN/NP/BI-02-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING, + 0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /* uid */ + 0xaa, 0xbb), /* counter */ + brs_pdu(0x02, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING, + 0x00, 0x01, 0x09)); + + /* Media Player Selection IOP tests */ + + /* Listing of available media players */ + define_test("/TP/MPS/BV-01-I", test_client, + raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS, + 0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST, + 0x00, 0x00, 0x00, 0x00, /* start */ + 0x00, 0x00, 0x00, 0x02, /* end */ + 0x00)); + + /* Connection Establishment for Browsing tests */ + + /* + * Tests are checking connection establishment and release + * for browsing channel. Since we are connected through socketpair + * the tests are dummy + */ + define_test("/TP/CON/BV-01-C", test_dummy, raw_pdu(0x00)); + define_test("/TP/CON/BV-02-C", test_dummy, raw_pdu(0x00)); + define_test("/TP/CON/BV-03-C", test_dummy, raw_pdu(0x00)); + define_test("/TP/CON/BV-04-C", test_dummy, raw_pdu(0x00)); + define_test("/TP/CON/BV-05-C", test_dummy, raw_pdu(0x00)); + + /* Connection Establishment for Control tests */ + + /* + * Tests are checking connection establishement and release + * for control channel. Since we are connected through socketpair + * the tests are dummy + */ + define_test("/TP/CEC/BV-01-I", test_dummy, raw_pdu(0x00)); + define_test("/TP/CEC/BV-02-I", test_dummy, raw_pdu(0x00)); + define_test("/TP/CRC/BV-01-I", test_dummy, raw_pdu(0x00)); + define_test("/TP/CRC/BV-02-I", test_dummy, raw_pdu(0x00)); + + /* Information collection for control tests */ + + define_test("/TP/ICC/BV-01-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0xf8, 0x30, + 0xff, 0xff, 0xff, 0xff, 0xff), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0xf8, 0x30, + 0x07, 0x48, 0xff, 0xff, 0xff)); + + define_test("/TP/ICC/BV-02-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0xf8, 0x31, + 0x07, 0xff, 0xff, 0xff, 0xff), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0xf8, 0x31, + 0x07, 0x48, 0xff, 0xff, 0xff)); + + define_test("/TP/PTT/BV-01-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c, + 0x44, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c, + 0x44, 0x00)); + + define_test("/TP/PTT/BV-02-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c, + AVC_VOLUME_UP, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c, + AVC_VOLUME_UP, 0x00)); + + define_test("/TP/PTT/BV-03-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c, + AVC_CHANNEL_UP, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c, + AVC_CHANNEL_UP, 0x00)); + + define_test("/TP/PTT/BV-04-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c, + AVC_SELECT, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c, + AVC_SELECT, 0x00)); + + define_test("/TP/PTT/BV-05-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c, + AVC_PLAY, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c, + AVC_PLAY, 0x00), + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c, + AVC_PLAY | 0x80, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c, + AVC_PLAY | 0x80, 0x00)); + + /* Metadata transfer tests */ + + define_test("/TP/CFG/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, 0x10, 0x00, 0x00, + 0x01, 0x03)); + + define_test("/TP/CFG/BV-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, 0x10, 0x00, 0x00, + 0x01, 0x02), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, 0x10, 0x00, 0x00, + 0x05, 0x02, 0x01, 0x00, 0x19, 0x58)); + + define_test("/TP/CFG/BI-01-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, 0x10, 0x00, 0x00, + 0x01, 0x7f), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED, + 0x48, 0x00, 0x00, 0x19, 0x58, 0x10, + 0x00, 0x00, 0x01, + AVRCP_STATUS_INVALID_PARAM)); + + /* Player Application Settings tests */ + + define_test("/TP/PAS/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, 0x11, 0x00, 0x00, + 0x00)); + + define_test("/TP/PAS/BV-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, 0x11, 0x00, 0x00, + 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, 0x11, 0x00, 0x00, + 0x01, 0x00)); + + define_test("/TP/PAS/BV-03-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + 0x00, 0x00, 0x03, 0x02, + AVRCP_ATTRIBUTE_EQUALIZER, + AVRCP_ATTRIBUTE_REPEAT_MODE)); + + define_test("/TP/PAS/BV-04-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + 0x00, 0x00, 0x02, 0x01, 0x01), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + 0x00, 0x00, 0x0e, 0x01, 0x01, 0x00, + 0x6a, 0x09, 0x65, 0x71, 0x75, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x72)); + + define_test("/TP/PAS/BV-05-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_LIST_PLAYER_VALUES, + 0x00, 0x00, 0x01, + AVRCP_ATTRIBUTE_EQUALIZER)); + + define_test("/TP/PAS/BV-06-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_LIST_PLAYER_VALUES, + 0x00, 0x00, 0x01, AVRCP_ATTRIBUTE_EQUALIZER), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_LIST_PLAYER_VALUES, + 0x00, 0x00, 0x01, 0x00)); + + define_test("/TP/PAS/BV-07-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_VALUE_TEXT, + 0x00, 0x00, 0x04, + AVRCP_ATTRIBUTE_EQUALIZER, 0x02, + AVRCP_EQUALIZER_OFF, + AVRCP_EQUALIZER_ON)); + + define_test("/TP/PAS/BV-08-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_VALUE_TEXT, + 0x00, 0x00, 0x03, AVRCP_ATTRIBUTE_EQUALIZER, + 0x01, 0x01), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_VALUE_TEXT, + 0x00, 0x00, 0x07, 0x01, 0x01, 0x00, + 0x6a, 0x02, 0x6f, 0x6e)); + + define_test("/TP/PAS/BV-09-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_CURRENT_PLAYER_VALUE, + 0x00, 0x00, 0x03, 0x02, + AVRCP_ATTRIBUTE_EQUALIZER, + AVRCP_ATTRIBUTE_REPEAT_MODE)); + + define_test("/TP/PAS/BV-10-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_CURRENT_PLAYER_VALUE, + 0x00, 0x00, 0x03, 0x02, + AVRCP_ATTRIBUTE_EQUALIZER, + AVRCP_ATTRIBUTE_REPEAT_MODE), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_CURRENT_PLAYER_VALUE, + 0x00, 0x00, 0x05, 0x02, + AVRCP_ATTRIBUTE_EQUALIZER, 0x00, + AVRCP_ATTRIBUTE_REPEAT_MODE, 0x00)); + + define_test("/TP/PAS/BV-11-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_SET_PLAYER_VALUE, + 0x00, 0x00, 0x05, 0x02, + AVRCP_ATTRIBUTE_EQUALIZER, 0xaa, + AVRCP_ATTRIBUTE_REPEAT_MODE, 0xff)); + + /* Get player app setting attribute text invalid behavior - TG */ + define_test("/TP/PAS/BI-01-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + 0x00, 0x00, 0x02, 0x01, + /* Invalid attribute id */ + 0x7f), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED, + 0x48, 0x00, 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + 0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM)); + + /* List player application setting values invalid behavior - TG */ + define_test("/TP/PAS/BI-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_LIST_PLAYER_VALUES, + 0x00, 0x00, 0x01, + /* Invalid attribute id */ + 0x7f), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED, + 0x48, 0x00, 0x00, 0x19, 0x58, + AVRCP_LIST_PLAYER_VALUES, + 0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM)); + + /* Get player application setting value text invalid behavior - TG */ + define_test("/TP/PAS/BI-03-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_VALUE_TEXT, + 0x00, 0x00, 0x03, AVRCP_ATTRIBUTE_EQUALIZER, + 0x01, + /* Invalid setting value */ + 0x7f), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED, + 0x48, 0x00, 0x00, 0x19, 0x58, + AVRCP_GET_PLAYER_VALUE_TEXT, + 0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM)); + + /* Get current player application setting value invalid behavior - TG */ + define_test("/TP/PAS/BI-04-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_GET_CURRENT_PLAYER_VALUE, + 0x00, 0x00, 0x02, 0x01, + /* Invalid attribute */ + 0x7f), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED, + 0x48, 0x00, 0x00, 0x19, 0x58, + AVRCP_GET_CURRENT_PLAYER_VALUE, + 0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM)); + + /* Set player application setting value invalid behavior - TG */ + define_test("/TP/PAS/BI-05-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00, + 0x00, 0x19, 0x58, + AVRCP_SET_PLAYER_VALUE, + 0x00, 0x00, 0x03, 0x01, + AVRCP_ATTRIBUTE_REPEAT_MODE, 0x7f), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED, + 0x48, 0x00, 0x00, 0x19, 0x58, + AVRCP_SET_PLAYER_VALUE, + 0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM)); + + /* Media Information Commands */ + + /* Get play status - CT */ + define_test("/TP/MDI/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS, + 0x00, 0x00, 0x00)); + + /* Get play status - TG */ + define_test("/TP/MDI/BV-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS, + 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS, + 0x00, 0x00, 0x09, + 0xbb, 0xbb, 0xbb, 0xbb, /* duration */ + 0xaa, 0xaa, 0xaa, 0xaa, /* position */ + 0x00)); + + /* Get element attributes - CT */ + define_test("/TP/MDI/BV-03-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); + + /* Get element attributes - TG */ + define_test("/TP/MDI/BV-04-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES, + 0x00, 0x00, 0x00)); + + /* Get element attributes - TG */ + define_test("/TP/MDI/BV-05-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES, + 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES, + 0x00, 0x00, 0x00)); + + /* Notification Commands */ + + /* Register notification - CT */ + define_test("/TP/NFY/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x05, AVRCP_EVENT_STATUS_CHANGED, + 0x00, 0x00, 0x00, 0x00)); + + /* Register notification - TG */ + define_test("/TP/NFY/BV-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED, + 0x00, 0x00, 0x00, 0x00), + frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff)); + + /* Register notification - TG */ + define_test("/TP/NFY/BV-03-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x05, + AVRCP_EVENT_SETTINGS_CHANGED, + 0x00, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x04, + AVRCP_EVENT_SETTINGS_CHANGED, + 0x01, 0x01, 0x02), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x04, + AVRCP_EVENT_SETTINGS_CHANGED, + 0x01, 0x01, 0x02)); + + /* Register notification - Track Changed - No Selected Track - TG */ + define_test("/TP/NFY/BV-04-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED, + 0x00, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff)); + + /* Register notification - Track Changed - Track Playing - TG */ + define_test("/TP/NFY/BV-05-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED, + 0x00, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00)); + + /* Register notification - Track Changed - Selected Track - TG */ + define_test("/TP/NFY/BV-08-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED, + 0x00, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00)); + + /* Register notification - Register for events invalid behavior - TG */ + define_test("/TP/NFY/BI-01-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x05, + /* Invalid event id */ + 0xff, + 0x00, 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED, + 0x48, 0x00, 0x00, 0x19, 0x58, + AVRCP_REGISTER_NOTIFICATION, + 0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM)); + + /* Invalid commands */ + + /* Invalid PDU ID - TG */ + define_test("/TP/INV/BI-01-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00, + 0x00, 0x19, 0x58, + /* Invalid PDU ID */ + 0xff, + 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED, + 0x48, 0x00, 0x00, 0x19, 0x58, + 0xff, 0x00, 0x00, 0x01, + AVRCP_STATUS_INVALID_COMMAND)); + + /* Invalid PDU ID - Browsing TG */ + define_test("/TP/INV/BI-02-C", test_server, + brs_pdu(0x00, 0x11, 0x0e, 0xff, 0x00, 0x00), + brs_pdu(0x02, 0x11, 0x0e, AVRCP_GENERAL_REJECT, + 0x00, 0x01, AVRCP_STATUS_INVALID_COMMAND)); + + /* Next Group command transfer - CT */ + define_test("/TP/BGN/BV-01-I", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, + AVC_OP_PASSTHROUGH, + AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19, + 0x58, 0x00, AVC_VENDOR_NEXT_GROUP)); + + /* Next Group command transfer - TG */ + define_test("/TP/BGN/BV-01-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, + AVC_OP_PASSTHROUGH, + AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19, + 0x58, 0x00, AVC_VENDOR_NEXT_GROUP), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_ACCEPTED, + 0x48, AVC_OP_PASSTHROUGH, + AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19, + 0x58, 0x00, AVC_VENDOR_NEXT_GROUP)); + + /* Previous Group command transfer - CT */ + define_test("/TP/BGN/BV-02-I", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, + AVC_OP_PASSTHROUGH, + AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19, + 0x58, 0x00, AVC_VENDOR_PREV_GROUP)); + + /* Previous Group command transfer - TG */ + define_test("/TP/BGN/BV-02-I", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, + AVC_OP_PASSTHROUGH, + AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19, + 0x58, 0x00, AVC_VENDOR_PREV_GROUP), + raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_ACCEPTED, + 0x48, AVC_OP_PASSTHROUGH, + AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19, + 0x58, 0x00, AVC_VENDOR_PREV_GROUP)); + + /* Volume Level Handling */ + + /* Set absolute volume – CT */ + define_test("/TP/VLH/BV-01-C", test_client, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME, + 0x00, 0x00, 0x01, 0x00)); + + /* Set absolute volume – TG */ + define_test("/TP/VLH/BV-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME, + 0x00, 0x00, 0x01, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME, + 0x00, 0x00, 0x01, 0x00)); + + /* Set absolute volume – TG */ + define_test("/TP/VLH/BI-01-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME, + 0x00, 0x00, 0x00), + raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME, + 0x00, 0x00, 0x01, 0x01)); + + /* Set absolute volume – TG */ + define_test("/TP/VLH/BI-02-C", test_server, + raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME, + 0x00, 0x00, 0x01, 0x80), + raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00, + 0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME, + 0x00, 0x00, 0x01, 0x00)); + + return g_test_run(); +} diff --git a/unit/test-eir.c b/unit/test-eir.c index 6d9d554..919a83b 100644 --- a/unit/test-eir.c +++ b/unit/test-eir.c @@ -25,6 +25,8 @@ #include #endif +#include + #include #include @@ -36,9 +38,9 @@ struct test_data { const void *eir_data; size_t eir_size; - int flags; + unsigned int flags; const char *name; - gboolean name_complete; + bool name_complete; int8_t tx_power; const char **uuid; }; @@ -91,9 +93,8 @@ static const char *macbookair_uuid[] = { static const struct test_data macbookair_test = { .eir_data = macbookair_data, .eir_size = sizeof(macbookair_data), - .flags = -1, .name = "Marcel’s MacBook Air", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = macbookair_uuid, }; @@ -146,9 +147,8 @@ static const char *iphone5_uuid[] = { static const struct test_data iphone5_test = { .eir_data = iphone5_data, .eir_size = sizeof(iphone5_data), - .flags = -1, .name = "Marcel’s iPhone 5", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = iphone5_uuid, }; @@ -199,9 +199,8 @@ static const char *ipadmini_uuid[] = { static const struct test_data ipadmini_test = { .eir_data = ipadmini_data, .eir_size = sizeof(ipadmini_data), - .flags = -1, .name = "Marcel's iPad mini", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = ipadmini_uuid, }; @@ -251,9 +250,8 @@ static const char *gigaset_sl400h_uuid[] = { static const struct test_data gigaset_sl400h_test = { .eir_data = gigaset_sl400h_data, .eir_size = sizeof(gigaset_sl400h_data), - .flags = -1, .name = "Marcel's SL400H", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = gigaset_sl400h_uuid, }; @@ -303,9 +301,8 @@ static const char *gigaset_sl910_uuid[] = { static const struct test_data gigaset_sl910_test = { .eir_data = gigaset_sl910_data, .eir_size = sizeof(gigaset_sl910_data), - .flags = -1, .name = "Marcel's SL910", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = gigaset_sl910_uuid, }; @@ -357,9 +354,8 @@ static const char *nokia_bh907_uuid[] = { static const struct test_data nokia_bh907_test = { .eir_data = nokia_bh907_data, .eir_size = sizeof(nokia_bh907_data), - .flags = -1, .name = "Nokia Reaction BH-907", - .name_complete = TRUE, + .name_complete = true, .tx_power = 4, .uuid = nokia_bh907_uuid, }; @@ -405,9 +401,8 @@ static const char *fuelband_uuid[] = { static const struct test_data fuelband_test = { .eir_data = fuelband_data, .eir_size = sizeof(fuelband_data), - .flags = -1, .name = "Nike+ FuelBand", - .name_complete = TRUE, + .name_complete = true, .tx_power = 0, .uuid = fuelband_uuid, }; @@ -429,7 +424,7 @@ static const struct test_data bluesc_test = { .eir_size = sizeof(bluesc_data), .flags = 0x06, .name = "Wahoo BlueSC v1.4", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = bluesc_uuid, }; @@ -451,7 +446,7 @@ static const struct test_data wahoo_scale_test = { .eir_size = sizeof(wahoo_scale_data), .flags = 0x06, .name = "Wahoo Scale v1.3", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = wahoo_scale_uuid, }; @@ -471,7 +466,7 @@ static const struct test_data mio_alpha_test = { .eir_size = sizeof(mio_alpha_data), .flags = 0x06, .name = "ALPHA", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = mio_alpha_uuid, }; @@ -493,7 +488,7 @@ static const struct test_data cookoo_test = { .eir_size = sizeof(cookoo_data), .flags = 0x05, .name = "COOKOO watch", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, .uuid = cookoo_uuid, }; @@ -510,7 +505,7 @@ static const struct test_data citizen_adv_test = { .eir_size = sizeof(citizen_adv_data), .flags = 0x05, .name = "Eco-Drive Proximity", - .name_complete = TRUE, + .name_complete = true, .tx_power = 127, }; @@ -528,7 +523,6 @@ static const char *citizen_scan_uuid[] = { static const struct test_data citizen_scan_test = { .eir_data = citizen_scan_data, .eir_size = sizeof(citizen_scan_data), - .flags = -1, .tx_power = 0, .uuid = citizen_scan_uuid, }; diff --git a/unit/test-gdbus-client.c b/unit/test-gdbus-client.c index 685729a..d0b6ce7 100644 --- a/unit/test-gdbus-client.c +++ b/unit/test-gdbus-client.c @@ -38,6 +38,7 @@ struct context { GDBusClient *dbus_client; GDBusProxy *proxy; void *data; + gboolean client_ready; guint timeout_source; }; @@ -85,6 +86,7 @@ static struct context *create_context(void) dbus_connection_set_exit_on_disconnect(context->dbus_conn, FALSE); g_dbus_attach_object_manager(context->dbus_conn); + context->client_ready = FALSE; return context; } @@ -101,6 +103,7 @@ static void destroy_context(struct context *context) dbus_connection_flush(context->dbus_conn); dbus_connection_close(context->dbus_conn); + dbus_connection_unref(context->dbus_conn); g_main_loop_unref(context->main_loop); @@ -956,6 +959,60 @@ static void client_force_disconnect(void) g_main_loop_run(context->main_loop); + g_dbus_unregister_interface(conn, SERVICE_PATH, SERVICE_NAME1); + g_dbus_detach_object_manager(conn); + dbus_connection_unref(conn); + + destroy_context(context); +} + +static void client_ready_watch(GDBusClient *client, void *user_data) +{ + struct context *context = user_data; + + context->client_ready = TRUE; +} + +static void proxy_added(GDBusProxy *proxy, void *user_data) +{ + struct context *context = user_data; + + /* + * Proxy added callback should not be called after Client ready + * watch. Ready means that all objects has been reported to the + * upper-layer. + */ + g_assert(context->client_ready == FALSE); + + g_main_loop_quit(context->main_loop); +} + +static void client_ready(void) +{ + struct context *context = create_context(); + static const GDBusPropertyTable string_properties[] = { + { "String", "s", get_string, set_string, string_exists }, + { }, + }; + + if (context == NULL) + return; + + g_dbus_register_interface(context->dbus_conn, + SERVICE_PATH, SERVICE_NAME, + methods, signals, string_properties, + context, NULL); + + context->dbus_client = g_dbus_client_new(context->dbus_conn, + SERVICE_NAME, SERVICE_PATH); + + g_dbus_client_set_ready_watch(context->dbus_client, client_ready_watch, + context); + g_dbus_client_set_proxy_handlers(context->dbus_client, + proxy_added, NULL, NULL, context); + + g_main_loop_run(context->main_loop); + destroy_context(context); } @@ -996,5 +1053,7 @@ int main(int argc, char *argv[]) g_test_add_func("/gdbus/client_force_disconnect", client_force_disconnect); + g_test_add_func("/gdbus/client_ready", client_ready); + return g_test_run(); } diff --git a/unit/test-gobex-apparam.c b/unit/test-gobex-apparam.c index e6a42cc..976c541 100644 --- a/unit/test-gobex-apparam.c +++ b/unit/test-gobex-apparam.c @@ -250,6 +250,8 @@ static void test_apparam_get_multi(void) g_assert(string != NULL); g_assert_cmpstr(string, ==, "ABC"); + g_free(string); + g_obex_apparam_free(apparam); } diff --git a/unit/test-gobex-transfer.c b/unit/test-gobex-transfer.c index ef05047..ffb5bdc 100644 --- a/unit/test-gobex-transfer.c +++ b/unit/test-gobex-transfer.c @@ -106,6 +106,9 @@ static guint8 get_req_first_srm_wait[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x27 0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0, G_OBEX_HDR_SRMP, 0x01 }; +static guint8 get_req_srm_wait[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x05, + G_OBEX_HDR_SRMP, 0x01 }; + static guint8 get_req_last[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x03, }; static guint8 get_rsp_first_app[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, 0x00, 0x0A, @@ -124,6 +127,8 @@ static guint8 get_rsp_first_srm_wait_next[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, G_OBEX_HDR_SRMP, 0x02, G_OBEX_HDR_BODY, 0x00, 0x0d, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +static guint8 get_rsp_srm_wait[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, + 0x00, 0x03 }; static guint8 get_rsp_zero[255] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, 0x00, 0xff, G_OBEX_HDR_BODY, 0x00, 0xfc }; static guint8 get_rsp_zero_wait_next[255] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, @@ -191,7 +196,13 @@ static void transfer_complete(GObex *obex, GError *err, gpointer user_data) static gboolean resume_obex(gpointer user_data) { - g_obex_resume(user_data); + struct test_data *d = user_data; + + if (!g_main_loop_is_running(d->mainloop)) + return FALSE; + + g_obex_resume(d->obex); + return FALSE; } @@ -232,7 +243,7 @@ static gssize provide_eagain(void *buf, gsize len, gpointer user_data) } if (d->provide_delay > 0) { - g_timeout_add(d->provide_delay, resume_obex, d->obex); + g_timeout_add(d->provide_delay, resume_obex, d); d->provide_delay = 0; return -EAGAIN; } @@ -260,7 +271,7 @@ static gssize provide_data(void *buf, gsize len, gpointer user_data) if (d->provide_delay > 0) { g_obex_suspend(d->obex); - g_timeout_add(d->provide_delay, resume_obex, d->obex); + g_timeout_add(d->provide_delay, resume_obex, d); } d->total += sizeof(body_data); @@ -511,6 +522,7 @@ static void test_stream_put_req_abort(void) g_obex_unref(obex); g_assert_error(d.err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED); + g_error_free(d.err); } static void test_stream_put_rsp_abort(void) @@ -556,6 +568,7 @@ static void test_stream_put_rsp_abort(void) g_obex_unref(obex); g_assert_error(d.err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED); + g_error_free(d.err); } static void handle_put_seq_wait(GObex *obex, GObexPacket *req, @@ -850,6 +863,66 @@ static void test_packet_get_req_wait(void) g_assert_no_error(d.err); } +static gboolean rcv_seq_delay(const void *buf, gsize len, gpointer user_data) +{ + struct test_data *d = user_data; + + if (d->provide_delay > 0) { + g_obex_suspend(d->obex); + g_timeout_add_seconds(d->provide_delay, resume_obex, d); + d->provide_delay = 0; + } + + return TRUE; +} + +static void test_packet_get_req_suspend_resume(void) +{ + GIOChannel *io; + GIOCondition cond; + guint io_id, timer_id; + GObex *obex; + struct test_data d = { 0, NULL, { + { get_req_first_srm, sizeof(get_req_first_srm) }, + { get_req_srm_wait, sizeof(get_req_srm_wait) }, + { get_req_srm_wait, sizeof(get_req_srm_wait) }, + { get_req_last, sizeof(get_req_last) } }, { + { get_rsp_first_srm, sizeof(get_rsp_first_srm) }, + { get_rsp_srm_wait, sizeof(get_rsp_srm_wait) }, + { get_rsp_srm_wait, sizeof(get_rsp_srm_wait) }, + { get_rsp_last, sizeof(get_rsp_last) } } }; + + create_endpoints(&obex, &io, SOCK_SEQPACKET); + d.obex = obex; + d.provide_delay = 1; + + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io_id = g_io_add_watch(io, cond, test_io_cb, &d); + + d.mainloop = g_main_loop_new(NULL, FALSE); + + timer_id = g_timeout_add_seconds(1, test_timeout, &d); + + g_obex_get_req(obex, rcv_seq_delay, transfer_complete, &d, &d.err, + G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type), + G_OBEX_HDR_NAME, "file.txt", + G_OBEX_HDR_INVALID); + g_assert_no_error(d.err); + + g_main_loop_run(d.mainloop); + + g_assert_cmpuint(d.count, ==, 4); + + g_main_loop_unref(d.mainloop); + + g_source_remove(timer_id); + g_io_channel_unref(io); + g_source_remove(io_id); + g_obex_unref(obex); + + g_assert_no_error(d.err); +} + static void test_packet_get_req_wait_next(void) { GIOChannel *io; @@ -1025,6 +1098,78 @@ static void test_stream_put_req(void) g_assert_no_error(d.err); } +static gssize provide_seq_delay(void *buf, gsize len, gpointer user_data) +{ + struct test_data *d = user_data; + int fd; + gssize ret; + + if (d->provide_delay > 0) { + g_obex_suspend(d->obex); + g_timeout_add_seconds(d->provide_delay, resume_obex, d); + d->provide_delay = 0; + } + + if (d->count == RANDOM_PACKETS - 1) + return 0; + + fd = open("/dev/urandom", O_RDONLY | O_NOCTTY, 0); + if (fd < 0) { + g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED, + "open(/dev/urandom): %s", strerror(errno)); + g_main_loop_quit(d->mainloop); + return -1; + } + + ret = read(fd, buf, len); + close(fd); + return ret; +} + +static void test_packet_put_req_suspend_resume(void) +{ + GIOChannel *io; + GIOCondition cond; + guint io_id; + GObex *obex; + struct test_data d = { 0, NULL, { + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { put_req_last, sizeof(put_req_last) } }, { + { put_rsp_first_srm, sizeof(put_rsp_first_srm) }, + { NULL, 0 }, + { NULL, 0 }, + { put_rsp_last, sizeof(put_rsp_last) } } }; + + create_endpoints(&obex, &io, SOCK_SEQPACKET); + d.obex = obex; + d.provide_delay = 1; + + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io_id = g_io_add_watch(io, cond, test_io_cb, &d); + + d.mainloop = g_main_loop_new(NULL, FALSE); + + g_obex_put_req(obex, provide_seq_delay, transfer_complete, &d, &d.err, + G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type), + G_OBEX_HDR_NAME, "random.bin", + G_OBEX_HDR_INVALID); + g_assert_no_error(d.err); + + g_main_loop_run(d.mainloop); + + g_assert_cmpuint(d.count, ==, RANDOM_PACKETS); + + g_main_loop_unref(d.mainloop); + + g_io_channel_unref(io); + g_source_remove(io_id); + g_obex_unref(obex); + + g_assert_no_error(d.err); +} + static void test_packet_put_req_wait(void) { GIOChannel *io; @@ -1547,7 +1692,7 @@ static gboolean rcv_data_delay(const void *buf, gsize len, gpointer user_data) if (d->provide_delay > 0) { g_obex_suspend(d->obex); - g_timeout_add(d->provide_delay, resume_obex, d->obex); + g_timeout_add(d->provide_delay, resume_obex, d); } return TRUE; @@ -1805,7 +1950,8 @@ static void test_conn_rsp(void) g_source_remove(timer_id); g_io_channel_unref(io); - g_source_remove(io_id); + if (!d.io_completed) + g_source_remove(io_id); g_obex_unref(obex); g_assert_no_error(d.err); @@ -2060,7 +2206,8 @@ static void test_conn_get_wrg_rsp(void) g_source_remove(timer_id); g_io_channel_unref(io); - g_source_remove(io_id); + if (!d.io_completed) + g_source_remove(io_id); g_obex_unref(obex); g_assert_no_error(d.err); @@ -2237,6 +2384,8 @@ int main(int argc, char *argv[]) g_test_add_func("/gobex/test_packet_put_req", test_packet_put_req); g_test_add_func("/gobex/test_packet_put_req_wait", test_packet_put_req_wait); + g_test_add_func("/gobex/test_packet_put_req_suspend_resume", + test_packet_put_req_suspend_resume); g_test_add_func("/gobex/test_packet_put_rsp", test_packet_put_rsp); g_test_add_func("/gobex/test_packet_put_rsp_wait", @@ -2249,6 +2398,8 @@ int main(int argc, char *argv[]) g_test_add_func("/gobex/test_packet_get_req", test_packet_get_req); g_test_add_func("/gobex/test_packet_get_req_wait", test_packet_get_req_wait); + g_test_add_func("/gobex/test_packet_get_req_suspend_resume", + test_packet_get_req_suspend_resume); g_test_add_func("/gobex/test_packet_get_req_wait_next", test_packet_get_req_wait_next); diff --git a/unit/test-gobex.c b/unit/test-gobex.c index 66307c2..180f817 100644 --- a/unit/test-gobex.c +++ b/unit/test-gobex.c @@ -45,6 +45,25 @@ static uint8_t pkt_connect_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT, static uint8_t pkt_connect_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x07, 0x10, 0x00, 0x10, 0x00 }; +static uint8_t pkt_disconnect_req[] = { G_OBEX_OP_DISCONNECT | FINAL_BIT, + 0x00, 0x03 }; +static uint8_t pkt_disconnect_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x03 }; + +static uint8_t pkt_unauth_rsp[] = { 0x41 | FINAL_BIT, 0x00, 0x1c, + 0x10, 0x00, 0x10, 0x00, 0x4d, 0x00, + 0x15, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 }; +static uint8_t pkt_auth_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT, 0x00, 0x1c, + 0x10, 0x00, 0x10, 0x00, 0x4e, 0x00, + 0x15, 0x00, 0x10, 0x5a, 0xd4, 0x93, + 0x93, 0xba, 0x4a, 0xf8, 0xac, 0xce, + 0x7f, 0x5b, 0x1a, 0x05, 0x38, 0x74, + 0x24 }; +static uint8_t pkt_auth_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x07, + 0x10, 0x00, 0x10, 0x00 }; + static uint8_t pkt_setpath_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT, 0x00, 0x10, 0x02, 0x00, G_OBEX_HDR_NAME, 0x00, 0x0b, @@ -235,7 +254,7 @@ static void send_req(GObexPacket *req, GObexResponseFunc rsp_func, GError *gerr = NULL; GIOChannel *io; GIOCondition cond; - guint io_id, timer_id, test_time; + guint timer_id, test_time; GObex *obex; create_endpoints(&obex, &io, transport_type); @@ -244,7 +263,7 @@ static void send_req(GObexPacket *req, GObexResponseFunc rsp_func, g_assert_no_error(gerr); cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; - io_id = g_io_add_watch(io, cond, send_rsp_func, &gerr); + g_io_add_watch(io, cond, send_rsp_func, &gerr); mainloop = g_main_loop_new(NULL, FALSE); @@ -262,7 +281,6 @@ static void send_req(GObexPacket *req, GObexResponseFunc rsp_func, g_source_remove(timer_id); g_io_channel_unref(io); - g_source_remove(io_id); g_obex_unref(obex); g_assert_no_error(gerr); @@ -466,6 +484,7 @@ struct rcv_buf_info { GError *err; const guint8 *buf; gsize len; + gboolean completed; }; static gboolean rcv_data(GIOChannel *io, GIOCondition cond, gpointer user_data) @@ -505,6 +524,7 @@ static gboolean rcv_data(GIOChannel *io, GIOCondition cond, gpointer user_data) done: g_main_loop_quit(mainloop); + r->completed = TRUE; return FALSE; } @@ -546,7 +566,8 @@ static void test_send_connect(int transport_type) g_source_remove(timer_id); g_io_channel_unref(io); - g_source_remove(io_id); + if (!r.completed) + g_source_remove(io_id); g_obex_unref(obex); g_assert_no_error(r.err); @@ -661,7 +682,8 @@ static void test_send_on_demand(int transport_type, GObexDataProducer func) g_source_remove(timer_id); g_io_channel_unref(io); - g_source_remove(io_id); + if (!r.completed) + g_source_remove(io_id); g_obex_unref(obex); g_assert_no_error(r.err); @@ -880,6 +902,120 @@ static void test_connect(void) g_assert_no_error(d.err); } +static void test_obex_disconnect(void) +{ + GIOChannel *io; + GIOCondition cond; + guint io_id, timer_id; + GObex *obex; + struct test_data d = { 0, NULL, { + { pkt_disconnect_req, sizeof(pkt_disconnect_req) } }, { + { pkt_disconnect_rsp, sizeof(pkt_disconnect_rsp) } } }; + + create_endpoints(&obex, &io, SOCK_STREAM); + + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io_id = g_io_add_watch(io, cond, test_io_cb, &d); + + d.mainloop = g_main_loop_new(NULL, FALSE); + + timer_id = g_timeout_add_seconds(1, test_timeout, &d); + + g_obex_disconnect(obex, req_complete, &d, &d.err); + g_assert_no_error(d.err); + + g_main_loop_run(d.mainloop); + + g_assert_cmpuint(d.count, ==, 1); + + g_main_loop_unref(d.mainloop); + + g_source_remove(timer_id); + g_io_channel_unref(io); + g_source_remove(io_id); + g_obex_unref(obex); + + g_assert_no_error(d.err); +} + +static void test_auth(void) +{ + GIOChannel *io; + GIOCondition cond; + guint io_id, timer_id; + GObex *obex; + struct test_data d = { 0, NULL, { + { pkt_connect_req, sizeof(pkt_connect_req) }, + { pkt_auth_req, sizeof(pkt_auth_req) } }, { + { pkt_unauth_rsp, sizeof(pkt_unauth_rsp) }, + { pkt_auth_rsp, sizeof(pkt_auth_rsp) } }, + }; + + create_endpoints(&obex, &io, SOCK_STREAM); + + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io_id = g_io_add_watch(io, cond, test_io_cb, &d); + + d.mainloop = g_main_loop_new(NULL, FALSE); + + timer_id = g_timeout_add_seconds(1, test_timeout, &d); + + g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID); + g_assert_no_error(d.err); + + g_main_loop_run(d.mainloop); + + g_assert_cmpuint(d.count, ==, 2); + + g_main_loop_unref(d.mainloop); + + g_source_remove(timer_id); + g_io_channel_unref(io); + g_source_remove(io_id); + g_obex_unref(obex); + + g_assert_no_error(d.err); +} + +static void test_auth_fail(void) +{ + GIOChannel *io; + GIOCondition cond; + guint io_id, timer_id; + GObex *obex; + struct test_data d = { 0, NULL, { + { pkt_connect_req, sizeof(pkt_connect_req) }, + { pkt_auth_req, sizeof(pkt_auth_req) } }, { + { pkt_unauth_rsp, sizeof(pkt_unauth_rsp) }, + { pkt_unauth_rsp, sizeof(pkt_unauth_rsp) } }, + }; + + create_endpoints(&obex, &io, SOCK_STREAM); + + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io_id = g_io_add_watch(io, cond, test_io_cb, &d); + + d.mainloop = g_main_loop_new(NULL, FALSE); + + timer_id = g_timeout_add_seconds(1, test_timeout, &d); + + g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID); + g_assert_no_error(d.err); + + g_main_loop_run(d.mainloop); + + g_assert_cmpuint(d.count, ==, 2); + + g_main_loop_unref(d.mainloop); + + g_source_remove(timer_id); + g_io_channel_unref(io); + g_source_remove(io_id); + g_obex_unref(obex); + + g_assert_no_error(d.err); +} + static void test_setpath(void) { GIOChannel *io; @@ -1185,6 +1321,9 @@ int main(int argc, char *argv[]) test_cancel_req_delay_pkt); g_test_add_func("/gobex/test_connect", test_connect); + g_test_add_func("/gobex/test_obex_disconnect", test_obex_disconnect); + g_test_add_func("/gobex/test_auth", test_auth); + g_test_add_func("/gobex/test_auth_fail", test_auth_fail); g_test_add_func("/gobex/test_setpath", test_setpath); g_test_add_func("/gobex/test_setpath_up", test_setpath_up); diff --git a/unit/test-hfp.c b/unit/test-hfp.c new file mode 100644 index 0000000..20aa0b5 --- /dev/null +++ b/unit/test-hfp.c @@ -0,0 +1,470 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. 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 + * + */ + +#include +#include +#include +#include +#include + +#include +#include "src/shared/hfp.h" + +struct context { + GMainLoop *main_loop; + guint watch_id; + int fd_server; + int fd_client; + struct hfp_gw *hfp; + const struct test_data *data; + unsigned int pdu_offset; +}; + +struct test_pdu { + bool valid; + const uint8_t *data; + size_t size; + enum hfp_gw_cmd_type type; + bool fragmented; +}; + +struct test_data { + char *test_name; + struct test_pdu *pdu_list; + hfp_result_func_t result_func; +}; + +#define data(args...) ((const unsigned char[]) { args }) + +#define raw_pdu(args...) \ + { \ + .valid = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + } + +#define data_end() \ + { \ + .valid = false, \ + } + +#define type_pdu(cmd_type, args...) \ + { \ + .valid = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + .type = cmd_type, \ + } + +#define frg_pdu(args...) \ + { \ + .valid = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + .fragmented = true, \ + } + +#define define_test(name, function, result_function, args...) \ + do { \ + const struct test_pdu pdus[] = { \ + args, { } \ + }; \ + static struct test_data data; \ + data.test_name = g_strdup(name); \ + data.pdu_list = g_malloc(sizeof(pdus)); \ + data.result_func = result_function; \ + memcpy(data.pdu_list, pdus, sizeof(pdus)); \ + g_test_add_data_func(name, &data, function); \ + } while (0) + +static void context_quit(struct context *context) +{ + g_main_loop_quit(context->main_loop); +} + +static void test_free(gconstpointer user_data) +{ + const struct test_data *data = user_data; + + g_free(data->test_name); + g_free(data->pdu_list); +} + +static gboolean test_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + g_assert(!pdu->valid); + context_quit(context); + + context->watch_id = 0; + + return FALSE; +} + +static void cmd_handler(const char *command, void *user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + unsigned int cmd_len = strlen(command); + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + g_assert(cmd_len == pdu->size); + g_assert(!memcmp(command, pdu->data, cmd_len)); + + hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR); +} + +static void prefix_handler(struct hfp_gw_result *result, + enum hfp_gw_cmd_type type, void *user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + g_assert(type == pdu->type); + + hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR); +} + +static struct context *create_context(gconstpointer data) +{ + struct context *context = g_new0(struct context, 1); + GIOChannel *channel; + int err, sv[2]; + + context->main_loop = g_main_loop_new(NULL, FALSE); + g_assert(context->main_loop); + + err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv); + g_assert(err == 0); + + channel = g_io_channel_unix_new(sv[1]); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + context->watch_id = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + test_handler, context); + g_assert(context->watch_id > 0); + + g_io_channel_unref(channel); + + context->fd_server = sv[1]; + context->fd_client = sv[0]; + context->data = data; + + return context; +} + +static void execute_context(struct context *context) +{ + g_main_loop_run(context->main_loop); + + if (context->watch_id) + g_source_remove(context->watch_id); + + g_main_loop_unref(context->main_loop); + + test_free(context->data); + + if (context->hfp) + hfp_gw_unref(context->hfp); + + g_free(context); +} + +static void test_init(gconstpointer data) +{ + struct context *context = create_context(data); + + context->hfp = hfp_gw_new(context->fd_client); + + g_assert(context->hfp); + g_assert(hfp_gw_set_close_on_unref(context->hfp, true)); + + hfp_gw_unref(context->hfp); + context->hfp = NULL; + + execute_context(context); +} + +static void test_command_handler(gconstpointer data) +{ + struct context *context = create_context(data); + const struct test_pdu *pdu; + ssize_t len; + bool ret; + + context->hfp = hfp_gw_new(context->fd_client); + g_assert(context->hfp); + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + ret = hfp_gw_set_close_on_unref(context->hfp, true); + g_assert(ret); + + ret = hfp_gw_set_command_handler(context->hfp, cmd_handler, + context, NULL); + g_assert(ret); + + len = write(context->fd_server, pdu->data, pdu->size); + g_assert_cmpint(len, ==, pdu->size); + + execute_context(context); +} + +static void test_register(gconstpointer data) +{ + struct context *context = create_context(data); + const struct test_pdu *pdu; + ssize_t len; + bool ret; + + context->hfp = hfp_gw_new(context->fd_client); + g_assert(context->hfp); + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + ret = hfp_gw_set_close_on_unref(context->hfp, true); + g_assert(ret); + + if (context->data->result_func) { + ret = hfp_gw_register(context->hfp, context->data->result_func, + (char *)pdu->data, context, NULL); + g_assert(ret); + } + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + len = write(context->fd_server, pdu->data, pdu->size); + g_assert_cmpint(len, ==, pdu->size); + + execute_context(context); +} + +static gboolean send_pdu(gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + ssize_t len; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + len = write(context->fd_server, pdu->data, pdu->size); + g_assert_cmpint(len, ==, pdu->size); + + pdu = &context->data->pdu_list[context->pdu_offset]; + if (pdu->fragmented) + g_idle_add(send_pdu, context); + + return FALSE; +} + +static void test_fragmented(gconstpointer data) +{ + struct context *context = create_context(data); + bool ret; + + context->hfp = hfp_gw_new(context->fd_client); + g_assert(context->hfp); + + ret = hfp_gw_set_close_on_unref(context->hfp, true); + g_assert(ret); + + g_idle_add(send_pdu, context); + + execute_context(context); +} + +static void check_ustring_1(struct hfp_gw_result *result, + enum hfp_gw_cmd_type type, void *user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + unsigned int i = 3, j = 0; + char str[10]; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + g_assert(type == pdu->type); + + g_assert(hfp_gw_result_get_unquoted_string(result, str, sizeof(str))); + + while (context->data->pdu_list[1].data[i] != '\r') { + g_assert(j < sizeof(str)); + g_assert(str[j] == context->data->pdu_list[1].data[i]); + + i++; + j++; + } + + g_assert(str[j] == '\0'); + + hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR); +} + +static void check_ustring_2(struct hfp_gw_result *result, + enum hfp_gw_cmd_type type, void *user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + char str[10]; + + memset(str, 'X', sizeof(str)); + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + g_assert(type == pdu->type); + + g_assert(!hfp_gw_result_get_unquoted_string(result, str, 3)); + + g_assert(str[3] == 'X'); + + hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR); +} + +static void check_string_1(struct hfp_gw_result *result, + enum hfp_gw_cmd_type type, void *user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + unsigned int i = 4, j = 0; + char str[10]; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + g_assert(type == pdu->type); + + g_assert(hfp_gw_result_get_string(result, str, sizeof(str))); + + while (context->data->pdu_list[1].data[i] != '\"') { + g_assert(j < sizeof(str)); + g_assert(str[j] == context->data->pdu_list[1].data[i]); + + i++; + j++; + } + + g_assert(context->data->pdu_list[1].data[i] == '\"'); + g_assert(str[j] == '\0'); + + hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR); +} + +static void check_string_2(struct hfp_gw_result *result, + enum hfp_gw_cmd_type type, void *user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + char str[10]; + + memset(str, 'X', sizeof(str)); + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + g_assert(type == pdu->type); + + g_assert(!hfp_gw_result_get_string(result, str, 3)); + + g_assert(str[3] == 'X'); + + hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + define_test("/hfp/test_init", test_init, NULL, data_end()); + define_test("/hfp/test_cmd_handler_1", test_command_handler, NULL, + raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\r'), + raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F'), + data_end()); + define_test("/hfp/test_cmd_handler_2", test_command_handler, NULL, + raw_pdu('A', 'T', 'D', '1', '2', '3', '4', '\r'), + raw_pdu('A', 'T', 'D', '1', '2', '3', '4'), + data_end()); + define_test("/hfp/test_register_1", test_register, prefix_handler, + raw_pdu('+', 'B', 'R', 'S', 'F', '\0'), + raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\r'), + type_pdu(HFP_GW_CMD_TYPE_COMMAND, 0), + data_end()); + define_test("/hfp/test_register_2", test_register, prefix_handler, + raw_pdu('+', 'B', 'R', 'S', 'F', '\0'), + raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '=', '\r'), + type_pdu(HFP_GW_CMD_TYPE_SET, 0), + data_end()); + define_test("/hfp/test_register_3", test_register, prefix_handler, + raw_pdu('+', 'B', 'R', 'S', 'F', '\0'), + raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '?', '\r'), + type_pdu(HFP_GW_CMD_TYPE_READ, 0), + data_end()); + define_test("/hfp/test_register_4", test_register, prefix_handler, + raw_pdu('+', 'B', 'R', 'S', 'F', '\0'), + raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '=', '?', + '\r'), + type_pdu(HFP_GW_CMD_TYPE_TEST, 0), + data_end()); + define_test("/hfp/test_register_5", test_register, prefix_handler, + raw_pdu('D', '\0'), + raw_pdu('A', 'T', 'D', '1', '2', '3', '4', '5', '\r'), + type_pdu(HFP_GW_CMD_TYPE_SET, 0), + data_end()); + define_test("/hfp/test_fragmented_1", test_fragmented, NULL, + frg_pdu('A'), frg_pdu('T'), frg_pdu('+'), frg_pdu('B'), + frg_pdu('R'), frg_pdu('S'), frg_pdu('F'), frg_pdu('\r'), + data_end()); + define_test("/hfp/test_ustring_1", test_register, check_ustring_1, + raw_pdu('D', '\0'), + raw_pdu('A', 'T', 'D', '0', '1', '2', '3', '\r'), + type_pdu(HFP_GW_CMD_TYPE_SET, 0), + data_end()); + define_test("/hfp/test_ustring_2", test_register, check_ustring_2, + raw_pdu('D', '\0'), + raw_pdu('A', 'T', 'D', '0', '1', '2', '3', '\r'), + type_pdu(HFP_GW_CMD_TYPE_SET, 0), + data_end()); + define_test("/hfp/test_string_1", test_register, check_string_1, + raw_pdu('D', '\0'), + raw_pdu('A', 'T', 'D', '\"', '0', '1', '2', '3', '\"', + '\r'), + type_pdu(HFP_GW_CMD_TYPE_SET, 0), + data_end()); + define_test("/hfp/test_string_2", test_register, check_string_2, + raw_pdu('D', '\0'), + raw_pdu('A', 'T', 'D', '\"', '0', '1', '2', '3', '\"', + '\r'), + type_pdu(HFP_GW_CMD_TYPE_SET, 0), + data_end()); + + return g_test_run(); +} diff --git a/unit/test-lib.c b/unit/test-lib.c index def133a..ef0cffc 100644 --- a/unit/test-lib.c +++ b/unit/test-lib.c @@ -30,9 +30,27 @@ #include #include +#include "src/shared/util.h" + #include "lib/sdp.h" #include "lib/sdp_lib.h" +static void test_ntoh64(void) +{ + uint64_t test = 0x123456789abcdef; + + g_assert(ntoh64(test) == be64toh(test)); + g_assert(ntoh64(test) == be64_to_cpu(test)); +} + +static void test_hton64(void) +{ + uint64_t test = 0x123456789abcdef; + + g_assert(hton64(test) == htobe64(test)); + g_assert(hton64(test) == cpu_to_be64(test)); +} + static void test_sdp_get_access_protos_valid(void) { sdp_record_t *rec; @@ -439,6 +457,9 @@ int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); + g_test_add_func("/lib/ntoh64", test_ntoh64); + g_test_add_func("/lib/hton64", test_hton64); + g_test_add_func("/lib/sdp_get_access_protos/valid", test_sdp_get_access_protos_valid); g_test_add_func("/lib/sdp_get_access_protos/nodata", diff --git a/unit/test-queue.c b/unit/test-queue.c new file mode 100644 index 0000000..7c6d2ad --- /dev/null +++ b/unit/test-queue.c @@ -0,0 +1,68 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. 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 +#endif + +#include + +#include "src/shared/util.h" +#include "src/shared/queue.h" + +static void test_basic(void) +{ + struct queue *queue; + unsigned int n, i; + + queue = queue_new(); + g_assert(queue != NULL); + + for (n = 0; n < 1024; n++) { + for (i = 1; i < n + 2; i++) + queue_push_tail(queue, UINT_TO_PTR(i)); + + g_assert(queue_length(queue) == n + 1); + + for (i = 1; i < n + 2; i++) { + void *ptr; + + ptr = queue_pop_head(queue); + g_assert(ptr != NULL); + g_assert(i == PTR_TO_UINT(ptr)); + } + + g_assert(queue_isempty(queue) == true); + } + + queue_destroy(queue, NULL); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/queue/basic", test_basic); + + return g_test_run(); +} diff --git a/unit/test-ringbuf.c b/unit/test-ringbuf.c new file mode 100644 index 0000000..e28e04b --- /dev/null +++ b/unit/test-ringbuf.c @@ -0,0 +1,154 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. 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 +#endif + +#include +#include +#include + +#include + +#include "src/shared/ringbuf.h" + +static unsigned int nlpo2(unsigned int x) +{ + x--; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x + 1; +} + +static unsigned int fls(unsigned int x) +{ + return x ? sizeof(x) * 8 - __builtin_clz(x) : 0; +} + +static unsigned int align_power2(unsigned int u) +{ + return 1 << fls(u - 1); +} + +static void test_power2(void) +{ + size_t i; + + for (i = 1; i < 1000000; i++) { + size_t size1, size2, size3 = 1; + + size1 = nlpo2(i); + size2 = align_power2(i); + + /* Find the next power of two */ + while (size3 < i && size3 < SIZE_MAX) + size3 <<= 1; + + if (g_test_verbose()) + g_print("%zu -> size1=%zu size2=%zu size3=%zu\n", + i, size1, size2, size3); + + g_assert(size1 == size2); + g_assert(size2 == size3); + g_assert(size3 == size1); + } +} + +static void test_alloc(void) +{ + int i; + + for (i = 2; i < 10000; i++) { + struct ringbuf *rb; + + if (g_test_verbose()) + g_print("Iteration %i\n", i); + + rb = ringbuf_new(i); + g_assert(rb != NULL); + + g_assert(ringbuf_capacity(rb) == ringbuf_avail(rb)); + + ringbuf_free(rb); + } +} + +static void test_printf(void) +{ + static size_t rb_size = 500; + static size_t rb_capa = 512; + struct ringbuf *rb; + int i; + + rb = ringbuf_new(rb_size); + g_assert(rb != NULL); + g_assert(ringbuf_capacity(rb) == rb_capa); + + for (i = 0; i < 10000; i++) { + size_t len, count = i % rb_capa; + char *str, *ptr; + + if (!count) + continue; + + if (g_test_verbose()) + g_print("Iteration %i\n", i); + + len = asprintf(&str, "%*c", (int) count, 'x'); + g_assert(len == count); + + len = ringbuf_printf(rb, "%s", str); + g_assert(len == count); + g_assert(ringbuf_len(rb) == count); + g_assert(ringbuf_avail(rb) == rb_capa - len); + + ptr = ringbuf_peek(rb, 0, &len); + g_assert(ptr != NULL); + g_assert(len == count); + g_assert(strncmp(str, ptr, len) == 0); + + len = ringbuf_drain(rb, count); + g_assert(len == count); + g_assert(ringbuf_len(rb) == 0); + g_assert(ringbuf_avail(rb) == rb_capa); + + free(str); + } + + ringbuf_free(rb); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/ringbuf/power2", test_power2); + g_test_add_func("/ringbuf/alloc", test_alloc); + g_test_add_func("/ringbuf/printf", test_printf); + + return g_test_run(); +} diff --git a/unit/test-sdp.c b/unit/test-sdp.c index 6d699e2..a89dbfc 100644 --- a/unit/test-sdp.c +++ b/unit/test-sdp.c @@ -37,6 +37,7 @@ #include "lib/sdp_lib.h" #include "src/shared/util.h" +#include "src/log.h" #include "src/sdpd.h" struct sdp_pdu { @@ -72,7 +73,7 @@ struct test_data { #define define_test(name, _mtu, args...) \ do { \ const struct sdp_pdu pdus[] = { \ - args, { }, { } \ + args, { } \ }; \ static struct test_data data; \ data.mtu = _mtu; \ @@ -128,12 +129,6 @@ static void sdp_debug(const char *str, void *user_data) g_print("%s%s\n", prefix, str); } -void btd_debug(const char *format, ...); - -void btd_debug(const char *format, ...) -{ -} - static void context_quit(struct context *context) { g_main_loop_quit(context->main_loop); @@ -797,6 +792,9 @@ int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); + if (g_test_verbose()) + __btd_log_init("*", 0); + /* * Service Search Request * diff --git a/unit/test-uuid.c b/unit/test-uuid.c index 79ab3e6..6c7e9d0 100644 --- a/unit/test-uuid.c +++ b/unit/test-uuid.c @@ -46,10 +46,10 @@ static unsigned char uuid_base_binary[] = { 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }; static struct uuid_test_data uuid_base = { - .str = "00000000-0000-1000-8000-00805F9B34FB", + .str = "00000000-0000-1000-8000-00805f9b34fb", .binary = uuid_base_binary, .type = BT_UUID128, - .str128 = "00000000-0000-1000-8000-00805F9B34FB", + .str128 = "00000000-0000-1000-8000-00805f9b34fb", .binary128 = uuid_base_binary, }; @@ -97,7 +97,6 @@ static void test_uuid(gconstpointer data) { const struct uuid_test_data *test_data = data; bt_uuid_t uuid; - uint128_t n128, u128; g_assert(bt_string_to_uuid(&uuid, test_data->str) == 0); g_assert(uuid.type == test_data->type); @@ -110,9 +109,11 @@ static void test_uuid(gconstpointer data) g_assert(uuid.value.u32 == test_data->val32); break; case BT_UUID128: - memcpy(&n128, test_data->binary, 16); - ntoh128(&n128, &u128); - g_assert(memcmp(&uuid.value.u128, &u128, 16) == 0); + /* + * No matter the system type: 128-bit UUID should use + * big-endian (human readable format). + */ + g_assert(memcmp(&uuid.value.u128, test_data->binary, 16) == 0); break; default: return; diff --git a/unit/util.c b/unit/util.c index c76acdf..71fe7ca 100644 --- a/unit/util.c +++ b/unit/util.c @@ -193,5 +193,6 @@ send: failed: g_main_loop_quit(d->mainloop); + d->io_completed = TRUE; return FALSE; } diff --git a/unit/util.h b/unit/util.h index 752ce61..96528a6 100644 --- a/unit/util.h +++ b/unit/util.h @@ -41,6 +41,7 @@ struct test_data { guint id; gsize total; GMainLoop *mainloop; + gboolean io_completed; }; #define TEST_ERROR test_error_quark() -- 2.7.4