Add hid_keyboard tool 03/220603/4
authorDoHyun Pyun <dh79.pyun@samsung.com>
Fri, 20 Dec 2019 02:13:48 +0000 (11:13 +0900)
committerDoHyun Pyun <dh79.pyun@samsung.com>
Fri, 20 Dec 2019 03:53:24 +0000 (12:53 +0900)
This tool works as HID device role and A2DP sink role,
and we can use this tool with Tizen TV product.

Change-Id: I655d685e1b11523b5f38e82ad64f450be1175479
Signed-off-by: DoHyun Pyun <dh79.pyun@samsung.com>
packaging/capi-network-bluetooth.spec
test/CMakeLists.txt
test/hid_keyboard.c [new file with mode: 0644]

index 309d21a0c4a2028bb411808ad70cae7292bf3d91..84a3bda6b6898759e4dbd67fa8e6893254d479ea 100644 (file)
@@ -121,6 +121,7 @@ install -m 0644 gcov-obj/* %{buildroot}%{_datadir}/gcov/obj
 %{_bindir}/bt_unit_test
 %{_bindir}/bt_onoff
 %{_bindir}/ble_mouse
+%{_bindir}/hid_keyboard
 #/etc/smack/accesses.d/capi-network-bluetooth-test.efl
 
 %files devel
index aa332c77271f7b545087f3dc7b9f78389e9b462d..34d5dd481562741cd522f5b48b6f2c28324ade5b 100644 (file)
@@ -19,3 +19,4 @@ ENDFOREACH()
 INSTALL(TARGETS bt_unit_test DESTINATION bin)
 INSTALL(TARGETS bt_onoff DESTINATION bin)
 INSTALL(TARGETS ble_mouse DESTINATION bin)
+INSTALL(TARGETS hid_keyboard DESTINATION bin)
diff --git a/test/hid_keyboard.c b/test/hid_keyboard.c
new file mode 100644 (file)
index 0000000..3b9c6cc
--- /dev/null
@@ -0,0 +1,761 @@
+/*
+ * 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 <bluetooth.h>
+#include <bluetooth_internal.h>
+#include <dlog.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "HID_KEYBOARD"
+
+/**
+ *   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;
+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;
+
+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)
+
+#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++;\
+       }\
+}
+
+typedef struct {
+       const char *tc_name;
+       int tc_code;
+} tc_table_t;
+
+typedef enum {
+       BT_HID_DEVICE_SET_ADDRESS = 1,
+       BT_HID_DEVICE_TEST_SETUP,
+       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,
+       BT_HID_DEVICE_TEST_SEARCH,
+       BT_HID_DEVICE_TEST_STOP_SEARCH,
+       BT_HID_DEVICE_TEST_FINISH = 0xFF,
+} bt_hid_test_type_e;
+
+tc_table_t tc_hid_device[] = {
+       {"SET ADDRESS"
+               , BT_HID_DEVICE_SET_ADDRESS},
+       {"Initial Setup (Bond & Connects)"
+               , BT_HID_DEVICE_TEST_SETUP},
+       {"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},
+       {"AUDIO Connect only"
+               , BT_HID_DEVICE_TEST_CONNECT_AUDIO},
+       {"AUDIO Disconnect only"
+               , BT_HID_DEVICE_TEST_DISCONNECT_AUDIO},
+       {"HID Connect only"
+               , BT_HID_DEVICE_TEST_CONNECT_HID},
+       {"HID Disconnect only"
+               , BT_HID_DEVICE_TEST_DISCONNECT_HID},
+       {"Search TV"
+               , BT_HID_DEVICE_TEST_SEARCH},
+       {"Stop to search"
+               , BT_HID_DEVICE_TEST_STOP_SEARCH},
+       {"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 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);
+
+       g_wait_flag = false;
+
+       if (connected == true && result == BT_ERROR_NONE) {
+               g_strlcpy(remote_addr, remote_address, 18);
+               hid_connected = true;
+       } 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);
+
+       g_wait_flag = false;
+
+       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));
+       }
+
+       g_wait_flag = false;
+}
+
+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);
+}
+
+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;
+}
+
+static gboolean __bt_start_service(gpointer user_data)
+{
+       int ret = BT_ERROR_NONE;
+
+       ret = bt_audio_initialize();
+
+       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) {
+               HID_PRT("No TV is connected, try to search...");
+               __bt_find_remote_tv();
+       }
+
+       return FALSE;
+}
+
+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;
+}
+
+
+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;
+       int test_id = (int)data;
+       GSList *l = NULL;
+       int cnt = 0;
+       bt_adapter_le_device_scan_result_info_s *info;
+       unsigned char report_id = 0xF7;
+       char pressed_data[6]    = {0x07, 0x00, 0x00, 0x00, 0x00, 0x00};
+       char released_data[6]   = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+       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;
+       }
+
+       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;
+       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;
+       case BT_HID_DEVICE_TEST_VOLUME_UP:
+               pressed_data[0] = 0x07;  /* Vol up */
+
+               ret = bt_hid_device_send_custom_event(remote_addr,
+                               report_id, pressed_data, 6);
+               HID_PRT("returns %d\n", ret);
+
+               ret = bt_hid_device_send_custom_event(remote_addr,
+                               report_id, released_data, 6);
+               break;
+       case BT_HID_DEVICE_TEST_VOLUME_DOWN:
+               pressed_data[0] = 0x0B; /* Vol down */
+
+               ret = bt_hid_device_send_custom_event(remote_addr,
+                               report_id, pressed_data, 6);
+               HID_PRT("returns %d\n", ret);
+
+               ret = bt_hid_device_send_custom_event(remote_addr,
+                               report_id, released_data, 6);
+               break;
+       case BT_HID_DEVICE_TEST_CHANNEL_UP:
+               pressed_data[0] = 0x12; /* CH up */
+
+               ret = bt_hid_device_send_custom_event(remote_addr,
+                               report_id, pressed_data, 6);
+               HID_PRT("returns %d\n", ret);
+
+               ret = bt_hid_device_send_custom_event(remote_addr,
+                               report_id, released_data, 6);
+               break;
+       case BT_HID_DEVICE_TEST_CHANNEL_DOWN:
+               pressed_data[0] = 0x10; /* CH down */
+
+               ret = bt_hid_device_send_custom_event(remote_addr,
+                               report_id, pressed_data, 6);
+               HID_PRT("returns %d\n", ret);
+
+               ret = bt_hid_device_send_custom_event(remote_addr,
+                               report_id, released_data, 6);
+               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;
+       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;
+       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 (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;
+       }
+
+       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;
+}