| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2012 Intel Corporation. All rights reserved. |
| * |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <getopt.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 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; |
| |
| static bool reset_on_init = false; |
| static bool reset_on_shutdown = false; |
| |
| static bool shutdown_timeout(void *user_data) |
| { |
| mainloop_quit(); |
| |
| return false; |
| } |
| |
| static void shutdown_complete(const void *data, uint8_t size, void *user_data) |
| { |
| unsigned int id = PTR_TO_UINT(user_data); |
| |
| timeout_remove(id); |
| mainloop_quit(); |
| } |
| |
| static void shutdown_device(void) |
| { |
| unsigned int id; |
| |
| bt_hci_flush(hci_dev); |
| |
| if (reset_on_shutdown) { |
| id = timeout_add(5000, shutdown_timeout, NULL, NULL); |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, |
| shutdown_complete, UINT_TO_PTR(id), NULL); |
| } else |
| mainloop_quit(); |
| } |
| |
| static void 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"); |
| } |
| |
| static void sync_train_received(const void *data, uint8_t size, |
| void *user_data) |
| { |
| const struct bt_hci_evt_sync_train_received *evt = data; |
| struct bt_hci_cmd_set_slave_broadcast_receive cmd; |
| |
| if (evt->status) { |
| printf("Failed to synchronize with 3D display\n"); |
| 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 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, |
| void *user_data) |
| { |
| const struct bt_hci_evt_truncated_page_complete *evt = data; |
| struct bt_hci_cmd_receive_sync_train cmd; |
| |
| if (evt->status) { |
| printf("Failed to contact 3D display\n"); |
| shutdown_device(); |
| return; |
| } |
| |
| printf("Attempt to synchronize with 3D display\n"); |
| |
| 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); |
| 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_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; |
| |
| if (evt->dev_class[0] != 0x3c || evt->dev_class[1] != 0x04 |
| || evt->dev_class[2] != 0x08) |
| return; |
| |
| if (evt->data[0]) { |
| struct bt_hci_cmd_truncated_page cmd; |
| |
| printf("Found 3D display\n"); |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY_CANCEL, NULL, 0, |
| NULL, NULL, NULL); |
| |
| memcpy(cmd.bdaddr, evt->bdaddr, 6); |
| cmd.pscan_rep_mode = evt->pscan_rep_mode; |
| cmd.clock_offset = evt->clock_offset; |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_TRUNCATED_PAGE, |
| &cmd, sizeof(cmd), NULL, NULL, NULL); |
| } |
| } |
| |
| static void inquiry_complete(const void *data, uint8_t size, void *user_data) |
| { |
| printf("No 3D display found\n"); |
| |
| start_inquiry(); |
| } |
| |
| static void read_local_version(const void *data, uint8_t size, void *user_data) |
| { |
| const struct bt_hci_rsp_read_local_version *rsp = data; |
| |
| if (rsp->status) { |
| printf("Failed to read local version information\n"); |
| shutdown_device(); |
| return; |
| } |
| |
| 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) |
| { |
| uint8_t evtmask1[] = { 0x03, 0xe0, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00 }; |
| uint8_t evtmask2[] = { 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| uint8_t inqmode = 0x02; |
| |
| if (reset_on_init) { |
| bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, |
| NULL, NULL, NULL); |
| bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask1, 8, |
| NULL, NULL, NULL); |
| } |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_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, |
| NULL, NULL, NULL); |
| |
| bt_hci_register(hci_dev, BT_HCI_EVT_INQUIRY_COMPLETE, |
| inquiry_complete, NULL, NULL); |
| bt_hci_register(hci_dev, BT_HCI_EVT_EXT_INQUIRY_RESULT, |
| ext_inquiry_result, NULL, NULL); |
| |
| 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); |
| |
| start_inquiry(); |
| } |
| |
| static bool sync_train_active = false; |
| |
| static void sync_train_complete(const void *data, uint8_t size, |
| void *user_data) |
| { |
| sync_train_active = false; |
| } |
| |
| static void start_sync_train(void) |
| { |
| struct bt_hci_cmd_write_sync_train_params cmd; |
| |
| if (sync_train_active) |
| return; |
| |
| printf("Starting new synchronization train\n"); |
| |
| cmd.min_interval = cpu_to_le16(0x0050); |
| cmd.max_interval = cpu_to_le16(0x00a0); |
| cmd.timeout = cpu_to_le32(0x0002ee00); /* 120 sec */ |
| cmd.service_data = SERVICE_DATA; |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS, |
| &cmd, sizeof(cmd), NULL, NULL, NULL); |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_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) |
| { |
| const struct bt_hci_rsp_read_inquiry_resp_tx_power *rsp = data; |
| struct bt_hci_cmd_write_ext_inquiry_response cmd; |
| uint8_t inqdata[] = { 0x03, 0x3d, 0x03, 0x43, 0x02, 0x0a, 0x00, 0x00 }; |
| uint8_t devclass[] = { 0x3c, 0x04, 0x08 }; |
| uint8_t scanmode = 0x03; |
| |
| inqdata[6] = (uint8_t) rsp->level; |
| |
| cmd.fec = 0x00; |
| memset(cmd.data, 0, sizeof(cmd.data)); |
| memcpy(cmd.data, inqdata, sizeof(inqdata)); |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE, |
| &cmd, sizeof(cmd), NULL, NULL, NULL); |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_CLASS_OF_DEV, devclass, 3, |
| NULL, NULL, NULL); |
| bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SCAN_ENABLE, &scanmode, 1, |
| NULL, NULL, NULL); |
| } |
| |
| static void 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 evtmask1[] = { 0x1c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| uint8_t evtmask2[] = { 0x00, 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| uint8_t sspmode = 0x01; |
| uint8_t ltaddr = LT_ADDR; |
| |
| if (reset_on_init) { |
| bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, |
| NULL, NULL, NULL); |
| bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask1, 8, |
| NULL, NULL, NULL); |
| } |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, evtmask2, 8, |
| NULL, NULL, NULL); |
| bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &sspmode, 1, |
| NULL, NULL, NULL); |
| bt_hci_send(hci_dev, BT_HCI_CMD_SET_RESERVED_LT_ADDR, <addr, 1, |
| NULL, NULL, NULL); |
| bt_hci_send(hci_dev, BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, NULL, 0, |
| NULL, NULL, NULL); |
| |
| bt_hci_register(hci_dev, BT_HCI_EVT_CONN_REQUEST, |
| conn_request, NULL, NULL); |
| |
| bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_PAGE_RESPONSE_TIMEOUT, |
| slave_page_response_timeout, NULL, NULL); |
| bt_hci_register(hci_dev, BT_HCI_EVT_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); |
| |
| cmd.enable = 0x01; |
| cmd.lt_addr = LT_ADDR; |
| cmd.lpo_allowed = 0x01; |
| cmd.pkt_type = cpu_to_le16(PKT_TYPE); |
| cmd.min_interval = cpu_to_le16(0x0050); /* 50 ms */ |
| cmd.max_interval = cpu_to_le16(0x00a0); /* 100 ms */ |
| cmd.timeout = cpu_to_le16(0xfffe); |
| |
| bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST, &cmd, sizeof(cmd), |
| set_slave_broadcast, NULL, NULL); |
| } |
| |
| static void signal_callback(int signum, void *user_data) |
| { |
| static bool terminated = false; |
| |
| switch (signum) { |
| case SIGINT: |
| case SIGTERM: |
| if (!terminated) { |
| shutdown_device(); |
| terminated = true; |
| } |
| break; |
| } |
| } |
| |
| static void usage(void) |
| { |
| printf("3dsp - 3D Synchronization Profile testing\n" |
| "Usage:\n"); |
| printf("\t3dsp [options]\n"); |
| printf("options:\n" |
| "\t-D, --display Use display role\n" |
| "\t-G, --glasses Use glasses role\n" |
| "\t-i, --index <num> Use specified controller\n" |
| "\t-h, --help Show help options\n"); |
| } |
| |
| static const struct option main_options[] = { |
| { "display", no_argument, NULL, 'D' }, |
| { "glasses", no_argument, NULL, 'G' }, |
| { "index", required_argument, NULL, 'i' }, |
| { "raw", no_argument, NULL, 'r' }, |
| { "version", no_argument, NULL, 'v' }, |
| { "help", no_argument, NULL, 'h' }, |
| { } |
| }; |
| |
| int main(int argc, char *argv[]) |
| { |
| bool display_role = false, glasses_role = false; |
| uint16_t index = 0; |
| const char *str; |
| bool use_raw = false; |
| sigset_t mask; |
| int exit_status; |
| |
| for (;;) { |
| int opt; |
| |
| opt = getopt_long(argc, argv, "DGi:rvh", main_options, NULL); |
| if (opt < 0) |
| break; |
| |
| switch (opt) { |
| case 'D': |
| display_role = true; |
| break; |
| case 'G': |
| glasses_role = true; |
| break; |
| case 'i': |
| if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3)) |
| str = optarg + 3; |
| else |
| str = optarg; |
| if (!isdigit(*str)) { |
| usage(); |
| return EXIT_FAILURE; |
| } |
| index = atoi(str); |
| break; |
| case 'r': |
| use_raw = true; |
| break; |
| case 'v': |
| printf("%s\n", VERSION); |
| return EXIT_SUCCESS; |
| case 'h': |
| usage(); |
| return EXIT_SUCCESS; |
| default: |
| return EXIT_FAILURE; |
| } |
| } |
| |
| if (argc - optind > 0) { |
| fprintf(stderr, "Invalid command line parameters\n"); |
| return EXIT_FAILURE; |
| } |
| |
| if (display_role == glasses_role) { |
| fprintf(stderr, "Specify either display or glasses role\n"); |
| return EXIT_FAILURE; |
| } |
| |
| mainloop_init(); |
| |
| sigemptyset(&mask); |
| sigaddset(&mask, SIGINT); |
| sigaddset(&mask, SIGTERM); |
| |
| mainloop_set_signal(&mask, signal_callback, NULL, NULL); |
| |
| printf("3D Synchronization Profile testing ver %s\n", VERSION); |
| |
| if (use_raw) { |
| hci_dev = bt_hci_new_raw_device(index); |
| if (!hci_dev) { |
| fprintf(stderr, "Failed to open HCI raw device\n"); |
| return EXIT_FAILURE; |
| } |
| } else { |
| hci_dev = bt_hci_new_user_channel(index); |
| if (!hci_dev) { |
| fprintf(stderr, "Failed to open HCI user channel\n"); |
| return EXIT_FAILURE; |
| } |
| |
| reset_on_init = true; |
| reset_on_shutdown = true; |
| } |
| |
| if (display_role) |
| start_display(); |
| else if (glasses_role) |
| start_glasses(); |
| |
| exit_status = mainloop_run(); |
| |
| bt_hci_unref(hci_dev); |
| |
| return exit_status; |
| } |