/* * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #ifdef LOG_TAG #undef LOG_TAG #endif #define LOG_TAG "HID_KEYBOARD" #define INIT_BOND 0 #define KEYBOARD_RETURN 0x28 // Enter #define KEYBOARD_ESCAPE 0x29 // Back #define KEYBOARD_F5 0x3e // Smart Hub #define KEYBOARD_F6 0x3f // Source #define KEYBOARD_F7 0x40 // Channel List #define KEYBOARD_F8 0x41 // Volume Mute #define KEYBOARD_F9 0x42 // Volume Down #define KEYBOARD_F10 0x43 // Volume Up #define KEYBOARD_F11 0x44 // Channel Down #define KEYBOARD_F12 0x45 // Channel Up #define KEYBOARD_RIGHT 0x4f // Arrow Right #define KEYBOARD_LEFT 0x50 // Arrow Left #define KEYBOARD_DOWN 0x51 // Arrow Down #define KEYBOARD_UP 0x52 // Arrow Up /** * Variables **/ static GMainLoop *g_mainloop = NULL; static bt_adapter_state_e bt_state = BT_ADAPTER_DISABLED; static char remote_addr[18] = "F6:FB:8F:D8:C8:7C"; static bool address_input = false; #if INIT_BOND static bool scanning = false; static bool setup_in_progress = false; static GSList *le_scan_list; static bool g_wait_flag; static int g_wait_count; #endif static bool device_bonded = false; static bool hid_connected = false; static bool audio_connected = false; #define PRT(format, args...) printf("%s:%d() "format, __FUNCTION__, __LINE__, ##args) #define HID_PRT(format, args...) PRT(format"\n", ##args) #if INIT_BOND #define WAIT_FOR_SYNC {\ g_wait_flag = true;\ g_wait_count = 0;\ while (g_wait_flag == true && g_wait_count < 50) {\ usleep(1000);\ HID_PRT("Initial Setup...");\ g_main_context_iteration(NULL, true);\ g_wait_count++;\ }\ } #endif typedef struct { const char *tc_name; int tc_code; } tc_table_t; typedef enum { BT_HID_DEVICE_SET_ADDRESS = 1, #if INIT_BOND BT_HID_DEVICE_TEST_SETUP, #endif BT_HID_DEVICE_TEST_VOLUME_UP, BT_HID_DEVICE_TEST_VOLUME_DOWN, BT_HID_DEVICE_TEST_CHANNEL_UP, BT_HID_DEVICE_TEST_CHANNEL_DOWN, BT_HID_DEVICE_TEST_CONNECT_HID, BT_HID_DEVICE_TEST_DISCONNECT_HID, BT_HID_DEVICE_TEST_CONNECT_AUDIO, BT_HID_DEVICE_TEST_DISCONNECT_AUDIO, #if INIT_BOND BT_HID_DEVICE_TEST_SEARCH, BT_HID_DEVICE_TEST_STOP_SEARCH, #endif BT_HID_DEVICE_TEST_FINISH = 0xFF, } bt_hid_test_type_e; tc_table_t tc_hid_device[] = { {"SET ADDRESS" , BT_HID_DEVICE_SET_ADDRESS}, #if INIT_BOND {"Initial Setup (Bond & Connects)" , BT_HID_DEVICE_TEST_SETUP}, #endif {"Volume Up" , BT_HID_DEVICE_TEST_VOLUME_UP}, {"Volume Down" , BT_HID_DEVICE_TEST_VOLUME_DOWN}, {"Channel Up" , BT_HID_DEVICE_TEST_CHANNEL_UP}, {"Channel Down" , BT_HID_DEVICE_TEST_CHANNEL_DOWN}, {"HID Connect only" , BT_HID_DEVICE_TEST_CONNECT_HID}, {"HID Disconnect only" , BT_HID_DEVICE_TEST_DISCONNECT_HID}, {"AUDIO Connect only" , BT_HID_DEVICE_TEST_CONNECT_AUDIO}, {"AUDIO Disconnect only" , BT_HID_DEVICE_TEST_DISCONNECT_AUDIO}, #if INIT_BOND {"Search TV" , BT_HID_DEVICE_TEST_SEARCH}, {"Stop to search" , BT_HID_DEVICE_TEST_STOP_SEARCH}, #endif {"Finish" , BT_HID_DEVICE_TEST_FINISH}, {NULL, 0x0000}, }; static const char *__bt_get_error_message(bt_error_e err) { const char *err_str = NULL; switch (err) { case BT_ERROR_NONE: err_str = "BT_ERROR_NONE"; break; case BT_ERROR_CANCELLED: err_str = "BT_ERROR_CANCELLED"; break; case BT_ERROR_INVALID_PARAMETER: err_str = "BT_ERROR_INVALID_PARAMETER"; break; case BT_ERROR_OUT_OF_MEMORY: err_str = "BT_ERROR_OUT_OF_MEMORY"; break; case BT_ERROR_RESOURCE_BUSY: err_str = "BT_ERROR_RESOURCE_BUSY"; break; case BT_ERROR_TIMED_OUT: err_str = "BT_ERROR_TIMED_OUT"; break; case BT_ERROR_NOW_IN_PROGRESS: err_str = "BT_ERROR_NOW_IN_PROGRESS"; break; case BT_ERROR_NOT_INITIALIZED: err_str = "BT_ERROR_NOT_INITIALIZED"; break; case BT_ERROR_NOT_ENABLED: err_str = "BT_ERROR_NOT_ENABLED"; break; case BT_ERROR_ALREADY_DONE: err_str = "BT_ERROR_ALREADY_DONE"; break; case BT_ERROR_OPERATION_FAILED: err_str = "BT_ERROR_OPERATION_FAILED"; break; case BT_ERROR_NOT_IN_PROGRESS: err_str = "BT_ERROR_NOT_IN_PROGRESS"; break; case BT_ERROR_REMOTE_DEVICE_NOT_BONDED: err_str = "BT_ERROR_REMOTE_DEVICE_NOT_BONDED"; break; case BT_ERROR_AUTH_REJECTED: err_str = "BT_ERROR_AUTH_REJECTED"; break; case BT_ERROR_AUTH_FAILED: err_str = "BT_ERROR_AUTH_FAILED"; break; case BT_ERROR_REMOTE_DEVICE_NOT_FOUND: err_str = "BT_ERROR_REMOTE_DEVICE_NOT_FOUND"; break; case BT_ERROR_SERVICE_SEARCH_FAILED: err_str = "BT_ERROR_SERVICE_SEARCH_FAILED"; break; case BT_ERROR_REMOTE_DEVICE_NOT_CONNECTED: err_str = "BT_ERROR_REMOTE_DEVICE_NOT_CONNECTED"; break; case BT_ERROR_PERMISSION_DENIED: err_str = "BT_ERROR_PERMISSION_DENIED"; break; case BT_ERROR_SERVICE_NOT_FOUND: err_str = "BT_ERROR_SERVICE_NOT_FOUND"; break; case BT_ERROR_NO_DATA: err_str = "BT_ERROR_NO_DATA"; break; case BT_ERROR_NOT_SUPPORTED: err_str = "BT_ERROR_NOT_SUPPORTED"; break; case BT_ERROR_DEVICE_POLICY_RESTRICTION: err_str = "DEVICE_POLICY_RESTRICTION"; break; default: err_str = "NOT Defined"; break; } return err_str; } /** * Callback functions **/ static gboolean __bt_timeout_func(gpointer data) { HID_PRT("Timeout."); if (g_mainloop) g_main_loop_quit((GMainLoop *)data); return FALSE; } static gboolean __bt_audio_connect_cb(gpointer user_data) { int ret; if (audio_connected == true) return FALSE; HID_PRT("Try to connect audio.."); ret = bt_audio_connect(remote_addr, BT_AUDIO_PROFILE_TYPE_A2DP_SINK); HID_PRT("returns %s", __bt_get_error_message(ret)); return TRUE; } static void __bt_hid_device_connection_state_changed_cb(int result, bool connected, const char *remote_address, void *user_data) { HID_PRT("result: %s", __bt_get_error_message(result)); HID_PRT("Remote Address: %s", remote_address); HID_PRT("Connected [%d]", connected); #if INIT_BOND g_wait_flag = false; #endif if (connected == true && result == BT_ERROR_NONE) { g_strlcpy(remote_addr, remote_address, 18); hid_connected = true; /* Try to connect Audio */ if (audio_connected == false) g_timeout_add(2000, __bt_audio_connect_cb, NULL); } else { hid_connected = false; } } void __bt_audio_connection_state_changed_cb(int result, bool connected, const char *remote_address, bt_audio_profile_type_e type, void *user_data) { HID_PRT("result [%s]", __bt_get_error_message(result)); HID_PRT("connected [%d]", connected); HID_PRT("address [%s]", remote_address); HID_PRT("type [%d]", type); #if INIT_BOND g_wait_flag = false; #endif if (connected == true && result == BT_ERROR_NONE) { g_strlcpy(remote_addr, remote_address, 18); audio_connected = true; } else { audio_connected = false; } } static bool __bt_adapter_hid_profile_connected_devices_cb( const char *remote_address, void *user_data) { HID_PRT("remote_address: %s", remote_address); g_strlcpy(remote_addr, remote_address, 18); hid_connected = true; return true; } static bool __bt_adapter_a2dp_profile_connected_devices_cb( const char *remote_address, void *user_data) { HID_PRT("remote_address: %s", remote_address); g_strlcpy(remote_addr, remote_address, 18); audio_connected = true; return true; } void __bt_device_bond_created_cb(int result, bt_device_info_s *device_info, void *user_data) { if (result == BT_ERROR_NONE) { HID_PRT("Callback: A bond is created."); HID_PRT("Callback: is_bonded - %d.", device_info->is_bonded); device_bonded = true; } else { HID_PRT("Callback: Creating a bond is failed."); HID_PRT("result: %s", __bt_get_error_message(result)); } #if INIT_BOND g_wait_flag = false; #endif } static void __bt_state_changed_impl(int result, bt_adapter_state_e adapter_state, void *user_data) { if (adapter_state == BT_ADAPTER_ENABLED) { if (result == BT_ERROR_NONE) { HID_PRT("Callback: BT was enabled successfully."); bt_state = BT_ADAPTER_ENABLED; } else { HID_PRT("Callback: Failed to enable BT."); } } if (g_mainloop) g_main_loop_quit(g_mainloop); } #if INIT_BOND gint __bt_compare_address(gpointer *a, gpointer *b) { bt_adapter_le_device_scan_result_info_s *info = (bt_adapter_le_device_scan_result_info_s *)a; char *address = (char *)b; return g_strcmp0(info->remote_address, address); } static void __bt_adapter_le_scan_result_cb( int result, bt_adapter_le_device_scan_result_info_s *info, void *user_data) { int i; bt_adapter_le_packet_type_e pkt_type = BT_ADAPTER_LE_PACKET_ADVERTISING; bt_adapter_le_device_scan_result_info_s *adv_info; if (info == NULL) { HID_PRT("No discovery_info!"); return; } if (info->adv_data_len > 31 || info->scan_data_len > 31) { HID_PRT("###################"); bt_adapter_le_stop_scan(); HID_PRT("###################"); return; } GSList *l = NULL; l = g_slist_find_custom(le_scan_list, info->remote_address, (GCompareFunc)__bt_compare_address); if (l != NULL) { adv_info = l->data; adv_info->rssi = info->rssi; return; } adv_info = g_malloc0(sizeof(bt_adapter_le_device_scan_result_info_s)); adv_info->remote_address= g_strdup(info->remote_address); adv_info->rssi = info->rssi; le_scan_list = g_slist_append(le_scan_list, adv_info); for (i = 0; i < 2; i++) { char *device_name; pkt_type += i; if (pkt_type == BT_ADAPTER_LE_PACKET_ADVERTISING && info->adv_data == NULL) continue; if (pkt_type == BT_ADAPTER_LE_PACKET_SCAN_RESPONSE && info->scan_data == NULL) break; if (bt_adapter_le_get_scan_result_device_name( info, pkt_type, &device_name) == BT_ERROR_NONE) { HID_PRT("\n%s Adv %d Scan resp %d RSSI %d Addr_type %d", info->remote_address, info->adv_data_len, info->scan_data_len, info->rssi, info->address_type); HID_PRT("Device name = %s", device_name); if (adv_info->adv_data == NULL) adv_info->adv_data= g_strdup(device_name); g_free(device_name); return; } } } static void __bt_find_remote_tv(void) { int ret = BT_ERROR_NONE; bt_scan_filter_h scan_filter = NULL; int manufacturer_id = 117; /* samsung */ scanning = true; ret = bt_adapter_le_scan_filter_unregister_all(); if (ret != BT_ERROR_NONE) HID_PRT("failed with [0x%04x]", ret); ret = bt_adapter_le_scan_filter_create(&scan_filter); if (ret != BT_ERROR_NONE) HID_PRT("failed with [0x%04x]", ret); ret = bt_adapter_le_scan_filter_set_manufacturer_data(scan_filter, manufacturer_id, NULL, 0); if (ret != BT_ERROR_NONE) HID_PRT("failed with [0x%04x]", ret); ret = bt_adapter_le_scan_filter_register(scan_filter); if (ret != BT_ERROR_NONE) HID_PRT("failed with [0x%04x]", ret); ret = bt_adapter_le_scan_filter_destroy(scan_filter); if (ret != BT_ERROR_NONE) HID_PRT("failed with [0x%04x]", ret); ret = bt_adapter_le_start_scan(__bt_adapter_le_scan_result_cb, NULL); HID_PRT("returns %s\n", __bt_get_error_message(ret)); if (ret == BT_ERROR_NONE) scanning = true; } #else static bool __bt_adapter_bonded_device_cb(bt_device_info_s *device_info, void *user_data) { if (device_info != NULL) { if (g_strrstr(device_info->remote_name, "[TV]")) { HID_PRT("TV is boned : %s [%s]", device_info->remote_name, device_info->remote_address); g_strlcpy(remote_addr, device_info->remote_address, 18); return false; } } return true; } #endif static gboolean __bt_start_service(gpointer user_data) { int ret = BT_ERROR_NONE; ret = bt_audio_initialize(); ret = bt_adapter_set_visibility(BT_ADAPTER_VISIBILITY_MODE_GENERAL_DISCOVERABLE, 0); ret = bt_adapter_set_name("Tizen Keyboard"); ret = bt_audio_select_role(BT_A2DP_SINK); ret = bt_device_set_bond_created_cb(__bt_device_bond_created_cb, NULL); ret = bt_audio_set_connection_state_changed_cb( __bt_audio_connection_state_changed_cb, NULL); ret = bt_hid_device_activate( __bt_hid_device_connection_state_changed_cb, NULL); HID_PRT("returns %s", __bt_get_error_message(ret)); if (ret != BT_ERROR_NONE && ret != BT_ERROR_NOW_IN_PROGRESS) { HID_PRT("bt_hid_device_activate() failed."); g_main_loop_quit(g_mainloop); return FALSE; } ret = bt_adapter_foreach_profile_connected_devices(BT_PROFILE_SERVICE_UUID_HID, __bt_adapter_hid_profile_connected_devices_cb, NULL); HID_PRT("HID connected devices returns %s\n", __bt_get_error_message(ret)); ret = bt_adapter_foreach_profile_connected_devices(BT_PROFILE_SERVICE_UUID_A2DP_SOURCE, __bt_adapter_a2dp_profile_connected_devices_cb, NULL); HID_PRT("A2DP connected devices returns %s\n", __bt_get_error_message(ret)); if (hid_connected == false && audio_connected == false) { #if INIT_BOND HID_PRT("No TV is connected, try to search..."); __bt_find_remote_tv(); #else ret = bt_adapter_foreach_bonded_device(__bt_adapter_bonded_device_cb, NULL); #endif } return FALSE; } #if INIT_BOND static void __bt_stop_setup(void) { setup_in_progress = false; } static int __bt_initial_setup(void) { int ret = BT_ERROR_NONE; bt_device_info_s *device_info = NULL; setup_in_progress = true; ret = bt_adapter_get_bonded_device_info(remote_addr, &device_info); HID_PRT("returns %s\n", __bt_get_error_message(ret)); if (ret == BT_ERROR_NONE) { /* Already bond */ HID_PRT("Device is already paired"); device_bonded = true; } else { /* Create Bond */ ret = bt_device_create_bond(remote_addr); HID_PRT("returns %s\n", __bt_get_error_message(ret)); if (ret != BT_ERROR_NONE) return ret; WAIT_FOR_SYNC; } if (device_bonded == false) return BT_ERROR_OPERATION_FAILED; /* Connect HID first */ ret = bt_hid_device_connect(remote_addr); HID_PRT("HID connect for %s: returns %s", remote_addr, __bt_get_error_message(ret)); if (ret == BT_ERROR_NONE) WAIT_FOR_SYNC; /* Connect Audio */ ret = bt_audio_connect(remote_addr, BT_AUDIO_PROFILE_TYPE_A2DP_SINK); HID_PRT("Audio connect for %s: returns %s", remote_addr, __bt_get_error_message(ret)); if (ret == BT_ERROR_NONE) WAIT_FOR_SYNC; if (hid_connected == false || audio_connected == false) ret = BT_ERROR_OPERATION_FAILED; setup_in_progress = false; return ret; } #endif static void __bt_tc_usage_print(void) { int i = 0; tc_table_t *tc_table = tc_hid_device; while (tc_table[i].tc_name) { HID_PRT("Key %d : usage %s", tc_table[i].tc_code, tc_table[i].tc_name); i++; } HID_PRT("\n"); } static int __bt_test_input_callback(void *data) { int ret = 0; #ifdef ARCH64 int test_id = (uintptr_t)data; #else int test_id = (int)data; #endif bt_hid_key_data_s send_data; char pressedkey[8] = { KEYBOARD_F10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; char pressedkey1[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #if INIT_BOND int cnt = 0; GSList *l = NULL; bt_adapter_le_device_scan_result_info_s *info; if (setup_in_progress == true && test_id != BT_HID_DEVICE_TEST_SETUP && test_id != BT_HID_DEVICE_TEST_FINISH) { HID_PRT("Initial Setup is in progress,,"); HID_PRT("Wait to finish setup"); return 0; } #endif switch (test_id) { case BT_HID_DEVICE_SET_ADDRESS: HID_PRT("Input the address of remote device." "(e.g.,F6:FB:8F:D8:C8:7C)\n\n"); address_input = true; break; #if INIT_BOND case BT_HID_DEVICE_TEST_SETUP: if (setup_in_progress == true) { HID_PRT("Stop the initial setup"); __bt_stop_setup(); } else { if (__bt_initial_setup() != BT_ERROR_NONE) { HID_PRT("Fail to setup: %s", __bt_get_error_message(ret)); } } break; #endif case BT_HID_DEVICE_TEST_VOLUME_UP: pressedkey[0] = KEYBOARD_F10; /* Vol up */ memcpy(send_data.key, pressedkey, 8); send_data.modifier = 0; ret = bt_hid_device_send_key_event(remote_addr, &send_data); HID_PRT("returns %d\n", ret); memcpy(send_data.key, pressedkey1, 8); ret = bt_hid_device_send_key_event(remote_addr, &send_data); break; case BT_HID_DEVICE_TEST_VOLUME_DOWN: pressedkey[0] = KEYBOARD_F9; /* Vol down */ memcpy(send_data.key, pressedkey, 8); send_data.modifier = 0; ret = bt_hid_device_send_key_event(remote_addr, &send_data); HID_PRT("returns %d\n", ret); memcpy(send_data.key, pressedkey1, 8); ret = bt_hid_device_send_key_event(remote_addr, &send_data); break; case BT_HID_DEVICE_TEST_CHANNEL_UP: pressedkey[0] = KEYBOARD_F12; /* CH up */ memcpy(send_data.key, pressedkey, 8); send_data.modifier = 0; ret = bt_hid_device_send_key_event(remote_addr, &send_data); HID_PRT("returns %d\n", ret); memcpy(send_data.key, pressedkey1, 8); ret = bt_hid_device_send_key_event(remote_addr, &send_data); break; case BT_HID_DEVICE_TEST_CHANNEL_DOWN: pressedkey[0] = KEYBOARD_F11; /* CH down */ memcpy(send_data.key, pressedkey, 8); send_data.modifier = 0; ret = bt_hid_device_send_key_event(remote_addr, &send_data); HID_PRT("returns %d\n", ret); memcpy(send_data.key, pressedkey1, 8); ret = bt_hid_device_send_key_event(remote_addr, &send_data); break; case BT_HID_DEVICE_TEST_CONNECT_AUDIO: ret = bt_audio_connect(remote_addr, BT_AUDIO_PROFILE_TYPE_A2DP_SINK); HID_PRT("returns %s", __bt_get_error_message(ret)); break; case BT_HID_DEVICE_TEST_DISCONNECT_AUDIO: ret = bt_audio_disconnect(remote_addr, BT_AUDIO_PROFILE_TYPE_A2DP_SINK); HID_PRT("returns %s", __bt_get_error_message(ret)); break; case BT_HID_DEVICE_TEST_CONNECT_HID: ret = bt_hid_device_connect(remote_addr); HID_PRT("returns %s", __bt_get_error_message(ret)); break; case BT_HID_DEVICE_TEST_DISCONNECT_HID: ret = bt_hid_device_disconnect(remote_addr); HID_PRT("returns %s", __bt_get_error_message(ret)); break; #if INIT_BOND case BT_HID_DEVICE_TEST_SEARCH: __bt_find_remote_tv(); break; case BT_HID_DEVICE_TEST_STOP_SEARCH: scanning = false; ret = bt_adapter_le_stop_scan(); HID_PRT("returns %s\n", __bt_get_error_message(ret)); HID_PRT("LE scan result :\n"); for (l = le_scan_list; l != NULL; l = g_slist_next(l)) { info = l->data; if (info) { if (info->adv_data) HID_PRT("[%d] %s, %d dBm, %s", ++cnt, info->remote_address, info->rssi, info->adv_data); g_free(info->remote_address); g_free(info->adv_data); g_free(info); } } g_slist_free(le_scan_list); le_scan_list = NULL; break; #endif case BT_HID_DEVICE_TEST_FINISH: HID_PRT("Finished"); g_main_loop_quit(g_mainloop); break; default: break; } return 0; } static gboolean __bt_key_event_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { char buf[49] = { 0 }; #ifdef ARCH64 unsigned long len = 0; #else unsigned int len = 0; #endif int test_id; memset(buf, 0, sizeof(buf)); if (g_io_channel_read_chars(chan, buf, sizeof(buf), &len, NULL) == G_IO_STATUS_ERROR) { HID_PRT("IO Channel read error"); return FALSE; } HID_PRT("%s", buf); if (address_input == true) { memcpy(remote_addr, buf, 17); remote_addr[17] = 0; address_input = false; } #if INIT_BOND if (scanning == true) { test_id = BT_HID_DEVICE_TEST_STOP_SEARCH; #ifdef ARCH64 g_idle_add(__bt_test_input_callback, (void *)(uintptr_t)test_id); #else g_idle_add(__bt_test_input_callback, (void *)test_id); #endif return TRUE; } #endif test_id = atoi(buf); __bt_tc_usage_print(); #ifdef ARCH64 g_idle_add(__bt_test_input_callback, (void *)(uintptr_t)test_id); #else g_idle_add(__bt_test_input_callback, (void *)test_id); #endif return TRUE; } int main() { int timeout_id = -1; int ret = BT_ERROR_NONE; GIOChannel *key_io; g_mainloop = g_main_loop_new(NULL, FALSE); HID_PRT("HID Keyboard starts"); if (bt_initialize() != BT_ERROR_NONE) { HID_PRT("bt_initialize() failed."); return -1; } if (bt_adapter_get_state(&bt_state) != BT_ERROR_NONE) { HID_PRT("bt_adapter_get_state() failed."); return -1; } /* Enable BT */ if (bt_state == BT_ADAPTER_DISABLED) { if (bt_adapter_set_state_changed_cb( __bt_state_changed_impl, NULL) != BT_ERROR_NONE) { HID_PRT("bt_adapter_set_state_changed_cb() failed."); return -1; } if (bt_adapter_enable() == BT_ERROR_NONE) { HID_PRT("bt_adapter_state_changed_cb will be called."); timeout_id = g_timeout_add(60000, __bt_timeout_func, g_mainloop); g_main_loop_run(g_mainloop); g_source_remove(timeout_id); } else { HID_PRT("bt_adapter_enable() failed."); return -1; } } else { HID_PRT("BT was already enabled."); } if (bt_state != BT_ADAPTER_ENABLED) { HID_PRT("BT is not enabled."); return -1; } g_idle_add(__bt_start_service, NULL); key_io = g_io_channel_unix_new(fileno(stdin)); g_io_channel_set_encoding(key_io, NULL, NULL); g_io_channel_set_flags(key_io, G_IO_FLAG_NONBLOCK, NULL); g_io_add_watch(key_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, __bt_key_event_cb, NULL); g_io_channel_unref(key_io); g_main_loop_run(g_mainloop); ret = bt_hid_device_deactivate(); HID_PRT("returns %s\n", __bt_get_error_message(ret)); bt_deinitialize(); HID_PRT("HID Keyboard ends."); return 0; }