diff options
author | DoHyun Pyun <dh79.pyun@samsung.com> | 2015-06-08 17:17:10 +0900 |
---|---|---|
committer | DoHyun Pyun <dh79.pyun@samsung.com> | 2015-06-08 20:46:56 +0900 |
commit | 82c1ec306908f5d6d4cd9ca328d3fbe7ebac92d3 (patch) | |
tree | 94cd4766383381127b03f8de9b66b4b3ecb19a4d /tools | |
parent | c270b3c99f32d1ce10b8552822b9353ab9a0484a (diff) | |
download | bluez-82c1ec306908f5d6d4cd9ca328d3fbe7ebac92d3.tar.gz bluez-82c1ec306908f5d6d4cd9ca328d3fbe7ebac92d3.tar.bz2 bluez-82c1ec306908f5d6d4cd9ca328d3fbe7ebac92d3.zip |
Upgrade Bluez 5.28 and synchronize Tizen 2.4's bluez
Change-Id: Ifaf2154ba0ab19b180946e0d92f1817a1d9ed839
Signed-off-by: DoHyun Pyun <dh79.pyun@samsung.com>
Diffstat (limited to 'tools')
59 files changed, 4075 insertions, 3882 deletions
diff --git a/tools/3dsp.c b/tools/3dsp.c index 68dcbb5a..686fe139 100644 --- a/tools/3dsp.c +++ b/tools/3dsp.c @@ -31,15 +31,37 @@ #include <string.h> #include <getopt.h> -#include "monitor/mainloop.h" #include "monitor/bt.h" +#include "src/shared/mainloop.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 +#define SERVICE_DATA 0x00 + +struct broadcast_message { + uint32_t frame_sync_instant; + uint16_t bluetooth_clock_phase; + uint16_t left_open_offset; + uint16_t left_close_offset; + uint16_t right_open_offset; + uint16_t right_close_offset; + uint16_t frame_sync_period; + uint8_t frame_sync_period_fraction; +} __attribute__ ((packed)); + +struct brcm_evt_sync_train_received { + uint8_t status; + uint8_t bdaddr[6]; + uint32_t offset; + uint8_t map[10]; + uint8_t service_data; + uint8_t lt_addr; + uint32_t instant; + uint16_t interval; +} __attribute__ ((packed)); static struct bt_hci *hci_dev; @@ -76,7 +98,34 @@ static void shutdown_device(void) mainloop_quit(); } -static void slave_broadcast_receive(const void *data, uint8_t size, +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_inquiry(void) +{ + struct bt_hci_cmd_inquiry cmd; + + 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 set_slave_broadcast_receive(const void *data, uint8_t size, void *user_data) { printf("Slave broadcast receiption enabled\n"); @@ -90,7 +139,12 @@ static void sync_train_received(const void *data, uint8_t size, if (evt->status) { printf("Failed to synchronize with 3D display\n"); - shutdown_device(); + start_inquiry(); + return; + } + + if (evt->lt_addr != LT_ADDR) { + printf("Ignoring synchronization for non 3D display\n"); return; } @@ -107,8 +161,42 @@ static void sync_train_received(const void *data, uint8_t size, 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); + &cmd, sizeof(cmd), + set_slave_broadcast_receive, NULL, NULL); +} + +static void brcm_sync_train_received(const void *data, uint8_t size, + void *user_data) +{ + const struct brcm_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"); + start_inquiry(); + return; + } + + if (evt->lt_addr != LT_ADDR) { + printf("Ignoring synchronization for non 3D display\n"); + 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), + set_slave_broadcast_receive, NULL, NULL); } static void truncated_page_complete(const void *data, uint8_t size, @@ -125,8 +213,22 @@ static void truncated_page_complete(const void *data, uint8_t size, 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 slave_broadcast_timeout(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_evt_slave_broadcast_timeout *evt = data; + struct bt_hci_cmd_receive_sync_train cmd; + + printf("Re-synchronizing with 3D display\n"); memcpy(cmd.bdaddr, evt->bdaddr, 6); cmd.timeout = cpu_to_le16(0x4000); @@ -137,6 +239,25 @@ static void truncated_page_complete(const void *data, uint8_t size, NULL, NULL, NULL); } +static void slave_broadcast_receive(const void *data, uint8_t size, + void *user_data) +{ + const struct bt_hci_evt_slave_broadcast_receive *evt = data; + struct bt_hci_cmd_read_clock cmd; + + if (evt->status != 0x00) + return; + + if (le32_to_cpu(evt->clock) != 0x00000000) + return; + + cmd.handle = cpu_to_le16(0x0000); + cmd.type = 0x00; + + bt_hci_send(hci_dev, BT_HCI_CMD_READ_CLOCK, &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; @@ -153,9 +274,6 @@ static void ext_inquiry_result(const void *data, uint8_t size, void *user_data) 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; @@ -169,25 +287,32 @@ static void inquiry_complete(const void *data, uint8_t size, void *user_data) { printf("No 3D display found\n"); - shutdown_device(); + start_inquiry(); } -static void inquiry_started(const void *data, uint8_t size, void *user_data) +static void read_local_version(const void *data, uint8_t size, void *user_data) { - uint8_t status = *((uint8_t *) data); + const struct bt_hci_rsp_read_local_version *rsp = data; - if (status) { - printf("Failed to search for 3D display\n"); + if (rsp->status) { + printf("Failed to read local version information\n"); shutdown_device(); return; } - printf("Searching for 3D display\n"); + if (rsp->manufacturer == 15) { + printf("Enabling receiver workaround for Broadcom\n"); + + bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED, + brcm_sync_train_received, NULL, NULL); + } else { + bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED, + sync_train_received, NULL, NULL); + } } 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; @@ -199,6 +324,9 @@ static void start_glasses(void) NULL, NULL, NULL); } + bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0, + read_local_version, 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, @@ -209,28 +337,14 @@ static void start_glasses(void) 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_register(hci_dev, BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE, + truncated_page_complete, NULL, NULL); + bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_BROADCAST_TIMEOUT, + slave_broadcast_timeout, NULL, NULL); + bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_BROADCAST_RECEIVE, + slave_broadcast_receive, NULL, NULL); - bt_hci_send(hci_dev, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd, sizeof(cmd), - NULL, NULL, NULL); + start_inquiry(); } static bool sync_train_active = false; @@ -241,8 +355,7 @@ static void sync_train_complete(const void *data, uint8_t size, sync_train_active = false; } -static void slave_page_response_timeout(const void *data, uint8_t size, - void *user_data) +static void start_sync_train(void) { struct bt_hci_cmd_write_sync_train_params cmd; @@ -259,15 +372,44 @@ static void slave_page_response_timeout(const void *data, uint8_t size, 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 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); + + start_sync_train(); +} + +static void slave_page_response_timeout(const void *data, uint8_t size, + void *user_data) +{ + printf("Incoming truncated page received\n"); + + start_sync_train(); +} + +static void slave_broadcast_channel_map_change(const void *data, uint8_t size, + void *user_data) +{ + printf("Broadcast channel map changed\n"); + + start_sync_train(); +} + static void inquiry_resp_tx_power(const void *data, uint8_t size, void *user_data) { @@ -292,10 +434,53 @@ static void inquiry_resp_tx_power(const void *data, uint8_t size, NULL, NULL, NULL); } +static void read_clock(const void *data, uint8_t size, void *user_data) +{ + const struct bt_hci_rsp_read_clock *rsp = data; + struct broadcast_message msg; + uint8_t bcastdata[sizeof(msg) + 3] = { LT_ADDR, 0x03, 0x11, }; + + if (rsp->status) { + printf("Failed to read local clock information\n"); + shutdown_device(); + return; + } + + msg.frame_sync_instant = rsp->clock; + msg.bluetooth_clock_phase = rsp->accuracy; + msg.left_open_offset = cpu_to_le16(50); + msg.left_close_offset = cpu_to_le16(300); + msg.right_open_offset = cpu_to_le16(350); + msg.right_close_offset = cpu_to_le16(600); + msg.frame_sync_period = cpu_to_le16(650); + msg.frame_sync_period_fraction = 0; + memcpy(bcastdata + 3, &msg, sizeof(msg)); + + bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA, + bcastdata, sizeof(bcastdata), NULL, NULL, NULL); +} + +static void set_slave_broadcast(const void *data, uint8_t size, void *user_data) +{ + const struct bt_hci_rsp_set_slave_broadcast *rsp = data; + struct bt_hci_cmd_read_clock cmd; + + if (rsp->status) { + printf("Failed to set slave broadcast transmission\n"); + shutdown_device(); + return; + } + + cmd.handle = cpu_to_le16(0x0000); + cmd.type = 0x00; + + bt_hci_send(hci_dev, BT_HCI_CMD_READ_CLOCK, &cmd, sizeof(cmd), + read_clock, 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; @@ -322,15 +507,14 @@ static void start_display(void) 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_SLAVE_BROADCAST_CHANNEL_MAP_CHANGE, + slave_broadcast_channel_map_change, 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; @@ -340,7 +524,7 @@ static void start_display(void) cmd.timeout = cpu_to_le16(0xfffe); bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST, &cmd, sizeof(cmd), - NULL, NULL, NULL); + set_slave_broadcast, NULL, NULL); } static void signal_callback(int signum, void *user_data) diff --git a/tools/amptest.c b/tools/amptest.c index bba0a9af..55747078 100644 --- a/tools/amptest.c +++ b/tools/amptest.c @@ -36,9 +36,9 @@ #include <sys/ioctl.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" static int activate_amp_controller(int dev_id) { diff --git a/tools/avinfo.c b/tools/avinfo.c index d9f809b6..3f406ca1 100644 --- a/tools/avinfo.c +++ b/tools/avinfo.c @@ -39,12 +39,12 @@ #include <sys/ioctl.h> #include <sys/socket.h> -#include "profiles/audio/a2dp-codecs.h" +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/l2cap.h" -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> +#include "profiles/audio/a2dp-codecs.h" #define AVDTP_PSM 25 diff --git a/tools/avtest.c b/tools/avtest.c index 541b3cdd..70c9b658 100644 --- a/tools/avtest.c +++ b/tools/avtest.c @@ -35,11 +35,11 @@ #include <sys/socket.h> #include <netinet/in.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> -#include <bluetooth/sdp.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/l2cap.h" +#include "lib/sdp.h" #define AVDTP_PKT_TYPE_SINGLE 0x00 #define AVDTP_PKT_TYPE_START 0x01 diff --git a/tools/bccmd.c b/tools/bccmd.c index 4649ad59..7cce4261 100644 --- a/tools/bccmd.c +++ b/tools/bccmd.c @@ -31,9 +31,9 @@ #include <getopt.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "csr.h" @@ -279,7 +279,6 @@ static int cmd_keylen(int transport, int argc, char *argv[]) if (err < 0) return -1; - handle = array[0] | (array[1] << 8); keylen = array[2] | (array[3] << 8); printf("Crypt key length: %d bit\n", keylen * 8); diff --git a/tools/bdaddr.c b/tools/bdaddr.c index 8356a8de..952e9907 100644 --- a/tools/bdaddr.c +++ b/tools/bdaddr.c @@ -33,9 +33,9 @@ #include <sys/ioctl.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "src/oui.h" diff --git a/tools/bluemoon.c b/tools/bluemoon.c index c785472a..650ab0ca 100644 --- a/tools/bluemoon.c +++ b/tools/bluemoon.c @@ -35,11 +35,20 @@ #include <sys/stat.h> #include <sys/param.h> -#include "monitor/mainloop.h" #include "monitor/bt.h" +#include "src/shared/mainloop.h" #include "src/shared/util.h" #include "src/shared/hci.h" +#define CMD_RESET 0xfc01 +struct cmd_reset { + uint8_t reset_type; + uint8_t patch_enable; + uint8_t otp_ddc_reload; + uint8_t boot_option; + uint32_t boot_addr; +} __attribute__ ((packed)); + #define CMD_NO_OPERATION 0xfc02 #define CMD_READ_VERSION 0xfc05 @@ -126,6 +135,11 @@ struct cmd_act_deact_traces { uint8_t rx_trace; } __attribute__ ((packed)); +#define CMD_TRIGGER_EXCEPTION 0xfc4d +struct cmd_trigger_exception { + uint8_t type; +} __attribute__ ((packed)); + #define CMD_MEMORY_WRITE 0xfc8e static struct bt_hci *hci_dev; @@ -146,7 +160,9 @@ static const char *check_firmware_value = NULL; uint8_t manufacturer_mode_reset = 0x00; static bool use_manufacturer_mode = false; static bool set_traces = false; +static bool set_exception = false; static bool reset_on_exit = false; +static bool cold_boot = false; static void reset_complete(const void *data, uint8_t size, void *user_data) { @@ -161,6 +177,25 @@ static void reset_complete(const void *data, uint8_t size, void *user_data) mainloop_quit(); } +static void cold_boot_complete(const void *data, uint8_t size, void *user_data) +{ + uint8_t status = *((uint8_t *) data); + + if (status) { + fprintf(stderr, "Failed to cold boot (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 leave_manufacturer_mode_complete(const void *data, uint8_t size, void *user_data) { @@ -286,6 +321,18 @@ static void act_deact_traces(void) act_deact_traces_complete, NULL, NULL); } +static void trigger_exception(void) +{ + struct cmd_trigger_exception cmd; + + cmd.type = 0x00; + + bt_hci_send(hci_dev, CMD_TRIGGER_EXCEPTION, &cmd, sizeof(cmd), + NULL, NULL, NULL); + + shutdown_device(); +} + static void write_bd_data_complete(const void *data, uint8_t size, void *user_data) { @@ -426,6 +473,11 @@ static void enter_manufacturer_mode_complete(const void *data, uint8_t size, return; } + if (set_exception) { + trigger_exception(); + return; + } + shutdown_device(); } @@ -601,6 +653,20 @@ static void read_version_complete(const void *data, uint8_t size, return; } + if (cold_boot) { + struct cmd_reset cmd; + + cmd.reset_type = 0x01; + cmd.patch_enable = 0x00; + cmd.otp_ddc_reload = 0x01; + cmd.boot_option = 0x00; + cmd.boot_addr = cpu_to_le32(0x00000000); + + bt_hci_send(hci_dev, CMD_RESET, &cmd, sizeof(cmd), + cold_boot_complete, NULL, NULL); + return; + } + if (load_firmware) { if (load_firmware_value) { printf("Firmware: %s\n", load_firmware_value); @@ -733,7 +799,6 @@ static void analyze_firmware(const char *path) if (len != st.st_size) { fprintf(stderr, "Failed to read complete firmware file\n"); goto done; - return; } if ((size_t) len < sizeof(*css)) { @@ -767,7 +832,7 @@ static void analyze_firmware(const char *path) printf("\n"); - if (len != le32_to_cpu(css->size) * 4) { + if ((size_t) len != le32_to_cpu(css->size) * 4) { fprintf(stderr, "CSS.size does not match file length\n"); goto done; } @@ -830,6 +895,8 @@ static void usage(void) "\t-F, --firmware [file] Load firmware\n" "\t-C, --check <file> Check firmware image\n" "\t-R, --reset Reset controller\n" + "\t-B, --coldboot Cold boot controller\n" + "\t-E, --exception Trigger exception\n" "\t-i, --index <num> Use specified controller\n" "\t-h, --help Show help options\n"); } @@ -841,7 +908,10 @@ static const struct option main_options[] = { { "check", required_argument, NULL, 'C' }, { "traces", no_argument, NULL, 'T' }, { "reset", no_argument, NULL, 'R' }, + { "coldboot", no_argument, NULL, 'B' }, + { "exception",no_argument, NULL, 'E' }, { "index", required_argument, NULL, 'i' }, + { "raw", no_argument, NULL, 'r' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } @@ -850,13 +920,14 @@ static const struct option main_options[] = { int main(int argc, char *argv[]) { const char *str; + bool use_raw = false; sigset_t mask; int exit_status; for (;;) { int opt; - opt = getopt_long(argc, argv, "A::DF::C:TRi:vh", + opt = getopt_long(argc, argv, "A::DF::C:TRBEi:rvh", main_options, NULL); if (opt < 0) break; @@ -881,6 +952,10 @@ int main(int argc, char *argv[]) check_firmware_value = optarg; check_firmware = true; break; + case 'E': + use_manufacturer_mode = true; + set_exception = true; + break; case 'T': use_manufacturer_mode = true; set_traces = true; @@ -888,6 +963,9 @@ int main(int argc, char *argv[]) case 'R': reset_on_exit = true; break; + case 'B': + cold_boot = true; + break; case 'i': if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3)) str = optarg + 3; @@ -899,6 +977,9 @@ int main(int argc, char *argv[]) } hci_index = atoi(str); break; + case 'r': + use_raw = true; + break; case 'v': printf("%s\n", VERSION); return EXIT_SUCCESS; @@ -930,10 +1011,18 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } - hci_dev = bt_hci_new_user_channel(hci_index); - if (!hci_dev) { - fprintf(stderr, "Failed to open HCI user channel\n"); - return EXIT_FAILURE; + if (use_raw) { + hci_dev = bt_hci_new_raw_device(hci_index); + if (!hci_dev) { + fprintf(stderr, "Failed to open HCI raw device\n"); + return EXIT_FAILURE; + } + } else { + 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, CMD_READ_VERSION, NULL, 0, diff --git a/tools/bluetooth-player.c b/tools/bluetooth-player.c index f090e1b1..9e199970 100644 --- a/tools/bluetooth-player.c +++ b/tools/bluetooth-player.c @@ -36,9 +36,9 @@ #include <readline/readline.h> #include <readline/history.h> #include <glib.h> -#include <gdbus.h> -#include <client/display.h> +#include "gdbus/gdbus.h" +#include "client/display.h" /* String display constants */ #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF @@ -654,7 +654,6 @@ static void cmd_show(int argc, char *argv[]) rl_printf("Player %s\n", g_dbus_proxy_get_path(proxy)); print_property(proxy, "Name"); - print_property(proxy, "Searchable"); print_property(proxy, "Repeat"); print_property(proxy, "Equalizer"); print_property(proxy, "Shuffle"); diff --git a/tools/btattach.c b/tools/btattach.c index bdbbe160..b7948a36 100644 --- a/tools/btattach.c +++ b/tools/btattach.c @@ -37,13 +37,13 @@ #include <sys/ioctl.h> #include <poll.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "hciattach.h" -#include "monitor/mainloop.h" #include "monitor/bt.h" +#include "src/shared/mainloop.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "src/shared/hci.h" diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index e2e05374..c90f2657 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -33,13 +33,13 @@ #include <limits.h> #include <errno.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/l2cap.h" #include "lib/uuid.h" -#include "monitor/mainloop.h" +#include "src/shared/mainloop.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" @@ -64,6 +64,7 @@ static bool verbose = false; struct client { int fd; + struct bt_att *att; struct gatt_db *db; struct bt_gatt_client *gatt; }; @@ -74,6 +75,48 @@ static void print_prompt(void) fflush(stdout); } +static const char *ecode_to_string(uint8_t ecode) +{ + switch (ecode) { + case BT_ATT_ERROR_INVALID_HANDLE: + return "Invalid Handle"; + case BT_ATT_ERROR_READ_NOT_PERMITTED: + return "Read Not Permitted"; + case BT_ATT_ERROR_WRITE_NOT_PERMITTED: + return "Write Not Permitted"; + case BT_ATT_ERROR_INVALID_PDU: + return "Invalid PDU"; + case BT_ATT_ERROR_AUTHENTICATION: + return "Authentication Required"; + case BT_ATT_ERROR_REQUEST_NOT_SUPPORTED: + return "Request Not Supported"; + case BT_ATT_ERROR_INVALID_OFFSET: + return "Invalid Offset"; + case BT_ATT_ERROR_AUTHORIZATION: + return "Authorization Required"; + case BT_ATT_ERROR_PREPARE_QUEUE_FULL: + return "Prepare Write Queue Full"; + case BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND: + return "Attribute Not Found"; + case BT_ATT_ERROR_ATTRIBUTE_NOT_LONG: + return "Attribute Not Long"; + case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE: + return "Insuficient Encryption Key Size"; + case BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN: + return "Invalid Attribute value len"; + case BT_ATT_ERROR_UNLIKELY: + return "Unlikely Error"; + case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION: + return "Insufficient Encryption"; + case BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE: + return "Group type Not Supported"; + case BT_ATT_ERROR_INSUFFICIENT_RESOURCES: + return "Insufficient Resources"; + default: + return "Unknown error type"; + } +} + static void att_disconnect_cb(int err, void *user_data) { printf("Device disconnected: %s\n", strerror(err)); @@ -127,7 +170,6 @@ static void service_removed_cb(struct gatt_db_attribute *attr, void *user_data) static struct client *client_create(int fd, uint16_t mtu) { struct client *cli; - struct bt_att *att; cli = new0(struct client, 1); if (!cli) { @@ -135,24 +177,25 @@ static struct client *client_create(int fd, uint16_t mtu) return NULL; } - att = bt_att_new(fd); - if (!att) { + cli->att = bt_att_new(fd); + if (!cli->att) { fprintf(stderr, "Failed to initialze ATT transport layer\n"); - bt_att_unref(att); + bt_att_unref(cli->att); free(cli); return NULL; } - if (!bt_att_set_close_on_unref(att, true)) { + if (!bt_att_set_close_on_unref(cli->att, true)) { fprintf(stderr, "Failed to set up ATT transport layer\n"); - bt_att_unref(att); + bt_att_unref(cli->att); free(cli); return NULL; } - if (!bt_att_register_disconnect(att, att_disconnect_cb, NULL, NULL)) { + if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL, + NULL)) { fprintf(stderr, "Failed to set ATT disconnect handler\n"); - bt_att_unref(att); + bt_att_unref(cli->att); free(cli); return NULL; } @@ -161,16 +204,16 @@ static struct client *client_create(int fd, uint16_t mtu) cli->db = gatt_db_new(); if (!cli->db) { fprintf(stderr, "Failed to create GATT database\n"); - bt_att_unref(att); + bt_att_unref(cli->att); free(cli); return NULL; } - cli->gatt = bt_gatt_client_new(cli->db, att, mtu); + cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu); if (!cli->gatt) { fprintf(stderr, "Failed to create GATT client\n"); gatt_db_unref(cli->db); - bt_att_unref(att); + bt_att_unref(cli->att); free(cli); return NULL; } @@ -179,7 +222,7 @@ static struct client *client_create(int fd, uint16_t mtu) NULL, NULL); if (verbose) { - bt_att_set_debug(att, att_debug_cb, "att: ", NULL); + bt_att_set_debug(cli->att, att_debug_cb, "att: ", NULL); bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ", NULL); } @@ -189,7 +232,6 @@ static struct client *client_create(int fd, uint16_t mtu) NULL); /* bt_gatt_client already holds a reference */ - bt_att_unref(att); gatt_db_unref(cli->db); return cli; @@ -198,6 +240,8 @@ static struct client *client_create(int fd, uint16_t mtu) static void client_destroy(struct client *cli) { bt_gatt_client_unref(cli->gatt); + bt_att_unref(cli->att); + free(cli); } static void print_uuid(const bt_uuid_t *uuid) @@ -457,6 +501,10 @@ static void cmd_read_multiple(struct client *cli, char *cmd_str) } value = malloc(sizeof(uint16_t) * argc); + if (!value) { + printf("Failed to construct value\n"); + return; + } for (i = 0; i < argc; i++) { value[i] = strtol(argv[i], &endptr, 0); @@ -485,7 +533,8 @@ static void read_cb(bool success, uint8_t att_ecode, const uint8_t *value, int i; if (!success) { - PRLOG("\nRead request failed: 0x%02x\n", att_ecode); + PRLOG("\nRead request failed: %s (0x%02x)\n", + ecode_to_string(att_ecode), att_ecode); return; } @@ -578,12 +627,14 @@ static void write_value_usage(void) printf("Usage: write-value [options] <value_handle> <value>\n" "Options:\n" "\t-w, --without-response\tWrite without response\n" + "\t-s, --signed-write\tSigned write command\n" "e.g.:\n" "\twrite-value 0x0001 00 01 00\n"); } static struct option write_value_options[] = { { "without-response", 0, 0, 'w' }, + { "signed-write", 0, 0, 's' }, { } }; @@ -592,7 +643,8 @@ static void write_cb(bool success, uint8_t att_ecode, void *user_data) if (success) { PRLOG("\nWrite successful\n"); } else { - PRLOG("\nWrite failed: 0x%02x\n", att_ecode); + PRLOG("\nWrite failed: %s (0x%02x)\n", + ecode_to_string(att_ecode), att_ecode); } } @@ -607,6 +659,7 @@ static void cmd_write_value(struct client *cli, char *cmd_str) int length; uint8_t *value = NULL; bool without_response = false; + bool signed_write = false; if (!bt_gatt_client_is_ready(cli->gatt)) { printf("GATT client not initialized\n"); @@ -621,12 +674,15 @@ static void cmd_write_value(struct client *cli, char *cmd_str) optind = 0; argv[0] = "write-value"; - while ((opt = getopt_long(argc, argv, "+w", write_value_options, + while ((opt = getopt_long(argc, argv, "+ws", write_value_options, NULL)) != -1) { switch (opt) { case 'w': without_response = true; break; + case 's': + signed_write = true; + break; default: write_value_usage(); return; @@ -680,7 +736,7 @@ static void cmd_write_value(struct client *cli, char *cmd_str) if (without_response) { if (!bt_gatt_client_write_without_response(cli->gatt, handle, - false, value, length)) { + signed_write, value, length)) { printf("Failed to initiate write without response " "procedure\n"); goto done; @@ -722,7 +778,8 @@ static void write_long_cb(bool success, bool reliable_error, uint8_t att_ecode, } else if (reliable_error) { PRLOG("Reliable write not verified\n"); } else { - PRLOG("Write failed: 0x%02x\n", att_ecode); + PRLOG("\nWrite failed: %s (0x%02x)\n", + ecode_to_string(att_ecode), att_ecode); } } @@ -852,16 +909,15 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value, PRLOG("\n"); } -static void register_notify_cb(unsigned int id, uint16_t att_ecode, - void *user_data) +static void register_notify_cb(uint16_t att_ecode, void *user_data) { - if (!id) { + if (att_ecode) { PRLOG("Failed to register notify handler " "- error code: 0x%02x\n", att_ecode); return; } - PRLOG("Registered notify handler with id: %u\n", id); + PRLOG("Registered notify handler!"); } static void cmd_register_notify(struct client *cli, char *cmd_str) @@ -869,6 +925,7 @@ static void cmd_register_notify(struct client *cli, char *cmd_str) char *argv[2]; int argc = 0; uint16_t value_handle; + unsigned int id; char *endptr = NULL; if (!bt_gatt_client_is_ready(cli->gatt)) { @@ -887,12 +944,15 @@ static void cmd_register_notify(struct client *cli, char *cmd_str) return; } - if (!bt_gatt_client_register_notify(cli->gatt, value_handle, + id = bt_gatt_client_register_notify(cli->gatt, value_handle, register_notify_cb, - notify_cb, NULL, NULL)) + notify_cb, NULL, NULL); + if (!id) { printf("Failed to register notify handler\n"); + return; + } - printf("\n"); + PRLOG("Registering notify handler with id: %u\n", id); } static void unregister_notify_usage(void) @@ -931,6 +991,125 @@ static void cmd_unregister_notify(struct client *cli, char *cmd_str) printf("Unregistered notify handler with id: %u\n", id); } +static void set_sec_level_usage(void) +{ + printf("Usage: set_sec_level <level>\n" + "level: 1-3\n" + "e.g.:\n" + "\tset-sec-level 2\n"); +} + +static void cmd_set_sec_level(struct client *cli, char *cmd_str) +{ + char *argvbuf[1]; + char **argv = argvbuf; + int argc = 0; + char *endptr = NULL; + int level; + + if (!bt_gatt_client_is_ready(cli->gatt)) { + printf("GATT client not initialized\n"); + return; + } + + if (!parse_args(cmd_str, 1, argv, &argc)) { + printf("Too many arguments\n"); + set_sec_level_usage(); + return; + } + + if (argc < 1) { + set_sec_level_usage(); + return; + } + + level = strtol(argv[0], &endptr, 0); + if (!endptr || *endptr != '\0' || level < 1 || level > 3) { + printf("Invalid level: %s\n", argv[0]); + return; + } + + if (!bt_gatt_client_set_sec_level(cli->gatt, level)) + printf("Could not set sec level\n"); + else + printf("Setting security level %d success\n", level); +} + +static void cmd_get_sec_level(struct client *cli, char *cmd_str) +{ + int level; + + if (!bt_gatt_client_is_ready(cli->gatt)) { + printf("GATT client not initialized\n"); + return; + } + + level = bt_gatt_client_get_sec_level(cli->gatt); + if (level < 0) + printf("Could not set sec level\n"); + else + printf("Security level: %u\n", level); +} + +static bool convert_sign_key(char *optarg, uint8_t key[16]) +{ + int i; + + if (strlen(optarg) != 32) { + printf("sign-key length is invalid\n"); + return false; + } + + for (i = 0; i < 16; i++) { + if (sscanf(optarg + (i * 2), "%2hhx", &key[i]) != 1) + return false; + } + + return true; +} + +static void set_sign_key_usage(void) +{ + printf("Usage: set-sign-key [options]\nOptions:\n" + "\t -c, --sign-key <csrk>\tCSRK\n" + "e.g.:\n" + "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n"); +} + +static bool local_counter(uint32_t *sign_cnt, void *user_data) +{ + static uint32_t cnt = 0; + + *sign_cnt = cnt++; + + return true; +} + +static void cmd_set_sign_key(struct client *cli, char *cmd_str) +{ + char *argv[3]; + int argc = 0; + uint8_t key[16]; + + memset(key, 0, 16); + + if (!parse_args(cmd_str, 2, argv, &argc)) { + set_sign_key_usage(); + return; + } + + if (argc != 2) { + set_sign_key_usage(); + return; + } + + if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) { + if (convert_sign_key(argv[1], key)) + bt_att_set_local_key(cli->att, key, local_counter, cli); + } else + set_sign_key_usage(); +} + static void cmd_help(struct client *cli, char *cmd_str); typedef void (*command_func_t)(struct client *cli, char *cmd_str); @@ -955,6 +1134,12 @@ static struct { "\tSubscribe to not/ind from a characteristic" }, { "unregister-notify", cmd_unregister_notify, "Unregister a not/ind session"}, + { "set-sec-level", cmd_set_sec_level, + "Set security level on le connection"}, + { "get-sec-level", cmd_get_sec_level, + "Get security level on le connection"}, + { "set-sign-key", cmd_set_sign_key, + "\tSet signing key for signed write command"}, { } }; diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c index f6ad8c3a..b4fbe607 100644 --- a/tools/btgatt-server.c +++ b/tools/btgatt-server.c @@ -29,13 +29,13 @@ #include <unistd.h> #include <errno.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/l2cap.h" #include "lib/uuid.h" -#include "monitor/mainloop.h" +#include "src/shared/mainloop.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" @@ -77,6 +77,7 @@ static bool verbose = false; struct server { int fd; + struct bt_att *att; struct gatt_db *db; struct bt_gatt_server *gatt; @@ -125,7 +126,7 @@ static void gatt_debug_cb(const char *str, void *user_data) static void gap_device_name_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { struct server *server = user_data; @@ -152,7 +153,7 @@ done: static void gap_device_name_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { struct server *server = user_data; @@ -196,7 +197,7 @@ done: static void gap_device_name_ext_prop_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { uint8_t value[2]; @@ -211,7 +212,7 @@ static void gap_device_name_ext_prop_read_cb(struct gatt_db_attribute *attrib, static void gatt_service_changed_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { PRLOG("Service Changed Read called\n"); @@ -221,7 +222,7 @@ static void gatt_service_changed_cb(struct gatt_db_attribute *attrib, static void gatt_svc_chngd_ccc_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { struct server *server = user_data; @@ -238,7 +239,7 @@ static void gatt_svc_chngd_ccc_read_cb(struct gatt_db_attribute *attrib, static void gatt_svc_chngd_ccc_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { struct server *server = user_data; @@ -272,7 +273,7 @@ done: static void hr_msrmt_ccc_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { struct server *server = user_data; @@ -326,7 +327,7 @@ static void update_hr_msrmt_simulation(struct server *server) static void hr_msrmt_ccc_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { struct server *server = user_data; @@ -366,7 +367,7 @@ done: static void hr_control_point_write_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, const uint8_t *value, size_t len, - uint8_t opcode, bdaddr_t *bdaddr, + uint8_t opcode, struct bt_att *att, void *user_data) { struct server *server = user_data; @@ -539,7 +540,6 @@ static void populate_db(struct server *server) static struct server *server_create(int fd, uint16_t mtu, bool hr_visible) { struct server *server; - struct bt_att *att; size_t name_len = strlen(test_device_name); server = new0(struct server, 1); @@ -548,18 +548,19 @@ static struct server *server_create(int fd, uint16_t mtu, bool hr_visible) return NULL; } - att = bt_att_new(fd); - if (!att) { + server->att = bt_att_new(fd); + if (!server->att) { fprintf(stderr, "Failed to initialze ATT transport layer\n"); goto fail; } - if (!bt_att_set_close_on_unref(att, true)) { + if (!bt_att_set_close_on_unref(server->att, true)) { fprintf(stderr, "Failed to set up ATT transport layer\n"); goto fail; } - if (!bt_att_register_disconnect(att, att_disconnect_cb, NULL, NULL)) { + if (!bt_att_register_disconnect(server->att, att_disconnect_cb, NULL, + NULL)) { fprintf(stderr, "Failed to set ATT disconnect handler\n"); goto fail; } @@ -581,7 +582,7 @@ static struct server *server_create(int fd, uint16_t mtu, bool hr_visible) goto fail; } - server->gatt = bt_gatt_server_new(server->db, att, mtu); + server->gatt = bt_gatt_server_new(server->db, server->att, mtu); if (!server->gatt) { fprintf(stderr, "Failed to create GATT server\n"); goto fail; @@ -590,7 +591,7 @@ static struct server *server_create(int fd, uint16_t mtu, bool hr_visible) server->hr_visible = hr_visible; if (verbose) { - bt_att_set_debug(att, att_debug_cb, "att: ", NULL); + bt_att_set_debug(server->att, att_debug_cb, "att: ", NULL); bt_gatt_server_set_debug(server->gatt, gatt_debug_cb, "server: ", NULL); } @@ -599,7 +600,6 @@ static struct server *server_create(int fd, uint16_t mtu, bool hr_visible) srand(time(NULL)); /* bt_gatt_server already holds a reference */ - bt_att_unref(att); populate_db(server); return server; @@ -607,7 +607,7 @@ static struct server *server_create(int fd, uint16_t mtu, bool hr_visible) fail: gatt_db_unref(server->db); free(server->device_name); - bt_att_unref(att); + bt_att_unref(server->att); free(server); return NULL; @@ -631,7 +631,7 @@ static void usage(void) "\t-s, --security-level <sec>\tSet security level (low|" "medium|high)\n" "\t-v, --verbose\t\t\tEnable extra logging\n" - "\t-r, --heart-rate\t\tEnable Heart Rate service" + "\t-r, --heart-rate\t\tEnable Heart Rate service\n" "\t-h, --help\t\t\tDisplay help\n"); } @@ -972,6 +972,69 @@ static void cmd_services(struct server *server, char *cmd_str) gatt_db_foreach_service(server->db, NULL, print_service, server); } +static bool convert_sign_key(char *optarg, uint8_t key[16]) +{ + int i; + + if (strlen(optarg) != 32) { + printf("sign-key length is invalid\n"); + return false; + } + + for (i = 0; i < 16; i++) { + if (sscanf(optarg + (i * 2), "%2hhx", &key[i]) != 1) + return false; + } + + return true; +} + +static void set_sign_key_usage(void) +{ + printf("Usage: set-sign-key [options]\nOptions:\n" + "\t -c, --sign-key <remote csrk>\tRemote CSRK\n" + "e.g.:\n" + "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n"); +} + +static bool remote_counter(uint32_t *sign_cnt, void *user_data) +{ + static uint32_t cnt = 0; + + if (*sign_cnt < cnt) + return false; + + cnt = *sign_cnt; + + return true; +} + +static void cmd_set_sign_key(struct server *server, char *cmd_str) +{ + char *argv[3]; + int argc = 0; + uint8_t key[16]; + + memset(key, 0, 16); + + if (!parse_args(cmd_str, 2, argv, &argc)) { + set_sign_key_usage(); + return; + } + + if (argc != 2) { + set_sign_key_usage(); + return; + } + + if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) { + if (convert_sign_key(argv[1], key)) + bt_att_set_remote_key(server->att, key, remote_counter, + server); + } else + set_sign_key_usage(); +} + static void cmd_help(struct server *server, char *cmd_str); typedef void (*command_func_t)(struct server *server, char *cmd_str); @@ -985,6 +1048,8 @@ static struct { { "notify", cmd_notify, "\tSend handle-value notification" }, { "heart-rate", cmd_heart_rate, "\tHide/Unhide Heart Rate Service" }, { "services", cmd_services, "\tEnumerate all services" }, + { "set-sign-key", cmd_set_sign_key, + "\tSet remote signing key for signed write command"}, { } }; diff --git a/tools/btinfo.c b/tools/btinfo.c index b838c251..fd11ac42 100644 --- a/tools/btinfo.c +++ b/tools/btinfo.c @@ -35,8 +35,8 @@ #include <sys/ioctl.h> #include <sys/socket.h> -#include "monitor/mainloop.h" #include "monitor/bt.h" +#include "src/shared/mainloop.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "src/shared/hci.h" diff --git a/tools/btiotest.c b/tools/btiotest.c index 6a87ffd2..10e78d57 100644 --- a/tools/btiotest.c +++ b/tools/btiotest.c @@ -35,7 +35,7 @@ #include <glib.h> -#include <bluetooth/bluetooth.h> +#include "lib/bluetooth.h" #include "btio/btio.h" diff --git a/tools/btmgmt.c b/tools/btmgmt.c index eac7204e..8eff56b8 100644 --- a/tools/btmgmt.c +++ b/tools/btmgmt.c @@ -24,6 +24,7 @@ #endif #include <stdio.h> +#include <stdarg.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> @@ -36,32 +37,99 @@ #include <poll.h> #include <getopt.h> #include <stdbool.h> +#include <wordexp.h> +#include <ctype.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> +#include <readline/readline.h> +#include <readline/history.h> + +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" #include "src/uuid-helper.h" #include "lib/mgmt.h" -#include "monitor/mainloop.h" +#include "client/display.h" +#include "src/shared/mainloop.h" +#include "src/shared/io.h" #include "src/shared/util.h" #include "src/shared/mgmt.h" -#include "src/shared/gap.h" -static bool monitor = false; +static struct mgmt *mgmt = NULL; +static uint16_t mgmt_index = MGMT_INDEX_NONE; + static bool discovery = false; static bool resolve_names = true; +static bool interactive = false; + +static char *saved_prompt = NULL; +static int saved_point = 0; -static int pending = 0; +static struct { + uint16_t index; + uint16_t req; + struct mgmt_addr_info addr; +} prompt = { + .index = MGMT_INDEX_NONE, +}; + +static int pending_index = 0; #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif -static size_t convert_hexstr(const char *hexstr, uint8_t *buf, size_t buflen) +#define PROMPT_ON COLOR_BLUE "[mgmt]" COLOR_OFF "# " + +static void update_prompt(uint16_t index) +{ + char str[32]; + + if (index == MGMT_INDEX_NONE) + snprintf(str, sizeof(str), "%s# ", + COLOR_BLUE "[mgmt]" COLOR_OFF); + else + snprintf(str, sizeof(str), + COLOR_BLUE "[hci%u]" COLOR_OFF "# ", index); + + if (saved_prompt) { + free(saved_prompt); + saved_prompt = strdup(str); + return; + } + + rl_set_prompt(str); +} + +static void noninteractive_quit(int status) +{ + if (interactive) + return; + + if (status == EXIT_SUCCESS) + mainloop_exit_success(); + else + mainloop_exit_failure(); +} + +#define print(fmt, arg...) do { \ + if (interactive) \ + rl_printf(fmt "\n", ## arg); \ + else \ + printf(fmt "\n", ## arg); \ +} while (0) + +#define error(fmt, arg...) do { \ + if (interactive) \ + rl_printf(COLOR_RED fmt "\n" COLOR_OFF, ## arg); \ + else \ + fprintf(stderr, fmt "\n", ## arg); \ +} while (0) + +static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen) { size_t i, len; @@ -74,6 +142,17 @@ static size_t convert_hexstr(const char *hexstr, uint8_t *buf, size_t buflen) return len; } +static size_t bin2hex(const uint8_t *buf, size_t buflen, char *str, + size_t strlen) +{ + size_t i; + + for (i = 0; i < buflen && i < (strlen / 2); i++) + sprintf(str + (i * 2), "%02x", buf[i]); + + return i; +} + static bool load_identity(uint16_t index, struct mgmt_irk_info *irk) { char identity_path[PATH_MAX]; @@ -87,7 +166,7 @@ static bool load_identity(uint16_t index, struct mgmt_irk_info *irk) fp = fopen(identity_path, "r"); if (!fp) { - perror("Failed to open identity file"); + error("Failed to open identity file: %s", strerror(errno)); return false; } @@ -99,7 +178,7 @@ static bool load_identity(uint16_t index, struct mgmt_irk_info *irk) return false; str2ba(addr, &irk->addr.bdaddr); - convert_hexstr(key, irk->val, sizeof(irk->val)); + hex2bin(key, irk->val, sizeof(irk->val)); free(addr); free(key); @@ -112,7 +191,7 @@ static bool load_identity(uint16_t index, struct mgmt_irk_info *irk) irk->addr.type = BDADDR_LE_RANDOM; break; default: - fprintf(stderr, "Invalid address type %u\n", type); + error("Invalid address type %u", type); return false; } @@ -125,41 +204,35 @@ static void controller_error(uint16_t index, uint16_t len, const struct mgmt_ev_controller_error *ev = param; if (len < sizeof(*ev)) { - fprintf(stderr, - "Too short (%u bytes) controller error event\n", len); + error("Too short (%u bytes) controller error event", len); return; } - if (monitor) - printf("hci%u error 0x%02x\n", index, ev->error_code); + print("hci%u error 0x%02x", index, ev->error_code); } static void index_added(uint16_t index, uint16_t len, const void *param, void *user_data) { - if (monitor) - printf("hci%u added\n", index); + print("hci%u added", index); } static void index_removed(uint16_t index, uint16_t len, const void *param, void *user_data) { - if (monitor) - printf("hci%u removed\n", index); + print("hci%u removed", index); } static void unconf_index_added(uint16_t index, uint16_t len, const void *param, void *user_data) { - if (monitor) - printf("hci%u added (unconfigured)\n", index); + print("hci%u added (unconfigured)", index); } static void unconf_index_removed(uint16_t index, uint16_t len, const void *param, void *user_data) { - if (monitor) - printf("hci%u removed (unconfigured)\n", index); + print("hci%u removed (unconfigured)", index); } static const char *options_str[] = { @@ -167,14 +240,22 @@ static const char *options_str[] = { "public-address", }; -static void print_options(uint32_t options) +static const char *options2str(uint32_t options) { + static char str[256]; unsigned i; + int off; + + off = 0; + str[0] = '\0'; for (i = 0; i < NELEM(options_str); i++) { if ((options & (1 << i)) != 0) - printf("%s ", options_str[i]); + off += snprintf(str + off, sizeof(str) - off, "%s ", + options_str[i]); } + + return str; } static void new_config_options(uint16_t index, uint16_t len, @@ -183,15 +264,11 @@ static void new_config_options(uint16_t index, uint16_t len, const uint32_t *ev = param; if (len < sizeof(*ev)) { - fprintf(stderr, "Too short new_config_options event (%u)\n", len); + error("Too short new_config_options event (%u)", len); return; } - if (monitor) { - printf("hci%u new_config_options: ", index); - print_options(get_le32(ev)); - printf("\n"); - } + print("hci%u new_config_options: %s", index, options2str(get_le32(ev))); } static const char *settings_str[] = { @@ -210,16 +287,25 @@ static const char *settings_str[] = { "debug-keys", "privacy", "configuration", + "static-addr", }; -static void print_settings(uint32_t settings) +static const char *settings2str(uint32_t settings) { + static char str[256]; unsigned i; + int off; + + off = 0; + str[0] = '\0'; for (i = 0; i < NELEM(settings_str); i++) { if ((settings & (1 << i)) != 0) - printf("%s ", settings_str[i]); + off += snprintf(str + off, sizeof(str) - off, "%s ", + settings_str[i]); } + + return str; } static void new_settings(uint16_t index, uint16_t len, @@ -228,15 +314,11 @@ static void new_settings(uint16_t index, uint16_t len, const uint32_t *ev = param; if (len < sizeof(*ev)) { - fprintf(stderr, "Too short new_settings event (%u)\n", len); + error("Too short new_settings event (%u)", len); return; } - if (monitor) { - printf("hci%u new_settings: ", index); - print_settings(get_le32(ev)); - printf("\n"); - } + print("hci%u new_settings: %s", index, settings2str(get_le32(ev))); } static void discovering(uint16_t index, uint16_t len, const void *param, @@ -245,44 +327,36 @@ static void discovering(uint16_t index, uint16_t len, const void *param, const struct mgmt_ev_discovering *ev = param; if (len < sizeof(*ev)) { - fprintf(stderr, "Too short (%u bytes) discovering event\n", - len); + error("Too short (%u bytes) discovering event", len); return; } - if (ev->discovering == 0 && discovery) { - mainloop_quit(); - return; - } + print("hci%u type %u discovering %s", index, ev->type, + ev->discovering ? "on" : "off"); - if (monitor) - printf("hci%u type %u discovering %s\n", index, - ev->type, ev->discovering ? "on" : "off"); + if (ev->discovering == 0 && discovery) + return noninteractive_quit(EXIT_SUCCESS); } static void new_link_key(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_new_link_key *ev = param; + char addr[18]; if (len != sizeof(*ev)) { - fprintf(stderr, "Invalid new_link_key length (%u bytes)\n", - len); + error("Invalid new_link_key length (%u bytes)", len); return; } - if (monitor) { - char addr[18]; - ba2str(&ev->key.addr.bdaddr, addr); - printf("hci%u new_link_key %s type 0x%02x pin_len %d " - "store_hint %u\n", index, addr, ev->key.type, - ev->key.pin_len, ev->store_hint); - } + ba2str(&ev->key.addr.bdaddr, addr); + print("hci%u new_link_key %s type 0x%02x pin_len %d store_hint %u", + index, addr, ev->key.type, ev->key.pin_len, ev->store_hint); } static const char *typestr(uint8_t type) { - const char *str[] = { "BR/EDR", "LE Public", "LE Random" }; + static const char *str[] = { "BR/EDR", "LE Public", "LE Random" }; if (type <= BDADDR_LE_RANDOM) return str[type]; @@ -295,91 +369,107 @@ static void connected(uint16_t index, uint16_t len, const void *param, { const struct mgmt_ev_device_connected *ev = param; uint16_t eir_len; + char addr[18]; if (len < sizeof(*ev)) { - fprintf(stderr, - "Invalid connected event length (%u bytes)\n", len); + error("Invalid connected event length (%u bytes)", len); return; } 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); + error("Invalid connected event length (%u != eir_len %u)", + len, eir_len); return; } - if (monitor) { - char addr[18]; - ba2str(&ev->addr.bdaddr, addr); - printf("hci%u %s type %s connected eir_len %u\n", index, addr, + ba2str(&ev->addr.bdaddr, addr); + print("hci%u %s type %s connected eir_len %u", index, addr, typestr(ev->addr.type), eir_len); - } +} + +static void release_prompt(void) +{ + if (!interactive) + return; + + memset(&prompt, 0, sizeof(prompt)); + prompt.index = MGMT_INDEX_NONE; + + if (!saved_prompt) + return; + + /* This will cause rl_expand_prompt to re-run over the last prompt, + * but our prompt doesn't expand anyway. + */ + rl_set_prompt(saved_prompt); + rl_replace_line("", 0); + rl_point = saved_point; + rl_redisplay(); + + free(saved_prompt); + saved_prompt = NULL; } static void disconnected(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_device_disconnected *ev = param; + char addr[18]; + uint8_t reason; if (len < sizeof(struct mgmt_addr_info)) { - fprintf(stderr, - "Invalid disconnected event length (%u bytes)\n", len); + error("Invalid disconnected event length (%u bytes)", len); return; } - if (monitor) { - char addr[18]; - uint8_t reason; + if (!memcmp(&ev->addr, &prompt.addr, sizeof(ev->addr))) + release_prompt(); - if (len < sizeof(*ev)) - reason = MGMT_DEV_DISCONN_UNKNOWN; - else - reason = ev->reason; + if (len < sizeof(*ev)) + reason = MGMT_DEV_DISCONN_UNKNOWN; + else + reason = ev->reason; - ba2str(&ev->addr.bdaddr, addr); - printf("hci%u %s type %s disconnected with reason %u\n", - index, addr, typestr(ev->addr.type), reason); - } + ba2str(&ev->addr.bdaddr, addr); + print("hci%u %s type %s disconnected with reason %u", + index, addr, typestr(ev->addr.type), reason); } static void conn_failed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_connect_failed *ev = param; + char addr[18]; if (len != sizeof(*ev)) { - fprintf(stderr, - "Invalid connect_failed event length (%u bytes)\n", len); + error("Invalid connect_failed event length (%u bytes)", len); return; } - if (monitor) { - char addr[18]; - ba2str(&ev->addr.bdaddr, addr); - printf("hci%u %s type %s connect failed (status 0x%02x, %s)\n", - index, addr, typestr(ev->addr.type), ev->status, - mgmt_errstr(ev->status)); - } + ba2str(&ev->addr.bdaddr, addr); + print("hci%u %s type %s connect failed (status 0x%02x, %s)", + index, addr, typestr(ev->addr.type), ev->status, + mgmt_errstr(ev->status)); } static void auth_failed(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_auth_failed *ev = param; + char addr[18]; if (len != sizeof(*ev)) { - fprintf(stderr, - "Invalid auth_failed event length (%u bytes)\n", len); + error("Invalid auth_failed event length (%u bytes)", len); return; } - if (monitor) { - char addr[18]; - ba2str(&ev->addr.bdaddr, addr); - printf("hci%u %s auth failed with status 0x%02x (%s)\n", + if (!memcmp(&ev->addr, &prompt.addr, sizeof(ev->addr))) + release_prompt(); + + ba2str(&ev->addr.bdaddr, addr); + print("hci%u %s auth failed with status 0x%02x (%s)", index, addr, ev->status, mgmt_errstr(ev->status)); - } } static void local_name_changed(uint16_t index, uint16_t len, const void *param, @@ -388,13 +478,11 @@ static void local_name_changed(uint16_t index, uint16_t len, const void *param, const struct mgmt_ev_local_name_changed *ev = param; if (len != sizeof(*ev)) { - fprintf(stderr, - "Invalid local_name_changed length (%u bytes)\n", len); + error("Invalid local_name_changed length (%u bytes)", len); return; } - if (monitor) - printf("hci%u name changed: %s\n", index, ev->name); + print("hci%u name changed: %s", index, ev->name); } static void confirm_name_rsp(uint8_t status, uint16_t len, @@ -404,25 +492,24 @@ static void confirm_name_rsp(uint8_t status, uint16_t len, char addr[18]; if (len == 0 && status != 0) { - fprintf(stderr, - "confirm_name failed with status 0x%02x (%s)\n", - status, mgmt_errstr(status)); + error("confirm_name failed with status 0x%02x (%s)", status, + mgmt_errstr(status)); return; } if (len != sizeof(*rp)) { - fprintf(stderr, "confirm_name rsp length %u instead of %zu\n", - len, sizeof(*rp)); + error("confirm_name rsp length %u instead of %zu", + len, sizeof(*rp)); return; } ba2str(&rp->addr.bdaddr, addr); if (status != 0) - fprintf(stderr, "confirm_name for %s failed: 0x%02x (%s)\n", + error("confirm_name for %s failed: 0x%02x (%s)", addr, status, mgmt_errstr(status)); else - printf("confirm_name succeeded for %s\n", addr); + print("confirm_name succeeded for %s", addr); } static char *eir_get_name(const uint8_t *eir, uint16_t eir_len) @@ -490,8 +577,7 @@ static void device_found(uint16_t index, uint16_t len, const void *param, uint32_t flags; if (len < sizeof(*ev)) { - fprintf(stderr, - "Too short device_found length (%u bytes)\n", len); + error("Too short device_found length (%u bytes)", len); return; } @@ -499,28 +585,28 @@ static void device_found(uint16_t index, uint16_t len, const void *param, eir_len = get_le16(&ev->eir_len); if (len != sizeof(*ev) + eir_len) { - fprintf(stderr, "dev_found: expected %zu bytes, got %u bytes\n", + error("dev_found: expected %zu bytes, got %u bytes", sizeof(*ev) + eir_len, len); return; } - if (monitor || discovery) { + if (discovery) { char addr[18], *name; ba2str(&ev->addr.bdaddr, addr); - printf("hci%u dev_found: %s type %s rssi %d " + print("hci%u dev_found: %s type %s rssi %d " "flags 0x%04x ", index, addr, typestr(ev->addr.type), ev->rssi, flags); if (ev->addr.type != BDADDR_BREDR) - printf("AD flags 0x%02x ", + print("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); + print("name %s", name); else - printf("eir_len %u\n", eir_len); + print("eir_len %u", eir_len); free(name); } @@ -544,14 +630,12 @@ static void pin_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { - fprintf(stderr, - "PIN Code reply failed with status 0x%02x (%s)\n", + error("PIN Code reply failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); - return; + return noninteractive_quit(EXIT_FAILURE); } - printf("PIN Reply successful\n"); + print("PIN Reply successful"); } static int mgmt_pin_reply(struct mgmt *mgmt, uint16_t index, @@ -573,14 +657,12 @@ static void pin_neg_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { - fprintf(stderr, - "PIN Neg reply failed with status 0x%02x (%s)\n", + error("PIN Neg reply failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); - return; + return noninteractive_quit(EXIT_FAILURE); } - printf("PIN Negative Reply successful\n"); + print("PIN Negative Reply successful"); } static int mgmt_pin_neg_reply(struct mgmt *mgmt, uint16_t index, @@ -595,57 +677,16 @@ static int mgmt_pin_neg_reply(struct mgmt *mgmt, uint16_t index, sizeof(cp), &cp, pin_neg_rsp, NULL, NULL); } -static void request_pin(uint16_t index, uint16_t len, const void *param, - void *user_data) -{ - const struct mgmt_ev_pin_code_request *ev = param; - struct mgmt *mgmt = user_data; - char pin[18]; - size_t pin_len; - - if (len != sizeof(*ev)) { - fprintf(stderr, - "Invalid pin_code request length (%u bytes)\n", len); - return; - } - - if (monitor) { - char addr[18]; - ba2str(&ev->addr.bdaddr, addr); - printf("hci%u %s request PIN\n", index, addr); - } - - printf("PIN Request (press enter to reject) >> "); - fflush(stdout); - - memset(pin, 0, sizeof(pin)); - - if (fgets(pin, sizeof(pin), stdin) == NULL || pin[0] == '\n') { - mgmt_pin_neg_reply(mgmt, index, &ev->addr); - return; - } - - pin_len = strlen(pin); - if (pin[pin_len - 1] == '\n') { - pin[pin_len - 1] = '\0'; - pin_len--; - } - - mgmt_pin_reply(mgmt, index, &ev->addr, pin, pin_len); -} - static void confirm_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { - fprintf(stderr, - "User Confirm reply failed. status 0x%02x (%s)\n", + error("User Confirm reply failed. status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); - return; + return noninteractive_quit(EXIT_FAILURE); } - printf("User Confirm Reply successful\n"); + print("User Confirm Reply successful"); } static int mgmt_confirm_reply(struct mgmt *mgmt, uint16_t index, @@ -664,14 +705,12 @@ static void confirm_neg_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { - fprintf(stderr, - "Confirm Neg reply failed. status 0x%02x (%s)\n", + error("Confirm Neg reply failed. status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); - return; + return noninteractive_quit(EXIT_FAILURE); } - printf("User Confirm Negative Reply successful\n"); + print("User Confirm Negative Reply successful"); } static int mgmt_confirm_neg_reply(struct mgmt *mgmt, uint16_t index, @@ -686,66 +725,16 @@ static int mgmt_confirm_neg_reply(struct mgmt *mgmt, uint16_t index, sizeof(cp), &cp, confirm_neg_rsp, NULL, NULL); } - -static void user_confirm(uint16_t index, uint16_t len, const void *param, - void *user_data) -{ - const struct mgmt_ev_user_confirm_request *ev = param; - struct mgmt *mgmt = user_data; - char rsp[5]; - size_t rsp_len; - uint32_t val; - char addr[18]; - - if (len != sizeof(*ev)) { - fprintf(stderr, - "Invalid user_confirm request length (%u)\n", len); - return; - } - - ba2str(&ev->addr.bdaddr, addr); - val = get_le32(&ev->value); - - if (monitor) - printf("hci%u %s User Confirm %06u hint %u\n", index, addr, - val, ev->confirm_hint); - - if (ev->confirm_hint) - printf("Accept pairing with %s (yes/no) >> ", addr); - else - printf("Confirm value %06u for %s (yes/no) >> ", val, addr); - - fflush(stdout); - - memset(rsp, 0, sizeof(rsp)); - - if (fgets(rsp, sizeof(rsp), stdin) == NULL || rsp[0] == '\n') { - mgmt_confirm_neg_reply(mgmt, index, &ev->addr); - return; - } - - rsp_len = strlen(rsp); - if (rsp[rsp_len - 1] == '\n') - rsp[rsp_len - 1] = '\0'; - - if (rsp[0] == 'y' || rsp[0] == 'Y') - mgmt_confirm_reply(mgmt, index, &ev->addr); - else - mgmt_confirm_neg_reply(mgmt, index, &ev->addr); -} - static void passkey_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { - fprintf(stderr, - "User Passkey reply failed. status 0x%02x (%s)\n", + error("User Passkey reply failed. status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); - return; + return noninteractive_quit(EXIT_FAILURE); } - printf("User Passkey Reply successful\n"); + print("User Passkey Reply successful"); } static int mgmt_passkey_reply(struct mgmt *mgmt, uint16_t index, @@ -766,14 +755,12 @@ static void passkey_neg_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { - fprintf(stderr, - "Passkey Neg reply failed. status 0x%02x (%s)\n", + error("Passkey Neg reply failed. status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); - return; + return noninteractive_quit(EXIT_FAILURE); } - printf("User Passkey Negative Reply successful\n"); + print("User Passkey Negative Reply successful"); } static int mgmt_passkey_neg_reply(struct mgmt *mgmt, uint16_t index, @@ -788,95 +775,214 @@ static int mgmt_passkey_neg_reply(struct mgmt *mgmt, uint16_t index, sizeof(cp), &cp, passkey_neg_rsp, NULL, NULL); } +static bool prompt_input(const char *input) +{ + size_t len; -static void request_passkey(uint16_t index, uint16_t len, const void *param, + if (!prompt.req) + return false; + + len = strlen(input); + + switch (prompt.req) { + case MGMT_EV_PIN_CODE_REQUEST: + if (len) + mgmt_pin_reply(mgmt, prompt.index, &prompt.addr, + input, len); + else + mgmt_pin_neg_reply(mgmt, prompt.index, &prompt.addr); + break; + case MGMT_EV_USER_PASSKEY_REQUEST: + if (strlen(input) > 0) + mgmt_passkey_reply(mgmt, prompt.index, &prompt.addr, + atoi(input)); + else + mgmt_passkey_neg_reply(mgmt, prompt.index, + &prompt.addr); + break; + case MGMT_EV_USER_CONFIRM_REQUEST: + if (input[0] == 'y' || input[0] == 'Y') + mgmt_confirm_reply(mgmt, prompt.index, &prompt.addr); + else + mgmt_confirm_neg_reply(mgmt, prompt.index, + &prompt.addr); + break; + } + + release_prompt(); + + return true; +} + +static void interactive_prompt(const char *msg) +{ + if (saved_prompt) + return; + + saved_prompt = strdup(rl_prompt); + if (!saved_prompt) + return; + + saved_point = rl_point; + + rl_set_prompt(""); + rl_redisplay(); + + rl_set_prompt(msg); + + rl_replace_line("", 0); + rl_redisplay(); +} + +static size_t get_input(char *buf, size_t buf_len) +{ + size_t len; + + if (!fgets(buf, buf_len, stdin)) + return 0; + + len = strlen(buf); + + /* Remove trailing white-space */ + while (len && isspace(buf[len - 1])) + buf[--len] = '\0'; + + return len; +} + +static void ask(uint16_t index, uint16_t req, const struct mgmt_addr_info *addr, + const char *fmt, ...) +{ + char msg[256], buf[18]; + va_list ap; + int off; + + prompt.index = index; + prompt.req = req; + memcpy(&prompt.addr, addr, sizeof(*addr)); + + va_start(ap, fmt); + off = vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + snprintf(msg + off, sizeof(msg) - off, " %s ", + COLOR_BOLDGRAY ">>" COLOR_OFF); + + if (interactive) { + interactive_prompt(msg); + va_end(ap); + return; + } + + printf("%s", msg); + fflush(stdout); + + memset(buf, 0, sizeof(buf)); + get_input(buf, sizeof(buf)); + prompt_input(buf); +} + +static void request_pin(uint16_t index, uint16_t len, const void *param, void *user_data) { - const struct mgmt_ev_user_passkey_request *ev = param; - struct mgmt *mgmt = user_data; - char passkey[7]; + const struct mgmt_ev_pin_code_request *ev = param; + char addr[18]; if (len != sizeof(*ev)) { - fprintf(stderr, - "Invalid passkey request length (%u bytes)\n", len); + error("Invalid pin_code request length (%u bytes)", len); return; } - if (monitor) { - char addr[18]; - ba2str(&ev->addr.bdaddr, addr); - printf("hci%u %s request passkey\n", index, addr); - } + ba2str(&ev->addr.bdaddr, addr); + print("hci%u %s request PIN", index, addr); - printf("Passkey Request (press enter to reject) >> "); - fflush(stdout); + ask(index, MGMT_EV_PIN_CODE_REQUEST, &ev->addr, + "PIN Request (press enter to reject)"); +} - memset(passkey, 0, sizeof(passkey)); +static void user_confirm(uint16_t index, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_ev_user_confirm_request *ev = param; + uint32_t val; + char addr[18]; - if (fgets(passkey, sizeof(passkey), stdin) == NULL || - passkey[0] == '\n') { - mgmt_passkey_neg_reply(mgmt, index, &ev->addr); + if (len != sizeof(*ev)) { + error("Invalid user_confirm request length (%u)", len); return; } - len = strlen(passkey); - if (passkey[len - 1] == '\n') { - passkey[len - 1] = '\0'; - len--; + ba2str(&ev->addr.bdaddr, addr); + val = get_le32(&ev->value); + + print("hci%u %s User Confirm %06u hint %u", index, addr, + val, ev->confirm_hint); + + if (ev->confirm_hint) + ask(index, MGMT_EV_USER_CONFIRM_REQUEST, &ev->addr, + "Accept pairing with %s (yes/no)", addr); + else + ask(index, MGMT_EV_USER_CONFIRM_REQUEST, &ev->addr, + "Confirm value %06u for %s (yes/no)", val, addr); +} + +static void request_passkey(uint16_t index, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_ev_user_passkey_request *ev = param; + char addr[18]; + + if (len != sizeof(*ev)) { + error("Invalid passkey request length (%u bytes)", len); + return; } - mgmt_passkey_reply(mgmt, index, &ev->addr, atoi(passkey)); + ba2str(&ev->addr.bdaddr, addr); + print("hci%u %s request passkey", index, addr); + + ask(index, MGMT_EV_USER_PASSKEY_REQUEST, &ev->addr, + "Passkey Request (press enter to reject)"); } static void passkey_notify(uint16_t index, uint16_t len, const void *param, void *user_data) { const struct mgmt_ev_passkey_notify *ev = param; + char addr[18]; if (len != sizeof(*ev)) { - fprintf(stderr, - "Invalid passkey request length (%u bytes)\n", len); + error("Invalid passkey request length (%u bytes)", len); return; } - if (monitor) { - char addr[18]; - ba2str(&ev->addr.bdaddr, addr); - printf("hci%u %s request passkey\n", index, addr); - } + ba2str(&ev->addr.bdaddr, addr); + print("hci%u %s request passkey", index, addr); - printf("Passkey Notify: %06u (entered %u)\n", get_le32(&ev->passkey), + print("Passkey Notify: %06u (entered %u)", get_le32(&ev->passkey), ev->entered); } -static void cmd_monitor(struct mgmt *mgmt, uint16_t index, int argc, - char **argv) -{ - printf("Monitoring mgmt events...\n"); - monitor = true; -} - static void version_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { const struct mgmt_rp_read_version *rp = param; if (status != 0) { - fprintf(stderr, "Reading mgmt version failed with status" - " 0x%02x (%s)\n", status, mgmt_errstr(status)); + error("Reading mgmt version failed with status 0x%02x (%s)", + status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small version reply (%u bytes)\n", len); + error("Too small version reply (%u bytes)", len); goto done; } - printf("MGMT Version %u, revision %u\n", rp->version, + print("MGMT Version %u, revision %u", rp->version, get_le16(&rp->revision)); done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_version(struct mgmt *mgmt, uint16_t index, int argc, @@ -884,8 +990,8 @@ static void cmd_version(struct mgmt *mgmt, uint16_t index, int argc, { if (mgmt_send(mgmt, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL, version_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send read_version cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send read_version cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -899,13 +1005,13 @@ static void commands_rsp(uint8_t status, uint16_t len, const void *param, int i; if (status != 0) { - fprintf(stderr, "Reading supported commands failed with status" - " 0x%02x (%s)\n", status, mgmt_errstr(status)); + error("Read Supported Commands failed: status 0x%02x (%s)", + status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small commands reply (%u bytes)\n", len); + error("Too small commands reply (%u bytes)", len); goto done; } @@ -916,27 +1022,27 @@ static void commands_rsp(uint8_t status, uint16_t len, const void *param, num_events * sizeof(uint16_t); if (len < expected_len) { - fprintf(stderr, "Too small commands reply (%u != %zu)\n", + error("Too small commands reply (%u != %zu)", len, expected_len); goto done; } opcode = rp->opcodes; - printf("%u commands:\n", num_commands); + print("%u commands:", num_commands); for (i = 0; i < num_commands; i++) { uint16_t op = get_le16(opcode++); - printf("\t%s (0x%04x)\n", mgmt_opstr(op), op); + print("\t%s (0x%04x)", mgmt_opstr(op), op); } - printf("%u events:\n", num_events); + print("%u events:", num_events); for (i = 0; i < num_events; i++) { uint16_t ev = get_le16(opcode++); - printf("\t%s (0x%04x)\n", mgmt_evstr(ev), ev); + print("\t%s (0x%04x)", mgmt_evstr(ev), ev); } done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc, @@ -944,8 +1050,8 @@ static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc, { if (mgmt_send(mgmt, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE, 0, NULL, commands_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send read_commands cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send read_commands cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -957,28 +1063,25 @@ static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param, unsigned int i; if (status != 0) { - fprintf(stderr, - "Reading index list failed with status 0x%02x (%s)\n", + error("Reading index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small index list reply (%u bytes)\n", - len); + error("Too small index list reply (%u bytes)", len); goto done; } count = get_le16(&rp->num_controllers); if (len < sizeof(*rp) + count * sizeof(uint16_t)) { - fprintf(stderr, - "Index count (%u) doesn't match reply length (%u)\n", + error("Index count (%u) doesn't match reply length (%u)", count, len); goto done; } - printf("Unconfigured index list with %u item%s\n", + print("Unconfigured index list with %u item%s", count, count != 1 ? "s" : ""); for (i = 0; i < count; i++) { @@ -986,12 +1089,12 @@ static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param, index = get_le16(&rp->index[i]); - printf("\thci%u\n", index); + print("\thci%u", index); } done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void config_info_rsp(uint8_t status, uint16_t len, const void *param, @@ -1001,29 +1104,25 @@ static void config_info_rsp(uint8_t status, uint16_t len, const void *param, uint16_t index = PTR_TO_UINT(user_data); if (status != 0) { - fprintf(stderr, - "Reading hci%u config failed with status 0x%02x (%s)\n", + error("Reading hci%u config failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small info reply (%u bytes)\n", len); + error("Too small info reply (%u bytes)", len); goto done; } - printf("hci%u:\tmanufacturer %u\n", index, get_le16(&rp->manufacturer)); - - printf("\tsupported options: "); - print_options(get_le32(&rp->supported_options)); - printf("\n"); + print("hci%u:\tmanufacturer %u", index, get_le16(&rp->manufacturer)); - printf("\tmissing options: "); - print_options(get_le32(&rp->missing_options)); - printf("\n"); + print("\tsupported options: %s", + options2str(get_le32(&rp->supported_options))); + print("\tmissing options: %s", + options2str(get_le32(&rp->missing_options))); done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1034,8 +1133,8 @@ static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, unconf_index_rsp, mgmt, NULL) == 0) { - fprintf(stderr, "Unable to send unconf_index_list cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send unconf_index_list cmd"); + return noninteractive_quit(EXIT_FAILURE); } return; @@ -1045,8 +1144,8 @@ static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL, config_info_rsp, data, NULL) == 0) { - fprintf(stderr, "Unable to send read_config_info cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send read_config_info cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1057,40 +1156,39 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param, uint16_t index = PTR_TO_UINT(user_data); char addr[18]; - pending--; + pending_index--; if (status != 0) { - fprintf(stderr, - "Reading hci%u info failed with status 0x%02x (%s)\n", + error("Reading hci%u info failed with status 0x%02x (%s)", index, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small info reply (%u bytes)\n", len); + error("Too small info reply (%u bytes)", len); goto done; } ba2str(&rp->bdaddr, addr); - printf("hci%u:\taddr %s version %u manufacturer %u" - " class 0x%02x%02x%02x\n", index, + print("hci%u:\taddr %s version %u manufacturer %u" + " class 0x%02x%02x%02x", 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(get_le32(&rp->supported_settings)); + print("\tsupported settings: %s", + settings2str(get_le32(&rp->supported_settings))); - printf("\n\tcurrent settings: "); - print_settings(get_le32(&rp->current_settings)); + print("\tcurrent settings: %s", + settings2str(get_le32(&rp->current_settings))); - printf("\n\tname %s\n", rp->name); - printf("\tshort name %s\n", rp->short_name); + print("\tname %s", rp->name); + print("\tshort name %s", rp->short_name); - if (pending > 0) + if (pending_index > 0) return; done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void index_rsp(uint8_t status, uint16_t len, const void *param, @@ -1102,36 +1200,25 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param, unsigned int i; if (status != 0) { - fprintf(stderr, - "Reading index list failed with status 0x%02x (%s)\n", + error("Reading index list failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small index list reply (%u bytes)\n", - len); - goto done; + error("Too small index list reply (%u bytes)", len); + return noninteractive_quit(EXIT_FAILURE); } count = get_le16(&rp->num_controllers); if (len < sizeof(*rp) + count * sizeof(uint16_t)) { - fprintf(stderr, - "Index count (%u) doesn't match reply length (%u)\n", + error("Index count (%u) doesn't match reply length (%u)", count, len); - goto done; + return noninteractive_quit(EXIT_FAILURE); } - if (monitor) - printf("Index list with %u item%s\n", - count, count != 1 ? "s" : ""); - - if (count == 0) - goto done; - - if (monitor && count > 0) - printf("\t"); + print("Index list with %u item%s", count, count != 1 ? "s" : ""); for (i = 0; i < count; i++) { uint16_t index; @@ -1139,27 +1226,19 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param, index = get_le16(&rp->index[i]); - if (monitor) - printf("hci%u ", index); - data = UINT_TO_PTR(index); if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp, data, NULL) == 0) { - fprintf(stderr, "Unable to send read_info cmd\n"); - goto done; + error("Unable to send read_info cmd"); + return noninteractive_quit(EXIT_FAILURE); } - pending++; + pending_index++; } - if (monitor && count > 0) - printf("\n"); - - return; - -done: - mainloop_quit(); + if (!count) + noninteractive_quit(EXIT_SUCCESS); } static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1170,8 +1249,8 @@ static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, index_rsp, mgmt, NULL) == 0) { - fprintf(stderr, "Unable to send index_list cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send index_list cmd"); + return noninteractive_quit(EXIT_FAILURE); } return; @@ -1181,8 +1260,8 @@ static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp, data, NULL) == 0) { - fprintf(stderr, "Unable to send read_info cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send read_info cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1232,24 +1311,22 @@ static void setting_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, const uint32_t *rp = param; if (status != 0) { - fprintf(stderr, - "%s for hci%u failed with status 0x%02x (%s)\n", + error("%s for hci%u failed with status 0x%02x (%s)", mgmt_opstr(op), id, status, mgmt_errstr(status)); goto done; } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small %s response (%u bytes)\n", + error("Too small %s response (%u bytes)", mgmt_opstr(op), len); goto done; } - printf("hci%u %s complete, settings: ", id, mgmt_opstr(op)); - print_settings(get_le32(rp)); - printf("\n"); + print("hci%u %s complete, settings: %s", id, mgmt_opstr(op), + settings2str(get_le32(rp))); done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op, @@ -1258,8 +1335,8 @@ static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op, uint8_t val; if (argc < 2) { - printf("Specify \"on\" or \"off\"\n"); - exit(EXIT_FAILURE); + print("Specify \"on\" or \"off\""); + return noninteractive_quit(EXIT_FAILURE); } if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) @@ -1273,8 +1350,8 @@ static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op, index = 0; if (send_cmd(mgmt, op, index, sizeof(val), &val, setting_rsp) == 0) { - fprintf(stderr, "Unable to send %s cmd\n", mgmt_opstr(op)); - exit(EXIT_FAILURE); + error("Unable to send %s cmd", mgmt_opstr(op)); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1289,8 +1366,8 @@ static void cmd_discov(struct mgmt *mgmt, uint16_t index, int argc, struct mgmt_cp_set_discoverable cp; if (argc < 2) { - printf("Usage: btmgmt %s <yes/no/limited> [timeout]\n", argv[0]); - exit(EXIT_FAILURE); + print("Usage: %s <yes/no/limited> [timeout]", argv[0]); + return noninteractive_quit(EXIT_FAILURE); } memset(&cp, 0, sizeof(cp)); @@ -1312,8 +1389,8 @@ static void cmd_discov(struct mgmt *mgmt, uint16_t index, int argc, if (send_cmd(mgmt, MGMT_OP_SET_DISCOVERABLE, index, sizeof(cp), &cp, setting_rsp) == 0) { - fprintf(stderr, "Unable to send set_discoverable cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send set_discoverable cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1351,8 +1428,8 @@ 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); + print("Specify \"on\" or \"off\" or \"only\""); + return noninteractive_quit(EXIT_FAILURE); } if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) @@ -1369,8 +1446,8 @@ static void cmd_sc(struct mgmt *mgmt, uint16_t index, int argc, char **argv) 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); + error("Unable to send set_secure_conn cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1401,8 +1478,8 @@ static void cmd_privacy(struct mgmt *mgmt, uint16_t index, int argc, struct mgmt_cp_set_privacy cp; if (argc < 2) { - printf("Specify \"on\" or \"off\"\n"); - exit(EXIT_FAILURE); + print("Specify \"on\" or \"off\""); + return noninteractive_quit(EXIT_FAILURE); } if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) @@ -1416,25 +1493,24 @@ static void cmd_privacy(struct mgmt *mgmt, uint16_t index, int argc, index = 0; if (argc > 2) { - if (convert_hexstr(argv[2], cp.irk, + if (hex2bin(argv[2], cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) { - fprintf(stderr, "Invalid key format\n"); - exit(EXIT_FAILURE); + error("Invalid key format"); + return noninteractive_quit(EXIT_FAILURE); } } else { int fd; fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { - fprintf(stderr, "open(/dev/urandom): %s\n", - strerror(errno)); - exit(EXIT_FAILURE); + error("open(/dev/urandom): %s", strerror(errno)); + return noninteractive_quit(EXIT_FAILURE); } if (read(fd, cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) { - fprintf(stderr, "Reading from urandom failed\n"); + error("Reading from urandom failed"); close(fd); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } close(fd); @@ -1442,8 +1518,8 @@ static void cmd_privacy(struct mgmt *mgmt, uint16_t index, int argc, 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); + error("Unable to send Set Privacy command"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1453,21 +1529,20 @@ static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, const struct mgmt_ev_class_of_dev_changed *rp = param; if (len == 0 && status != 0) { - fprintf(stderr, "%s failed, status 0x%02x (%s)\n", + error("%s failed, status 0x%02x (%s)", mgmt_opstr(op), status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { - fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len); - goto done; + error("Unexpected %s len %u", mgmt_opstr(op), len); + return noninteractive_quit(EXIT_FAILURE); } - printf("%s succeeded. Class 0x%02x%02x%02x\n", mgmt_opstr(op), + print("%s succeeded. Class 0x%02x%02x%02x", mgmt_opstr(op), rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]); -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_class(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1475,8 +1550,8 @@ static void cmd_class(struct mgmt *mgmt, uint16_t index, int argc, char **argv) uint8_t class[2]; if (argc < 3) { - printf("Usage: btmgmt %s <major> <minor>\n", argv[0]); - exit(EXIT_FAILURE); + print("Usage: %s <major> <minor>", argv[0]); + return noninteractive_quit(EXIT_FAILURE); } class[0] = atoi(argv[1]); @@ -1487,8 +1562,8 @@ static void cmd_class(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (send_cmd(mgmt, MGMT_OP_SET_DEV_CLASS, index, sizeof(class), class, class_rsp) == 0) { - fprintf(stderr, "Unable to send set_dev_class cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send set_dev_class cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1499,33 +1574,30 @@ static void disconnect_rsp(uint8_t status, uint16_t len, const void *param, char addr[18]; if (len == 0 && status != 0) { - fprintf(stderr, "Disconnect failed with status 0x%02x (%s)\n", + error("Disconnect failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { - fprintf(stderr, "Invalid disconnect response length (%u)\n", - len); - goto done; + error("Invalid disconnect response length (%u)", len); + return noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->addr.bdaddr, addr); if (status == 0) - printf("%s disconnected\n", addr); + print("%s disconnected", addr); else - fprintf(stderr, - "Disconnecting %s failed with status 0x%02x (%s)\n", + error("Disconnecting %s failed with status 0x%02x (%s)", addr, status, mgmt_errstr(status)); -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void disconnect_usage(void) { - printf("Usage: btmgmt disconnect [-t type] <remote address>\n"); + print("Usage: disconnect [-t type] <remote address>"); } static struct option disconnect_options[] = { @@ -1548,9 +1620,11 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc, type = strtol(optarg, NULL, 0); break; case 'h': + disconnect_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: disconnect_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1560,7 +1634,7 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc, if (argc < 1) { disconnect_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -1572,8 +1646,8 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc, if (mgmt_send(mgmt, MGMT_OP_DISCONNECT, index, sizeof(cp), &cp, disconnect_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send disconnect cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send disconnect cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1584,16 +1658,15 @@ static void con_rsp(uint8_t status, uint16_t len, const void *param, uint16_t count, i; if (len < sizeof(*rp)) { - fprintf(stderr, "Too small (%u bytes) get_connections rsp\n", - len); - goto done; + error("Too small (%u bytes) get_connections rsp", len); + return noninteractive_quit(EXIT_FAILURE); } 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); - goto done; + error("Invalid get_connections length (count=%u, len=%u)", + count, len); + return noninteractive_quit(EXIT_FAILURE); } for (i = 0; i < count; i++) { @@ -1601,11 +1674,10 @@ static void con_rsp(uint8_t status, uint16_t len, const void *param, ba2str(&rp->addr[i].bdaddr, addr); - printf("%s type %s\n", addr, typestr(rp->addr[i].type)); + print("%s type %s", addr, typestr(rp->addr[i].type)); } -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_con(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1615,8 +1687,8 @@ static void cmd_con(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_GET_CONNECTIONS, index, 0, NULL, con_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send get_connections cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send get_connections cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1624,20 +1696,18 @@ static void find_service_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { - fprintf(stderr, - "Unable to start service discovery. status 0x%02x (%s)\n", - status, mgmt_errstr(status)); - mainloop_quit(); - return; + error("Start Service Discovery failed: status 0x%02x (%s)", + status, mgmt_errstr(status)); + return noninteractive_quit(EXIT_FAILURE); } - printf("Service discovery started\n"); + print("Service discovery started"); discovery = true; } static void find_service_usage(void) { - printf("Usage: btmgmt find-service [-u UUID] [-r RSSI_Threshold] [-l|-b]\n"); + print("Usage: find-service [-u UUID] [-r RSSI_Threshold] [-l|-b]"); } static struct option find_service_options[] = { @@ -1678,39 +1748,39 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc, index = 0; type = 0; - hci_set_bit(BDADDR_BREDR, &type); - hci_set_bit(BDADDR_LE_PUBLIC, &type); - hci_set_bit(BDADDR_LE_RANDOM, &type); + type |= (1 << BDADDR_BREDR); + type |= (1 << BDADDR_LE_PUBLIC); + type |= (1 << BDADDR_LE_RANDOM); rssi = 127; count = 0; if (argc == 1) { find_service_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } while ((opt = getopt_long(argc, argv, "+lbu:r:p:h", find_service_options, NULL)) != -1) { switch (opt) { case 'l': - hci_clear_bit(BDADDR_BREDR, &type); - hci_set_bit(BDADDR_LE_PUBLIC, &type); - hci_set_bit(BDADDR_LE_RANDOM, &type); + type &= ~(1 << BDADDR_BREDR); + type |= (1 << BDADDR_LE_PUBLIC); + type |= (1 << BDADDR_LE_RANDOM); break; case 'b': - hci_set_bit(BDADDR_BREDR, &type); - hci_clear_bit(BDADDR_LE_PUBLIC, &type); - hci_clear_bit(BDADDR_LE_RANDOM, &type); + type |= (1 << BDADDR_BREDR); + type &= ~(1 << BDADDR_LE_PUBLIC); + type &= ~(1 << BDADDR_LE_RANDOM); break; case 'u': if (count == MAX_UUIDS) { - printf("Max %u UUIDs supported\n", MAX_UUIDS); - exit(EXIT_FAILURE); + print("Max %u UUIDs supported", MAX_UUIDS); + return noninteractive_quit(EXIT_FAILURE); } if (bt_string2uuid(&uuid, optarg) < 0) { - printf("Invalid UUID: %s\n", optarg); - exit(EXIT_FAILURE); + print("Invalid UUID: %s", optarg); + return noninteractive_quit(EXIT_FAILURE); } cp = (void *) buf; uuid_to_uuid128(&uuid128, &uuid); @@ -1723,10 +1793,10 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc, break; case 'h': find_service_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_SUCCESS); default: find_service_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1736,7 +1806,7 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc, if (argc > 0) { find_service_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } cp = (void *) buf; @@ -1747,8 +1817,8 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc, if (mgmt_send(mgmt, MGMT_OP_START_SERVICE_DISCOVERY, index, sizeof(*cp) + count * 16, cp, find_service_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send start_service_discovery cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send start_service_discovery cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1756,20 +1826,18 @@ static void find_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) { - fprintf(stderr, - "Unable to start discovery. status 0x%02x (%s)\n", + error("Unable to start discovery. status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); - return; + return noninteractive_quit(EXIT_FAILURE); } - printf("Discovery started\n"); + print("Discovery started"); discovery = true; } static void find_usage(void) { - printf("Usage: btmgmt find [-l|-b]>\n"); + print("Usage: find [-l|-b]>"); } static struct option find_options[] = { @@ -1789,27 +1857,29 @@ static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv) index = 0; type = 0; - hci_set_bit(BDADDR_BREDR, &type); - hci_set_bit(BDADDR_LE_PUBLIC, &type); - hci_set_bit(BDADDR_LE_RANDOM, &type); + type |= (1 << BDADDR_BREDR); + type |= (1 << BDADDR_LE_PUBLIC); + type |= (1 << BDADDR_LE_RANDOM); while ((opt = getopt_long(argc, argv, "+lbh", find_options, NULL)) != -1) { switch (opt) { case 'l': - hci_clear_bit(BDADDR_BREDR, &type); - hci_set_bit(BDADDR_LE_PUBLIC, &type); - hci_set_bit(BDADDR_LE_RANDOM, &type); + type &= ~(1 << BDADDR_BREDR); + type |= (1 << BDADDR_LE_PUBLIC); + type |= (1 << BDADDR_LE_RANDOM); break; case 'b': - hci_set_bit(BDADDR_BREDR, &type); - hci_clear_bit(BDADDR_LE_PUBLIC, &type); - hci_clear_bit(BDADDR_LE_RANDOM, &type); + type |= (1 << BDADDR_BREDR); + type &= ~(1 << BDADDR_LE_PUBLIC); + type &= ~(1 << BDADDR_LE_RANDOM); break; case 'h': + find_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: find_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1822,8 +1892,8 @@ static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_START_DISCOVERY, index, sizeof(cp), &cp, find_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send start_discovery cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send start_discovery cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1831,11 +1901,10 @@ static void name_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) - fprintf(stderr, "Unable to set local name " - "with status 0x%02x (%s)\n", + error("Unable to set local name with status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_name(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -1843,8 +1912,8 @@ static void cmd_name(struct mgmt *mgmt, uint16_t index, int argc, char **argv) struct mgmt_cp_set_local_name cp; if (argc < 2) { - printf("Usage: btmgmt %s <name> [shortname]\n", argv[0]); - exit(EXIT_FAILURE); + print("Usage: %s <name> [shortname]", argv[0]); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -1858,8 +1927,8 @@ static void cmd_name(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_SET_LOCAL_NAME, index, sizeof(cp), &cp, name_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send set_name cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send set_name cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1870,35 +1939,34 @@ static void pair_rsp(uint8_t status, uint16_t len, const void *param, char addr[18]; if (len == 0 && status != 0) { - fprintf(stderr, "Pairing failed with status 0x%02x (%s)\n", + error("Pairing failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { - fprintf(stderr, "Unexpected pair_rsp len %u\n", len); - goto done; + error("Unexpected pair_rsp len %u", len); + return noninteractive_quit(EXIT_FAILURE); } + if (!memcmp(&rp->addr, &prompt.addr, sizeof(rp->addr))) + release_prompt(); + ba2str(&rp->addr.bdaddr, addr); - if (status != 0) { - fprintf(stderr, - "Pairing with %s (%s) failed. status 0x%02x (%s)\n", + if (status) + error("Pairing with %s (%s) failed. status 0x%02x (%s)", addr, typestr(rp->addr.type), status, mgmt_errstr(status)); - goto done; - } - - printf("Paired with %s (%s)\n", addr, typestr(rp->addr.type)); + else + print("Paired with %s (%s)", addr, typestr(rp->addr.type)); -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void pair_usage(void) { - printf("Usage: btmgmt pair [-c cap] [-t type] <remote address>\n"); + print("Usage: pair [-c cap] [-t type] <remote address>"); } static struct option pair_options[] = { @@ -1926,9 +1994,11 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv) type = strtol(optarg, NULL, 0); break; case 'h': + pair_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: pair_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1938,7 +2008,7 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (argc < 1) { pair_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -1950,12 +2020,12 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv) cp.io_cap = cap; ba2str(&cp.addr.bdaddr, addr); - printf("Pairing with %s (%s)\n", addr, typestr(cp.addr.type)); + print("Pairing with %s (%s)", 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"); - exit(EXIT_FAILURE); + error("Unable to send pair_device cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -1966,35 +2036,31 @@ static void cancel_pair_rsp(uint8_t status, uint16_t len, const void *param, char addr[18]; if (len == 0 && status != 0) { - fprintf(stderr, "Cancel Pairing failed with 0x%02x (%s)\n", + error("Cancel Pairing failed with 0x%02x (%s)", status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { - fprintf(stderr, "Unexpected cancel_pair_rsp len %u\n", len); - goto done; + error("Unexpected cancel_pair_rsp len %u", len); + return noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->bdaddr, addr); - if (status != 0) { - fprintf(stderr, - "Cancel Pairing with %s (%s) failed. 0x%02x (%s)\n", + if (status) + error("Cancel Pairing with %s (%s) failed. 0x%02x (%s)", addr, typestr(rp->type), status, mgmt_errstr(status)); - goto done; - } - - printf("Pairing Cancelled with %s\n", addr); + else + print("Pairing Cancelled with %s", addr); -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cancel_pair_usage(void) { - printf("Usage: btmgmt cancelpair [-t type] <remote address>\n"); + print("Usage: cancelpair [-t type] <remote address>"); } static struct option cancel_pair_options[] = { @@ -2017,9 +2083,11 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc, type = strtol(optarg, NULL, 0); break; case 'h': + cancel_pair_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: cancel_pair_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2029,7 +2097,7 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc, if (argc < 1) { cancel_pair_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2041,8 +2109,8 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc, if (mgmt_send(mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, index, sizeof(cp), &cp, cancel_pair_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send cancel_pair_device cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send cancel_pair_device cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2053,34 +2121,30 @@ static void unpair_rsp(uint8_t status, uint16_t len, const void *param, char addr[18]; if (len == 0 && status != 0) { - fprintf(stderr, "Unpair device failed. status 0x%02x (%s)\n", + error("Unpair device failed. status 0x%02x (%s)", status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { - fprintf(stderr, "Unexpected unpair_device_rsp len %u\n", len); - goto done; + error("Unexpected unpair_device_rsp len %u", len); + return noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->addr.bdaddr, addr); - if (status != 0) { - fprintf(stderr, - "Unpairing %s failed. status 0x%02x (%s)\n", + if (status) + error("Unpairing %s failed. status 0x%02x (%s)", addr, status, mgmt_errstr(status)); - goto done; - } - - printf("%s unpaired\n", addr); + else + print("%s unpaired", addr); -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void unpair_usage(void) { - printf("Usage: btmgmt unpair [-t type] <remote address>\n"); + print("Usage: unpair [-t type] <remote address>"); } static struct option unpair_options[] = { @@ -2103,9 +2167,11 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc, type = strtol(optarg, NULL, 0); break; case 'h': + unpair_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: unpair_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2115,7 +2181,7 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc, if (argc < 1) { unpair_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2128,8 +2194,8 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc, if (mgmt_send(mgmt, MGMT_OP_UNPAIR_DEVICE, index, sizeof(cp), &cp, unpair_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send unpair_device cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send unpair_device cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2137,12 +2203,12 @@ static void keys_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", + error("Load keys failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else - printf("Keys successfully loaded\n"); + print("Keys successfully loaded"); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_keys(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -2156,8 +2222,8 @@ static void cmd_keys(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_LOAD_LINK_KEYS, index, sizeof(cp), &cp, keys_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send load_keys cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send load_keys cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2165,12 +2231,12 @@ 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", + error("Load keys failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else - printf("Long term keys successfully loaded\n"); + print("Long term keys successfully loaded"); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_ltks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -2184,8 +2250,8 @@ static void cmd_ltks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) 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); + error("Unable to send load_ltks cmd"); + return noninteractive_quit(EXIT_SUCCESS); } } @@ -2193,17 +2259,17 @@ 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", + error("Load IRKs failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else - printf("Identity Resolving Keys successfully loaded\n"); + print("Identity Resolving Keys successfully loaded"); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void irks_usage(void) { - printf("Usage: btmgmt irks [--local]\n"); + print("Usage: irks [--local]"); } static struct option irks_options[] = { @@ -2232,8 +2298,8 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) switch (opt) { case 'l': if (count >= MAX_IRKS) { - fprintf(stderr, "Number of IRKs exceeded\n"); - exit(EXIT_FAILURE); + error("Number of IRKs exceeded"); + return noninteractive_quit(EXIT_FAILURE); } if (strlen(optarg) > 3 && strncasecmp(optarg, "hci", 3) == 0) @@ -2241,17 +2307,17 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) else local_index = atoi(optarg); if (!load_identity(local_index, &cp->irks[count])) { - fprintf(stderr, "Unable to load identity\n"); - exit(EXIT_FAILURE); + error("Unable to load identity"); + return noninteractive_quit(EXIT_FAILURE); } count++; break; case 'h': irks_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_SUCCESS); default: irks_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2261,7 +2327,7 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (argc > 0) { irks_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } cp->irk_count = cpu_to_le16(count); @@ -2269,8 +2335,8 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index, sizeof(*cp) + count * 23, cp, irks_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send load_irks cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send load_irks cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2281,34 +2347,31 @@ static void block_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len, char addr[18]; if (len == 0 && status != 0) { - fprintf(stderr, "%s failed, status 0x%02x (%s)\n", + error("%s failed, status 0x%02x (%s)", mgmt_opstr(op), status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len != sizeof(*rp)) { - fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len); - goto done; + error("Unexpected %s len %u", mgmt_opstr(op), len); + return noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->bdaddr, addr); - if (status != 0) { - fprintf(stderr, "%s %s (%s) failed. status 0x%02x (%s)\n", + if (status) + error("%s %s (%s) failed. status 0x%02x (%s)", mgmt_opstr(op), addr, typestr(rp->type), status, mgmt_errstr(status)); - goto done; - } - - printf("%s %s succeeded\n", mgmt_opstr(op), addr); + else + print("%s %s succeeded", mgmt_opstr(op), addr); -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void block_usage(void) { - printf("Usage: btmgmt block [-t type] <remote address>\n"); + print("Usage: block [-t type] <remote address>"); } static struct option block_options[] = { @@ -2330,9 +2393,11 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv) type = strtol(optarg, NULL, 0); break; case 'h': + block_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: block_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2342,7 +2407,7 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (argc < 1) { block_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2354,14 +2419,14 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (send_cmd(mgmt, MGMT_OP_BLOCK_DEVICE, index, sizeof(cp), &cp, block_rsp) == 0) { - fprintf(stderr, "Unable to send block_device cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send block_device cmd"); + return noninteractive_quit(EXIT_FAILURE); } } static void unblock_usage(void) { - printf("Usage: btmgmt unblock [-t type] <remote address>\n"); + print("Usage: unblock [-t type] <remote address>"); } static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc, @@ -2378,9 +2443,11 @@ static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc, type = strtol(optarg, NULL, 0); break; case 'h': + unblock_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: unblock_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2390,7 +2457,7 @@ static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc, if (argc < 1) { unblock_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2402,8 +2469,8 @@ static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc, if (send_cmd(mgmt, MGMT_OP_UNBLOCK_DEVICE, index, sizeof(cp), &cp, block_rsp) == 0) { - fprintf(stderr, "Unable to send unblock_device cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send unblock_device cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2415,16 +2482,16 @@ static void cmd_add_uuid(struct mgmt *mgmt, uint16_t index, int argc, uuid_t uuid, uuid128; if (argc < 3) { - printf("UUID and service hint needed\n"); - exit(EXIT_FAILURE); + print("UUID and service hint needed"); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) index = 0; if (bt_string2uuid(&uuid, argv[1]) < 0) { - printf("Invalid UUID: %s\n", argv[1]); - exit(EXIT_FAILURE); + print("Invalid UUID: %s", argv[1]); + return noninteractive_quit(EXIT_FAILURE); } memset(&cp, 0, sizeof(cp)); @@ -2437,8 +2504,8 @@ static void cmd_add_uuid(struct mgmt *mgmt, uint16_t index, int argc, if (send_cmd(mgmt, MGMT_OP_ADD_UUID, index, sizeof(cp), &cp, class_rsp) == 0) { - fprintf(stderr, "Unable to send add_uuid cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send add_uuid cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2450,16 +2517,16 @@ static void cmd_remove_uuid(struct mgmt *mgmt, uint16_t index, int argc, uuid_t uuid, uuid128; if (argc < 2) { - printf("UUID needed\n"); - exit(EXIT_FAILURE); + print("UUID needed"); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) index = 0; if (bt_string2uuid(&uuid, argv[1]) < 0) { - printf("Invalid UUID: %s\n", argv[1]); - exit(EXIT_FAILURE); + print("Invalid UUID: %s", argv[1]); + return noninteractive_quit(EXIT_FAILURE); } memset(&cp, 0, sizeof(cp)); @@ -2470,8 +2537,8 @@ static void cmd_remove_uuid(struct mgmt *mgmt, uint16_t index, int argc, if (send_cmd(mgmt, MGMT_OP_REMOVE_UUID, index, sizeof(cp), &cp, class_rsp) == 0) { - fprintf(stderr, "Unable to send remove_uuid cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send remove_uuid cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2489,46 +2556,35 @@ static void local_oob_rsp(uint8_t status, uint16_t len, const void *param, { const struct mgmt_rp_read_local_oob_data *rp = param; const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param; - int i; + char str[33]; if (status != 0) { - fprintf(stderr, "Read Local OOB Data failed " - "with status 0x%02x (%s)\n", + error("Read Local OOB Data failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small (%u bytes) read_local_oob rsp\n", - len); - goto done; + error("Too small (%u bytes) read_local_oob rsp", len); + return noninteractive_quit(EXIT_FAILURE); } - printf("Hash C from P-192: "); - for (i = 0; i < 16; i++) - printf("%02x", rp->hash[i]); - printf("\n"); + bin2hex(rp->hash, 16, str, sizeof(str)); + print("Hash C from P-192: %s", str); - printf("Randomizer R with P-192: "); - for (i = 0; i < 16; i++) - printf("%02x", rp->randomizer[i]); - printf("\n"); + bin2hex(rp->randomizer, 16, str, sizeof(str)); + print("Randomizer R with P-192: %s", str); if (len < sizeof(*rp_ext)) - goto done; + return noninteractive_quit(EXIT_SUCCESS); - printf("Hash C from P-256: "); - for (i = 0; i < 16; i++) - printf("%02x", rp_ext->hash256[i]); - printf("\n"); + bin2hex(rp_ext->hash256, 16, str, sizeof(str)); + print("Hash C from P-256: %s", str); - printf("Randomizer R with P-256: "); - for (i = 0; i < 16; i++) - printf("%02x", rp_ext->randomizer256[i]); - printf("\n"); + bin2hex(rp_ext->randomizer256, 16, str, sizeof(str)); + print("Randomizer R with P-256: %s", str); -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_local_oob(struct mgmt *mgmt, uint16_t index, @@ -2539,8 +2595,8 @@ static void cmd_local_oob(struct mgmt *mgmt, uint16_t index, 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); + error("Unable to send read_local_oob cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2551,26 +2607,25 @@ static void remote_oob_rsp(uint8_t status, uint16_t len, const void *param, char addr[18]; if (status != 0) { - fprintf(stderr, "Add Remote OOB Data failed: 0x%02x (%s)\n", + error("Add Remote OOB Data failed: 0x%02x (%s)", status, mgmt_errstr(status)); return; } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small (%u bytes) add_remote_oob rsp\n", - len); + error("Too small (%u bytes) add_remote_oob rsp", len); return; } ba2str(&rp->bdaddr, addr); - printf("Remote OOB data added for %s (%u)\n", addr, rp->type); + print("Remote OOB data added for %s (%u)", addr, rp->type); } static void remote_oob_usage(void) { - printf("Usage: btmgmt remote-oob [-t <addr_type>] " + print("Usage: remote-oob [-t <addr_type>] " "[-r <rand192>] [-h <hash192>] [-R <rand256>] [-H <hash256>] " - "<addr>\n"); + "<addr>"); } static struct option remote_oob_opt[] = { @@ -2595,20 +2650,20 @@ static void cmd_remote_oob(struct mgmt *mgmt, uint16_t index, cp.addr.type = strtol(optarg, NULL, 0); break; case 'r': - convert_hexstr(optarg, cp.rand192, 16); + hex2bin(optarg, cp.rand192, 16); break; case 'h': - convert_hexstr(optarg, cp.hash192, 16); + hex2bin(optarg, cp.hash192, 16); break; case 'R': - convert_hexstr(optarg, cp.rand256, 16); + hex2bin(optarg, cp.rand256, 16); break; case 'H': - convert_hexstr(optarg, cp.hash256, 16); + hex2bin(optarg, cp.hash256, 16); break; default: remote_oob_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2618,7 +2673,7 @@ static void cmd_remote_oob(struct mgmt *mgmt, uint16_t index, if (argc < 1) { remote_oob_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2626,13 +2681,13 @@ static void cmd_remote_oob(struct mgmt *mgmt, uint16_t index, str2ba(argv[0], &cp.addr.bdaddr); - printf("Adding OOB data for %s (%s)\n", argv[0], typestr(cp.addr.type)); + print("Adding OOB data for %s (%s)", argv[0], typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA, index, sizeof(cp), &cp, remote_oob_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send add_remote_oob cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send add_remote_oob cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2640,19 +2695,18 @@ static void did_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) - fprintf(stderr, "Set Device ID failed " - "with status 0x%02x (%s)\n", + error("Set Device ID failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else - printf("Device ID successfully set\n"); + print("Device ID successfully set"); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void did_usage(void) { - printf("Usage: btmgmt did <source>:<vendor>:<product>:<version>\n"); - printf(" possible source values: bluetooth, usb\n"); + print("Usage: did <source>:<vendor>:<product>:<version>"); + print(" possible source values: bluetooth, usb"); } static void cmd_did(struct mgmt *mgmt, uint16_t index, int argc, char **argv) @@ -2663,7 +2717,7 @@ static void cmd_did(struct mgmt *mgmt, uint16_t index, int argc, char **argv) if (argc < 2) { did_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } result = sscanf(argv[1], "bluetooth:%4hx:%4hx:%4hx", &vendor, &product, @@ -2681,7 +2735,7 @@ static void cmd_did(struct mgmt *mgmt, uint16_t index, int argc, char **argv) } did_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); done: if (index == MGMT_INDEX_NONE) @@ -2694,8 +2748,8 @@ done: if (mgmt_send(mgmt, MGMT_OP_SET_DEVICE_ID, index, sizeof(cp), &cp, did_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send set_device_id cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send set_device_id cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2703,18 +2757,17 @@ static void static_addr_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) - fprintf(stderr, "Set static address failed " - "with status 0x%02x (%s)\n", + error("Set static address failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else - printf("Static address successfully set\n"); + print("Static address successfully set"); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void static_addr_usage(void) { - printf("Usage: btmgmt static-addr <address>\n"); + print("Usage: static-addr <address>"); } static void cmd_static_addr(struct mgmt *mgmt, uint16_t index, @@ -2724,7 +2777,7 @@ static void cmd_static_addr(struct mgmt *mgmt, uint16_t index, if (argc < 2) { static_addr_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2734,8 +2787,8 @@ static void cmd_static_addr(struct mgmt *mgmt, uint16_t index, if (mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, sizeof(cp), &cp, static_addr_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send set_static_address cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send set_static_address cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2745,24 +2798,21 @@ static void options_rsp(uint16_t op, uint16_t id, uint8_t status, const uint32_t *rp = param; if (status != 0) { - fprintf(stderr, - "%s for hci%u failed with status 0x%02x (%s)\n", + error("%s for hci%u failed with status 0x%02x (%s)", mgmt_opstr(op), id, status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { - fprintf(stderr, "Too small %s response (%u bytes)\n", + error("Too small %s response (%u bytes)", mgmt_opstr(op), len); - goto done; + return noninteractive_quit(EXIT_FAILURE); } - printf("hci%u %s complete, options: ", id, mgmt_opstr(op)); - print_options(get_le32(rp)); - printf("\n"); + print("hci%u %s complete, options: %s", id, mgmt_opstr(op), + options2str(get_le32(rp))); -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_public_addr(struct mgmt *mgmt, uint16_t index, @@ -2771,8 +2821,8 @@ static void cmd_public_addr(struct mgmt *mgmt, uint16_t index, struct mgmt_cp_set_public_address cp; if (argc < 2) { - printf("Usage: btmgmt public-addr <address>\n"); - exit(EXIT_FAILURE); + print("Usage: public-addr <address>"); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2782,8 +2832,8 @@ static void cmd_public_addr(struct mgmt *mgmt, uint16_t index, if (send_cmd(mgmt, MGMT_OP_SET_PUBLIC_ADDRESS, index, sizeof(cp), &cp, options_rsp) == 0) { - fprintf(stderr, "Unable to send Set Public Address cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send Set Public Address cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2793,8 +2843,8 @@ static void cmd_ext_config(struct mgmt *mgmt, uint16_t index, struct mgmt_cp_set_external_config cp; if (argc < 2) { - printf("Specify \"on\" or \"off\"\n"); - exit(EXIT_FAILURE); + print("Specify \"on\" or \"off\""); + return noninteractive_quit(EXIT_FAILURE); } if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0) @@ -2809,8 +2859,8 @@ static void cmd_ext_config(struct mgmt *mgmt, uint16_t index, if (send_cmd(mgmt, MGMT_OP_SET_EXTERNAL_CONFIG, index, sizeof(cp), &cp, options_rsp) == 0) { - fprintf(stderr, "Unable to send Set External Config cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send Set External Config cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2826,37 +2876,35 @@ static void conn_info_rsp(uint8_t status, uint16_t len, const void *param, 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", + error("Get Conn Info failed, status 0x%02x (%s)", status, mgmt_errstr(status)); - goto done; + return noninteractive_quit(EXIT_FAILURE); } if (len < sizeof(*rp)) { - fprintf(stderr, "Unexpected Get Conn Info len %u\n", len); - goto done; + error("Unexpected Get Conn Info len %u", len); + return noninteractive_quit(EXIT_FAILURE); } ba2str(&rp->addr.bdaddr, addr); - if (status != 0) { - fprintf(stderr, "Get Conn Info for %s (%s) failed. status 0x%02x (%s)\n", + if (status) { + error("Get Conn Info for %s (%s) failed. status 0x%02x (%s)", addr, typestr(rp->addr.type), status, mgmt_errstr(status)); - goto done; - } - - printf("Connection Information for %s (%s)\n", + } else { + print("Connection Information for %s (%s)", addr, typestr(rp->addr.type)); - printf("\tRSSI %d\n\tTX power %d\n\tmaximum TX power %d\n", + print("\tRSSI %d\tTX power %d\tmaximum TX power %d", rp->rssi, rp->tx_power, rp->max_tx_power); + } -done: - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void conn_info_usage(void) { - printf("Usage: btmgmt conn-info [-t type] <remote address>\n"); + print("Usage: conn-info [-t type] <remote address>"); } static struct option conn_info_options[] = { @@ -2879,9 +2927,11 @@ static void cmd_conn_info(struct mgmt *mgmt, uint16_t index, type = strtol(optarg, NULL, 0); break; case 'h': + conn_info_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: conn_info_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2891,7 +2941,7 @@ static void cmd_conn_info(struct mgmt *mgmt, uint16_t index, if (argc < 1) { conn_info_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2903,8 +2953,8 @@ static void cmd_conn_info(struct mgmt *mgmt, uint16_t index, 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); + error("Unable to send get_conn_info cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2912,18 +2962,17 @@ static void io_cap_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) - fprintf(stderr, "Could not set IO Capability with " - "status 0x%02x (%s)\n", + error("Could not set IO Capability with status 0x%02x (%s)", status, mgmt_errstr(status)); else - printf("IO Capabilities successfully set\n"); + print("IO Capabilities successfully set"); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void io_cap_usage(void) { - printf("Usage: btmgmt io-cap <cap>\n"); + print("Usage: io-cap <cap>"); } static void cmd_io_cap(struct mgmt *mgmt, uint16_t index, @@ -2934,7 +2983,7 @@ static void cmd_io_cap(struct mgmt *mgmt, uint16_t index, if (argc < 2) { io_cap_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2946,8 +2995,8 @@ static void cmd_io_cap(struct mgmt *mgmt, uint16_t index, if (mgmt_send(mgmt, MGMT_OP_SET_IO_CAPABILITY, index, sizeof(cp), &cp, io_cap_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send set-io-cap cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send set-io-cap cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2955,17 +3004,17 @@ static void scan_params_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) - fprintf(stderr, "Set scan parameters failed with status 0x%02x (%s)\n", + error("Set scan parameters failed with status 0x%02x (%s)", status, mgmt_errstr(status)); else - printf("Scan parameters successfully set\n"); + print("Scan parameters successfully set"); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void scan_params_usage(void) { - printf("Usage: btmgmt scan-params <interval> <window>\n"); + print("Usage: scan-params <interval> <window>"); } static void cmd_scan_params(struct mgmt *mgmt, uint16_t index, @@ -2975,7 +3024,7 @@ static void cmd_scan_params(struct mgmt *mgmt, uint16_t index, if (argc < 3) { scan_params_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -2986,8 +3035,8 @@ static void cmd_scan_params(struct mgmt *mgmt, uint16_t index, if (mgmt_send(mgmt, MGMT_OP_SET_SCAN_PARAMS, index, sizeof(cp), &cp, scan_params_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send set_scan_params cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send set_scan_params cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -2997,21 +3046,21 @@ static void clock_info_rsp(uint8_t status, uint16_t len, const void *param, const struct mgmt_rp_get_clock_info *rp = param; if (len < sizeof(*rp)) { - fprintf(stderr, "Unexpected Get Clock Info len %u\n", len); - exit(EXIT_FAILURE); + error("Unexpected Get Clock Info len %u", len); + return noninteractive_quit(EXIT_FAILURE); } if (status) { - fprintf(stderr, "Get Clock Info failed with status 0x%02x (%s)\n", + error("Get Clock Info failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } - printf("Local Clock: %u\n", le32_to_cpu(rp->local_clock)); - printf("Piconet Clock: %u\n", le32_to_cpu(rp->piconet_clock)); - printf("Accurary: %u\n", le16_to_cpu(rp->accuracy)); + print("Local Clock: %u", le32_to_cpu(rp->local_clock)); + print("Piconet Clock: %u", le32_to_cpu(rp->piconet_clock)); + print("Accurary: %u", le16_to_cpu(rp->accuracy)); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void cmd_clock_info(struct mgmt *mgmt, uint16_t index, @@ -3029,8 +3078,8 @@ static void cmd_clock_info(struct mgmt *mgmt, uint16_t index, if (mgmt_send(mgmt, MGMT_OP_GET_CLOCK_INFO, index, sizeof(cp), &cp, clock_info_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send get_clock_info cmd\n"); - exit(EXIT_FAILURE); + error("Unable to send get_clock_info cmd"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -3038,14 +3087,14 @@ static void add_device_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) - fprintf(stderr, "Add device failed with status 0x%02x (%s)\n", + error("Add device failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void add_device_usage(void) { - printf("Usage: btmgmt add-device [-a action] [-t type] <address>\n"); + print("Usage: add-device [-a action] [-t type] <address>"); } static struct option add_device_options[] = { @@ -3074,9 +3123,11 @@ static void cmd_add_device(struct mgmt *mgmt, uint16_t index, type = strtol(optarg, NULL, 0); break; case 'h': + add_device_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: add_device_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -3086,7 +3137,7 @@ static void cmd_add_device(struct mgmt *mgmt, uint16_t index, if (argc < 1) { add_device_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -3098,12 +3149,12 @@ static void cmd_add_device(struct mgmt *mgmt, uint16_t index, cp.action = action; ba2str(&cp.addr.bdaddr, addr); - printf("Adding device with %s (%s)\n", addr, typestr(cp.addr.type)); + print("Adding device with %s (%s)", addr, typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_ADD_DEVICE, index, sizeof(cp), &cp, add_device_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send add device command\n"); - exit(EXIT_FAILURE); + error("Unable to send add device command"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -3111,14 +3162,14 @@ static void remove_device_rsp(uint8_t status, uint16_t len, const void *param, void *user_data) { if (status != 0) - fprintf(stderr, "Remove device failed with status 0x%02x (%s)\n", + error("Remove device failed with status 0x%02x (%s)", status, mgmt_errstr(status)); - mainloop_quit(); + noninteractive_quit(EXIT_SUCCESS); } static void del_device_usage(void) { - printf("Usage: btmgmt del-device [-t type] <address>\n"); + print("Usage: del-device [-t type] <address>"); } static struct option del_device_options[] = { @@ -3142,9 +3193,11 @@ static void cmd_del_device(struct mgmt *mgmt, uint16_t index, type = strtol(optarg, NULL, 0); break; case 'h': + del_device_usage(); + return noninteractive_quit(EXIT_SUCCESS); default: del_device_usage(); - exit(EXIT_SUCCESS); + return noninteractive_quit(EXIT_FAILURE); } } @@ -3154,7 +3207,7 @@ static void cmd_del_device(struct mgmt *mgmt, uint16_t index, if (argc < 1) { del_device_usage(); - exit(EXIT_FAILURE); + return noninteractive_quit(EXIT_FAILURE); } if (index == MGMT_INDEX_NONE) @@ -3165,12 +3218,12 @@ static void cmd_del_device(struct mgmt *mgmt, uint16_t index, cp.addr.type = type; ba2str(&cp.addr.bdaddr, addr); - printf("Removing device with %s (%s)\n", addr, typestr(cp.addr.type)); + print("Removing device with %s (%s)", addr, typestr(cp.addr.type)); if (mgmt_send(mgmt, MGMT_OP_REMOVE_DEVICE, index, sizeof(cp), &cp, remove_device_rsp, NULL, NULL) == 0) { - fprintf(stderr, "Unable to send remove device command\n"); - exit(EXIT_FAILURE); + error("Unable to send remove device command"); + return noninteractive_quit(EXIT_FAILURE); } } @@ -3183,12 +3236,15 @@ static void cmd_clr_devices(struct mgmt *mgmt, uint16_t index, cmd_del_device(mgmt, index, 2, rm_argv); } -static struct { +struct cmd_info { char *cmd; void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv); char *doc; -} command[] = { - { "monitor", cmd_monitor, "Monitor events" }, + char * (*gen) (const char *text, int state); + void (*disp) (char **matches, int num_matches, int max_length); +}; + +static struct cmd_info all_cmd[] = { { "version", cmd_version, "Get the MGMT Version" }, { "commands", cmd_commands, "List supported commands" }, { "config", cmd_config, "Show configuration info" }, @@ -3238,16 +3294,237 @@ static struct { { "add-device", cmd_add_device, "Add Device" }, { "del-device", cmd_del_device, "Remove Device" }, { "clr-devices",cmd_clr_devices,"Clear Devices" }, - { } }; -static void gap_ready(bool status, void *user_data) +static void cmd_quit(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + mainloop_exit_success(); +} + +static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index) +{ + mgmt_register(mgmt, MGMT_EV_CONTROLLER_ERROR, index, controller_error, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, index, index_added, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_INDEX_REMOVED, index, index_removed, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_NEW_SETTINGS, index, new_settings, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_DISCOVERING, index, discovering, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index, new_link_key, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_DEVICE_CONNECTED, index, connected, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_DEVICE_DISCONNECTED, index, disconnected, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_CONNECT_FAILED, index, conn_failed, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed, + NULL, NULL); + mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index, + local_name_changed, NULL, NULL); + mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found, + mgmt, NULL); + mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index, request_pin, + mgmt, NULL); + mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm, + mgmt, NULL); + mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index, + request_passkey, mgmt, NULL); + mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index, + passkey_notify, mgmt, NULL); + mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_ADDED, index, + unconf_index_added, NULL, NULL); + mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_REMOVED, index, + unconf_index_removed, NULL, NULL); + mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index, + new_config_options, NULL, NULL); + +} + +static void cmd_select(struct mgmt *mgmt, uint16_t index, + int argc, char **argv) +{ + if (argc != 2) { + error("Usage: select <index>"); + return; + } + + mgmt_cancel_all(mgmt); + mgmt_unregister_all(mgmt); + + if (!strcmp(argv[1], "none") || !strcmp(argv[1], "any") || + !strcmp(argv[1], "all")) + mgmt_index = MGMT_INDEX_NONE; + else if (!strncmp(argv[1], "hci", 3)) + mgmt_index = atoi(&argv[1][3]); + else + mgmt_index = atoi(argv[1]); + + register_mgmt_callbacks(mgmt, mgmt_index); + + print("Selected index %u", mgmt_index); + + update_prompt(mgmt_index); +} + +static struct cmd_info interactive_cmd[] = { + { "select", cmd_select, "Select a different index" }, + { "quit", cmd_quit, "Exit program" }, + { "exit", cmd_quit, "Exit program" }, + { "help", NULL, "List supported commands" }, +}; + +static char *cmd_generator(const char *text, int state) +{ + static size_t i, j, len; + const char *cmd; + + if (!state) { + i = 0; + j = 0; + len = strlen(text); + } + + while (i < NELEM(all_cmd)) { + cmd = all_cmd[i++].cmd; + + if (!strncmp(cmd, text, len)) + return strdup(cmd); + } + + while (j < NELEM(interactive_cmd)) { + cmd = interactive_cmd[j++].cmd; + + if (!strncmp(cmd, text, len)) + return strdup(cmd); + } + + return NULL; +} + +static char **cmd_completion(const char *text, int start, int end) { + char **matches = NULL; + + if (start > 0) { + unsigned int i; + + for (i = 0; i < NELEM(all_cmd); i++) { + struct cmd_info *c = &all_cmd[i]; + + if (strncmp(c->cmd, rl_line_buffer, start - 1)) + continue; + + if (!c->gen) + continue; + + rl_completion_display_matches_hook = c->disp; + matches = rl_completion_matches(text, c->gen); + break; + } + } else { + rl_completion_display_matches_hook = NULL; + matches = rl_completion_matches(text, cmd_generator); + } + + if (!matches) + rl_attempted_completion_over = 1; + + return matches; +} + +static struct cmd_info *find_cmd(const char *cmd, struct cmd_info table[], + size_t cmd_count) +{ + size_t i; + + for (i = 0; i < cmd_count; i++) { + if (!strcmp(table[i].cmd, cmd)) + return &table[i]; + } + + return NULL; +} + +static void rl_handler(char *input) +{ + struct cmd_info *c; + wordexp_t w; + char *cmd, **argv; + size_t argc, i; + + if (!input) { + rl_insert_text("quit"); + rl_redisplay(); + rl_crlf(); + mainloop_quit(); + return; + } + + if (!strlen(input)) + goto done; + + if (prompt_input(input)) + goto done; + + add_history(input); + + if (wordexp(input, &w, WRDE_NOCMD)) + goto done; + + if (w.we_wordc == 0) + goto free_we; + + cmd = w.we_wordv[0]; + argv = w.we_wordv; + argc = w.we_wordc; + + c = find_cmd(cmd, all_cmd, NELEM(all_cmd)); + if (!c && interactive) + c = find_cmd(cmd, interactive_cmd, NELEM(interactive_cmd)); + + if (c && c->func) { + c->func(mgmt, mgmt_index, argc, argv); + goto free_we; + } + + if (strcmp(cmd, "help")) { + print("Invalid command"); + goto free_we; + } + + print("Available commands:"); + + for (i = 0; i < NELEM(all_cmd); i++) { + c = &all_cmd[i]; + if (c->doc) + print(" %s %-*s %s", c->cmd, + (int)(25 - strlen(c->cmd)), "", c->doc ? : ""); + } + + if (!interactive) + goto free_we; + + for (i = 0; i < NELEM(interactive_cmd); i++) { + c = &interactive_cmd[i]; + if (c->doc) + print(" %s %-*s %s", c->cmd, + (int)(25 - strlen(c->cmd)), "", c->doc ? : ""); + } + +free_we: + wordfree(&w); +done: + free(input); } static void usage(void) { - int i; + unsigned int i; printf("btmgmt ver %s\n", VERSION); printf("Usage:\n" @@ -3259,8 +3536,8 @@ static void usage(void) "\t--help\tDisplay help\n"); printf("Commands:\n"); - for (i = 0; command[i].cmd; i++) - printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc); + for (i = 0; i < NELEM(all_cmd); i++) + printf("\t%-15s\t%s\n", all_cmd[i].cmd, all_cmd[i].doc); printf("\n" "For more information on the usage of each command use:\n" @@ -3274,15 +3551,32 @@ static struct option main_options[] = { { 0, 0, 0, 0 } }; +static bool prompt_read(struct io *io, void *user_data) +{ + rl_callback_read_char(); + return true; +} + +static struct io *setup_stdin(void) +{ + struct io *io; + + io = io_new(STDIN_FILENO); + if (!io) + return io; + + io_set_read_handler(io, prompt_read, NULL, NULL); + + return io; +} + int main(int argc, char *argv[]) { - struct bt_gap *gap; - int opt, i; + struct io *input; uint16_t index = MGMT_INDEX_NONE; - struct mgmt *mgmt; - int exit_status; + int status, opt; - while ((opt = getopt_long(argc, argv, "+hvi:", + while ((opt = getopt_long(argc, argv, "+hi:", main_options, NULL)) != -1) { switch (opt) { case 'i': @@ -3292,9 +3586,6 @@ int main(int argc, char *argv[]) else index = atoi(optarg); break; - case 'v': - monitor = true; - break; case 'h': default: usage(); @@ -3306,86 +3597,61 @@ int main(int argc, char *argv[]) argv += optind; optind = 0; - if (argc < 1) { - usage(); - return 0; - } - mainloop_init(); - if (index == MGMT_INDEX_NONE) - gap = bt_gap_new_default(); - else - gap = bt_gap_new_index(index); - - bt_gap_set_ready_handler(gap, gap_ready, NULL, NULL); - mgmt = mgmt_new_default(); if (!mgmt) { fprintf(stderr, "Unable to open mgmt_socket\n"); return EXIT_FAILURE; } - for (i = 0; command[i].cmd; i++) { - if (strcmp(command[i].cmd, argv[0]) != 0) - continue; + if (argc > 0) { + struct cmd_info *c; + + c = find_cmd(argv[0], all_cmd, NELEM(all_cmd)); + if (!c) { + fprintf(stderr, "Unknown command: %s\n", argv[0]); + mgmt_unref(mgmt); + return EXIT_FAILURE; + } - command[i].func(mgmt, index, argc, argv); - break; + c->func(mgmt, index, argc, argv); } - if (command[i].cmd == NULL) { - fprintf(stderr, "Unknown command: %s\n", argv[0]); - mgmt_unref(mgmt); - return EXIT_FAILURE; + register_mgmt_callbacks(mgmt, index); + + /* Interactive mode */ + if (!argc) + input = setup_stdin(); + else + input = NULL; + + if (input) { + interactive = true; + + rl_attempted_completion_function = cmd_completion; + + rl_erase_empty_line = 1; + rl_callback_handler_install(NULL, rl_handler); + + update_prompt(index); + rl_redisplay(); } - mgmt_register(mgmt, MGMT_EV_CONTROLLER_ERROR, index, controller_error, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, index, index_added, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_INDEX_REMOVED, index, index_removed, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_NEW_SETTINGS, index, new_settings, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_DISCOVERING, index, discovering, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index, new_link_key, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_DEVICE_CONNECTED, index, connected, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_DEVICE_DISCONNECTED, index, disconnected, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_CONNECT_FAILED, index, conn_failed, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed, - NULL, NULL); - mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index, - local_name_changed, NULL, NULL); - mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found, - mgmt, NULL); - mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index, request_pin, - mgmt, NULL); - mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm, - mgmt, NULL); - mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index, - request_passkey, mgmt, NULL); - mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index, - passkey_notify, mgmt, NULL); - mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_ADDED, index, - unconf_index_added, NULL, NULL); - mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_REMOVED, index, - unconf_index_removed, NULL, NULL); - mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index, - new_config_options, NULL, NULL); + mgmt_index = index; - exit_status = mainloop_run(); + status = mainloop_run(); + + if (input) { + io_destroy(input); + + rl_message(""); + rl_callback_handler_remove(); + } mgmt_cancel_all(mgmt); mgmt_unregister_all(mgmt); mgmt_unref(mgmt); - bt_gap_unref(gap); - - return exit_status; + return status; } diff --git a/tools/btproxy.c b/tools/btproxy.c index 35031482..43ccac17 100644 --- a/tools/btproxy.c +++ b/tools/btproxy.c @@ -44,9 +44,12 @@ #include <arpa/inet.h> #include "src/shared/util.h" -#include "monitor/mainloop.h" +#include "src/shared/mainloop.h" #include "monitor/bt.h" +#define HCI_BREDR 0x00 +#define HCI_AMP 0x01 + #define BTPROTO_HCI 1 struct sockaddr_hci { sa_family_t hci_family; @@ -563,6 +566,7 @@ static void usage(void) "\t-u, --unix [path] Use Unix server\n" "\t-p, --port <port> Use specified TCP port\n" "\t-i, --index <num> Use specified controller\n" + "\t-a, --amp Create AMP controller\n" "\t-d, --debug Enable debugging output\n" "\t-h, --help Show help options\n"); } @@ -573,6 +577,7 @@ static const struct option main_options[] = { { "unix", optional_argument, NULL, 'u' }, { "port", required_argument, NULL, 'p' }, { "index", required_argument, NULL, 'i' }, + { "amp", no_argument, NULL, 'a' }, { "debug", no_argument, NULL, 'd' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, @@ -585,18 +590,22 @@ int main(int argc, char *argv[]) const char *server_address = NULL; const char *unix_path = NULL; unsigned short tcp_port = 0xb1ee; /* 45550 */ + uint8_t type = HCI_BREDR; const char *str; sigset_t mask; for (;;) { int opt; - opt = getopt_long(argc, argv, "c:l::u::p:i:dvh", + opt = getopt_long(argc, argv, "ac:l::u::p:i:dvh", main_options, NULL); if (opt < 0) break; switch (opt) { + case 'a': + type = HCI_AMP; + break; case 'c': connect_address = optarg; break; @@ -674,7 +683,7 @@ int main(int argc, char *argv[]) printf("Opening virtual device\n"); - host_fd = open_vhci(0x00); + host_fd = open_vhci(type); if (host_fd < 0) { close(dev_fd); return EXIT_FAILURE; diff --git a/tools/btsnoop.c b/tools/btsnoop.c index 71191ebc..3eb8082d 100644 --- a/tools/btsnoop.c +++ b/tools/btsnoop.c @@ -230,7 +230,7 @@ next_packet: if (flags & 0x01) opcode = BTSNOOP_OPCODE_SCO_RX_PKT; else - opcode = BTSNOOP_OPCODE_ACL_TX_PKT; + opcode = BTSNOOP_OPCODE_SCO_TX_PKT; break; case 0x04: opcode = BTSNOOP_OPCODE_EVENT_PKT; diff --git a/tools/ciptool.c b/tools/ciptool.c index 3ff9fb1f..e60493d1 100644 --- a/tools/ciptool.c +++ b/tools/ciptool.c @@ -36,13 +36,13 @@ #include <sys/ioctl.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> -#include <bluetooth/cmtp.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/l2cap.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "lib/cmtp.h" static volatile sig_atomic_t __io_canceled = 0; diff --git a/tools/cltest.c b/tools/cltest.c index 0231805b..95fa7b63 100644 --- a/tools/cltest.c +++ b/tools/cltest.c @@ -36,12 +36,12 @@ #include <sys/ioctl.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/l2cap.h" -#include "monitor/mainloop.h" +#include "src/shared/mainloop.h" static bool send_message(const bdaddr_t *src, const bdaddr_t *dst, uint16_t psm) diff --git a/tools/csr.c b/tools/csr.c index 36af921e..2c091890 100644 --- a/tools/csr.c +++ b/tools/csr.c @@ -35,9 +35,9 @@ #include <sys/mman.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "csr.h" diff --git a/tools/csr_hci.c b/tools/csr_hci.c index 6bd37c35..d2e4ab9c 100644 --- a/tools/csr_hci.c +++ b/tools/csr_hci.c @@ -30,9 +30,9 @@ #include <string.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "csr.h" diff --git a/tools/gap-tester.c b/tools/gap-tester.c index 2a0be915..2aa40426 100644 --- a/tools/gap-tester.c +++ b/tools/gap-tester.c @@ -25,7 +25,7 @@ #include <config.h> #endif -#include <gdbus.h> +#include "gdbus/gdbus.h" #include "src/shared/tester.h" #include "emulator/hciemu.h" diff --git a/tools/gatt-example b/tools/gatt-example new file mode 100644 index 00000000..a6f5cbe1 --- /dev/null +++ b/tools/gatt-example @@ -0,0 +1,533 @@ +#!/usr/bin/python + +import dbus +import dbus.exceptions +import dbus.mainloop.glib +import dbus.service + +import array +import gobject + +from random import randint + +mainloop = None + +BLUEZ_SERVICE_NAME = 'org.bluez' +GATT_MANAGER_IFACE = 'org.bluez.GattManager1' +DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' +DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' + +GATT_SERVICE_IFACE = 'org.bluez.GattService1' +GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1' +GATT_DESC_IFACE = 'org.bluez.GattDescriptor1' + +class InvalidArgsException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs' + +class NotSupportedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.NotSupported' + +class NotPermittedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.NotPermitted' + +class InvalidValueLengthException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.InvalidValueLength' + +class FailedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.Failed' + + +class Service(dbus.service.Object): + PATH_BASE = '/org/bluez/example/service' + + def __init__(self, bus, index, uuid, primary): + self.path = self.PATH_BASE + str(index) + self.bus = bus + self.uuid = uuid + self.primary = primary + self.characteristics = [] + dbus.service.Object.__init__(self, bus, self.path) + + def get_properties(self): + return { + GATT_SERVICE_IFACE: { + 'UUID': self.uuid, + 'Primary': self.primary, + 'Characteristics': dbus.Array( + self.get_characteristic_paths(), + signature='o') + } + } + + def get_path(self): + return dbus.ObjectPath(self.path) + + def add_characteristic(self, characteristic): + self.characteristics.append(characteristic) + + def get_characteristic_paths(self): + result = [] + for chrc in self.characteristics: + result.append(chrc.get_path()) + return result + + def get_characteristics(self): + return self.characteristics + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + if interface != GATT_SERVICE_IFACE: + raise InvalidArgsException() + + return self.get_properties[GATT_SERVICE_IFACE] + + @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') + def GetManagedObjects(self): + response = {} + print 'GetManagedObjects' + + response[self.get_path()] = self.get_properties() + chrcs = self.get_characteristics() + for chrc in chrcs: + response[chrc.get_path()] = chrc.get_properties() + descs = chrc.get_descriptors() + for desc in descs: + response[desc.get_path()] = desc.get_properties() + + return response + + +class Characteristic(dbus.service.Object): + def __init__(self, bus, index, uuid, flags, service): + self.path = service.path + '/char' + str(index) + self.bus = bus + self.uuid = uuid + self.service = service + self.flags = flags + self.descriptors = [] + dbus.service.Object.__init__(self, bus, self.path) + + def get_properties(self): + return { + GATT_CHRC_IFACE: { + 'Service': self.service.get_path(), + 'UUID': self.uuid, + 'Flags': self.flags, + 'Descriptors': dbus.Array( + self.get_descriptor_paths(), + signature='o') + } + } + + def get_path(self): + return dbus.ObjectPath(self.path) + + def add_descriptor(self, descriptor): + self.descriptors.append(descriptor) + + def get_descriptor_paths(self): + result = [] + for desc in self.descriptors: + result.append(desc.get_path()) + return result + + def get_descriptors(self): + return self.descriptors + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + if interface != GATT_CHRC_IFACE: + raise InvalidArgsException() + + return self.get_properties[GATT_CHRC_IFACE] + + @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay') + def ReadValue(self): + print 'Default ReadValue called, returning error' + raise NotSupportedException() + + @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay') + def WriteValue(self, value): + print 'Default WriteValue called, returning error' + raise NotSupportedException() + + @dbus.service.method(GATT_CHRC_IFACE) + def StartNotify(self): + print 'Default StartNotify called, returning error' + raise NotSupportedException() + + @dbus.service.method(GATT_CHRC_IFACE) + def StopNotify(self): + print 'Default StopNotify called, returning error' + raise NotSupportedException() + + @dbus.service.signal(DBUS_PROP_IFACE, + signature='sa{sv}as') + def PropertiesChanged(self, interface, changed, invalidated): + pass + + +class Descriptor(dbus.service.Object): + def __init__(self, bus, index, uuid, characteristic): + self.path = characteristic.path + '/desc' + str(index) + self.bus = bus + self.uuid = uuid + self.chrc = characteristic + dbus.service.Object.__init__(self, bus, self.path) + + def get_properties(self): + return { + GATT_DESC_IFACE: { + 'Characteristic': self.chrc.get_path(), + 'UUID': self.uuid, + } + } + + def get_path(self): + return dbus.ObjectPath(self.path) + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + if interface != GATT_DESC_IFACE: + raise InvalidArgsException() + + return self.get_properties[GATT_CHRC_IFACE] + + @dbus.service.method(GATT_DESC_IFACE, out_signature='ay') + def ReadValue(self): + print 'Default ReadValue called, returning error' + raise NotSupportedException() + + @dbus.service.method(GATT_DESC_IFACE, in_signature='ay') + def WriteValue(self, value): + print 'Default WriteValue called, returning error' + raise NotSupportedException() + + +class HeartRateService(Service): + """ + Fake Heart Rate Service that simulates a fake heart beat and control point + behavior. + + """ + HR_UUID = '0000180d-0000-1000-8000-00805f9b34fb' + + def __init__(self, bus, index): + Service.__init__(self, bus, index, self.HR_UUID, True) + self.add_characteristic(HeartRateMeasurementChrc(bus, 0, self)) + self.add_characteristic(BodySensorLocationChrc(bus, 1, self)) + self.add_characteristic(HeartRateControlPointChrc(bus, 2, self)) + self.energy_expended = 0 + + +class HeartRateMeasurementChrc(Characteristic): + HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.HR_MSRMT_UUID, + ['notify'], + service) + self.notifying = False + self.hr_ee_count = 0 + + def hr_msrmt_cb(self): + value = [] + value.append(dbus.Byte(0x06)) + + value.append(dbus.Byte(randint(90, 130))) + + if self.hr_ee_count % 10 == 0: + value[0] = dbus.Byte(value[0] | 0x08) + value.append(dbus.Byte(self.service.energy_expended & 0xff)) + value.append(dbus.Byte((self.service.energy_expended >> 8) & 0xff)) + + self.service.energy_expended = \ + min(0xffff, self.service.energy_expended + 1) + self.hr_ee_count += 1 + + print 'Updating value: ' + repr(value) + + self.PropertiesChanged(GATT_CHRC_IFACE, { 'Value': value }, []) + + return self.notifying + + def _update_hr_msrmt_simulation(self): + print 'Update HR Measurement Simulation' + + if not self.notifying: + return + + gobject.timeout_add(1000, self.hr_msrmt_cb) + + def StartNotify(self): + if self.notifying: + print 'Already notifying, nothing to do' + return + + self.notifying = True + self._update_hr_msrmt_simulation() + + def StopNotify(self): + if not self.notifying: + print 'Not notifying, nothing to do' + return + + self.notifying = False + self._update_hr_msrmt_simulation() + + +class BodySensorLocationChrc(Characteristic): + BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.BODY_SNSR_LOC_UUID, + ['read'], + service) + + def ReadValue(self): + # Return 'Chest' as the sensor location. + return [ 0x01 ] + +class HeartRateControlPointChrc(Characteristic): + HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.HR_CTRL_PT_UUID, + ['write'], + service) + + def WriteValue(self, value): + print 'Heart Rate Control Point WriteValue called' + + if len(value) != 1: + raise InvalidValueLengthException() + + byte = value[0] + print 'Control Point value: ' + repr(byte) + + if byte != 1: + raise FailedException("0x80") + + print 'Energy Expended field reset!' + self.service.energy_expended = 0 + + +class BatteryService(Service): + """ + Fake Battery service that emulates a draining battery. + + """ + BATTERY_UUID = '180f' + + def __init__(self, bus, index): + Service.__init__(self, bus, index, self.BATTERY_UUID, True) + self.add_characteristic(BatteryLevelCharacteristic(bus, 0, self)) + + +class BatteryLevelCharacteristic(Characteristic): + """ + Fake Battery Level characteristic. The battery level is drained by 2 points + every 5 seconds. + + """ + BATTERY_LVL_UUID = '2a19' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.BATTERY_LVL_UUID, + ['read', 'notify'], + service) + self.notifying = False + self.battery_lvl = 100 + gobject.timeout_add(5000, self.drain_battery) + + def notify_battery_level(self): + if not self.notifying: + return + self.PropertiesChanged( + GATT_CHRC_IFACE, + { 'Value': [dbus.Byte(self.battery_lvl)] }, []) + + def drain_battery(self): + if self.battery_lvl > 0: + self.battery_lvl -= 2 + if self.battery_lvl < 0: + self.battery_lvl = 0 + print 'Battery Level drained: ' + repr(self.battery_lvl) + self.notify_battery_level() + return True + + def ReadValue(self): + print 'Battery Level read: ' + repr(self.battery_lvl) + return [dbus.Byte(self.battery_lvl)] + + def StartNotify(self): + if self.notifying: + print 'Already notifying, nothing to do' + return + + self.notifying = True + self.notify_battery_level() + + def StopNotify(self): + if not self.notifying: + print 'Not notifying, nothing to do' + return + + self.notifying = False + + +class TestService(Service): + """ + Dummy test service that provides characteristics and descriptors that + exercise various API functionality. + + """ + TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0' + + def __init__(self, bus, index): + Service.__init__(self, bus, index, self.TEST_SVC_UUID, False) + self.add_characteristic(TestCharacteristic(bus, 0, self)) + + +class TestCharacteristic(Characteristic): + """ + Dummy test characteristic. Allows writing arbitrary bytes to its value, and + contains "extended properties", as well as a test descriptor. + + """ + TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef1' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.TEST_CHRC_UUID, + ['read', 'write', 'writable-auxiliaries'], + service) + self.value = [] + self.add_descriptor(TestDescriptor(bus, 0, self)) + self.add_descriptor( + CharacteristicUserDescriptionDescriptor(bus, 1, self)) + + def ReadValue(self): + print 'TestCharacteristic Read: ' + repr(self.value) + return self.value + + def WriteValue(self, value): + print 'TestCharacteristic Write: ' + repr(value) + self.value = value + + +class TestDescriptor(Descriptor): + """ + Dummy test descriptor. Returns a static value. + + """ + TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef2' + + def __init__(self, bus, index, characteristic): + Descriptor.__init__( + self, bus, index, + self.TEST_DESC_UUID, + characteristic) + + def ReadValue(self): + return [ + dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t') + ] + + +class CharacteristicUserDescriptionDescriptor(Descriptor): + """ + Writable CUD descriptor. + + """ + CUD_UUID = '2901' + + def __init__(self, bus, index, characteristic): + self.writable = 'writable-auxiliaries' in characteristic.flags + self.value = array.array('B', 'This is a characteristic for testing') + self.value = self.value.tolist() + Descriptor.__init__( + self, bus, index, + self.CUD_UUID, + characteristic) + + def ReadValue(self): + return self.value + + def WriteValue(self, value): + if not self.writable: + raise NotPermittedException() + self.value = value + + +def register_service_cb(): + print 'GATT service registered' + + +def register_service_error_cb(error): + print 'Failed to register service: ' + str(error) + mainloop.quit() + + +def find_adapter(bus): + remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), + DBUS_OM_IFACE) + objects = remote_om.GetManagedObjects() + + for o, props in objects.iteritems(): + if props.has_key(GATT_MANAGER_IFACE): + return o + + return None + +def main(): + global mainloop + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + + adapter = find_adapter(bus) + if not adapter: + print 'GattManager1 interface not found' + return + + service_manager = dbus.Interface( + bus.get_object(BLUEZ_SERVICE_NAME, adapter), + GATT_MANAGER_IFACE) + + hr_service = HeartRateService(bus, 0) + bat_service = BatteryService(bus, 1) + test_service = TestService(bus, 2) + + mainloop = gobject.MainLoop() + + service_manager.RegisterService(hr_service.get_path(), {}, + reply_handler=register_service_cb, + error_handler=register_service_error_cb) + service_manager.RegisterService(bat_service.get_path(), {}, + reply_handler=register_service_cb, + error_handler=register_service_error_cb) + service_manager.RegisterService(test_service.get_path(), {}, + reply_handler=register_service_cb, + error_handler=register_service_error_cb) + + mainloop.run() + +if __name__ == '__main__': + main() diff --git a/tools/gatt-service.c b/tools/gatt-service.c index 6bca4047..33e0d6a0 100644 --- a/tools/gatt-service.c +++ b/tools/gatt-service.c @@ -33,7 +33,8 @@ #include <glib.h> #include <dbus/dbus.h> -#include <gdbus/gdbus.h> + +#include "gdbus/gdbus.h" #include "src/error.h" diff --git a/tools/hciattach.c b/tools/hciattach.c index 3535faa7..150c5d04 100644 --- a/tools/hciattach.c +++ b/tools/hciattach.c @@ -45,9 +45,9 @@ #include <sys/param.h> #include <sys/ioctl.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "hciattach.h" @@ -1315,7 +1315,7 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) if (tcgetattr(fd, &ti) < 0) { perror("Can't get port settings"); - return -1; + goto fail; } cfmakeraw(&ti); @@ -1331,13 +1331,13 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) if (tcsetattr(fd, TCSANOW, &ti) < 0) { perror("Can't set port settings"); - return -1; + goto fail; } /* Set initial baudrate */ if (set_speed(fd, &ti, u->init_speed) < 0) { perror("Can't set initial baud rate"); - return -1; + goto fail; } tcflush(fd, TCIOFLUSH); @@ -1358,39 +1358,43 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) #endif if (u->init && u->init(fd, u, &ti) < 0) - return -1; + goto fail; tcflush(fd, TCIOFLUSH); /* Set actual baudrate */ if (set_speed(fd, &ti, u->speed) < 0) { perror("Can't set baud rate"); - return -1; + goto fail; } /* Set TTY to N_HCI line discipline */ i = N_HCI; if (ioctl(fd, TIOCSETD, &i) < 0) { perror("Can't set line discipline"); - return -1; + goto fail; } if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) { perror("Can't set UART flags"); - return -1; + goto fail; } if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) { perror("Can't set device"); - return -1; + goto fail; } #if 0 if (u->post && u->post(fd, u, &ti) < 0) - return -1; + goto fail; #endif return fd; + +fail: + close(fd); + return -1; } #endif /* __BROADCOM_PATCH__ */ @@ -1530,7 +1534,7 @@ int main(int argc, char *argv[]) dev[0] = 0; if (!strchr(opt, '/')) strcpy(dev, "/dev/"); - strncat(dev, opt, PATH_MAX); + strcat(dev, opt); break; case 1: diff --git a/tools/hciattach_ath3k.c b/tools/hciattach_ath3k.c index 23208c66..a76b4483 100644 --- a/tools/hciattach_ath3k.c +++ b/tools/hciattach_ath3k.c @@ -33,9 +33,9 @@ #include <sys/param.h> #include <sys/ioctl.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "hciattach.h" @@ -840,17 +840,8 @@ static int ath_ps_download(int fd) goto download_cmplete; } - /* - * It is not necessary that Patch file be available, - * continue with PS Operations if patch file is not available. - */ - if (patch_file[0] == '\0') - err = 0; - stream = fopen(patch_file, "r"); - if (!stream) - err = 0; - else { + if(stream) { patch_count = ps_patch_download(fd, stream); fclose(stream); diff --git a/tools/hciattach_bcm43xx.c b/tools/hciattach_bcm43xx.c index 3d36c209..81f38cbb 100644 --- a/tools/hciattach_bcm43xx.c +++ b/tools/hciattach_bcm43xx.c @@ -36,9 +36,9 @@ #include <time.h> #include <limits.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "hciattach.h" diff --git a/tools/hciattach_intel.c b/tools/hciattach_intel.c index 749098ef..2650dcb6 100644 --- a/tools/hciattach_intel.c +++ b/tools/hciattach_intel.c @@ -35,9 +35,9 @@ #include <sys/ioctl.h> #include <time.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "hciattach.h" diff --git a/tools/hciattach_qualcomm.c b/tools/hciattach_qualcomm.c index 0e25905f..22ac6297 100644 --- a/tools/hciattach_qualcomm.c +++ b/tools/hciattach_qualcomm.c @@ -42,9 +42,9 @@ #include <sys/ioctl.h> #include <sys/uio.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "hciattach.h" diff --git a/tools/hciattach_st.c b/tools/hciattach_st.c index dbb7c47b..474545a7 100644 --- a/tools/hciattach_st.c +++ b/tools/hciattach_st.c @@ -35,7 +35,7 @@ #include <dirent.h> #include <sys/param.h> -#include <bluetooth/bluetooth.h> +#include "lib/bluetooth.h" #include "hciattach.h" diff --git a/tools/hciattach_ti.c b/tools/hciattach_ti.c index 9099bb44..14053d0b 100644 --- a/tools/hciattach_ti.c +++ b/tools/hciattach_ti.c @@ -37,9 +37,9 @@ #include <sys/param.h> #include <sys/ioctl.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "hciattach.h" diff --git a/tools/hciattach_tialt.c b/tools/hciattach_tialt.c index a7c17061..f6ef068f 100644 --- a/tools/hciattach_tialt.c +++ b/tools/hciattach_tialt.c @@ -41,9 +41,9 @@ #include <sys/ioctl.h> #include <sys/uio.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "hciattach.h" diff --git a/tools/hciconfig.c b/tools/hciconfig.c index 8ec1e8aa..6397e715 100644 --- a/tools/hciconfig.c +++ b/tools/hciconfig.c @@ -39,9 +39,9 @@ #include <sys/socket.h> #include <sys/stat.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "src/textfile.h" #include "src/shared/util.h" @@ -624,6 +624,9 @@ static void cmd_features(int ctl, int hdev, char *opt) exit(1); } + if (max_page < 1 && (features[6] & LMP_SIMPLE_PAIR)) + max_page = 1; + print_dev_hdr(&di); printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", diff --git a/tools/hcieventmask.c b/tools/hcieventmask.c index 87beac95..b5f818d3 100644 --- a/tools/hcieventmask.c +++ b/tools/hcieventmask.c @@ -31,9 +31,9 @@ #include <getopt.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" static struct option main_options[] = { { "device", 1, 0, 'i' }, diff --git a/tools/hcisecfilter.c b/tools/hcisecfilter.c index 9ad4ce0c..18c90333 100644 --- a/tools/hcisecfilter.c +++ b/tools/hcisecfilter.c @@ -29,11 +29,11 @@ #include <stdio.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" -int main(void) +int main(int argc, char *argv[]) { uint32_t type_mask; uint32_t event_mask[2]; diff --git a/tools/hcitool.1 b/tools/hcitool.1 index 85498dc6..7d065563 100644 --- a/tools/hcitool.1 +++ b/tools/hcitool.1 @@ -203,6 +203,52 @@ The clock can be for the local clock or .BR 1 for the piconet clock (which is default). +.TP +.BI lescan " [--privacy] [--passive] [--whitelist] [--discovery=g|l] \ +[--duplicates]" +Start LE scan +.TP +.BI leinfo " [--static] [--random] <bdaddr>" +Get LE remote information +.TP +.BI lewladd " [--random] <bdaddr>" +Add device to LE White List +.TP +.BI lewlrm " <bdaddr>" +Remove device from LE White List +.TP +.BI lewlsz +Read size of LE White List +.TP +.BI lewlclr +Clear LE White List +.TP +.BI lerladd " [--local irk] [--peer irk] [--random] <bdaddr>" +Add device to LE Resolving List +.TP +.BI lerlrm " <bdaddr>" +Remove device from LE Resolving List +.TP +.BI lerlclr +Clear LE Resolving List +.TP +.BI lerlsz +Read size of LE Resolving List +.TP +.BI lerlon +Enable LE Address Resolution +.TP +.BI lerloff +Disable LE Address Resolution +.TP +.BI lecc " [--static] [--random] <bdaddr> | [--whitelist]" +Create a LE Connection +.TP +.BI ledc " <handle> [reason]" +Disconnect a LE Connection +.TP +.BI lecup " <handle> <min> <max> <latency> <timeout>" +LE Connection Update .SH AUTHORS Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org> .PP diff --git a/tools/hcitool.c b/tools/hcitool.c index 2c84dc7d..02c4ebe1 100644 --- a/tools/hcitool.c +++ b/tools/hcitool.c @@ -40,9 +40,9 @@ #include <sys/socket.h> #include <signal.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" #include "src/oui.h" @@ -967,6 +967,9 @@ static void cmd_info(int dev_id, int argc, char **argv) hci_read_remote_ext_features(dd, handle, 0, &max_page, features, 20000); + if (max_page < 1 && (features[6] & LMP_SIMPLE_PAIR)) + max_page = 1; + printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", (max_page > 0) ? " page 0" : "", @@ -2404,9 +2407,7 @@ failed: static int print_advertising_devices(int dd, uint8_t filter_type) { - unsigned char buf_array[HCI_MAX_EVENT_SIZE+1] = {0}; - unsigned char *buf = buf_array; - unsigned char *ptr = NULL; + unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr; struct hci_filter nf, of; struct sigaction sa; socklen_t olen; @@ -2433,14 +2434,11 @@ static int print_advertising_devices(int dd, uint8_t filter_type) sigaction(SIGINT, &sa, NULL); while (1) { - evt_le_meta_event *meta = NULL; - le_advertising_info *info = NULL; - char addr_array[18]; - char *addr = addr_array; - - buf[HCI_MAX_EVENT_SIZE] = 0; + evt_le_meta_event *meta; + le_advertising_info *info; + char addr[18]; - while ((len = read(dd, buf, HCI_MAX_EVENT_SIZE)) < 0) { + while ((len = read(dd, buf, sizeof(buf))) < 0) { if (errno == EINTR && signal_received == SIGINT) { len = 0; goto done; @@ -2462,16 +2460,14 @@ static int print_advertising_devices(int dd, uint8_t filter_type) /* Ignoring multiple reports */ info = (le_advertising_info *) (meta->data + 1); if (check_report_filter(filter_type, info)) { - char name_array[30]; - char *name = name_array; + char name[30]; - memset(name, 0, 30); + memset(name, 0, sizeof(name)); ba2str(&info->bdaddr, addr); eir_parse_name(info->data, info->length, - name, 29); + name, sizeof(name) - 1); - name[29] = '\0'; printf("%s %s\n", addr, name); } } @@ -3300,18 +3296,19 @@ static const char *lecup_help = "Usage:\n" "\tlecup <handle> <min> <max> <latency> <timeout>\n" "\tOptions:\n" - "\t -H, --handle <0xXXXX> LE connection handle\n" - "\t -m, --min <interval> Range: 0x0006 to 0x0C80\n" - "\t -M, --max <interval> Range: 0x0006 to 0x0C80\n" - "\t -l, --latency <range> Slave latency. Range: 0x0000 to 0x03E8\n" - "\t -t, --timeout <time> N * 10ms. Range: 0x000A to 0x0C80\n" + "\t --handle=<0xXXXX> LE connection handle\n" + "\t --min=<interval> Range: 0x0006 to 0x0C80\n" + "\t --max=<interval> Range: 0x0006 to 0x0C80\n" + "\t --latency=<range> Slave latency. Range: 0x0000 to 0x03E8\n" + "\t --timeout=<time> N * 10ms. Range: 0x000A to 0x0C80\n" "\n\t min/max range: 7.5ms to 4s. Multiply factor: 1.25ms" "\n\t timeout range: 100ms to 32.0s. Larger than max interval\n"; static void cmd_lecup(int dev_id, int argc, char **argv) { uint16_t handle = 0, min, max, latency, timeout; - int opt, dd, base; + int opt, dd; + int options = 0; /* Aleatory valid values */ min = 0x0C8; @@ -3320,31 +3317,38 @@ static void cmd_lecup(int dev_id, int argc, char **argv) timeout = 0x0C80; for_each_opt(opt, lecup_options, NULL) { - if (optarg && strncasecmp("0x", optarg, 2) == 0) - base = 16; - else - base = 10; - switch (opt) { case 'H': - handle = strtoul(optarg, NULL, base); + handle = strtoul(optarg, NULL, 0); break; case 'm': - min = strtoul(optarg, NULL, base); + min = strtoul(optarg, NULL, 0); break; case 'M': - max = strtoul(optarg, NULL, base); + max = strtoul(optarg, NULL, 0); break; case 'l': - latency = strtoul(optarg, NULL, base); + latency = strtoul(optarg, NULL, 0); break; case 't': - timeout = strtoul(optarg, NULL, base); + timeout = strtoul(optarg, NULL, 0); break; default: printf("%s", lecup_help); return; } + + options = 1; + } + + if (options == 0) { + helper_arg(5, 5, &argc, &argv, lecup_help); + + handle = strtoul(argv[0], NULL, 0); + min = strtoul(argv[1], NULL, 0); + max = strtoul(argv[2], NULL, 0); + latency = strtoul(argv[3], NULL, 0); + timeout = strtoul(argv[4], NULL, 0); } if (handle == 0) { diff --git a/tools/hex2hcd.c b/tools/hex2hcd.c index d9b5d3b2..943531cd 100644 --- a/tools/hex2hcd.c +++ b/tools/hex2hcd.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2012 Canonical + * Copyright (C) 2012-2013 Intel Corporation * * * This program is free software; you can redistribute it and/or modify @@ -25,119 +25,422 @@ #include <config.h> #endif -#include <stdlib.h> #include <stdio.h> #include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <dirent.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> +#include <sys/stat.h> + +static ssize_t process_record(int fd, const char *line, uint16_t *upper_addr) +{ + const char *ptr = line + 1; + char str[3]; + size_t len; + uint8_t *buf; + uint32_t addr; + uint8_t sum = 0; + int n = 0; + + if (line[0] != ':') { + fprintf(stderr, "Invalid record start code (%c)\n", line[0]); + return -EINVAL; + } + + len = strlen(line); + if (len < 11) { + fprintf(stderr, "Record information is too short\n"); + return -EILSEQ; + } -#define RBUF_SIZE 640 + buf = malloc((len / 2) + 3); + if (!buf) { + fprintf(stderr, "Failed to allocate memory for record data\n"); + return -ENOMEM; + } + + while (1) { + str[0] = *ptr++; + str[1] = *ptr++; + str[2] = '\0'; + + buf[3 + n] = strtol(str, NULL, 16); + + if (*ptr == '\r' || *ptr == '\n') + break; + + sum += buf[3 + n++]; + } + + sum = 0x100 - (sum & 0xff); + + if (n < 4 || buf[3] + 4 != n) { + fprintf(stderr, "Record length is not matching data\n"); + free(buf); + return -EILSEQ; + } + + if (buf[3 + n] != sum) { + fprintf(stderr, "Checksum mismatch\n"); + free(buf); + return -EILSEQ; + } + + switch (buf[6]) { + case 0x00: + addr = (*upper_addr << 16) + (buf[4] << 8) + buf[5]; + + buf[0] = 0x4c; + buf[1] = 0xfc; + buf[2] = n; + + buf[3] = (addr & 0x000000ff); + buf[4] = (addr & 0x0000ff00) >> 8; + buf[5] = (addr & 0x00ff0000) >> 16; + buf[6] = (addr & 0xff000000) >> 24; + + if (write(fd, buf, n + 3) < 0) { + perror("Failed to write data record"); + free(buf); + return -errno; + } + break; + case 0x01: + buf[0] = 0x4e; + buf[1] = 0xfc; + buf[2] = 0x04; + + buf[3] = 0xff; + buf[4] = 0xff; + buf[5] = 0xff; + buf[6] = 0xff; + + if (write(fd, buf, 7) < 0) { + perror("Failed to write end record"); + free(buf); + return -errno; + } + break; + case 0x04: + *upper_addr = (buf[7] << 8) + buf[8]; + break; + default: + fprintf(stderr, "Unsupported record type (%02X)\n", buf[3]); + free(buf); + return -EILSEQ; + } -static unsigned int asc_to_int(char a) + free(buf); + + return len; +} + +static void convert_file(const char *input_path, const char *output_path) { - if (a >= 'A') - return (a - 'A') + 10; - else - return a - '0'; + uint16_t upper_addr = 0x0000; + 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), ".hcd"); + } else { + if (asprintf(&path, "%s.hcd", 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; + ssize_t len; + + str = fgets(line_buffer, line_size - 1, fp); + if (!str) + break; + + len = process_record(fd, str, &upper_addr); + if (len < 0) + goto done; + + cur += len; + } + + if (cur != st.st_size) { + fprintf(stderr, "Data length does not match file length\n"); + goto done; + } + +done: + fclose(fp); + + close(fd); } -static unsigned int hex_to_int(const char *h) +struct ver_data { + uint16_t num; + char name[20]; + char major[4]; + char minor[4]; + char build[4]; + struct ver_data *next; +}; + +static struct ver_data *ver_list = NULL; + +static void ver_parse_file(const char *pathname) { - return asc_to_int(*h) * 0x10 + asc_to_int(*(h + 1)); + struct ver_data *ver, *tmp, *prev; + char dummy1[5], dummy2[5]; + + if (strlen(pathname) < 7) + return; + + if (strncmp(pathname, "BCM", 3)) + return; + + ver = malloc(sizeof(*ver)); + if (!ver) + return; + + memset(ver, 0, sizeof(*ver)); + + if (sscanf(pathname, "%[A-Z0-9]_%3c.%3c.%3c.%4c.%4c.hex", + ver->name, ver->major, ver->minor, + ver->build, dummy1, dummy2) != 6) { + printf("\t/* failed to parse %s */\n", pathname); + free(ver); + return; + } + + ver->num = atoi(ver->build) + (atoi(ver->minor) << 8) + + (atoi(ver->major) << 13); + + if (!ver_list) { + ver_list = ver; + return; + } + + for (tmp = ver_list, prev = NULL; tmp; prev = tmp, tmp = tmp->next) { + if (ver->num == tmp->num) { + free(ver); + return; + } + + if (ver->num < tmp->num) { + if (prev) { + prev->next = ver; + ver->next = tmp; + } else { + ver->next = ver_list; + ver_list = ver; + } + return; + } + } + + prev->next = ver; } -static unsigned int lhex_to_int(const char *h) +static void ver_parse_entry(const char *pathname) { - return hex_to_int(h) * 0x100 + hex_to_int(h + 2); + struct stat st; + int fd; + + fd = open(pathname, O_RDONLY); + if (fd < 0) { + printf("\t/* failed to open %s */\n", pathname); + return; + } + + if (fstat(fd, &st) < 0) { + printf("\t/* failed to stat %s */\n", pathname); + goto done; + } + + if (S_ISREG(st.st_mode)) { + ver_parse_file(basename(pathname)); + goto done; + } + + if (S_ISDIR(st.st_mode)) { + DIR *dir; + + dir = fdopendir(fd); + if (!dir) + goto done; + + while (1) { + struct dirent *d; + + d = readdir(dir); + if (!d) + break; + + if (d->d_type == DT_REG) + ver_parse_file(d->d_name); + } + + closedir(dir); + } + +done: + close(fd); } -static int check_sum(const char *str, int len) +static void ver_print_table(int argc, char *argv[]) { - 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; + struct ver_data *ver; + + printf("static const struct {\n"); + printf("\tuint16_t ver;\n"); + printf("\tconst char *str\n"); + printf("} table[] = {\n"); + + if (argc > 0) { + int i; + + for (i = 0; i < argc; i++) + ver_parse_entry(argv[i]); + } else + ver_parse_entry("."); + + for (ver = ver_list; ver; ) { + struct ver_data *tmp = ver; + + printf("\t{ 0x%4.4x, \"%s\"\t},\t/* %s.%s.%s */\n", + ver->num, ver->name, + ver->major, ver->minor, ver->build); + + ver = ver->next; + free(tmp); + } + + printf(" { }\n"); + printf("};\n"); } -static int check_hex_line(const char *str, unsigned int len) +static void usage(void) { - if ((str[0] != ':') || (len < 11) || !check_sum(str, len) || - (hex_to_int(str + 1) * 2 + 11 != len)) - return 0; - return 1; + printf("Broadcom Bluetooth firmware converter\n" + "Usage:\n"); + printf("\thex2hcd [options] <file>\n"); + printf("Options:\n" + "\t-o, --output <file> Provide firmware output file\n" + "\t-h, --help Show help options\n"); } +static const struct option main_options[] = { + { "table", no_argument, NULL, 'T' }, + { "output", required_argument, NULL, 'o' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } +}; + 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 <input hex file> <output file>\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)) + const char *output_path = NULL; + bool print_table = false; + int i; + + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "To:vh", main_options, NULL); + if (opt < 0) 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; + + switch (opt) { + case 'T': + print_table = true; + break; + 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; } } - puts("hex file formatting error"); - return -EINVAL; + if (print_table) { + ver_print_table(argc - optind, argv + optind); + return EXIT_SUCCESS; + } -output_err: - puts("error on writing output file"); - return -EIO; + if (argc - optind < 1) { + fprintf(stderr, "No input firmware files provided\n"); + return EXIT_FAILURE; + } -end: - return 0; -} + 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/hid2hci.1 b/tools/hid2hci.1 index 8c5d520f..c6876a30 100644 --- a/tools/hid2hci.1 +++ b/tools/hid2hci.1 @@ -32,7 +32,7 @@ mode and back. .B --mode= [hid, hci] Sets the mode to switch the device into .TP -.B --method= [csr, logitech-hid, dell] +.B --method= [csr, csr2, logitech-hid, dell] Which vendor method to use for switching the device. .TP .B --devpath= diff --git a/tools/hid2hci.c b/tools/hid2hci.c index a183bfa3..8f060f25 100644 --- a/tools/hid2hci.c +++ b/tools/hid2hci.c @@ -59,7 +59,7 @@ struct usbfs_ctrltransfer { uint16_t wIndex; uint16_t wLength; uint32_t timeout; /* in milliseconds */ - void *data; /* pointer to data */ + const void *data; /* pointer to data */ }; @@ -77,7 +77,7 @@ struct usbfs_disconnect{ static int control_message(int fd, int requesttype, int request, int value, int index, - char *bytes, int size, int timeout) + const uint8_t *bytes, int size, int timeout) { struct usbfs_ctrltransfer transfer; @@ -119,6 +119,52 @@ static int usb_switch_csr(int fd, enum mode mode) return err; } +static int usb_switch_csr2(int fd, enum mode mode) +{ + int err = 0; + struct usbfs_disconnect disconnect; + const uint8_t report[] = { + 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + switch (mode) { + case HCI: + /* send report as is */ + disconnect.interface = 0; + disconnect.flags = USBFS_DISCONNECT_EXCEPT_DRIVER; + strcpy(disconnect.driver, "usbfs"); + + if (ioctl(fd, USBFS_IOCTL_DISCONNECT, &disconnect) < 0) { + fprintf(stderr, "Can't claim interface: %s (%d)\n", + strerror(errno), errno); + return -1; + } + + /* Set_report request with + * report id: 0x01, report type: feature (0x03) + * on interface 0 + */ + err = control_message(fd, + USB_ENDPOINT_OUT | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + USB_REQ_SET_CONFIGURATION, + 0x01 | (0x03 << 8), + 0, report, sizeof(report), 5000); + /* unable to detect whether the previous state + * already was HCI (EALREADY) + */ + break; + case HID: + /* currently unknown how to switch to HID */ + fprintf(stderr, + "csr2: Switching to hid mode is not implemented\n"); + err = -1; + break; + } + + return err; +} + static int hid_logitech_send_report(int fd, const char *buf, size_t size) { struct hiddev_report_info rinfo; @@ -180,7 +226,7 @@ out: static int usb_switch_dell(int fd, enum mode mode) { - char report[] = { 0x7f, 0x00, 0x00, 0x00 }; + uint8_t report[] = { 0x7f, 0x00, 0x00, 0x00 }; struct usbfs_disconnect disconnect; int err; @@ -258,7 +304,7 @@ static void usage(const char *error) printf("Usage: hid2hci [options]\n" " --mode= mode to switch to [hid|hci] (default hci)\n" " --devpath= sys device path\n" - " --method= method to use to switch [csr|logitech-hid|dell]\n" + " --method= method to use to switch [csr|csr2|logitech-hid|dell]\n" " --help\n\n"); } @@ -311,6 +357,9 @@ int main(int argc, char *argv[]) if (!strcmp(optarg, "csr")) { method = METHOD_CSR; usb_switch = usb_switch_csr; + } else if (!strcmp(optarg, "csr2")) { + method = METHOD_CSR; + usb_switch = usb_switch_csr2; } else if (!strcmp(optarg, "logitech-hid")) { method = METHOD_LOGITECH_HID; } else if (!strcmp(optarg, "dell")) { diff --git a/tools/hwdb.c b/tools/hwdb.c index 3b712e1d..8a42dce3 100644 --- a/tools/hwdb.c +++ b/tools/hwdb.c @@ -27,7 +27,7 @@ #include <stdio.h> -#include <bluetooth/bluetooth.h> +#include "lib/bluetooth.h" static const struct { uint16_t vendor; diff --git a/tools/ibeacon.c b/tools/ibeacon.c index 28967de3..9d48e66b 100644 --- a/tools/ibeacon.c +++ b/tools/ibeacon.c @@ -36,8 +36,8 @@ #include <sys/ioctl.h> #include <sys/socket.h> -#include "monitor/mainloop.h" #include "monitor/bt.h" +#include "src/shared/mainloop.h" #include "src/shared/timeout.h" #include "src/shared/util.h" #include "src/shared/hci.h" diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c index cf0fa381..7f03591a 100644 --- a/tools/l2cap-tester.c +++ b/tools/l2cap-tester.c @@ -82,6 +82,9 @@ struct l2cap_data { const void *pin; uint8_t client_pin_len; const void *client_pin; + + bool addr_type_avail; + uint8_t addr_type; }; static void mgmt_debug(const char *str, void *user_data) @@ -441,6 +444,12 @@ static const struct l2cap_data le_client_connect_reject_test_1 = { .expect_err = ECONNREFUSED, }; +static const struct l2cap_data le_client_connect_reject_test_2 = { + .client_psm = 0x0080, + .addr_type_avail = true, + .addr_type = BDADDR_LE_PUBLIC, +}; + static const struct l2cap_data le_client_connect_nval_psm_test = { .client_psm = 0x0080, .expect_err = ECONNREFUSED, @@ -943,6 +952,7 @@ failed: static int create_l2cap_sock(struct test_data *data, uint16_t psm, uint16_t cid, int sec_level) { + const struct l2cap_data *l2data = data->test_data; const uint8_t *master_bdaddr; struct sockaddr_l2 addr; int sk, err; @@ -968,7 +978,10 @@ static int create_l2cap_sock(struct test_data *data, uint16_t psm, addr.l2_psm = htobs(psm); addr.l2_cid = htobs(cid); bacpy(&addr.l2_bdaddr, (void *) master_bdaddr); - if (data->hciemu_type == HCIEMU_TYPE_LE) + + if (l2data && l2data->addr_type_avail) + addr.l2_bdaddr_type = l2data->addr_type; + else if (data->hciemu_type == HCIEMU_TYPE_LE) addr.l2_bdaddr_type = BDADDR_LE_PUBLIC; else addr.l2_bdaddr_type = BDADDR_BREDR; @@ -1003,6 +1016,7 @@ static int create_l2cap_sock(struct test_data *data, uint16_t psm, static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm, uint16_t cid) { + const struct l2cap_data *l2data = data->test_data; const uint8_t *client_bdaddr; struct sockaddr_l2 addr; int err; @@ -1018,7 +1032,10 @@ static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm, bacpy(&addr.l2_bdaddr, (void *) client_bdaddr); addr.l2_psm = htobs(psm); addr.l2_cid = htobs(cid); - if (data->hciemu_type == HCIEMU_TYPE_LE) + + if (l2data && l2data->addr_type_avail) + addr.l2_bdaddr_type = l2data->addr_type; + else if (data->hciemu_type == HCIEMU_TYPE_LE) addr.l2_bdaddr_type = BDADDR_LE_PUBLIC; else addr.l2_bdaddr_type = BDADDR_BREDR; @@ -1084,6 +1101,27 @@ static void test_connect(const void *test_data) tester_print("Connect in progress"); } +static void test_connect_reject(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *l2data = data->test_data; + int sk; + + 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, + l2data->cid) < 0) + tester_test_passed(); + else + tester_test_failed(); + + close(sk); +} + static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { @@ -1396,6 +1434,9 @@ int main(int argc, char *argv[]) test_l2cap_le("L2CAP LE Client - Command Reject", &le_client_connect_reject_test_1, setup_powered_client, test_connect); + test_l2cap_bredr("L2CAP LE Client - Connection Reject", + &le_client_connect_reject_test_2, + setup_powered_client, test_connect_reject); test_l2cap_le("L2CAP LE Client - Invalid PSM", &le_client_connect_nval_psm_test, setup_powered_client, test_connect); diff --git a/tools/l2ping.c b/tools/l2ping.c index 3dd437e8..fa97fe30 100644 --- a/tools/l2ping.c +++ b/tools/l2ping.c @@ -38,10 +38,10 @@ #include <poll.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/l2cap.h" /* Defaults */ static bdaddr_t bdaddr; diff --git a/tools/l2test.c b/tools/l2test.c index a0636b97..83c5cbde 100644 --- a/tools/l2test.c +++ b/tools/l2test.c @@ -42,10 +42,10 @@ #include <sys/ioctl.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/l2cap.h" #include "src/shared/util.h" diff --git a/tools/mcaptest.c b/tools/mcaptest.c index 42734ebf..30928737 100644 --- a/tools/mcaptest.c +++ b/tools/mcaptest.c @@ -30,12 +30,12 @@ #include <getopt.h> #include <unistd.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> - #include <glib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" + #include "btio/btio.h" #include "lib/l2cap.h" #include "profiles/health/mcap.h" @@ -71,20 +71,27 @@ static gboolean no_close = FALSE; #define REQ_CLOCK_ACC 0x1400 -static void mdl_connected_cb(struct mcap_mdl *mdl, void *data) +static void mdl_close(struct mcap_mdl *mdl) { int fd = -1; printf("%s\n", __func__); - if (mdl_disconnect && mdl_disconnect_timeout >= 0) { + if (mdl_disconnect_timeout >= 0) sleep(mdl_disconnect_timeout); - fd = mcap_mdl_get_fd(mdl); + fd = mcap_mdl_get_fd(mdl); - if (fd > 0) - close(fd); - } + if (fd > 0) + close(fd); +} + +static void mdl_connected_cb(struct mcap_mdl *mdl, void *data) +{ + printf("%s\n", __func__); + + if (mdl_disconnect) + mdl_close(mdl); } static void mdl_closed_cb(struct mcap_mdl *mdl, void *data) @@ -94,7 +101,13 @@ static void mdl_closed_cb(struct mcap_mdl *mdl, void *data) if (mcl_disconnect && mcl_disconnect_timeout >= 0) { sleep(mcl_disconnect_timeout); + printf("Closing MCAP communication link\n"); mcap_close_mcl(mcl, TRUE); + + if (no_close) + return; + + g_main_loop_quit(mloop); } } @@ -102,6 +115,10 @@ static void mdl_deleted_cb(struct mcap_mdl *mdl, void *data) { /* TODO */ printf("%s\n", __func__); + + /* Disconnecting MDL latency timeout */ + if (mdl_disconnect_timeout >= 0) + sleep(mdl_disconnect_timeout); } static void mdl_aborted_cb(struct mcap_mdl *mdl, void *data) @@ -152,7 +169,7 @@ static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data) static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data) { /* TODO */ - printf("MCL disconnected\n"); + printf("%s\n", __func__); if (no_close) return; @@ -163,14 +180,19 @@ static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data) static void mcl_uncached(struct mcap_mcl *mcl, gpointer data) { /* TODO */ - printf("MCL uncached unsupported\n"); + printf("%s\n", __func__); } static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data) { mdlid = mcap_mdl_get_mdlid(mdl); - printf("MDL %d connected\n", mdlid); + printf("%s\n", __func__); + + if (mdlid == MCAP_MDLID_RESERVED) + printf("MCAP mdlid is reserved"); + else + printf("MDL %d connected\n", mdlid); } static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr, @@ -178,6 +200,8 @@ static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr, { GError *err = NULL; + printf("%s\n", __func__); + if (gerr) { printf("MDL error: %s\n", gerr->message); @@ -234,6 +258,7 @@ static void trigger_mdl_action(int mode) } if (mode == MODE_CONNECT) { + printf("Creating MCAP Data End Point\n"); mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr); if (gerr) { printf("Could not connect MDL: %s\n", gerr->message); @@ -286,6 +311,7 @@ static void create_mcl_cb(struct mcap_mcl *mcap_mcl, GError *err, gpointer data) mcl = mcap_mcl_ref(mcap_mcl); trigger_mdl_action(data_mode); } + static void usage(void) { printf("mcaptest - MCAP testing ver %s\n", VERSION); @@ -293,10 +319,12 @@ static void usage(void) "\tmcaptest <control_mode> <data_mode> [options]\n"); printf("Control Link Mode:\n" "\t-c connect <dst_addr>\n" + "\t-b close control link after closing data link\n" "\t-e <timeout> disconnect MCL and quit after MDL is closed\n" "\t-g send clock sync capability request if MCL connected\n"); printf("Data Link Mode:\n" "\t-d connect\n" + "\t-a close data link immediately after being connected" "\t-f <timeout> disconnect MDL after it's connected\n" "\t-u send \'Unavailable\' on first MDL connection request\n"); printf("Options:\n" @@ -313,6 +341,8 @@ static struct option main_options[] = { { "disconnect_cl", 1, 0, 'e' }, { "synccap_req", 0, 0, 'g' }, { "connect_dl", 0, 0, 'd' }, + { "disconnect_da", 0, 0, 'a' }, + { "disconnect_ca", 0, 0, 'b' }, { "disconnect_dl", 1, 0, 'f' }, { "unavailable_dl", 0, 0, 'u' }, { "no exit mcl dis/err",0, 0, 'n' }, @@ -320,6 +350,7 @@ static struct option main_options[] = { { "data_ch", 1, 0, 'D' }, { 0, 0, 0, 0 } }; + int main(int argc, char *argv[]) { GError *err = NULL; @@ -337,7 +368,7 @@ int main(int argc, char *argv[]) exit(1); } - while ((opt = getopt_long(argc, argv, "+i:c:C:D:e:f:dghun", + while ((opt = getopt_long(argc, argv, "+i:c:C:D:e:f:dghunab", main_options, NULL)) != EOF) { switch (opt) { case 'i': @@ -359,14 +390,22 @@ int main(int argc, char *argv[]) break; - case 'e': + case 'a': + mdl_disconnect = TRUE; + + break; + + case 'b': mcl_disconnect = TRUE; + + break; + + case 'e': mcl_disconnect_timeout = atoi(optarg); break; case 'f': - mdl_disconnect = TRUE; mdl_disconnect_timeout = atoi(optarg); break; diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c index 790426ad..e5fb8894 100644 --- a/tools/mgmt-tester.c +++ b/tools/mgmt-tester.c @@ -56,6 +56,7 @@ struct test_data { unsigned int mgmt_settings_id; unsigned int mgmt_alt_settings_id; unsigned int mgmt_alt_ev_id; + unsigned int mgmt_discov_ev_id; uint8_t mgmt_version; uint16_t mgmt_revision; uint16_t mgmt_index; @@ -310,7 +311,7 @@ static void test_condition_complete(struct test_data *data) user->test_data = data; \ user->expected_version = 0x08; \ user->expected_manufacturer = 0x003f; \ - user->expected_supported_settings = 0x00003fff; \ + user->expected_supported_settings = 0x0000bfff; \ user->initial_settings = 0x00000080; \ user->unmet_conditions = 0; \ tester_add_full(name, data, \ @@ -348,7 +349,7 @@ static void test_condition_complete(struct test_data *data) user->test_data = data; \ user->expected_version = 0x08; \ user->expected_manufacturer = 0x003f; \ - user->expected_supported_settings = 0x00003e1b; \ + user->expected_supported_settings = 0x0000be1b; \ user->initial_settings = 0x00000200; \ user->unmet_conditions = 0; \ tester_add_full(name, data, \ @@ -406,6 +407,8 @@ struct generic_data { bool client_enable_sc; bool expect_sc_key; bool force_power_off; + bool addr_type_avail; + uint8_t addr_type; }; static const char dummy_data[] = { 0x00 }; @@ -2395,7 +2398,10 @@ static const void *pair_device_send_param_func(uint16_t *len) static uint8_t param[8]; memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6); - if (data->hciemu_type == HCIEMU_TYPE_LE) + + if (test->addr_type_avail) + param[6] = test->addr_type; + else if (data->hciemu_type == HCIEMU_TYPE_LE) param[6] = 0x01; /* Address type */ else param[6] = 0x00; /* Address type */ @@ -2409,10 +2415,14 @@ static const void *pair_device_send_param_func(uint16_t *len) static const void *pair_device_expect_param_func(uint16_t *len) { struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; static uint8_t param[7]; memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6); - if (data->hciemu_type == HCIEMU_TYPE_LE) + + if (test->addr_type_avail) + param[6] = test->addr_type; + else if (data->hciemu_type == HCIEMU_TYPE_LE) param[6] = 0x01; /* Address type */ else param[6] = 0x00; /* Address type */ @@ -2501,6 +2511,52 @@ static const void *client_bdaddr_param_func(uint8_t *len) return bdaddr; } +static const struct generic_data pair_device_not_supported_test_1 = { + .setup_settings = settings_powered_bondable, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_NOT_SUPPORTED, + .expect_func = pair_device_expect_param_func, + .addr_type_avail = true, + .addr_type = BDADDR_BREDR, +}; + +static const struct generic_data pair_device_not_supported_test_2 = { + .setup_settings = settings_powered_bondable, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_NOT_SUPPORTED, + .expect_func = pair_device_expect_param_func, + .addr_type_avail = true, + .addr_type = BDADDR_LE_PUBLIC, +}; + +static uint16_t settings_powered_bondable_le[] = { MGMT_OP_SET_LE, + MGMT_OP_SET_BONDABLE, + MGMT_OP_SET_POWERED, + 0 }; + +static const struct generic_data pair_device_reject_transport_not_enabled_1 = { + .setup_settings = settings_powered_bondable_le, + .setup_nobredr = true, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_REJECTED, + .expect_func = pair_device_expect_param_func, + .addr_type_avail = true, + .addr_type = BDADDR_BREDR, +}; + +static const struct generic_data pair_device_reject_transport_not_enabled_2 = { + .setup_settings = settings_powered_bondable, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .expect_status = MGMT_STATUS_REJECTED, + .expect_func = pair_device_expect_param_func, + .addr_type_avail = true, + .addr_type = BDADDR_LE_PUBLIC, +}; + static const struct generic_data pair_device_reject_test_1 = { .setup_settings = settings_powered_bondable, .send_opcode = MGMT_OP_PAIR_DEVICE, @@ -2833,6 +2889,25 @@ static const struct generic_data pair_device_smp_bredr_test_1 = { .client_io_cap = 0x03, /* NoInputNoOutput */ }; +static const struct generic_data pair_device_smp_bredr_test_2 = { + .setup_settings = settings_powered_sc_bondable_le_ssp, + .client_enable_ssp = true, + .client_enable_le = true, + .client_enable_sc = true, + .expect_sc_key = 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_LONG_TERM_KEY, + .expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key), + .verify_alt_ev_func = verify_ltk, + .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_le_reject_test_1 = { .setup_settings = settings_powered_bondable, .io_cap = 0x02, /* KeyboardOnly */ @@ -3021,6 +3096,44 @@ static const struct generic_data pairing_acceptor_ssp_4 = { .client_auth_req = 0x02, /* Dedicated Bonding - No MITM */ }; +static uint16_t settings_powered_sc_bondable_connectable_le_ssp[] = { + MGMT_OP_SET_BONDABLE, + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_LE, + MGMT_OP_SET_SSP, + MGMT_OP_SET_SECURE_CONN, + MGMT_OP_SET_POWERED, + 0 }; + +static const struct generic_data pairing_acceptor_smp_bredr_1 = { + .setup_settings = settings_powered_sc_bondable_connectable_le_ssp, + .client_enable_ssp = true, + .client_enable_le = true, + .client_enable_sc = true, + .expect_sc_key = true, + .expect_alt_ev = MGMT_EV_NEW_LONG_TERM_KEY, + .expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key), + .verify_alt_ev_func = verify_ltk, + .just_works = true, + .io_cap = 0x03, /* NoInputNoOutput */ + .client_io_cap = 0x03, /* No InputNoOutput */ + .client_auth_req = 0x00, /* No Bonding - No MITM */ +}; + +static const struct generic_data pairing_acceptor_smp_bredr_2 = { + .setup_settings = settings_powered_sc_bondable_connectable_le_ssp, + .client_enable_ssp = true, + .client_enable_le = true, + .client_enable_sc = true, + .expect_sc_key = true, + .expect_alt_ev = MGMT_EV_NEW_LONG_TERM_KEY, + .expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key), + .verify_alt_ev_func = verify_ltk, + .io_cap = 0x01, /* DisplayYesNo */ + .client_io_cap = 0x01, /* DisplayYesNo */ + .client_auth_req = 0x02, /* Dedicated Bonding - No MITM */ +}; + static uint16_t settings_powered_bondable_connectable_advertising[] = { MGMT_OP_SET_BONDABLE, MGMT_OP_SET_CONNECTABLE, @@ -3160,12 +3273,27 @@ static const struct generic_data unblock_device_invalid_param_test_1 = { static const char set_static_addr_valid_param[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0xc0 }; +static const char set_static_addr_settings[] = { 0x00, 0x82, 0x00, 0x00 }; static const struct generic_data set_static_addr_success_test = { .send_opcode = MGMT_OP_SET_STATIC_ADDRESS, .send_param = set_static_addr_valid_param, .send_len = sizeof(set_static_addr_valid_param), .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_static_addr_settings, + .expect_len = sizeof(set_static_addr_settings), + .expect_settings_set = MGMT_SETTING_STATIC_ADDRESS, +}; + +static const char set_static_addr_settings_dual[] = { 0x80, 0x00, 0x00, 0x00 }; + +static const struct generic_data set_static_addr_success_test_2 = { + .send_opcode = MGMT_OP_SET_STATIC_ADDRESS, + .send_param = set_static_addr_valid_param, + .send_len = sizeof(set_static_addr_valid_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_static_addr_settings_dual, + .expect_len = sizeof(set_static_addr_settings_dual), }; static const struct generic_data set_static_addr_failure_test = { @@ -3176,6 +3304,14 @@ static const struct generic_data set_static_addr_failure_test = { .expect_status = MGMT_STATUS_REJECTED, }; +static const struct generic_data set_static_addr_failure_test_2 = { + .setup_settings = settings_powered, + .send_opcode = MGMT_OP_SET_STATIC_ADDRESS, + .send_param = set_static_addr_valid_param, + .send_len = sizeof(set_static_addr_valid_param), + .expect_status = MGMT_STATUS_NOT_SUPPORTED, +}; + static const char set_scan_params_valid_param[] = { 0x60, 0x00, 0x30, 0x00 }; static const struct generic_data set_scan_params_success_test = { @@ -3706,10 +3842,9 @@ static void discovering_event(uint16_t index, uint16_t length, const void *param, void *user_data) { struct test_data *data = tester_get_data(); - unsigned int id = PTR_TO_UINT(user_data); const struct mgmt_ev_discovering *ev = param; - mgmt_unregister(data->mgmt, id); + mgmt_unregister(data->mgmt, data->mgmt_discov_ev_id); if (length != sizeof(*ev)) { tester_warn("Incorrect discovering event length"); @@ -3743,10 +3878,11 @@ static void setup_start_discovery(const void *test_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; - unsigned int id = 0; + unsigned int id; id = mgmt_register(data->mgmt, MGMT_EV_DISCOVERING, data->mgmt_index, - discovering_event, UINT_TO_PTR(id), NULL); + discovering_event, NULL, NULL); + data->mgmt_discov_ev_id = id; mgmt_send(data->mgmt, test->setup_send_opcode, data->mgmt_index, send_len, send_param, setup_discovery_callback, @@ -5006,6 +5142,18 @@ int main(int argc, char *argv[]) test_bredrle("Pair Device - Power off 1", &pair_device_power_off_test_1, NULL, test_command_generic); + test_le("Pair Device - Incorrect transport reject 1", + &pair_device_not_supported_test_1, + NULL, test_command_generic); + test_bredr("Pair Device - Incorrect transport reject 2", + &pair_device_not_supported_test_2, + NULL, test_command_generic); + test_bredrle("Pair Device - Reject on not enabled transport 1", + &pair_device_reject_transport_not_enabled_1, + NULL, test_command_generic); + test_bredrle("Pair Device - Reject on not enabled transport 2", + &pair_device_reject_transport_not_enabled_2, + NULL, test_command_generic); test_bredrle("Pair Device - Invalid Parameters 1", &pair_device_invalid_param_test_1, NULL, test_command_generic); @@ -5060,9 +5208,12 @@ int main(int argc, char *argv[]) test_bredrle("Pair Device - SSP Non-bondable 1", &pair_device_ssp_nonbondable_1, NULL, test_command_generic); - test_bredrle("Pair Device - SMP over BR/EDR Just-Works Success 1", + test_bredrle("Pair Device - SMP over BR/EDR Success 1", &pair_device_smp_bredr_test_1, NULL, test_command_generic); + test_bredrle("Pair Device - SMP over BR/EDR Success 2", + &pair_device_smp_bredr_test_2, + NULL, test_command_generic); test_le("Pair Device - LE Success 1", &pair_device_le_success_test_1, NULL, test_command_generic); @@ -5109,6 +5260,12 @@ int main(int argc, char *argv[]) test_bredrle("Pairing Acceptor - SSP 4", &pairing_acceptor_ssp_4, setup_pairing_acceptor, test_pairing_acceptor); + test_bredrle("Pairing Acceptor - SMP over BR/EDR 1", + &pairing_acceptor_smp_bredr_1, + setup_pairing_acceptor, test_pairing_acceptor); + test_bredrle("Pairing Acceptor - SMP over BR/EDR 2", + &pairing_acceptor_smp_bredr_2, + setup_pairing_acceptor, test_pairing_acceptor); test_le("Pairing Acceptor - LE 1", &pairing_acceptor_le_1, setup_pairing_acceptor, test_pairing_acceptor); @@ -5147,12 +5304,18 @@ int main(int argc, char *argv[]) &unblock_device_invalid_param_test_1, NULL, test_command_generic); - test_bredrle("Set Static Address - Success", + test_le("Set Static Address - Success 1", &set_static_addr_success_test, NULL, test_command_generic); - test_bredrle("Set Static Address - Failure", + test_bredrle("Set Static Address - Success 2", + &set_static_addr_success_test_2, + NULL, test_command_generic); + test_bredrle("Set Static Address - Failure 1", &set_static_addr_failure_test, NULL, test_command_generic); + test_bredr("Set Static Address - Failure 2", + &set_static_addr_failure_test_2, + NULL, test_command_generic); test_bredrle("Set Scan Parameters - Success", &set_scan_params_success_test, diff --git a/tools/mpris-player.c b/tools/mpris-player.c deleted file mode 100755 index c94330cb..00000000 --- a/tools/mpris-player.c +++ /dev/null @@ -1,2593 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <signal.h> -#include <getopt.h> -#include <string.h> -#include <inttypes.h> - -#include <dbus/dbus.h> -#include <glib.h> -#include <gdbus/gdbus.h> - -#define BLUEZ_BUS_NAME "org.bluez" -#define BLUEZ_PATH "/org/bluez" -#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1" -#define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1" -#define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1" -#define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1" -#define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1" -#define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1" -#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2." -#define MPRIS_INTERFACE "org.mpris.MediaPlayer2" -#define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player" -#define MPRIS_TRACKLIST_INTERFACE "org.mpris.MediaPlayer2.TrackList" -#define MPRIS_PLAYLISTS_INTERFACE "org.mpris.MediaPlayer2.Playlists" -#define MPRIS_PLAYER_PATH "/org/mpris/MediaPlayer2" -#define ERROR_INTERFACE "org.mpris.MediaPlayer2.Error" - -static GMainLoop *main_loop; -static GDBusProxy *adapter = NULL; -static DBusConnection *sys = NULL; -static DBusConnection *session = NULL; -static GDBusClient *client = NULL; -static GSList *players = NULL; -static GSList *transports = NULL; - -static gboolean option_version = FALSE; -static gboolean option_export = FALSE; - -struct tracklist { - GDBusProxy *proxy; - GSList *items; -}; - -struct player { - char *bus_name; - DBusConnection *conn; - GDBusProxy *proxy; - GDBusProxy *folder; - GDBusProxy *device; - GDBusProxy *transport; - GDBusProxy *playlist; - struct tracklist *tracklist; -}; - -typedef int (* parse_metadata_func) (DBusMessageIter *iter, const char *key, - DBusMessageIter *metadata); - -static void dict_append_entry(DBusMessageIter *dict, const char *key, int type, - void *val); - -static void sig_term(int sig) -{ - g_main_loop_quit(main_loop); -} - -static DBusMessage *get_all(DBusConnection *conn, const char *name) -{ - DBusMessage *msg, *reply; - DBusError err; - const char *iface = MPRIS_PLAYER_INTERFACE; - - msg = dbus_message_new_method_call(name, MPRIS_PLAYER_PATH, - DBUS_INTERFACE_PROPERTIES, "GetAll"); - if (!msg) { - fprintf(stderr, "Can't allocate new method call\n"); - return NULL; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface, - DBUS_TYPE_INVALID); - - dbus_error_init(&err); - - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); - - dbus_message_unref(msg); - - if (!reply) { - if (dbus_error_is_set(&err)) { - fprintf(stderr, "%s\n", err.message); - dbus_error_free(&err); - } - return NULL; - } - - return reply; -} - -static void append_variant(DBusMessageIter *iter, int type, void *val) -{ - DBusMessageIter value; - char sig[2] = { type, '\0' }; - - dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value); - - dbus_message_iter_append_basic(&value, type, val); - - dbus_message_iter_close_container(iter, &value); -} - -static void append_array_variant(DBusMessageIter *iter, int type, void *val, - int n_elements) -{ - DBusMessageIter variant, array; - char type_sig[2] = { type, '\0' }; - char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' }; - - dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, - array_sig, &variant); - - dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, - type_sig, &array); - - if (dbus_type_is_fixed(type) == TRUE) { - dbus_message_iter_append_fixed_array(&array, type, val, - n_elements); - } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) { - const char ***str_array = val; - int i; - - for (i = 0; i < n_elements; i++) - dbus_message_iter_append_basic(&array, type, - &((*str_array)[i])); - } - - dbus_message_iter_close_container(&variant, &array); - - dbus_message_iter_close_container(iter, &variant); -} - -static void dict_append_array(DBusMessageIter *dict, const char *key, int type, - void *val, int n_elements) -{ - DBusMessageIter entry; - - dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, - NULL, &entry); - - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); - - append_array_variant(&entry, type, val, n_elements); - - dbus_message_iter_close_container(dict, &entry); -} - -static void append_basic(DBusMessageIter *base, DBusMessageIter *iter, - int type) -{ - const void *value; - - dbus_message_iter_get_basic(iter, &value); - dbus_message_iter_append_basic(base, type, &value); -} - -static void append_iter(DBusMessageIter *base, DBusMessageIter *iter); -static void append_container(DBusMessageIter *base, DBusMessageIter *iter, - int type) -{ - DBusMessageIter iter_sub, base_sub; - char *sig; - - dbus_message_iter_recurse(iter, &iter_sub); - - switch (type) { - case DBUS_TYPE_ARRAY: - case DBUS_TYPE_VARIANT: - sig = dbus_message_iter_get_signature(&iter_sub); - break; - default: - sig = NULL; - break; - } - - dbus_message_iter_open_container(base, type, sig, &base_sub); - - if (sig != NULL) - dbus_free(sig); - - append_iter(&base_sub, &iter_sub); - - dbus_message_iter_close_container(base, &base_sub); -} - -static void append_iter(DBusMessageIter *base, DBusMessageIter *iter) -{ - int type; - - while ((type = dbus_message_iter_get_arg_type(iter)) != - DBUS_TYPE_INVALID) { - if (dbus_type_is_basic(type)) - append_basic(base, iter, type); - else if (dbus_type_is_container(type)) - append_container(base, iter, type); - - dbus_message_iter_next(iter); - } -} - -static void dict_append_iter(DBusMessageIter *dict, const char *key, - DBusMessageIter *iter) -{ - DBusMessageIter entry; - - dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, - NULL, &entry); - - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); - - append_iter(&entry, iter); - - dbus_message_iter_close_container(dict, &entry); -} - -static int parse_metadata_entry(DBusMessageIter *entry, const char *key, - DBusMessageIter *metadata) -{ - if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT) - return -EINVAL; - - dict_append_iter(metadata, key, entry); - - return 0; -} - -static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata, - parse_metadata_func func) -{ - DBusMessageIter dict; - int ctype; - - ctype = dbus_message_iter_get_arg_type(args); - if (ctype != DBUS_TYPE_ARRAY) - return -EINVAL; - - dbus_message_iter_recurse(args, &dict); - - while ((ctype = dbus_message_iter_get_arg_type(&dict)) != - DBUS_TYPE_INVALID) { - DBusMessageIter entry; - const char *key; - - if (ctype != DBUS_TYPE_DICT_ENTRY) - return -EINVAL; - - dbus_message_iter_recurse(&dict, &entry); - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) - return -EINVAL; - - dbus_message_iter_get_basic(&entry, &key); - dbus_message_iter_next(&entry); - - if (func(&entry, key, metadata) < 0) - return -EINVAL; - - dbus_message_iter_next(&dict); - } - - return 0; -} - -static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict, - parse_metadata_func func) -{ - DBusMessageIter value, metadata; - - dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}", - &value); - - dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata); - - parse_metadata(dict, &metadata, func); - - dbus_message_iter_close_container(&value, &metadata); - dbus_message_iter_close_container(iter, &value); -} - -static void dict_append_entry(DBusMessageIter *dict, const char *key, int type, - void *val) -{ - DBusMessageIter entry; - - if (type == DBUS_TYPE_STRING) { - const char *str = *((const char **) val); - if (str == NULL) - return; - } - - dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, - NULL, &entry); - - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); - - if (strcasecmp(key, "Metadata") == 0) - append_metadata(&entry, val, parse_metadata_entry); - else - append_variant(&entry, type, val); - - dbus_message_iter_close_container(dict, &entry); -} - -static char *sender2path(const char *sender) -{ - char *path; - - path = g_strconcat("/", sender, NULL); - return g_strdelimit(path, ":.", '_'); -} - -static void copy_reply(DBusPendingCall *call, void *user_data) -{ - DBusMessage *msg = user_data; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusMessage *copy; - DBusMessageIter args, iter; - - copy = dbus_message_new_method_return(msg); - if (copy == NULL) { - dbus_message_unref(reply); - return; - } - - dbus_message_iter_init_append(copy, &iter); - - if (!dbus_message_iter_init(reply, &args)) - goto done; - - append_iter(&iter, &args); - - dbus_connection_send(sys, copy, NULL); - -done: - dbus_message_unref(copy); - dbus_message_unref(reply); -} - -static DBusHandlerResult player_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - char *owner = data; - DBusMessage *copy; - DBusMessageIter args, iter; - DBusPendingCall *call; - - dbus_message_iter_init(msg, &args); - - copy = dbus_message_new_method_call(owner, - MPRIS_PLAYER_PATH, - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - if (copy == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - dbus_message_iter_init_append(copy, &iter); - append_iter(&iter, &args); - - if (!dbus_connection_send_with_reply(session, copy, &call, -1)) - goto done; - - dbus_message_ref(msg); - dbus_pending_call_set_notify(call, copy_reply, msg, NULL); - dbus_pending_call_unref(call); - -done: - dbus_message_unref(copy); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static struct player *find_player_by_bus_name(const char *name) -{ - GSList *l; - - for (l = players; l; l = l->next) { - struct player *player = l->data; - - if (strcmp(player->bus_name, name) == 0) - return player; - } - - return NULL; -} - -static const DBusObjectPathVTable player_table = { - .message_function = player_message, -}; - -static void add_player(DBusConnection *conn, const char *name, - const char *sender) -{ - DBusMessage *reply = NULL; - DBusMessage *msg; - DBusMessageIter iter, args; - DBusError err; - char *path, *owner; - struct player *player; - - if (!adapter) - return; - - player = find_player_by_bus_name(name); - if (player == NULL) { - reply = get_all(conn, name); - if (reply == NULL) - return; - dbus_message_iter_init(reply, &args); - } - - msg = dbus_message_new_method_call(BLUEZ_BUS_NAME, - g_dbus_proxy_get_path(adapter), - BLUEZ_MEDIA_INTERFACE, - "RegisterPlayer"); - if (!msg) { - fprintf(stderr, "Can't allocate new method call\n"); - return; - } - - path = sender2path(sender); - dbus_connection_get_object_path_data(sys, path, (void **) &owner); - - if (owner != NULL) - goto done; - - dbus_message_iter_init_append(msg, &iter); - - dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); - - if (player != NULL) { - if (!g_dbus_get_properties(player->conn, - MPRIS_PLAYER_PATH, - MPRIS_PLAYER_INTERFACE, - &iter)) - goto done; - } else { - append_iter(&iter, &args); - dbus_message_unref(reply); - } - - dbus_error_init(&err); - - owner = strdup(sender); - - if (!dbus_connection_register_object_path(sys, path, &player_table, - owner)) { - fprintf(stderr, "Can't register object path for player\n"); - free(owner); - goto done; - } - - reply = dbus_connection_send_with_reply_and_block(sys, msg, -1, &err); - if (!reply) { - fprintf(stderr, "Can't register player\n"); - free(owner); - if (dbus_error_is_set(&err)) { - fprintf(stderr, "%s\n", err.message); - dbus_error_free(&err); - } - } - -done: - if (reply) - dbus_message_unref(reply); - dbus_message_unref(msg); - g_free(path); -} - -static void remove_player(DBusConnection *conn, const char *sender) -{ - DBusMessage *msg; - char *path, *owner; - - if (!adapter) - return; - - path = sender2path(sender); - dbus_connection_get_object_path_data(sys, path, (void **) &owner); - - if (owner == NULL) { - g_free(path); - return; - } - - msg = dbus_message_new_method_call(BLUEZ_BUS_NAME, - g_dbus_proxy_get_path(adapter), - BLUEZ_MEDIA_INTERFACE, - "UnregisterPlayer"); - if (!msg) { - fprintf(stderr, "Can't allocate new method call\n"); - g_free(path); - return; - } - - dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); - - dbus_connection_send(sys, msg, NULL); - - dbus_connection_unregister_object_path(sys, path); - - dbus_message_unref(msg); - g_free(path); - g_free(owner); -} - -static gboolean player_signal(DBusConnection *conn, DBusMessage *msg, - void *user_data) -{ - DBusMessage *signal; - DBusMessageIter iter, args; - char *path, *owner; - - dbus_message_iter_init(msg, &iter); - - path = sender2path(dbus_message_get_sender(msg)); - dbus_connection_get_object_path_data(sys, path, (void **) &owner); - - if (owner == NULL) - goto done; - - signal = dbus_message_new_signal(path, dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - if (signal == NULL) { - fprintf(stderr, "Unable to allocate new %s.%s signal", - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - goto done; - } - - dbus_message_iter_init_append(signal, &args); - - append_iter(&args, &iter); - - dbus_connection_send(sys, signal, NULL); - dbus_message_unref(signal); - -done: - g_free(path); - - return TRUE; -} - -static gboolean name_owner_changed(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *name, *old, *new; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old, - DBUS_TYPE_STRING, &new, - DBUS_TYPE_INVALID)) { - fprintf(stderr, "Invalid arguments for NameOwnerChanged signal"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - if (!g_str_has_prefix(name, "org.mpris")) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (*new == '\0') { - printf("player %s at %s disappear\n", name, old); - remove_player(conn, old); - } else if (option_export || find_player_by_bus_name(name) == NULL) { - printf("player %s at %s found\n", name, new); - add_player(conn, name, new); - } - - return TRUE; -} - -static char *get_name_owner(DBusConnection *conn, const char *name) -{ - DBusMessage *msg, *reply; - DBusError err; - char *owner; - - msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, "GetNameOwner"); - - if (!msg) { - fprintf(stderr, "Can't allocate new method call\n"); - return NULL; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - - dbus_error_init(&err); - - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); - - dbus_message_unref(msg); - - if (!reply) { - if (dbus_error_is_set(&err)) { - fprintf(stderr, "%s\n", err.message); - dbus_error_free(&err); - } - return NULL; - } - - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &owner, - DBUS_TYPE_INVALID)) { - dbus_message_unref(reply); - return NULL; - } - - owner = g_strdup(owner); - - dbus_message_unref(reply); - - dbus_connection_flush(conn); - - return owner; -} - -static void parse_list_names(DBusConnection *conn, DBusMessageIter *args) -{ - DBusMessageIter array; - int ctype; - - ctype = dbus_message_iter_get_arg_type(args); - if (ctype != DBUS_TYPE_ARRAY) - return; - - dbus_message_iter_recurse(args, &array); - - while ((ctype = dbus_message_iter_get_arg_type(&array)) != - DBUS_TYPE_INVALID) { - const char *name; - char *owner; - - if (ctype != DBUS_TYPE_STRING) - goto next; - - dbus_message_iter_get_basic(&array, &name); - - if (!g_str_has_prefix(name, "org.mpris")) - goto next; - - owner = get_name_owner(conn, name); - - if (owner == NULL) - goto next; - - printf("player %s at %s found\n", name, owner); - - add_player(conn, name, owner); - - g_free(owner); -next: - dbus_message_iter_next(&array); - } -} - -static void list_names(DBusConnection *conn) -{ - DBusMessage *msg, *reply; - DBusMessageIter iter; - DBusError err; - - msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, "ListNames"); - - if (!msg) { - fprintf(stderr, "Can't allocate new method call\n"); - return; - } - - dbus_error_init(&err); - - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); - - dbus_message_unref(msg); - - if (!reply) { - if (dbus_error_is_set(&err)) { - fprintf(stderr, "%s\n", err.message); - dbus_error_free(&err); - } - return; - } - - dbus_message_iter_init(reply, &iter); - - parse_list_names(conn, &iter); - - dbus_message_unref(reply); - - dbus_connection_flush(conn); -} - -static void usage(void) -{ - printf("Bluetooth mpris-player ver %s\n\n", VERSION); - - printf("Usage:\n"); -} - -static GOptionEntry options[] = { - { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, - "Show version information and exit" }, - { "export", 'e', 0, G_OPTION_ARG_NONE, &option_export, - "Export remote players" }, - { NULL }, -}; - -static void connect_handler(DBusConnection *connection, void *user_data) -{ - printf("org.bluez appeared\n"); -} - -static void disconnect_handler(DBusConnection *connection, void *user_data) -{ - printf("org.bluez disappeared\n"); -} - -static void unregister_tracklist(struct player *player) -{ - struct tracklist *tracklist = player->tracklist; - - g_slist_free(tracklist->items); - g_dbus_proxy_unref(tracklist->proxy); - g_free(tracklist); - player->tracklist = NULL; -} - -static void player_free(void *data) -{ - struct player *player = data; - - if (player->tracklist != NULL) - unregister_tracklist(player); - - if (player->conn) { - dbus_connection_close(player->conn); - dbus_connection_unref(player->conn); - } - - g_dbus_proxy_unref(player->device); - g_dbus_proxy_unref(player->proxy); - - if (player->transport) - g_dbus_proxy_unref(player->transport); - - if (player->playlist) - g_dbus_proxy_unref(player->playlist); - - g_free(player->bus_name); - g_free(player); -} - -struct pending_call { - struct player *player; - DBusMessage *msg; -}; - -static void pending_call_free(void *data) -{ - struct pending_call *p = data; - - if (p->msg) - dbus_message_unref(p->msg); - - g_free(p); -} - -static void player_reply(DBusMessage *message, void *user_data) -{ - struct pending_call *p = user_data; - struct player *player = p->player; - DBusMessage *msg = p->msg; - DBusMessage *reply; - DBusError err; - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, message)) { - fprintf(stderr, "error: %s", err.name); - reply = g_dbus_create_error(msg, err.name, "%s", err.message); - dbus_error_free(&err); - } else - reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - - g_dbus_send_message(player->conn, reply); -} - -static void player_control(struct player *player, DBusMessage *msg, - const char *name) -{ - struct pending_call *p; - - p = g_new0(struct pending_call, 1); - p->player = player; - p->msg = dbus_message_ref(msg); - - g_dbus_proxy_method_call(player->proxy, name, NULL, player_reply, - p, pending_call_free); -} - -static const char *status_to_playback(const char *status) -{ - if (strcasecmp(status, "playing") == 0) - return "Playing"; - else if (strcasecmp(status, "paused") == 0) - return "Paused"; - else - return "Stopped"; -} - -static const char *player_get_status(struct player *player) -{ - const char *status; - DBusMessageIter value; - - if (g_dbus_proxy_get_property(player->proxy, "Status", &value)) { - dbus_message_iter_get_basic(&value, &status); - return status_to_playback(status); - } - - if (player->transport == NULL) - goto done; - - if (!g_dbus_proxy_get_property(player->transport, "State", &value)) - goto done; - - dbus_message_iter_get_basic(&value, &status); - - if (strcasecmp(status, "active") == 0) - return "Playing"; - -done: - return "Stopped"; -} - -static DBusMessage *player_toggle(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct player *player = data; - const char *status; - - status = player_get_status(player); - - if (strcasecmp(status, "Playing") == 0) - player_control(player, msg, "Pause"); - else - player_control(player, msg, "Play"); - - return NULL; -} - -static DBusMessage *player_play(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct player *player = data; - - player_control(player, msg, "Play"); - - return NULL; -} - -static DBusMessage *player_pause(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct player *player = data; - - player_control(player, msg, "Pause"); - - return NULL; -} - -static DBusMessage *player_stop(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct player *player = data; - - player_control(player, msg, "Stop"); - - return NULL; -} - -static DBusMessage *player_next(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct player *player = data; - - player_control(player, msg, "Next"); - - return NULL; -} - -static DBusMessage *player_previous(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct player *player = data; - - player_control(player, msg, "Previous"); - - return NULL; -} - -static gboolean status_exists(const GDBusPropertyTable *property, void *data) -{ - struct player *player = data; - - return player_get_status(player) != NULL; -} - -static gboolean get_status(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - const char *status; - - status = player_get_status(player); - - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status); - - return TRUE; -} - -static gboolean repeat_exists(const GDBusPropertyTable *property, void *data) -{ - DBusMessageIter iter; - struct player *player = data; - - return g_dbus_proxy_get_property(player->proxy, "Repeat", &iter); -} - -static const char *repeat_to_loopstatus(const char *value) -{ - if (strcasecmp(value, "off") == 0) - return "None"; - else if (strcasecmp(value, "singletrack") == 0) - return "Track"; - else if (strcasecmp(value, "alltracks") == 0) - return "Playlist"; - - return NULL; -} - -static gboolean get_repeat(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - DBusMessageIter value; - const char *status; - - if (!g_dbus_proxy_get_property(player->proxy, "Repeat", &value)) - return FALSE; - - dbus_message_iter_get_basic(&value, &status); - - status = repeat_to_loopstatus(status); - if (status == NULL) - return FALSE; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status); - - return TRUE; -} - -static const char *loopstatus_to_repeat(const char *value) -{ - if (strcasecmp(value, "None") == 0) - return "off"; - else if (strcasecmp(value, "Track") == 0) - return "singletrack"; - else if (strcasecmp(value, "Playlist") == 0) - return "alltracks"; - - return NULL; -} - -static void property_result(const DBusError *err, void *user_data) -{ - GDBusPendingPropertySet id = GPOINTER_TO_UINT(user_data); - - if (!dbus_error_is_set(err)) - return g_dbus_pending_property_success(id); - - g_dbus_pending_property_error(id, err->name, err->message); -} - -static void set_repeat(const GDBusPropertyTable *property, - DBusMessageIter *iter, GDBusPendingPropertySet id, - void *data) -{ - struct player *player = data; - const char *value; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { - g_dbus_pending_property_error(id, - ERROR_INTERFACE ".InvalidArguments", - "Invalid arguments in method call"); - return; - } - - dbus_message_iter_get_basic(iter, &value); - - value = loopstatus_to_repeat(value); - if (value == NULL) { - g_dbus_pending_property_error(id, - ERROR_INTERFACE ".InvalidArguments", - "Invalid arguments in method call"); - return; - } - - g_dbus_proxy_set_property_basic(player->proxy, "Repeat", - DBUS_TYPE_STRING, &value, - property_result, GUINT_TO_POINTER(id), - NULL); -} - -static gboolean get_double(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - double value = 1.0; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value); - - return TRUE; -} - -static gboolean shuffle_exists(const GDBusPropertyTable *property, void *data) -{ - DBusMessageIter iter; - struct player *player = data; - - return g_dbus_proxy_get_property(player->proxy, "Shuffle", &iter); -} - -static gboolean get_shuffle(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - DBusMessageIter value; - const char *string; - dbus_bool_t shuffle; - - if (!g_dbus_proxy_get_property(player->proxy, "Shuffle", &value)) - return FALSE; - - dbus_message_iter_get_basic(&value, &string); - - shuffle = strcmp(string, "off") != 0; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &shuffle); - - return TRUE; -} - -static void set_shuffle(const GDBusPropertyTable *property, - DBusMessageIter *iter, GDBusPendingPropertySet id, - void *data) -{ - struct player *player = data; - dbus_bool_t shuffle; - const char *value; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) { - g_dbus_pending_property_error(id, - ERROR_INTERFACE ".InvalidArguments", - "Invalid arguments in method call"); - return; - } - - dbus_message_iter_get_basic(iter, &shuffle); - value = shuffle ? "alltracks" : "off"; - - g_dbus_proxy_set_property_basic(player->proxy, "Shuffle", - DBUS_TYPE_STRING, &value, - property_result, GUINT_TO_POINTER(id), - NULL); -} - -static gboolean position_exists(const GDBusPropertyTable *property, void *data) -{ - DBusMessageIter iter; - struct player *player = data; - - return g_dbus_proxy_get_property(player->proxy, "Position", &iter); -} - -static gboolean get_position(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - DBusMessageIter var; - uint32_t position; - int64_t value; - - if (!g_dbus_proxy_get_property(player->proxy, "Position", &var)) - return FALSE; - - dbus_message_iter_get_basic(&var, &position); - - value = position * 1000; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &value); - - return TRUE; -} - -static gboolean track_exists(const GDBusPropertyTable *property, void *data) -{ - DBusMessageIter iter; - struct player *player = data; - - return g_dbus_proxy_get_property(player->proxy, "Track", &iter); -} - -static gboolean parse_string_metadata(DBusMessageIter *iter, const char *key, - DBusMessageIter *metadata) -{ - const char *value; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) - return FALSE; - - dbus_message_iter_get_basic(iter, &value); - - dict_append_entry(metadata, key, DBUS_TYPE_STRING, &value); - - return TRUE; -} - -static gboolean parse_array_metadata(DBusMessageIter *iter, const char *key, - DBusMessageIter *metadata) -{ - char **value; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) - return FALSE; - - value = dbus_malloc0(sizeof(char *)); - - dbus_message_iter_get_basic(iter, &(value[0])); - - dict_append_array(metadata, key, DBUS_TYPE_STRING, &value, 1); - - dbus_free(value); - - return TRUE; -} - -static gboolean parse_int64_metadata(DBusMessageIter *iter, const char *key, - DBusMessageIter *metadata) -{ - uint32_t duration; - int64_t value; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) - return FALSE; - - dbus_message_iter_get_basic(iter, &duration); - - value = duration * 1000; - - dict_append_entry(metadata, key, DBUS_TYPE_INT64, &value); - - return TRUE; -} - -static gboolean parse_int32_metadata(DBusMessageIter *iter, const char *key, - DBusMessageIter *metadata) -{ - uint32_t value; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) - return FALSE; - - dbus_message_iter_get_basic(iter, &value); - - dict_append_entry(metadata, key, DBUS_TYPE_INT32, &value); - - return TRUE; -} - -static gboolean parse_path_metadata(DBusMessageIter *iter, const char *key, - DBusMessageIter *metadata) -{ - const char *value; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH) - return FALSE; - - dbus_message_iter_get_basic(iter, &value); - - dict_append_entry(metadata, key, DBUS_TYPE_OBJECT_PATH, &value); - - return TRUE; -} - -static int parse_track_entry(DBusMessageIter *entry, const char *key, - DBusMessageIter *metadata) -{ - DBusMessageIter var; - - if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT) - return -EINVAL; - - dbus_message_iter_recurse(entry, &var); - - if (strcasecmp(key, "Title") == 0) { - if (!parse_string_metadata(&var, "xesam:title", metadata)) - return -EINVAL; - } else if (strcasecmp(key, "Artist") == 0) { - if (!parse_array_metadata(&var, "xesam:artist", metadata)) - return -EINVAL; - } else if (strcasecmp(key, "Album") == 0) { - if (!parse_string_metadata(&var, "xesam:album", metadata)) - return -EINVAL; - } else if (strcasecmp(key, "Genre") == 0) { - if (!parse_array_metadata(&var, "xesam:genre", metadata)) - return -EINVAL; - } else if (strcasecmp(key, "Duration") == 0) { - if (!parse_int64_metadata(&var, "mpris:length", metadata)) - return -EINVAL; - } else if (strcasecmp(key, "TrackNumber") == 0) { - if (!parse_int32_metadata(&var, "xesam:trackNumber", metadata)) - return -EINVAL; - } else if (strcasecmp(key, "Item") == 0) { - if (!parse_path_metadata(&var, "mpris:trackid", metadata)) - return -EINVAL; - } - - return 0; -} - -static gboolean get_track(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - DBusMessageIter var, metadata; - - if (!g_dbus_proxy_get_property(player->proxy, "Track", &var)) - return FALSE; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata); - - parse_metadata(&var, &metadata, parse_track_entry); - - dbus_message_iter_close_container(iter, &metadata); - - return TRUE; -} - -static gboolean get_enable(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - dbus_bool_t value = TRUE; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); - - return TRUE; -} - - -static gboolean get_volume(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - double value = 0.0; - uint16_t volume; - DBusMessageIter var; - - if (player->transport == NULL) - goto done; - - if (!g_dbus_proxy_get_property(player->transport, "Volume", &var)) - goto done; - - dbus_message_iter_get_basic(&var, &volume); - - value = (double) volume / 127; - -done: - dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value); - - return TRUE; -} - -static const GDBusMethodTable player_methods[] = { - { GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) }, - { GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) }, - { GDBUS_ASYNC_METHOD("Pause", NULL, NULL, player_pause) }, - { GDBUS_ASYNC_METHOD("Stop", NULL, NULL, player_stop) }, - { GDBUS_ASYNC_METHOD("Next", NULL, NULL, player_next) }, - { GDBUS_ASYNC_METHOD("Previous", NULL, NULL, player_previous) }, - { } -}; - -static const GDBusSignalTable player_signals[] = { - { GDBUS_SIGNAL("Seeked", GDBUS_ARGS({"Position", "x"})) }, - { } -}; - -static const GDBusPropertyTable player_properties[] = { - { "PlaybackStatus", "s", get_status, NULL, status_exists }, - { "LoopStatus", "s", get_repeat, set_repeat, repeat_exists }, - { "Rate", "d", get_double, NULL, NULL }, - { "MinimumRate", "d", get_double, NULL, NULL }, - { "MaximumRate", "d", get_double, NULL, NULL }, - { "Shuffle", "b", get_shuffle, set_shuffle, shuffle_exists }, - { "Position", "x", get_position, NULL, position_exists }, - { "Metadata", "a{sv}", get_track, NULL, track_exists }, - { "Volume", "d", get_volume, NULL, NULL }, - { "CanGoNext", "b", get_enable, NULL, NULL }, - { "CanGoPrevious", "b", get_enable, NULL, NULL }, - { "CanPlay", "b", get_enable, NULL, NULL }, - { "CanPause", "b", get_enable, NULL, NULL }, - { "CanSeek", "b", get_enable, NULL, NULL }, - { "CanControl", "b", get_enable, NULL, NULL }, - { } -}; - -static gboolean get_disable(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - dbus_bool_t value = FALSE; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); - - return TRUE; -} - -static gboolean get_name(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - DBusMessageIter var; - const char *alias; - char *name; - - if (!g_dbus_proxy_get_property(player->device, "Alias", &var)) - return FALSE; - - dbus_message_iter_get_basic(&var, &alias); - - if (g_dbus_proxy_get_property(player->proxy, "Name", &var)) { - dbus_message_iter_get_basic(&var, &name); - name = g_strconcat(alias, " ", name, NULL); - } else - name = g_strdup(alias); - - dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name); - - g_free(name); - - return TRUE; -} - -static const GDBusMethodTable mpris_methods[] = { - { } -}; - -static gboolean get_tracklist(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - dbus_bool_t value; - - value = player->tracklist != NULL ? TRUE : FALSE; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); - - return TRUE; -} - -static const GDBusPropertyTable mpris_properties[] = { - { "CanQuit", "b", get_disable, NULL, NULL }, - { "Fullscreen", "b", get_disable, NULL, NULL }, - { "CanSetFullscreen", "b", get_disable, NULL, NULL }, - { "CanRaise", "b", get_disable, NULL, NULL }, - { "HasTrackList", "b", get_tracklist, NULL, NULL }, - { "Identity", "s", get_name, NULL, NULL }, - { } -}; - -static GDBusProxy *find_item(struct player *player, const char *path) -{ - struct tracklist *tracklist = player->tracklist; - GSList *l; - - for (l = tracklist->items; l; l = l->next) { - GDBusProxy *proxy = l->data; - const char *p = g_dbus_proxy_get_path(proxy); - - if (g_str_equal(path, p)) - return proxy; - } - - return NULL; -} - -static void append_item_metadata(void *data, void *user_data) -{ - GDBusProxy *item = data; - DBusMessageIter *iter = user_data; - DBusMessageIter var, metadata; - const char *path = g_dbus_proxy_get_path(item); - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata); - - dict_append_entry(&metadata, "mpris:trackid", DBUS_TYPE_OBJECT_PATH, - &path); - - if (g_dbus_proxy_get_property(item, "Metadata", &var)) - parse_metadata(&var, &metadata, parse_track_entry); - - dbus_message_iter_close_container(iter, &metadata); - - return; -} - -static DBusMessage *tracklist_get_metadata(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct player *player = data; - DBusMessage *reply; - DBusMessageIter args, array; - GSList *l = NULL; - - dbus_message_iter_init(msg, &args); - - if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid Arguments"); - - dbus_message_iter_recurse(&args, &array); - - while (dbus_message_iter_get_arg_type(&array) == - DBUS_TYPE_OBJECT_PATH) { - const char *path; - GDBusProxy *item; - - dbus_message_iter_get_basic(&array, &path); - - item = find_item(player, path); - if (item == NULL) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid Arguments"); - - l = g_slist_append(l, item); - - dbus_message_iter_next(&array); - } - - reply = dbus_message_new_method_return(msg); - - dbus_message_iter_init_append(reply, &args); - - dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, - DBUS_TYPE_ARRAY_AS_STRING - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &array); - - g_slist_foreach(l, append_item_metadata, &array); - - dbus_message_iter_close_container(&args, &array); - - return reply; -} - -static void item_play_reply(DBusMessage *message, void *user_data) -{ - struct pending_call *p = user_data; - struct player *player = p->player; - DBusMessage *msg = p->msg; - DBusMessage *reply; - DBusError err; - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, message)) { - fprintf(stderr, "error: %s", err.name); - reply = g_dbus_create_error(msg, err.name, "%s", err.message); - dbus_error_free(&err); - } else - reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - - g_dbus_send_message(player->conn, reply); -} - -static void item_play(struct player *player, DBusMessage *msg, - GDBusProxy *item) -{ - struct pending_call *p; - - p = g_new0(struct pending_call, 1); - p->player = player; - p->msg = dbus_message_ref(msg); - - g_dbus_proxy_method_call(item, "Play", NULL, item_play_reply, - p, pending_call_free); -} - -static DBusMessage *tracklist_goto(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct player *player = data; - GDBusProxy *item; - const char *path; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid arguments"); - - item = find_item(player, path); - if (item == NULL) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid arguments"); - - item_play(player, msg, item); - - return NULL; -} - -static DBusMessage *tracklist_add_track(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".NotImplemented", - "Not implemented"); -} - -static DBusMessage *tracklist_remove_track(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".NotImplemented", - "Not implemented"); -} - -static const GDBusMethodTable tracklist_methods[] = { - { GDBUS_METHOD("GetTracksMetadata", - GDBUS_ARGS({ "tracks", "ao" }), - GDBUS_ARGS({ "metadata", "aa{sv}" }), - tracklist_get_metadata) }, - { GDBUS_METHOD("AddTrack", - GDBUS_ARGS({ "uri", "s" }, { "after", "o" }, - { "current", "b" }), - NULL, - tracklist_add_track) }, - { GDBUS_METHOD("RemoveTrack", - GDBUS_ARGS({ "track", "o" }), NULL, - tracklist_remove_track) }, - { GDBUS_ASYNC_METHOD("GoTo", - GDBUS_ARGS({ "track", "o" }), NULL, - tracklist_goto) }, - { }, -}; - -static const GDBusSignalTable tracklist_signals[] = { - { GDBUS_SIGNAL("TrackAdded", GDBUS_ARGS({"metadata", "a{sv}"}, - {"after", "o"})) }, - { GDBUS_SIGNAL("TrackRemoved", GDBUS_ARGS({"track", "o"})) }, - { GDBUS_SIGNAL("TrackMetadataChanged", GDBUS_ARGS({"track", "o"}, - {"metadata", "a{sv}"})) }, - { } -}; - -static gboolean tracklist_exists(const GDBusPropertyTable *property, void *data) -{ - struct player *player = data; - - return player->tracklist != NULL; -} - -static void append_path(gpointer data, gpointer user_data) -{ - GDBusProxy *proxy = data; - DBusMessageIter *iter = user_data; - const char *path = g_dbus_proxy_get_path(proxy); - - dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); -} - -static gboolean get_tracks(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - struct tracklist *tracklist = player->tracklist; - DBusMessageIter value; - - if (tracklist == NULL) - return FALSE; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_OBJECT_PATH_AS_STRING, - &value); - g_slist_foreach(player->tracklist->items, append_path, &value); - dbus_message_iter_close_container(iter, &value); - - return TRUE; -} - -static const GDBusPropertyTable tracklist_properties[] = { - { "Tracks", "ao", get_tracks, NULL, tracklist_exists }, - { "CanEditTracks", "b", get_disable, NULL, NULL }, - { } -}; - -static void list_items_setup(DBusMessageIter *iter, void *user_data) -{ - DBusMessageIter dict; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING - DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, - &dict); - dbus_message_iter_close_container(iter, &dict); -} - -static void change_folder_reply(DBusMessage *message, void *user_data) -{ - struct player *player = user_data; - struct tracklist *tracklist = player->tracklist; - DBusError err; - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, message)) { - fprintf(stderr, "error: %s", err.name); - return; - } - - g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYLISTS_INTERFACE, - "ActivePlaylist"); - - g_dbus_proxy_method_call(tracklist->proxy, "ListItems", - list_items_setup, NULL, NULL, NULL); -} - -static void change_folder_setup(DBusMessageIter *iter, void *user_data) -{ - struct player *player = user_data; - const char *path; - - path = g_dbus_proxy_get_path(player->playlist); - - dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); -} - -static DBusMessage *playlist_activate(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct player *player = data; - struct tracklist *tracklist = player->tracklist; - const char *path; - - if (player->playlist == NULL || tracklist == NULL) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid Arguments"); - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid Arguments"); - - if (!g_str_equal(path, g_dbus_proxy_get_path(player->playlist))) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid Arguments"); - - g_dbus_proxy_method_call(tracklist->proxy, "ChangeFolder", - change_folder_setup, change_folder_reply, - player, NULL); - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static DBusMessage *playlist_get(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct player *player = data; - uint32_t index, count; - const char *order; - dbus_bool_t reverse; - DBusMessage *reply; - DBusMessageIter iter, entry, value, name; - const char *string, *path; - const char *empty = ""; - - if (player->playlist == NULL) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid Arguments"); - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_UINT32, &index, - DBUS_TYPE_UINT32, &count, - DBUS_TYPE_STRING, &order, - DBUS_TYPE_BOOLEAN, &reverse, - DBUS_TYPE_INVALID)) - return g_dbus_create_error(msg, - ERROR_INTERFACE ".InvalidArguments", - "Invalid Arguments"); - - path = g_dbus_proxy_get_path(player->playlist); - - reply = dbus_message_new_method_return(msg); - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(oss)", - &entry); - dbus_message_iter_open_container(&entry, DBUS_TYPE_STRUCT, NULL, - &value); - dbus_message_iter_append_basic(&value, DBUS_TYPE_OBJECT_PATH, &path); - if (g_dbus_proxy_get_property(player->playlist, "Name", &name)) { - dbus_message_iter_get_basic(&name, &string); - dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, - &string); - } else { - dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, - &path); - } - dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &empty); - dbus_message_iter_close_container(&entry, &value); - dbus_message_iter_close_container(&iter, &entry); - - return reply; -} - -static const GDBusMethodTable playlist_methods[] = { - { GDBUS_METHOD("ActivatePlaylist", - GDBUS_ARGS({ "playlist", "o" }), NULL, - playlist_activate) }, - { GDBUS_METHOD("GetPlaylists", - GDBUS_ARGS({ "index", "u" }, { "maxcount", "u"}, - { "order", "s" }, { "reverse", "b" }), - GDBUS_ARGS({ "playlists", "a(oss)"}), - playlist_get) }, - { }, -}; - -static gboolean playlist_exists(const GDBusPropertyTable *property, void *data) -{ - struct player *player = data; - - return player->playlist != NULL; -} - -static gboolean get_playlist_count(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - uint32_t count = 1; - - if (player->playlist == NULL) - return FALSE; - - dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &count); - - return TRUE; -} - -static gboolean get_orderings(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - DBusMessageIter value; - const char *order = "User"; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_OBJECT_PATH_AS_STRING, - &value); - dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &order); - dbus_message_iter_close_container(iter, &value); - - return TRUE; -} - -static gboolean get_active_playlist(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct player *player = data; - DBusMessageIter value, entry; - dbus_bool_t enabled = TRUE; - const char *path, *empty = ""; - - if (player->playlist == NULL) - return FALSE; - - path = g_dbus_proxy_get_path(player->playlist); - - dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, - NULL, &value); - dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &enabled); - dbus_message_iter_open_container(&value, DBUS_TYPE_STRUCT, NULL, - &entry); - dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path); - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &path); - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &empty); - dbus_message_iter_close_container(&value, &entry); - dbus_message_iter_close_container(iter, &value); - - return TRUE; -} - -static const GDBusPropertyTable playlist_properties[] = { - { "PlaylistCount", "u", get_playlist_count, NULL, playlist_exists }, - { "Orderings", "as", get_orderings, NULL, NULL }, - { "ActivePlaylist", "(b(oss))", get_active_playlist, NULL, - playlist_exists }, - { } -}; - -#define a_z "abcdefghijklmnopqrstuvwxyz" -#define A_Z "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -#define _0_9 "_0123456789" - -static char *mpris_busname(char *name) -{ - if (g_ascii_isdigit(name[0])) - return g_strconcat(MPRIS_BUS_NAME, "bt_", - g_strcanon(name, A_Z a_z _0_9, '_'), NULL); - else - return g_strconcat(MPRIS_BUS_NAME, - g_strcanon(name, A_Z a_z _0_9, '_'), NULL); -} - -static GDBusProxy *find_transport_by_path(const char *path) -{ - GSList *l; - - for (l = transports; l; l = l->next) { - GDBusProxy *transport = l->data; - DBusMessageIter iter; - const char *value; - - if (!g_dbus_proxy_get_property(transport, "Device", &iter)) - continue; - - dbus_message_iter_get_basic(&iter, &value); - - if (strcmp(path, value) == 0) - return transport; - } - - return NULL; -} - -static struct player *find_player(GDBusProxy *proxy) -{ - GSList *l; - - for (l = players; l; l = l->next) { - struct player *player = l->data; - const char *path, *p; - - if (player->proxy == proxy) - return player; - - path = g_dbus_proxy_get_path(proxy); - p = g_dbus_proxy_get_path(player->proxy); - if (g_str_equal(path, p)) - return player; - } - - return NULL; -} - -static void register_tracklist(GDBusProxy *proxy) -{ - struct player *player; - struct tracklist *tracklist; - - player = find_player(proxy); - if (player == NULL) - return; - - if (player->tracklist != NULL) - return; - - tracklist = g_new0(struct tracklist, 1); - tracklist->proxy = g_dbus_proxy_ref(proxy); - - player->tracklist = tracklist; - - g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH, - MPRIS_INTERFACE, - "HasTrackList"); - - if (player->playlist == NULL) - return; - - g_dbus_proxy_method_call(player->tracklist->proxy, "ChangeFolder", - change_folder_setup, change_folder_reply, - player, NULL); -} - -static void register_player(GDBusProxy *proxy) -{ - struct player *player; - DBusMessageIter iter; - const char *path, *alias, *name; - char *busname; - GDBusProxy *device, *transport; - - if (!g_dbus_proxy_get_property(proxy, "Device", &iter)) - return; - - dbus_message_iter_get_basic(&iter, &path); - - device = g_dbus_proxy_new(client, path, "org.bluez.Device1"); - if (device == NULL) - return; - - if (!g_dbus_proxy_get_property(device, "Alias", &iter)) - return; - - dbus_message_iter_get_basic(&iter, &alias); - - if (g_dbus_proxy_get_property(proxy, "Name", &iter)) { - dbus_message_iter_get_basic(&iter, &name); - busname = g_strconcat(alias, " ", name, NULL); - } else - busname = g_strdup(alias); - - player = g_new0(struct player, 1); - player->bus_name = mpris_busname(busname); - player->proxy = g_dbus_proxy_ref(proxy); - player->device = device; - - g_free(busname); - - players = g_slist_prepend(players, player); - - printf("Player %s created\n", player->bus_name); - - player->conn = g_dbus_setup_private(DBUS_BUS_SESSION, player->bus_name, - NULL); - if (!session) { - fprintf(stderr, "Could not register bus name %s", - player->bus_name); - goto fail; - } - - if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH, - MPRIS_INTERFACE, - mpris_methods, - NULL, - mpris_properties, - player, NULL)) { - fprintf(stderr, "Could not register interface %s", - MPRIS_INTERFACE); - goto fail; - } - - if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYER_INTERFACE, - player_methods, - player_signals, - player_properties, - player, player_free)) { - fprintf(stderr, "Could not register interface %s", - MPRIS_PLAYER_INTERFACE); - goto fail; - } - - if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH, - MPRIS_TRACKLIST_INTERFACE, - tracklist_methods, - tracklist_signals, - tracklist_properties, - player, NULL)) { - fprintf(stderr, "Could not register interface %s", - MPRIS_TRACKLIST_INTERFACE); - goto fail; - } - - if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYLISTS_INTERFACE, - playlist_methods, - NULL, - playlist_properties, - player, NULL)) { - fprintf(stderr, "Could not register interface %s", - MPRIS_PLAYLISTS_INTERFACE); - goto fail; - } - - transport = find_transport_by_path(path); - if (transport) - player->transport = g_dbus_proxy_ref(transport); - - return; - -fail: - players = g_slist_remove(players, player); - player_free(player); -} - -static struct player *find_player_by_device(const char *device) -{ - GSList *l; - - for (l = players; l; l = l->next) { - struct player *player = l->data; - const char *path = g_dbus_proxy_get_path(player->device); - - if (g_strcmp0(device, path) == 0) - return player; - } - - return NULL; -} - -static void register_transport(GDBusProxy *proxy) -{ - struct player *player; - DBusMessageIter iter; - const char *path; - - if (g_slist_find(transports, proxy) != NULL) - return; - - if (!g_dbus_proxy_get_property(proxy, "Volume", &iter)) - return; - - if (!g_dbus_proxy_get_property(proxy, "Device", &iter)) - return; - - dbus_message_iter_get_basic(&iter, &path); - - transports = g_slist_append(transports, proxy); - - player = find_player_by_device(path); - if (player == NULL || player->transport != NULL) - return; - - player->transport = g_dbus_proxy_ref(proxy); -} - -static struct player *find_player_by_item(const char *item) -{ - GSList *l; - - for (l = players; l; l = l->next) { - struct player *player = l->data; - const char *path = g_dbus_proxy_get_path(player->proxy); - - if (g_str_has_prefix(item, path)) - return player; - } - - return NULL; -} - -static void register_playlist(struct player *player, GDBusProxy *proxy) -{ - const char *path; - DBusMessageIter iter; - - if (!g_dbus_proxy_get_property(player->proxy, "Playlist", &iter)) - return; - - dbus_message_iter_get_basic(&iter, &path); - - if (!g_str_equal(path, g_dbus_proxy_get_path(proxy))) - return; - - player->playlist = g_dbus_proxy_ref(proxy); - - g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYLISTS_INTERFACE, - "PlaylistCount"); - - if (player->tracklist == NULL) - return; - - g_dbus_proxy_method_call(player->tracklist->proxy, "ChangeFolder", - change_folder_setup, change_folder_reply, - player, NULL); -} - -static void register_item(struct player *player, GDBusProxy *proxy) -{ - struct tracklist *tracklist; - const char *path, *playlist; - DBusMessage *signal; - DBusMessageIter iter, args, metadata; - GSList *l; - GDBusProxy *after; - - if (player->playlist == NULL) { - register_playlist(player, proxy); - return; - } - - tracklist = player->tracklist; - if (tracklist == NULL) - return; - - path = g_dbus_proxy_get_path(proxy); - playlist = g_dbus_proxy_get_path(player->playlist); - if (!g_str_has_prefix(path, playlist)) - return; - - l = g_slist_last(tracklist->items); - tracklist->items = g_slist_append(tracklist->items, proxy); - - g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH, - MPRIS_TRACKLIST_INTERFACE, - "Tracks"); - - if (l == NULL) - return; - - signal = dbus_message_new_signal(MPRIS_PLAYER_PATH, - MPRIS_TRACKLIST_INTERFACE, - "TrackAdded"); - if (!signal) { - fprintf(stderr, "Unable to allocate new %s.TrackAdded signal", - MPRIS_TRACKLIST_INTERFACE); - return; - } - - dbus_message_iter_init_append(signal, &args); - - if (!g_dbus_proxy_get_property(proxy, "Metadata", &iter)) { - dbus_message_unref(signal); - return; - } - - dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata); - - parse_metadata(&iter, &metadata, parse_track_entry); - - dbus_message_iter_close_container(&args, &metadata); - - after = l->data; - path = g_dbus_proxy_get_path(after); - dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &path); - - g_dbus_send_message(player->conn, signal); -} - -static void proxy_added(GDBusProxy *proxy, void *user_data) -{ - const char *interface; - const char *path; - - interface = g_dbus_proxy_get_interface(proxy); - path = g_dbus_proxy_get_path(proxy); - - if (!strcmp(interface, BLUEZ_ADAPTER_INTERFACE)) { - if (adapter != NULL) - return; - - printf("Bluetooth Adapter %s found\n", path); - adapter = proxy; - list_names(session); - } else if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) { - printf("Bluetooth Player %s found\n", path); - register_player(proxy); - } else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) { - printf("Bluetooth Transport %s found\n", path); - register_transport(proxy); - } else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE)) { - printf("Bluetooth Folder %s found\n", path); - register_tracklist(proxy); - } else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE)) { - struct player *player; - - player = find_player_by_item(path); - if (player == NULL) - return; - - printf("Bluetooth Item %s found\n", path); - register_item(player, proxy); - } -} - -static void unregister_player(struct player *player) -{ - players = g_slist_remove(players, player); - - if (player->tracklist != NULL) { - g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYLISTS_INTERFACE); - g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH, - MPRIS_TRACKLIST_INTERFACE); - } - - g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH, - MPRIS_INTERFACE); - - g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYER_INTERFACE); -} - -static struct player *find_player_by_transport(GDBusProxy *proxy) -{ - GSList *l; - - for (l = players; l; l = l->next) { - struct player *player = l->data; - - if (player->transport == proxy) - return player; - } - - return NULL; -} - -static void unregister_transport(GDBusProxy *proxy) -{ - struct player *player; - - if (g_slist_find(transports, proxy) == NULL) - return; - - transports = g_slist_remove(transports, proxy); - - player = find_player_by_transport(proxy); - if (player == NULL) - return; - - g_dbus_proxy_unref(player->transport); - player->transport = NULL; -} - -static void unregister_item(struct player *player, GDBusProxy *proxy) -{ - struct tracklist *tracklist = player->tracklist; - const char *path; - - if (tracklist == NULL) - return; - - if (g_slist_find(tracklist->items, proxy) == NULL) - return; - - path = g_dbus_proxy_get_path(proxy); - - tracklist->items = g_slist_remove(tracklist->items, proxy); - - g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH, - MPRIS_TRACKLIST_INTERFACE, - "Tracks"); - - g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH, - MPRIS_TRACKLIST_INTERFACE, "TrackRemoved", - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); -} - -static void remove_players(DBusConnection *conn) -{ - char **paths; - int i; - - dbus_connection_list_registered(conn, "/", &paths); - - for (i = 0; paths[i]; i++) { - char *path; - void *data; - - path = g_strdup_printf("/%s", paths[i]); - dbus_connection_get_object_path_data(sys, path, &data); - dbus_connection_unregister_object_path(sys, path); - - g_free(path); - g_free(data); - } - - dbus_free_string_array(paths); -} - -static void proxy_removed(GDBusProxy *proxy, void *user_data) -{ - const char *interface; - const char *path; - - if (adapter == NULL) - return; - - interface = g_dbus_proxy_get_interface(proxy); - path = g_dbus_proxy_get_path(proxy); - - if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0) { - if (adapter != proxy) - return; - printf("Bluetooth Adapter %s removed\n", path); - adapter = NULL; - remove_players(sys); - } else if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0) { - struct player *player; - - player = find_player(proxy); - if (player == NULL) - return; - - printf("Bluetooth Player %s removed\n", path); - unregister_player(player); - } else if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) { - printf("Bluetooth Transport %s removed\n", path); - unregister_transport(proxy); - } else if (strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE) == 0) { - struct player *player; - - player = find_player_by_item(path); - if (player == NULL) - return; - - printf("Bluetooth Item %s removed\n", path); - unregister_item(player, proxy); - } -} - -static const char *property_to_mpris(const char *property) -{ - if (strcasecmp(property, "Repeat") == 0) - return "LoopStatus"; - else if (strcasecmp(property, "Shuffle") == 0) - return "Shuffle"; - else if (strcasecmp(property, "Status") == 0) - return "PlaybackStatus"; - else if (strcasecmp(property, "Position") == 0) - return "Position"; - else if (strcasecmp(property, "Track") == 0) - return "Metadata"; - - return NULL; -} - -static void player_property_changed(GDBusProxy *proxy, const char *name, - DBusMessageIter *iter, void *user_data) -{ - struct player *player; - const char *property; - uint32_t position; - uint64_t value; - - player = find_player(proxy); - if (player == NULL) - return; - - property = property_to_mpris(name); - if (property == NULL) - return; - - g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYER_INTERFACE, - property); - - if (strcasecmp(name, "Position") != 0) - return; - - dbus_message_iter_get_basic(iter, &position); - - value = position * 1000; - - g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYER_INTERFACE, "Seeked", - DBUS_TYPE_INT64, &value, - DBUS_TYPE_INVALID); -} - -static void transport_property_changed(GDBusProxy *proxy, const char *name, - DBusMessageIter *iter, void *user_data) -{ - struct player *player; - DBusMessageIter var; - const char *path; - - if (strcasecmp(name, "Volume") != 0 && strcasecmp(name, "State") != 0) - return; - - if (!g_dbus_proxy_get_property(proxy, "Device", &var)) - return; - - dbus_message_iter_get_basic(&var, &path); - - player = find_player_by_device(path); - if (player == NULL) - return; - - if (strcasecmp(name, "State") == 0) { - if (!g_dbus_proxy_get_property(player->proxy, "Status", &var)) - g_dbus_emit_property_changed(player->conn, - MPRIS_PLAYER_PATH, - MPRIS_PLAYER_INTERFACE, - "PlaybackStatus"); - return; - } - - g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH, - MPRIS_PLAYER_INTERFACE, - name); -} - -static void item_property_changed(GDBusProxy *proxy, const char *name, - DBusMessageIter *iter, void *user_data) -{ - struct player *player; - DBusMessage *signal; - DBusMessageIter args; - const char *path; - - path = g_dbus_proxy_get_path(proxy); - - player = find_player_by_item(path); - if (player == NULL) - return; - - if (strcasecmp(name, "Metadata") != 0) - return; - - signal = dbus_message_new_signal(MPRIS_PLAYER_PATH, - MPRIS_TRACKLIST_INTERFACE, - "TrackMetadataChanged"); - if (!signal) { - fprintf(stderr, "Unable to allocate new %s.TrackAdded signal", - MPRIS_TRACKLIST_INTERFACE); - return; - } - - dbus_message_iter_init_append(signal, &args); - - dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &path); - - append_iter(&args, iter); - - g_dbus_send_message(player->conn, signal); -} - -static void property_changed(GDBusProxy *proxy, const char *name, - DBusMessageIter *iter, void *user_data) -{ - const char *interface; - - interface = g_dbus_proxy_get_interface(proxy); - - if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0) - return player_property_changed(proxy, name, iter, user_data); - - if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) - return transport_property_changed(proxy, name, iter, - user_data); - - if (strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE) == 0) - return item_property_changed(proxy, name, iter, user_data); -} - -int main(int argc, char *argv[]) -{ - GOptionContext *context; - GError *error = NULL; - guint owner_watch, properties_watch, signal_watch; - struct sigaction sa; - - context = g_option_context_new(NULL); - g_option_context_add_main_entries(context, options, NULL); - - if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { - if (error != NULL) { - g_printerr("%s\n", error->message); - g_error_free(error); - } else - g_printerr("An unknown error occurred\n"); - exit(1); - } - - g_option_context_free(context); - - if (option_version == TRUE) { - usage(); - exit(0); - } - - main_loop = g_main_loop_new(NULL, FALSE); - - sys = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); - if (!sys) { - fprintf(stderr, "Can't get on system bus"); - exit(1); - } - - session = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL); - if (!session) { - fprintf(stderr, "Can't get on session bus"); - exit(1); - } - - owner_watch = g_dbus_add_signal_watch(session, NULL, NULL, - DBUS_INTERFACE_DBUS, - "NameOwnerChanged", - name_owner_changed, - NULL, NULL); - - properties_watch = g_dbus_add_properties_watch(session, NULL, NULL, - MPRIS_PLAYER_INTERFACE, - player_signal, - NULL, NULL); - - signal_watch = g_dbus_add_signal_watch(session, NULL, NULL, - MPRIS_PLAYER_INTERFACE, - NULL, player_signal, - NULL, NULL); - - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - client = g_dbus_client_new(sys, BLUEZ_BUS_NAME, BLUEZ_PATH); - - g_dbus_client_set_connect_watch(client, connect_handler, NULL); - g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); - - g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, - property_changed, NULL); - - g_main_loop_run(main_loop); - - g_dbus_remove_watch(session, owner_watch); - g_dbus_remove_watch(session, properties_watch); - g_dbus_remove_watch(session, signal_watch); - - g_dbus_client_unref(client); - - dbus_connection_unref(session); - dbus_connection_unref(sys); - - g_main_loop_unref(main_loop); - - return 0; -} diff --git a/tools/mpris-proxy.c b/tools/mpris-proxy.c index 397f0647..693055ed 100644 --- a/tools/mpris-proxy.c +++ b/tools/mpris-proxy.c @@ -36,7 +36,8 @@ #include <dbus/dbus.h> #include <glib.h> -#include <gdbus/gdbus.h> + +#include "gdbus/gdbus.h" #define BLUEZ_BUS_NAME "org.bluez" #define BLUEZ_PATH "/org/bluez" diff --git a/tools/obex-client-tool.c b/tools/obex-client-tool.c index 4716a8c1..4f931f6d 100644 --- a/tools/obex-client-tool.c +++ b/tools/obex-client-tool.c @@ -36,7 +36,7 @@ #include <readline/readline.h> #include <readline/history.h> -#include <gobex/gobex.h> +#include "gobex/gobex.h" #include "btio/btio.h" static GMainLoop *main_loop = NULL; diff --git a/tools/obex-server-tool.c b/tools/obex-server-tool.c index 3fed6dc9..a16c46f8 100644 --- a/tools/obex-server-tool.c +++ b/tools/obex-server-tool.c @@ -33,7 +33,7 @@ #include <string.h> #include <errno.h> -#include <gobex/gobex.h> +#include "gobex/gobex.h" #include "btio/btio.h" static GMainLoop *main_loop = NULL; diff --git a/tools/obexctl.c b/tools/obexctl.c index 512a145a..4faff6b6 100644 --- a/tools/obexctl.c +++ b/tools/obexctl.c @@ -33,13 +33,14 @@ #include <signal.h> #include <sys/signalfd.h> #include <inttypes.h> +#include <wordexp.h> #include <readline/readline.h> #include <readline/history.h> #include <glib.h> -#include <gdbus.h> -#include <client/display.h> +#include "gdbus/gdbus.h" +#include "client/display.h" /* String display constants */ #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF @@ -1232,7 +1233,6 @@ static void list_messages_reply(DBusMessage *message, void *user_data) { DBusError error; DBusMessageIter iter, array; - int ctype; dbus_error_init(&error); @@ -1249,8 +1249,8 @@ static void list_messages_reply(DBusMessage *message, void *user_data) dbus_message_iter_recurse(&iter, &array); - while ((ctype = dbus_message_iter_get_arg_type(&array)) == - DBUS_TYPE_DICT_ENTRY) { + while ((dbus_message_iter_get_arg_type(&array)) == + DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *obj; @@ -2030,8 +2030,9 @@ static char **cmd_completion(const char *text, int start, int end) static void rl_handler(char *input) { + wordexp_t w; int argc; - char **argv = NULL; + char **argv; int i; if (!input) { @@ -2045,18 +2046,16 @@ static void rl_handler(char *input) if (!strlen(input)) goto done; - g_strstrip(input); add_history(input); - argv = g_strsplit(input, " ", -1); - if (argv == NULL) + if (wordexp(input, &w, WRDE_NOCMD)) goto done; - for (argc = 0; argv[argc];) - argc++; + if (w.we_wordc == 0) + goto free_we; - if (argc == 0) - goto done; + argv = w.we_wordv; + argc = w.we_wordc; for (i = 0; cmd_table[i].cmd; i++) { if (strcmp(argv[0], cmd_table[i].cmd)) @@ -2064,13 +2063,13 @@ static void rl_handler(char *input) if (cmd_table[i].func) { cmd_table[i].func(argc, argv); - goto done; + goto free_we; } } if (strcmp(argv[0], "help")) { printf("Invalid command\n"); - goto done; + goto free_we; } printf("Available commands:\n"); @@ -2083,8 +2082,9 @@ static void rl_handler(char *input) cmd_table[i].desc ? : ""); } +free_we: + wordfree(&w); done: - g_strfreev(argv); free(input); } diff --git a/tools/oobtest.c b/tools/oobtest.c new file mode 100644 index 00000000..9cc6c162 --- /dev/null +++ b/tools/oobtest.c @@ -0,0 +1,800 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011-2012 Intel Corporation + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <getopt.h> + +#include "lib/bluetooth.h" +#include "lib/mgmt.h" + +#include "src/shared/mainloop.h" +#include "src/shared/util.h" +#include "src/shared/mgmt.h" + +static bool use_bredr = false; +static bool use_le = false; +static bool use_sc = false; +static bool use_sconly = false; +static bool use_legacy = false; +static bool use_random = false; +static bool use_debug = false; +static bool use_cross = false; +static bool provide_p192 = false; +static bool provide_p256 = false; + +static struct mgmt *mgmt; +static uint16_t index1 = MGMT_INDEX_NONE; +static uint16_t index2 = MGMT_INDEX_NONE; +static bdaddr_t bdaddr1; +static bdaddr_t bdaddr2; + +static void pin_code_request_event(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_pin_code_request *ev = param; + struct mgmt_cp_pin_code_reply cp; + char str[18]; + + ba2str(&ev->addr.bdaddr, str); + + printf("[Index %u]\n", index); + printf(" Pin code request: %s\n", str); + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.addr, &ev->addr, sizeof(cp.addr)); + cp.pin_len = 4; + memset(cp.pin_code, '0', 4); + + mgmt_reply(mgmt, MGMT_OP_PIN_CODE_REPLY, index, sizeof(cp), &cp, + NULL, NULL, NULL); +} + +static void new_link_key_event(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_link_key *ev = param; + const char *type; + char str[18]; + int i; + + ba2str(&ev->key.addr.bdaddr, str); + + switch (ev->key.type) { + case 0x00: + type = "Legacy"; + break; + case 0x01: + type = "Local Unit"; + break; + case 0x02: + type = "Remote Unit"; + break; + case 0x03: + type = "Debug"; + break; + case 0x04: + type = "Unauthenticated, P-192"; + break; + case 0x05: + type = "Authenticated, P-192"; + break; + case 0x06: + type = "Changed"; + break; + case 0x07: + type = "Unauthenticated, P-256"; + break; + case 0x08: + type = "Authenticated, P-256"; + break; + default: + type = "<unknown>"; + break; + } + + printf("[Index %u]\n", index); + printf(" New link key: %s\n", str); + printf(" Type: %s (%u)\n", type, ev->key.type); + printf(" Key: "); + for (i = 0; i < 16; i++) + printf("%02x", ev->key.val[i]); + printf("\n"); +} + +static void new_long_term_key_event(uint16_t index, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_long_term_key *ev = param; + const char *type; + char str[18]; + int i; + + ba2str(&ev->key.addr.bdaddr, str); + + switch (ev->key.type) { + case 0x00: + if (ev->key.master) + type = "Unauthenticated, Master"; + else + type = "Unauthenticated, Slave"; + break; + case 0x01: + if (ev->key.master) + type = "Authenticated, Master"; + else + type = "Authenticated, Slave"; + break; + case 0x02: + type = "Unauthenticated, P-256"; + break; + case 0x03: + type = "Authenticated, P-256"; + break; + case 0x04: + type = "Debug"; + break; + default: + type = "<unknown>"; + break; + } + + printf("[Index %u]\n", index); + printf(" New long term key: %s\n", str); + printf(" Type: %s (%u)\n", type, ev->key.type); + printf(" Key: "); + for (i = 0; i < 16; i++) + printf("%02x", ev->key.val[i]); + printf("\n"); +} + +static void pair_device_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + + if (status) { + fprintf(stderr, "Pair device from index %u failed: %s\n", + index, mgmt_errstr(status)); + } + + mainloop_quit(); +} + +static void pair_device(uint16_t index, const bdaddr_t *bdaddr) +{ + struct mgmt_cp_pair_device cp; + char str[18]; + + ba2str(bdaddr, str); + + printf("[Index %u]\n", index); + printf(" Starting pairing: %s\n", str); + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, bdaddr); + if (use_bredr) + cp.addr.type = BDADDR_BREDR; + else if (use_random) + cp.addr.type = BDADDR_LE_RANDOM; + else + cp.addr.type = BDADDR_LE_PUBLIC; + cp.io_cap = 0x03; + + mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp, + pair_device_complete, + UINT_TO_PTR(index), NULL); +} + +static void add_remote_oob_data_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_addr_info *rp = param; + uint16_t index = PTR_TO_UINT(user_data); + char str[18]; + + if (status) { + fprintf(stderr, "Adding OOB data for index %u failed: %s\n", + index, mgmt_errstr(status)); + } + + ba2str(&rp->bdaddr, str); + + printf("[Index %u]\n", index); + printf(" Remote data added: %s\n", str); + + if (index == index1) { + uint8_t val = 0x01; + + mgmt_send(mgmt, MGMT_OP_SET_CONNECTABLE, index2, 1, &val, + NULL, NULL, NULL); + + if (use_le) + mgmt_send(mgmt, MGMT_OP_SET_ADVERTISING, index2, + 1, &val, NULL, NULL, NULL); + + pair_device(index1, &bdaddr2); + } +} + +static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr, + const uint8_t *hash192, const uint8_t *rand192, + const uint8_t *hash256, const uint8_t *rand256) +{ + struct mgmt_cp_add_remote_oob_data cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, bdaddr); + if (use_bredr) + cp.addr.type = BDADDR_BREDR; + else if (use_random) + cp.addr.type = BDADDR_LE_RANDOM; + else + cp.addr.type = BDADDR_LE_PUBLIC; + if (hash192 && rand192) { + memcpy(cp.hash192, hash192, 16); + memcpy(cp.rand192, rand192, 16); + } else { + memset(cp.hash192, 0, 16); + memset(cp.rand192, 0, 16); + } + if (hash256 && rand256) { + memcpy(cp.hash256, hash256, 16); + memcpy(cp.rand256, rand256, 16); + } else { + memset(cp.hash256, 0, 16); + memset(cp.rand256, 0, 16); + } + + mgmt_send(mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA, index, sizeof(cp), &cp, + add_remote_oob_data_complete, + UINT_TO_PTR(index), NULL); +} + +static void read_oob_data_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + const struct mgmt_rp_read_local_oob_ext_data *rp = param; + uint16_t index = PTR_TO_UINT(user_data); + const uint8_t *hash192, *rand192, *hash256, *rand256; + int i; + + if (status) { + fprintf(stderr, "Reading OOB data for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } + + printf("[Index %u]\n", index); + + if (provide_p192) { + hash192 = rp->hash192; + rand192 = rp->randomizer192; + } else { + hash192 = NULL; + rand192 = NULL; + } + + printf(" Hash C from P-192: "); + for (i = 0; i < 16; i++) + printf("%02x", rp->hash192[i]); + printf("\n"); + + printf(" Randomizer R with P-192: "); + for (i = 0; i < 16; i++) + printf("%02x", rp->randomizer192[i]); + printf("\n"); + + if (len < sizeof(*rp)) { + hash256 = NULL; + rand256 = NULL; + goto done; + } + + if (provide_p256) { + hash256 = rp->hash256; + rand256 = rp->randomizer256; + } else { + hash256 = NULL; + rand256 = NULL; + } + + printf(" Hash C from P-256: "); + for (i = 0; i < 16; i++) + printf("%02x", rp->hash256[i]); + printf("\n"); + + printf(" Randomizer R with P-256: "); + for (i = 0; i < 16; i++) + printf("%02x", rp->randomizer256[i]); + printf("\n"); + +done: + if (index == index1) + add_remote_oob_data(index2, &bdaddr1, + hash192, rand192, hash256, rand256); + else if (index == index2) + add_remote_oob_data(index1, &bdaddr2, + hash192, rand192, hash256, rand256); +} + +static void set_powered_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + uint16_t index = PTR_TO_UINT(user_data); + uint32_t settings; + uint8_t val; + + if (status) { + fprintf(stderr, "Powering on for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } + + settings = get_le32(param); + + if (!(settings & MGMT_SETTING_POWERED)) { + fprintf(stderr, "Controller is not powered\n"); + mainloop_quit(); + return; + } + + if (use_debug) { + if (index == index1) { + val = 0x02; + mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val, + NULL, NULL, NULL); + } else if (index == index2) { + val = 0x01; + mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val, + NULL, NULL, NULL); + } + } + + if (use_bredr && (provide_p192 || provide_p256)) { + mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL, + read_oob_data_complete, + UINT_TO_PTR(index), NULL); + } else { + if (index == index1) + add_remote_oob_data(index2, &bdaddr1, + NULL, NULL, NULL, NULL); + else if (index == index2) + add_remote_oob_data(index1, &bdaddr2, + NULL, NULL, NULL, NULL); + } +} + +static void clear_link_keys(uint16_t index) +{ + struct mgmt_cp_load_link_keys cp; + + memset(&cp, 0, sizeof(cp)); + cp.debug_keys = 0x00; + cp.key_count = cpu_to_le16(0); + + mgmt_send(mgmt, MGMT_OP_LOAD_LINK_KEYS, index, + sizeof(cp), &cp, NULL, NULL, NULL); +} + +static void clear_long_term_keys(uint16_t index) +{ + struct mgmt_cp_load_long_term_keys cp; + + memset(&cp, 0, sizeof(cp)); + cp.key_count = cpu_to_le16(0); + + mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index, + sizeof(cp), &cp, NULL, NULL, NULL); +} + +static void clear_remote_oob_data(uint16_t index) +{ + struct mgmt_cp_remove_remote_oob_data cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, BDADDR_ANY); + cp.addr.type = BDADDR_BREDR; + + mgmt_send(mgmt, MGMT_OP_REMOVE_REMOTE_OOB_DATA, index, + sizeof(cp), &cp, NULL, NULL, NULL); +} + +static void read_info(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_read_info *rp = param; + uint16_t index = PTR_TO_UINT(user_data); + uint32_t supported_settings; + uint8_t val; + char str[18]; + + if (status) { + fprintf(stderr, "Reading info for index %u failed: %s\n", + index, mgmt_errstr(status)); + mainloop_quit(); + return; + } + + ba2str(&rp->bdaddr, str); + + printf("[Index %u]\n", index); + printf(" Address: %s\n", str); + + if (index == index1) + bacpy(&bdaddr1, &rp->bdaddr); + else if (index == index2) + bacpy(&bdaddr2, &rp->bdaddr); + + supported_settings = le32_to_cpu(rp->supported_settings); + + if (use_bredr && !(supported_settings & MGMT_SETTING_BREDR)) { + fprintf(stderr, "BR/EDR support missing\n"); + mainloop_quit(); + return; + } + + if (!use_legacy && !(supported_settings & MGMT_SETTING_SSP)) { + fprintf(stderr, "Secure Simple Pairing support missing\n"); + mainloop_quit(); + return; + } + + if (use_le && !(supported_settings & MGMT_SETTING_LE)) { + fprintf(stderr, "Low Energy support missing\n"); + mainloop_quit(); + return; + } + + if (use_sc && !(supported_settings & MGMT_SETTING_SECURE_CONN)) { + fprintf(stderr, "Secure Connections support missing\n"); + mainloop_quit(); + return; + } + + if (use_sconly && !(supported_settings & MGMT_SETTING_SECURE_CONN)) { + fprintf(stderr, "Secure Connections Only support missing\n"); + mainloop_quit(); + return; + } + + if (use_debug && !(supported_settings & MGMT_SETTING_DEBUG_KEYS)) { + fprintf(stderr, "Debug keys support missing\n"); + mainloop_quit(); + return; + } + + if (use_cross && (!(supported_settings & MGMT_SETTING_BREDR) || + !(supported_settings & MGMT_SETTING_LE))) { + fprintf(stderr, "Dual-mode support is support missing\n"); + mainloop_quit(); + return; + } + + mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index, + pin_code_request_event, + UINT_TO_PTR(index), NULL); + + mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index, + new_link_key_event, + UINT_TO_PTR(index), NULL); + + mgmt_register(mgmt, MGMT_EV_NEW_LONG_TERM_KEY, index, + new_long_term_key_event, + UINT_TO_PTR(index), NULL); + + val = 0x00; + mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val, + NULL, NULL, NULL); + + clear_link_keys(index); + clear_long_term_keys(index); + clear_remote_oob_data(index); + + if (use_bredr) { + val = 0x01; + mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val, + NULL, NULL, NULL); + + val = use_cross ? 0x01 : 0x00; + mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val, + NULL, NULL, NULL); + + val = use_legacy ? 0x00 : 0x01; + mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val, + NULL, NULL, NULL); + } else if (use_le) { + val = 0x01; + mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val, + NULL, NULL, NULL); + + val = use_cross ? 0x01 : 0x00; + mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val, + NULL, NULL, NULL); + } else { + fprintf(stderr, "Invalid transport for pairing\n"); + mainloop_quit(); + return; + } + + if (use_random) { + bdaddr_t bdaddr; + + str2ba("c0:00:aa:bb:00:00", &bdaddr); + bdaddr.b[0] = index; + + mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, + 6, &bdaddr, NULL, NULL, NULL); + + if (index == index1) + bacpy(&bdaddr1, &bdaddr); + else if (index == index2) + bacpy(&bdaddr2, &bdaddr); + } else { + bdaddr_t bdaddr; + + bacpy(&bdaddr, BDADDR_ANY); + + mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, + 6, &bdaddr, NULL, NULL, NULL); + } + + if (use_sc) { + val = 0x01; + mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val, + NULL, NULL, NULL); + } else if (use_sconly) { + val = 0x02; + mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val, + NULL, NULL, NULL); + } else { + val = 0x00; + mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val, + NULL, NULL, NULL); + } + + val = 0x00; + mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val, + NULL, NULL, NULL); + + val = 0x01; + mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val, + NULL, NULL, NULL); + + val = 0x01; + mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val, + set_powered_complete, + UINT_TO_PTR(index), NULL); +} + +static void read_index_list(uint8_t status, uint16_t len, const void *param, + void *user_data) +{ + const struct mgmt_rp_read_index_list *rp = param; + uint16_t count; + int i; + + if (status) { + fprintf(stderr, "Reading index list failed: %s\n", + mgmt_errstr(status)); + mainloop_quit(); + return; + } + + count = le16_to_cpu(rp->num_controllers); + + if (count < 2) { + fprintf(stderr, "At least 2 controllers are required\n"); + mainloop_quit(); + return; + } + + for (i = 0; i < count; i++) { + uint16_t index = cpu_to_le16(rp->index[i]); + + if (index < index1) + index1 = index; + } + + for (i = 0; i < count; i++) { + uint16_t index = cpu_to_le16(rp->index[i]); + + if (index < index2 && index > index1) + index2 = index; + } + + printf("Selecting index %u for initiator\n", index1); + printf("Selecting index %u for acceptor\n", index2); + + mgmt_send(mgmt, MGMT_OP_READ_INFO, index1, 0, NULL, + read_info, UINT_TO_PTR(index1), NULL); + mgmt_send(mgmt, MGMT_OP_READ_INFO, index2, 0, NULL, + read_info, UINT_TO_PTR(index2), NULL); +} + +static void signal_callback(int signum, void *user_data) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + mainloop_quit(); + break; + } +} + +static void usage(void) +{ + printf("oobtest - Out-of-band pairing testing\n" + "Usage:\n"); + printf("\toobtest [options]\n"); + printf("options:\n" + "\t-B, --bredr Use BR/EDR transport\n" + "\t-L, --le Use LE transport\n" + "\t-S, --sc Use Secure Connections\n" + "\t-O, --sconly Use Secure Connections Only\n" + "\t-P, --legacy Use Legacy Pairing\n" + "\t-R, --random Use Static random address\n" + "\t-D, --debug Use Pairing debug keys\n" + "\t-C, --cross Use cross-transport pairing\n" + "\t-1, --p192 Provide P-192 OOB data\n" + "\t-2, --p256 Provide P-256 OOB data\n" + "\t-h, --help Show help options\n"); +} + +static const struct option main_options[] = { + { "bredr", no_argument, NULL, 'B' }, + { "le", no_argument, NULL, 'L' }, + { "sc", no_argument, NULL, 'S' }, + { "sconly", no_argument, NULL, 'O' }, + { "legacy", no_argument, NULL, 'P' }, + { "random", no_argument, NULL, 'R' }, + { "static", no_argument, NULL, 'R' }, + { "debug", no_argument, NULL, 'D' }, + { "cross", no_argument, NULL, 'C' }, + { "dual", no_argument, NULL, 'C' }, + { "p192", no_argument, NULL, '1' }, + { "p256", no_argument, NULL, '2' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { } +}; + +int main(int argc ,char *argv[]) +{ + sigset_t mask; + int exit_status; + + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "BLSOPRDC12vh", + main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'B': + use_bredr = true; + break; + case 'L': + use_le = true; + break; + case 'S': + use_sc = true; + break; + case 'O': + use_sconly = true; + break; + case 'P': + use_legacy = true; + break; + case 'R': + use_random = true; + break; + case 'D': + use_debug = true; + break; + case 'C': + use_cross = true; + break; + case '1': + provide_p192 = true; + break; + case '2': + provide_p256 = 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 (use_bredr == use_le) { + fprintf(stderr, "Specify either --bredr or --le\n"); + return EXIT_FAILURE; + } + + if (use_legacy && !use_bredr) { + fprintf(stderr, "Specify --legacy with --bredr\n"); + return EXIT_FAILURE; + } + + if (use_random && !use_le) { + fprintf(stderr, "Specify --random with --le\n"); + return EXIT_FAILURE; + } + + if (use_random && use_cross) { + fprintf(stderr, "Only --random or --cross can be used\n"); + return EXIT_FAILURE; + } + + if (use_sc && use_sconly) { + fprintf(stderr, "Only --sc or --sconly can be used\n"); + return EXIT_FAILURE; + } + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + mgmt = mgmt_new_default(); + if (!mgmt) { + fprintf(stderr, "Failed to open management socket\n"); + return EXIT_FAILURE; + } + + if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST, + MGMT_INDEX_NONE, 0, NULL, + read_index_list, NULL, NULL)) { + fprintf(stderr, "Failed to read index list\n"); + exit_status = EXIT_FAILURE; + goto done; + } + + exit_status = mainloop_run(); + +done: + mgmt_unref(mgmt); + + return exit_status; +} diff --git a/tools/parser/hci.c b/tools/parser/hci.c index cd52cb53..0918eeea 100644 --- a/tools/parser/hci.c +++ b/tools/parser/hci.c @@ -127,7 +127,7 @@ static char *event_str[EVENT_NUM + 1] = { "AMP Status Change", }; -#define LE_EV_NUM 5 +#define LE_EV_NUM 11 static char *ev_le_meta_str[LE_EV_NUM + 1] = { "Unknown", "LE Connection Complete", @@ -135,6 +135,12 @@ static char *ev_le_meta_str[LE_EV_NUM + 1] = { "LE Connection Update Complete", "LE Read Remote Used Features Complete", "LE Long Term Key Request", + "LE Remote Connection Parameter Request", + "LE Data Length Change", + "LE Read Local P-256 Public Key Complete", + "LE Generate DHKey Complete", + "LE Enhanced Connection Complete", + "LE Direct Advertising Report", }; #define CMD_LINKCTL_NUM 60 @@ -446,9 +452,9 @@ static char *error_code_str[ERROR_CODE_NUM + 1] = { "SCO Offset Rejected", "SCO Interval Rejected", "SCO Air Mode Rejected", - "Invalid LMP Parameters", + "Invalid LMP Parameters / Invalid LL Parameters", "Unspecified Error", - "Unsupported LMP Parameter Value", + "Unsupported LMP Parameter Value / Unsupported LL Parameter Value", "Role Change Not Allowed", "LMP Response Timeout", "LMP Error Transaction Collision", @@ -475,7 +481,7 @@ static char *error_code_str[ERROR_CODE_NUM + 1] = { "Host Busy - Pairing", "Connection Rejected due to No Suitable Channel Found", "Controller Busy", - "Unacceptable Connection Interval", + "Unacceptable Connection Parameters", "Directed Advertising Timeout", "Connection Terminated Due to MIC Failure", "Connection Failed to be Established", @@ -3667,7 +3673,11 @@ static inline void le_meta_ev_dump(int level, struct frame *frm) frm->len -= EVT_LE_META_EVENT_SIZE; p_indent(level, frm); - printf("%s\n", ev_le_meta_str[subevent]); + + if (subevent <= LE_EV_NUM) + printf("%s\n", ev_le_meta_str[subevent]); + else + printf("%s\n", ev_le_meta_str[0]); switch (mevt->subevent) { case EVT_LE_CONN_COMPLETE: diff --git a/tools/rctest.c b/tools/rctest.c index 11ae37fb..bdc1eb56 100644 --- a/tools/rctest.c +++ b/tools/rctest.c @@ -40,12 +40,12 @@ #include <sys/socket.h> #include <sys/stat.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/rfcomm.h> -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/rfcomm.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" #include "src/shared/util.h" diff --git a/tools/rfcomm-tester.c b/tools/rfcomm-tester.c index ba324bef..55b6ac63 100644 --- a/tools/rfcomm-tester.c +++ b/tools/rfcomm-tester.c @@ -409,20 +409,20 @@ 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; + const struct rfcomm_client_data *cli = 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) { + ret = read(sk, buf, cli->data_len); + if (cli->data_len != ret) { tester_test_failed(); return false; } - if (memcmp(client_data->read_data, buf, client_data->data_len)) + if (memcmp(cli->read_data, buf, cli->data_len)) tester_test_failed(); else tester_test_passed(); @@ -431,13 +431,15 @@ static gboolean client_received_data(GIOChannel *io, GIOCondition cond, } static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) + gpointer user_data) { struct test_data *data = tester_get_data(); - const struct rfcomm_client_data *client_data = data->test_data; + const struct rfcomm_client_data *cli = data->test_data; socklen_t len = sizeof(int); int sk, err, sk_err; + tester_print("Connected"); + data->io_id = 0; sk = g_io_channel_unix_get_fd(io); @@ -447,27 +449,30 @@ static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond, else err = -sk_err; - if (client_data->expected_connect_err && - err == client_data->expected_connect_err) { + if (cli->expected_connect_err && err == cli->expected_connect_err) { tester_test_passed(); return false; } - if (client_data->send_data) { + if (cli->send_data) { ssize_t ret; - ret = write(sk, client_data->send_data, client_data->data_len); - if (client_data->data_len != ret) + tester_print("Writing %u bytes of data", cli->data_len); + + ret = write(sk, cli->send_data, cli->data_len); + if (cli->data_len != ret) { + tester_warn("Failed to write %u bytes: %s (%d)", + cli->data_len, strerror(errno), errno); tester_test_failed(); + } return false; - } else if (client_data->read_data) { + } else if (cli->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); + cli->client_channel, + cli->read_data, cli->data_len); return false; } @@ -483,15 +488,17 @@ 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; + const struct rfcomm_client_data *cli = test_data->test_data; ssize_t ret; - if (client_data->data_len != len) { + tester_print("bthost received %u bytes of data", len); + + if (cli->data_len != len) { tester_test_failed(); return; } - ret = memcmp(client_data->send_data, data, len); + ret = memcmp(cli->send_data, data, len); if (ret) tester_test_failed(); else @@ -502,15 +509,15 @@ 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; + const struct rfcomm_server_data *srv = test_data->test_data; ssize_t ret; - if (server_data->data_len != len) { + if (srv->data_len != len) { tester_test_failed(); return; } - ret = memcmp(server_data->send_data, data, len); + ret = memcmp(srv->send_data, data, len); if (ret) tester_test_failed(); else @@ -521,14 +528,14 @@ 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; + const struct rfcomm_client_data *cli = data->test_data; struct bthost *bthost = hciemu_client_get_host(data->hciemu); - if (client_data->send_data) + if (cli->send_data) bthost_add_rfcomm_chan_hook(bthost, handle, - client_data->client_channel, + cli->client_channel, client_hook_func, NULL); - else if (client_data->read_data) + else if (cli->read_data) data->conn_handle = handle; } @@ -536,13 +543,13 @@ 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 struct rfcomm_client_data *cli = 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, + bthost_add_rfcomm_server(bthost, cli->server_channel, rfcomm_connect_cb, NULL); master_addr = hciemu_get_master_bdaddr(data->hciemu); @@ -551,7 +558,7 @@ static void test_connect(const void *test_data) sk = create_rfcomm_sock((bdaddr_t *) master_addr, 0); if (connect_rfcomm_sock(sk, (const bdaddr_t *) client_addr, - client_data->client_channel) < 0) { + cli->client_channel) < 0) { close(sk); tester_test_failed(); return; @@ -571,20 +578,20 @@ 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; + const struct rfcomm_server_data *srv = 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) { + ret = read(sk, buf, srv->data_len); + if (ret != srv->data_len) { tester_test_failed(); return false; } - if (memcmp(buf, server_data->read_data, server_data->data_len)) + if (memcmp(buf, srv->read_data, srv->data_len)) tester_test_failed(); else tester_test_passed(); @@ -596,7 +603,7 @@ 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; + const struct rfcomm_server_data *srv = data->test_data; int sk, new_sk; data->io_id = 0; @@ -609,17 +616,16 @@ static gboolean rfcomm_listen_cb(GIOChannel *io, GIOCondition cond, return false; } - if (server_data->send_data) { + if (srv->send_data) { ssize_t ret; - ret = write(new_sk, server_data->send_data, - server_data->data_len); - if (ret != server_data->data_len) + ret = write(new_sk, srv->send_data, srv->data_len); + if (ret != srv->data_len) tester_test_failed(); close(new_sk); return false; - } else if (server_data->read_data) { + } else if (srv->read_data) { GIOChannel *new_io; new_io = g_io_channel_unix_new(new_sk); @@ -643,21 +649,20 @@ 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; + const struct rfcomm_server_data *srv = data->test_data; struct bthost *bthost = hciemu_client_get_host(data->hciemu); - if (server_data->read_data) { + if (srv->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); + srv->client_channel, + srv->read_data, srv->data_len); return; - } else if (server_data->data_len) { + } else if (srv->data_len) { return; } - if (server_data->expected_status == status) + if (srv->expected_status == status) tester_test_passed(); else tester_test_failed(); @@ -666,21 +671,20 @@ static void connection_cb(uint16_t handle, uint16_t cid, void *user_data, 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; + const struct rfcomm_server_data *srv = data->test_data; struct bthost *bthost; bthost = hciemu_client_get_host(data->hciemu); - bthost_add_rfcomm_chan_hook(bthost, handle, - server_data->client_channel, + bthost_add_rfcomm_chan_hook(bthost, handle, srv->client_channel, server_hook_func, NULL); - bthost_connect_rfcomm(bthost, handle, server_data->client_channel, + bthost_connect_rfcomm(bthost, handle, srv->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 struct rfcomm_server_data *srv = data->test_data; const uint8_t *master_addr; struct bthost *bthost; GIOChannel *io; @@ -688,8 +692,7 @@ static void test_server(const void *test_data) master_addr = hciemu_get_master_bdaddr(data->hciemu); - sk = create_rfcomm_sock((bdaddr_t *) master_addr, - server_data->server_channel); + sk = create_rfcomm_sock((bdaddr_t *) master_addr, srv->server_channel); if (sk < 0) { tester_test_failed(); return; diff --git a/tools/rfcomm.c b/tools/rfcomm.c index 659bbec0..809c2404 100644 --- a/tools/rfcomm.c +++ b/tools/rfcomm.c @@ -40,10 +40,10 @@ #include <sys/socket.h> #include <sys/wait.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/rfcomm.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/rfcomm.h" static int rfcomm_raw_tty = 0; static int auth = 0; diff --git a/tools/scotest.c b/tools/scotest.c index d033ae0b..596e403c 100644 --- a/tools/scotest.c +++ b/tools/scotest.c @@ -37,8 +37,8 @@ #include <sys/time.h> #include <sys/socket.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/sco.h> +#include "lib/bluetooth.h" +#include "lib/sco.h" #include "src/shared/util.h" diff --git a/tools/sdptool.c b/tools/sdptool.c index 1fef8006..891d3883 100644 --- a/tools/sdptool.c +++ b/tools/sdptool.c @@ -36,15 +36,14 @@ #include <string.h> #include <getopt.h> #include <sys/socket.h> - -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> - #include <netinet/in.h> +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" + #include "src/sdp-xml.h" #ifndef APPLE_AGENT_SVCLASS_ID diff --git a/tools/update_compids.sh b/tools/update_compids.sh index 95c961d6..95c961d6 100755..100644 --- a/tools/update_compids.sh +++ b/tools/update_compids.sh |