#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "avrcpctrl.h" #include "bluez_ctrl.h" #include "a2dp_source/a2dp_masterctrl.h" #include "a2dp_source/shell.h" #include "spp_server/spp_server.h" #include "bluez_alsa_client/ctl-client.h" #include "bluez_alsa_client/rfcomm_msg.h" #include "obex_client.h" #include "gatt_client.h" #include "utility.h" #include "slog.h" extern volatile bt_control_t bt_control; #define BT_CONFIG_FAILED 2 #define BT_CONFIG_OK 1 typedef struct { int sockfd; pthread_t tid; RK_BT_SINK_UNDERRUN_CB cb; } underrun_handler_t; static underrun_handler_t g_underrun_handler = { -1, 0, NULL, }; static int bt_hal_source_close(bool disconnect); static int bt_hal_hfp_close(bool disconnect); static int bt_hal_ble_stop(bool disconnect); static void bt_hal_ble_client_close(bool disconnect); static int bt_hal_sink_close(bool disconnect); #if 0 void rk_printf_system_time(char *tag) { struct timeval tv; gettimeofday(&tv, NULL); printf("---%s: time: %lld ms\n", tag, tv.tv_sec * 1000 + tv.tv_usec/1000 + tv.tv_usec%1000); } #endif /***************************************************************** * Rockchip bluetooth LE api * *****************************************************************/ int rk_bt_ble_set_visibility(const int visiable, const int connect) { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_ble_start(RkBleContent *ble_content) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } if(ble_is_open()) { pr_info("%s: ble has been opened\n", __func__); return -1; } pr_info("%s, tid(%lu)\n", __func__, pthread_self()); if(ble_client_is_open()) { pr_info("ble client has been opened, close ble client\n"); rk_ble_client_close(); } if(rk_bt_control(BT_BLE_OPEN, NULL, 0) < 0) return -1; ble_state_send(RK_BLE_STATE_IDLE); return 0; } static int bt_hal_ble_stop(bool disconnect) { if (!ble_is_open()) { pr_info("%s: ble has been closed\n", __func__); return -1; } pr_info("%s, tid(%lu)\n", __func__, pthread_self()); bt_close_ble(disconnect); return 0; } int rk_ble_stop() { return bt_hal_ble_stop(true); } int rk_ble_get_state(RK_BLE_STATE *p_state) { ble_get_state(p_state); return 0; } int rk_ble_set_address(char *address) { return ble_set_address(address); } int rk_ble_write(const char *uuid, char *data, int len) { RkBleConfig ble_cfg; if (!ble_is_open()) { pr_info("ble isn't open, please open\n"); return -1; } ble_cfg.len = len > BT_ATT_MAX_VALUE_LEN ? BT_ATT_MAX_VALUE_LEN : len; memcpy(ble_cfg.data, data, ble_cfg.len); strcpy(ble_cfg.uuid, uuid); rk_bt_control(BT_BLE_WRITE, &ble_cfg, sizeof(RkBleConfig)); return 0; } int rk_ble_register_status_callback(RK_BLE_STATE_CALLBACK cb) { ble_register_state_callback(cb); return 0; } void rk_ble_register_mtu_callback(RK_BT_MTU_CALLBACK cb) { ble_register_mtu_callback(cb); } int rk_ble_register_recv_callback(RK_BLE_RECV_CALLBACK cb) { if (cb) { pr_info("BlueZ does not support this interface." "Please set the callback function when init BT.\n"); } return 0; } void rk_ble_register_request_data_callback(RK_BLE_REQUEST_DATA cb) { if (cb) { pr_info("BlueZ does not support this interface." "Please set the callback function when init BT.\n"); } } int rk_ble_disconnect() { if (!ble_is_open()) { pr_info("%s: ble isn't open, please open\n", __func__); return -1; } pr_info("%s, tid(%lu)\n", __func__, pthread_self()); return ble_disconnect(); } void rk_ble_set_local_privacy(bool local_privacy) { pr_info("bluez don't support %s\n", __func__); } int rk_ble_set_adv_interval(unsigned short adv_int_min, unsigned short adv_int_max) { return ble_set_adv_interval(adv_int_min, adv_int_max); } /***************************************************************** * Rockchip bluetooth LE Client api * *****************************************************************/ void rk_ble_client_register_state_callback(RK_BLE_CLIENT_STATE_CALLBACK cb) { gatt_client_register_state_callback(cb); } void rk_ble_client_register_recv_callback(RK_BLE_CLIENT_RECV_CALLBACK cb) { gatt_client_register_recv_callback(cb); } void rk_ble_client_register_mtu_callback(RK_BT_MTU_CALLBACK cb) { ble_register_mtu_callback(cb); } int rk_ble_client_open(bool mtu_change) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } if(ble_client_is_open()) { pr_info("%s: ble client has been opened\n", __func__); return -1; } if(ble_is_open()) { pr_info("%s: ble has been opened, close ble\n", __func__); rk_ble_stop(); } gatt_client_open(); bt_control.is_ble_client_open = true; gatt_client_state_send(RK_BLE_CLIENT_STATE_IDLE); return 0; } static void bt_hal_ble_client_close(bool disconnect) { if(!ble_client_is_open()) { pr_info("%s: ble client has been closed\n", __func__); return; } if(disconnect) { if(!ble_disconnect()) sleep(3); } gatt_client_close(); bt_control.is_ble_client_open = false; } void rk_ble_client_close() { bt_hal_ble_client_close(true); } RK_BLE_CLIENT_STATE rk_ble_client_get_state() { if(!ble_client_is_open()) { pr_info("%s: ble client isn't open, please open\n", __func__); return -1; } return gatt_client_get_state(); } int rk_ble_client_connect(char *address) { if(!ble_client_is_open()) { pr_info("%s: ble client isn't open, please open\n", __func__); return -1; } return connect_by_address(address); } int rk_ble_client_disconnect(char *address) { if(!ble_client_is_open()) { pr_info("%s: ble client isn't open, please open\n", __func__); return -1; } return ble_disconnect(); } int rk_ble_client_get_service_info(char *address, RK_BLE_CLIENT_SERVICE_INFO *info) { if(!ble_client_is_open()) { pr_info("%s: ble client isn't open, please open\n", __func__); return -1; } return gatt_client_get_service_info(address, info); } int rk_ble_client_read(const char *uuid) { if(!ble_client_is_open()) { pr_info("%s: ble client isn't open, please open\n", __func__); return -1; } return gatt_client_read(uuid, 0); } int rk_ble_client_write(const char *uuid, char *data, int data_len) { if(!ble_client_is_open()) { pr_info("%s: ble client isn't open, please open\n", __func__); return -1; } return gatt_client_write(uuid, data, data_len, 0); } bool rk_ble_client_is_notifying(const char *uuid) { if(!ble_client_is_open()) { pr_info("%s: ble client isn't open, please open\n", __func__); return -1; } return gatt_client_is_notifying(uuid); } int rk_ble_client_notify(const char *uuid, bool is_indicate, bool enable) { if(!ble_client_is_open()) { pr_info("%s: ble client isn't open, please open\n", __func__); return -1; } return gatt_client_notify(uuid, enable); } int rk_ble_client_get_eir_data(char *address, char *eir_data, int len) { if(!ble_client_is_open()) { pr_err("ble client isn't open, please open\n"); return -1; } return gatt_client_get_eir_data(address, eir_data, len); } int rk_ble_client_default_data_length() { pr_info("bluez don't support %s\n", __func__); return -1; } /***************************************************************** * Rockchip bluetooth master api * *****************************************************************/ static pthread_t g_btmaster_thread = 0; static void* _btmaster_autoscan_and_connect(void *data) { BtScanParam scan_param; BtDeviceInfo *start; int max_rssi = -100; int ret = 0; char target_address[17] = {0}; bool target_vaild = false; int scan_cnt, i; /* Scan bluetooth devices */ scan_param.mseconds = 10000; /* 10s for default */ scan_param.item_cnt = 0; scan_cnt = 3; prctl(PR_SET_NAME,"_btmaster_autoscan_and_connect"); scan_retry: pr_info("=== BT_SOURCE_SCAN ===\n"); ret = rk_bt_source_scan(&scan_param); if (ret && (scan_cnt--)) { sleep(1); goto scan_retry; } else if (ret) { pr_info("%s: ERROR: Scan error!\n", __func__); a2dp_master_event_send(BT_SOURCE_EVENT_CONNECT_FAILED, "", ""); return NULL; } /* * Find the audioSink device from the device list, * which has the largest rssi value. */ max_rssi = -100; for (i = 0; i < scan_param.item_cnt; i++) { start = &scan_param.devices[i]; if (start->rssi_valid && (start->rssi > max_rssi) && (!strcmp(start->playrole, "Audio Sink"))) { pr_info("#%02d Name:%s\n", i, start->name); pr_info("\tAddress:%s\n", start->address); pr_info("\tRSSI:%d\n", start->rssi); pr_info("\tPlayrole:%s\n", start->playrole); max_rssi = start->rssi; memcpy(target_address, start->address, 17); target_vaild = true; } } if (!target_vaild) { pr_info("=== Cannot find audio Sink devices. ===\n"); a2dp_master_event_send(BT_SOURCE_EVENT_CONNECT_FAILED, "", ""); return NULL; } else if (max_rssi < -80) { pr_info("=== BT SOURCE RSSI is is too weak !!! ===\n"); a2dp_master_event_send(BT_SOURCE_EVENT_CONNECT_FAILED, "", ""); return NULL; } /* Connect target device */ if (!a2dp_master_status(NULL, 0, NULL, 0)) a2dp_master_connect(target_address); pr_info("%s: Exit _btmaster_autoscan_and_connect thread!\n", __func__); return NULL; } /* * Turn on Bluetooth and scan SINK devices. * Features:  * 1. turn on Bluetooth * 2. enter the bt source mode * 3. Scan surrounding SINK type devices * 4. If the SINK device is found, the device with the strongest * signal is automatically connected. * Return: * 0: The function is executed successfully and needs to listen * for Bluetooth connection events. * -1: Function execution failed. */ int rk_bt_source_auto_connect_start(void *userdata, RK_BT_SOURCE_CALLBACK cb) { int ret; if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } if (g_btmaster_thread) { pr_info("%s: The last operation is still in progress, please stop then start\n", __func__); return -1; } /* Register callback and userdata */ rk_bt_source_register_status_cb(userdata, cb); ret = rk_bt_source_open(); if (ret < 0) return ret; /* Create thread to do connect task. */ ret = pthread_create(&g_btmaster_thread, NULL, _btmaster_autoscan_and_connect, NULL); if (ret) { pr_info("%s: _btmaster_autoscan_and_connect thread create failed!\n", __func__); return -1; } return 0; } int rk_bt_source_auto_connect_stop(void) { if (g_btmaster_thread) { pthread_join(g_btmaster_thread, NULL); g_btmaster_thread = 0; } return rk_bt_source_close(); } int rk_bt_source_open() { pr_info("%s, tid(%lu)\n", __func__, pthread_self()); if (!bt_is_open()) { pr_err("%s: Please open bt!!!\n", __func__); return -1; } if (g_btmaster_thread) { pr_err("%s: The last operation is still in progress\n", __func__); return -1; } if (bt_source_is_open()) { pr_info("%s: bt source has been opened\n", __func__); return -1; } if (bt_sink_is_open()){ pr_info("%s: bt sink has been opened, close bt sink", __func__); rk_bt_sink_close(); } if (bt_interface(BT_SOURCE_OPEN, NULL) < 0) { bt_control.is_a2dp_source_open = false; return -1; } reconn_last_devices(BT_DEVICES_A2DP_SINK); bt_control.is_a2dp_source_open = true; return 0; } static int bt_hal_source_close(bool disconnect) { pr_info("%s, tid(%lu)\n", __func__, pthread_self()); if(rk_bt_is_discovering()) rk_bt_cancel_discovery(); if (!bt_source_is_open()) { pr_info("%s: bt source has been closed\n", __func__); return -1; } a2dp_master_save_status(NULL); source_set_reconnect_tag(false); bt_close_source(disconnect); a2dp_master_deregister_cb(); bt_control.is_a2dp_source_open = false; return 0; } int rk_bt_source_close() { return bt_hal_source_close(true); } int rk_bt_source_scan(BtScanParam *data) { if (!bt_source_is_open()) { pr_info("%s: bt source isn't open, please open\n", __func__); return -1; } source_set_reconnect_tag(false); return a2dp_master_scan(data, sizeof(BtScanParam), SCAN_TYPE_BREDR); } int rk_bt_source_connect_by_addr(char *address) { char bd_addr[18]; memset(bd_addr, 0, 18); if (!bt_source_is_open()) { pr_info("%s: bt source isn't open, please open\n", __func__); return -1; } pr_info("%s thread tid = %lu\n", __func__, pthread_self()); return a2dp_master_connect(address); } int rk_bt_source_disconnect_by_addr(char *address) { if (!bt_source_is_open()) { pr_info("%s: bt source isn't open, please open\n", __func__); return -1; } pr_info("%s thread tid = %lu\n", __func__, pthread_self()); return disconnect_by_address(address); } int rk_bt_source_disconnect() { if (!bt_source_is_open()) { pr_info("%s: bt source isn't open, please open\n", __func__); return -1; } return disconnect_current_devices(); } int rk_bt_source_remove(char *address) { if (!bt_source_is_open()) { pr_info("%s: bt source isn't open, please open\n", __func__); return -1; } return remove_by_address(address); } int rk_bt_source_register_status_cb(void *userdata, RK_BT_SOURCE_CALLBACK cb) { a2dp_master_register_cb(userdata, cb); return 0; } int rk_bt_source_get_device_name(char *name, int len) { return rk_bt_get_device_name(name, len); } int rk_bt_source_get_device_addr(char *addr, int len) { return rk_bt_get_device_addr(addr, len); } int rk_bt_source_get_status(RK_BT_SOURCE_STATUS *pstatus, char *name, int name_len, char *address, int addr_len) { if (!pstatus) return 0; if (a2dp_master_status(address, addr_len, name, name_len)) *pstatus = BT_SOURCE_STATUS_CONNECTED; else *pstatus = BT_SOURCE_STATUS_DISCONNECTED; return 0; } int rk_bt_source_resume() { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_source_stop() { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_source_pause() { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_source_vol_up() { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_source_vol_down() { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_source_set_vol(int vol) { pr_info("bluez don't support %s\n", __func__); return 0; } /***************************************************************** * Rockchip bluetooth sink api * *****************************************************************/ void *sink_underrun_listen(void *arg) { int ret = 0; char buff[100] = {0}; struct sockaddr_un clientAddr; struct sockaddr_un serverAddr; socklen_t addr_len; g_underrun_handler.sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (g_underrun_handler.sockfd < 0) { pr_err("%s: Create socket failed!\n", __func__); return NULL; } serverAddr.sun_family = AF_UNIX; strcpy(serverAddr.sun_path, "/tmp/rk_deviceio_a2dp_underrun"); system("rm -rf /tmp/rk_deviceio_a2dp_underrun"); ret = bind(g_underrun_handler.sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); if (ret < 0) { pr_err("%s: Bind Local addr failed!\n", __func__); return NULL; } pr_info("%s: underrun listen...\n", __func__); while(1) { memset(buff, 0, sizeof(buff)); ret = recvfrom(g_underrun_handler.sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&clientAddr, &addr_len); if (ret <= 0) { if (ret == 0) pr_info("%s: socket closed!\n", __func__); break; } pr_info("%s: recv a message(%s)\n", __func__, buff); if (!bt_sink_is_open()) break; if (!strstr(buff, "a2dp underrun;")) { pr_warning("%s: recv a unsupport msg:%s\n", __func__, buff); continue; } if (g_underrun_handler.cb) g_underrun_handler.cb(); else break; } pr_info("%s: Exit underrun listen thread!\n", __func__); return NULL; } static int underrun_listen_thread_create(RK_BT_SINK_UNDERRUN_CB cb) { pr_info("%s: underrun_listen_thread_create\n", __func__); g_underrun_handler.cb = cb; /* Create a thread to listen for bluez-alsa sink underrun. */ if (!g_underrun_handler.tid) { if (pthread_create(&g_underrun_handler.tid, NULL, sink_underrun_listen, NULL)) { pr_err("%s: Create underrun listen pthread failed\n", __func__); return -1; } pthread_setname_np(g_underrun_handler.tid, "underrun_listen"); } return 0; } static void underrun_listen_thread_delete() { pr_debug("%s enter\n", __func__); if (g_underrun_handler.sockfd >= 0) { shutdown(g_underrun_handler.sockfd, SHUT_RDWR); g_underrun_handler.sockfd = -1; } if(g_underrun_handler.tid) { pthread_join(g_underrun_handler.tid, NULL); g_underrun_handler.tid = 0; } g_underrun_handler.cb = NULL; pr_debug("%s exit\n", __func__); } int rk_bt_sink_register_callback(RK_BT_SINK_CALLBACK cb) { a2dp_sink_register_cb(cb); return 0; } void rk_bt_sink_register_underurn_callback(RK_BT_SINK_UNDERRUN_CB cb) { underrun_listen_thread_create(cb); } int rk_bt_sink_register_volume_callback(RK_BT_SINK_VOLUME_CALLBACK cb) { a2dp_sink_register_volume_cb(cb); return 0; } int rk_bt_sink_register_track_callback(RK_BT_AVRCP_TRACK_CHANGE_CB cb) { a2dp_sink_register_track_cb(cb); return 0; } int rk_bt_sink_register_position_callback(RK_BT_AVRCP_PLAY_POSITION_CB cb) { a2dp_sink_register_position_cb(cb); return 0; } int rk_bt_sink_get_default_dev_addr(char *addr, int len) { return bt_get_default_dev_addr(addr, len); } int rk_bt_sink_open() { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } if (bt_sink_is_open()) { pr_info("%s: bt sink has been opened\n", __func__); return -1; } if(bt_source_is_open()) { pr_info("%s: bt source has been opened, close bt source", __func__); rk_bt_source_close(); } if (bt_interface(BT_SINK_OPEN, NULL) < 0) { bt_control.is_a2dp_sink_open = false; return -1; } reconn_last_devices(BT_DEVICES_A2DP_SOURCE); bt_control.is_a2dp_sink_open = true; return 0; } static int bt_hal_sink_close(bool disconnect) { if (!bt_sink_is_open()) { pr_info("%s: bt sink has been closed\n", __func__); return -1; } bt_close_sink(disconnect); underrun_listen_thread_delete(); a2dp_sink_clear_cb(); return 0; } int rk_bt_sink_close() { return bt_hal_sink_close(true); } int rk_bt_sink_get_state(RK_BT_SINK_STATE *pState) { return a2dp_sink_status(pState); } int rk_bt_sink_play(void) { if (bt_control_cmd_send(BT_RESUME_PLAY) < 0) return -1; return 0; } int rk_bt_sink_pause(void) { if (bt_control_cmd_send(BT_PAUSE_PLAY) < 0) return -1; return 0; } int rk_bt_sink_prev(void) { if (bt_control_cmd_send(BT_AVRCP_BWD) < 0) return -1; return 0; } int rk_bt_sink_next(void) { if (bt_control_cmd_send(BT_AVRCP_FWD) < 0) return -1; return 0; } int rk_bt_sink_stop(void) { if (bt_control_cmd_send(BT_AVRCP_STOP) < 0) return -1; return 0; } int rk_bt_sink_get_play_status() { if (!bt_sink_is_open()) { pr_info("%s: bt sink isn't open, please open\n", __func__); return -1; } return get_play_status_avrcp(); } bool rk_bt_sink_get_poschange() { if (!bt_sink_is_open()) { pr_info("%s: bt sink isn't open, please open\n", __func__); return false; } return get_poschange_avrcp(); } int rk_bt_sink_disconnect() { if (!bt_sink_is_open()) { pr_info("%s: bt sink isn't open, please open\n", __func__); return -1; } return disconnect_current_devices(); } int rk_bt_sink_connect_by_addr(char *addr) { if (!bt_sink_is_open()) { pr_info("%s: bt sink isn't open, please open\n", __func__); return -1; } return connect_by_address(addr); } int rk_bt_sink_disconnect_by_addr(char *addr) { if(bt_sink_is_open()) { return disconnect_by_address(addr); } return -1; } static int _get_bluealsa_plugin_volume_ctrl_info(char *name, int *value) { char buff[1024] = {0}; char ctrl_name[128] = {0}; int ctrl_value = 0; char *start = NULL; char *end = NULL; if (!name && !value) return -1; if (name) { exec_command("amixer -D bluealsa scontents", buff, sizeof(buff)); start = strstr(buff, "Simple mixer control "); end = strstr(buff, "A2DP'"); if (!start || (!strstr(start, "A2DP"))) return -1; start += strlen("Simple mixer control '"); end += strlen("A2DP"); if ((end - start) < strlen(" - A2DP")) return -1; memcpy(ctrl_name, start, end-start); memcpy(name, ctrl_name, strlen(ctrl_name)); } if (value) { start = strstr(buff, "Front Left: Capture "); if (!start) return -1; start += strlen("Front Left: Capture "); if ((*start < '0') || (*start > '9')) return -1; /* Max volume value:127, the length of volume value string must be <= 3 */ ctrl_value += (*start - '0'); start++; if ((*start >= '0') && (*start <= '9')) ctrl_value = 10 * ctrl_value + (*start - '0'); start++; if ((*start >= '0') && (*start <= '9')) ctrl_value = 10 * ctrl_value + (*start - '0'); *value = ctrl_value; } return 0; } static int _set_bluealsa_plugin_volume_ctrl_info(char *name, int value) { char buff[1024] = {0}; char cmd[256] = {0}; char ctrl_name[128] = {0}; int new_volume = 0; if (!name) return -1; sprintf(cmd, "amixer -D bluealsa sset \"%s\" %d", name, value); exec_command(cmd, buff, sizeof(buff)); if (_get_bluealsa_plugin_volume_ctrl_info(ctrl_name, &new_volume) == -1) return -1; if (new_volume != value) return -1; return 0; } int rk_bt_sink_volume_up(void) { char ctrl_name[128] = {0}; int current_volume = 0; int ret = 0; if (!bt_sink_is_open()) { pr_info("%s: bt sink isn't open, please open\n", __func__); return -1; } ret = _get_bluealsa_plugin_volume_ctrl_info(ctrl_name, ¤t_volume); if (ret) return ret; ret = _set_bluealsa_plugin_volume_ctrl_info(ctrl_name, current_volume + 8); return ret; } int rk_bt_sink_volume_down(void) { char ctrl_name[128] = {0}; int current_volume = 0; int ret = 0; if (!bt_sink_is_open()) { pr_info("%s: bt sink isn't open, please open\n", __func__); return -1; } ret = _get_bluealsa_plugin_volume_ctrl_info(ctrl_name, ¤t_volume); if (ret) return ret; if (current_volume < 8) current_volume = 0; else current_volume -= 8; ret = _set_bluealsa_plugin_volume_ctrl_info(ctrl_name, current_volume); return ret; } int rk_bt_sink_set_volume(int volume) { char ctrl_name[128] = {0}; int new_volume = 0; int ret = 0; if (!bt_sink_is_open()) { pr_info("%s: bt sink isn't open, please open\n", __func__); return -1; } if (volume < 0) new_volume = 0; else if (volume > 127) new_volume = 127; else new_volume = volume; ret = _get_bluealsa_plugin_volume_ctrl_info(ctrl_name, NULL); if (ret) return ret; ret = _set_bluealsa_plugin_volume_ctrl_info(ctrl_name, new_volume); return ret; } void rk_bt_sink_set_alsa_device(char *alsa_dev) { pr_info("bluez don't support %s\n", __func__); } /***************************************************************** * Rockchip bluetooth spp api * *****************************************************************/ int rk_bt_spp_open() { int ret = 0; if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } ret = bt_spp_server_open(); return ret; } int rk_bt_spp_register_status_cb(RK_BT_SPP_STATUS_CALLBACK cb) { bt_spp_register_status_callback(cb); return 0; } int rk_bt_spp_register_recv_cb(RK_BT_SPP_RECV_CALLBACK cb) { bt_spp_register_recv_callback(cb); return 0; } int rk_bt_spp_close(void) { bt_spp_server_close(); return 0; } int rk_bt_spp_get_state(RK_BT_SPP_STATE *pState) { if (pState) *pState = bt_spp_get_status(); return 0; } int rk_bt_spp_write(char *data, int len) { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_spp_listen() { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_spp_connect(char *address) { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_spp_disconnect(char *address) { pr_info("bluez don't support %s\n", __func__); return 0; } //====================================================// static GMainLoop *bt_main_loop = NULL; static pthread_t main_loop_thread = 0; GMainContext *Bluez_Context = NULL; static void *main_loop_init_thread(void *data) { #ifdef DefGContext bt_main_loop = g_main_loop_new(NULL, FALSE); pr_info("%s: bt mainloop run with default context\n", __func__); #else Bluez_Context = g_main_context_new(); bt_main_loop = g_main_loop_new(Bluez_Context, FALSE); pr_info("%s: bt mainloop run with Bluez_Context\n", __func__); #endif g_main_loop_run(bt_main_loop); g_main_loop_unref(bt_main_loop); bt_main_loop = NULL; pr_info("%s: bt mainloop exit\n", __func__); return NULL; } static int main_loop_init() { if (main_loop_thread) return 0; if (pthread_create(&main_loop_thread, NULL, main_loop_init_thread, NULL)) { pr_err("%s: Create bt mainloop thread failed\n", __func__); return -1; } pthread_setname_np(main_loop_thread, "main_loop_thread"); return 0; } static int main_loop_deinit() { if(bt_main_loop) { pr_info("%s bt mainloop quit\n", __func__); g_main_loop_quit(bt_main_loop); } if(main_loop_thread) { if (pthread_join(main_loop_thread, NULL)) { pr_err("%s: bt mainloop exit failed!\n", __func__); return -1; } else { pr_info("%s: bt mainloop thread exit ok\n", __func__); } main_loop_thread = 0; } return 0; } static pthread_t rk_bt_init_thread = 0; static int _rk_bt_init(void *p) { RkBtContent *p_bt_content = p; if (bt_is_open()) { pr_info("%s: bluetooth has been opened!\n", __func__); return -1; } pr_info("enter %s, tid(%lu)\n", __func__, pthread_self()); bt_state_send(RK_BT_STATE_TURNING_ON); main_loop_init(); setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/var/run/dbus/system_bus_socket", 1); if (rk_bt_control(BT_OPEN, p_bt_content, sizeof(RkBtContent))) { bt_state_send(RK_BT_STATE_OFF); return -1; } pr_info("exit %s\n", __func__); return 0; } int rk_bt_init(RkBtContent *p_bt_content) { if (rk_bt_init_thread) return 0; if (pthread_create(&rk_bt_init_thread, NULL, _rk_bt_init, (void *)p_bt_content)) { pr_err("%s: Create rk_bt_init_thread thread failed\n", __func__); return -1; } pthread_setname_np(rk_bt_init_thread, "rk_bt_init_thread"); pthread_detach(rk_bt_init_thread); return 0; } int _rk_bt_deinit(void *p) { rk_bt_init_thread = 0; if (!bt_is_open()) { pr_info("%s: bluetooth has been closed!\n", __func__); return -1; } pr_info("enter %s, tid(%lu)\n", __func__, pthread_self()); bt_state_send(RK_BT_STATE_TURNING_OFF); bt_hal_hfp_close(false); bt_hal_sink_close(false); bt_hal_source_close(false); rk_bt_spp_close(); bt_hal_ble_stop(false); bt_hal_ble_client_close(false); rk_bt_obex_pbap_deinit(); bt_close(); kill_task("obexd"); kill_task("bluealsa"); kill_task("bluealsa-aplay"); kill_task("bluetoothctl"); msleep(600); kill_task("bluetoothd"); exec_command_system("hciconfig hci0 down"); kill_task("rtk_hciattach"); kill_task("brcm_patchram_plus1"); main_loop_deinit(); bt_deregister_bond_callback(); bt_deregister_discovery_callback(); bt_deregister_dev_found_callback(); bt_deregister_name_change_callback(); bt_control.is_bt_open = false; bt_state_send(RK_BT_STATE_OFF); bt_deregister_state_callback(); pr_info("exit %s\n", __func__); return 0; } static pthread_t rk_bt_deinit_thread = 0; int rk_bt_deinit() { if (pthread_create(&rk_bt_deinit_thread, NULL, _rk_bt_deinit, NULL)){ pr_err("%s: Create rk_bt_init_thread thread failed\n", __func__); return -1; } pthread_setname_np(rk_bt_deinit_thread, "rk_bt_deinit_thread"); pthread_detach(rk_bt_deinit_thread); return 0; } void rk_bt_register_state_callback(RK_BT_STATE_CALLBACK cb) { bt_register_state_callback(cb); } void rk_bt_register_bond_callback(RK_BT_BOND_CALLBACK cb) { bt_register_bond_callback(cb); } void rk_bt_register_discovery_callback(RK_BT_DISCOVERY_CALLBACK cb) { bt_register_discovery_callback(cb); } void rk_bt_register_dev_found_callback(RK_BT_DEV_FOUND_CALLBACK cb) { bt_register_dev_found_callback(cb); } void rk_bt_register_name_change_callback(RK_BT_NAME_CHANGE_CALLBACK cb) { bt_register_name_change_callback(cb); } /* * / # hcitool con * Connections: * > ACL 64:A2:F9:68:1E:7E handle 1 state 1 lm SLAVE AUTH ENCRYPT * > LE 60:9C:59:31:7F:B9 handle 16 state 1 lm SLAVE */ int rk_bt_is_connected(void) { return bt_is_connected(); } int rk_bt_set_class(int value) { char cmd[100] = {0}; pr_info("#%s value:0x%x\n", __func__, value); sprintf(cmd, "hciconfig hci0 class 0x%x", value); exec_command_system(cmd); msleep(100); return 0; } int rk_bt_set_sleep_mode() { pr_info("bluez don't support %s\n", __func__); return 0; } int rk_bt_enable_reconnect(int value) { int ret = 0; int fd = 0; fd = open("/userdata/cfg/bt_reconnect", O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) { pr_err("open /userdata/cfg/bt_reconnect failed!\n"); return -1; } if (value) ret = write(fd, "bluez-reconnect:enable", strlen("bluez-reconnect:enable")); else ret = write(fd, "bluez-reconnect:disable", strlen("bluez-reconnect:disable")); close(fd); return (ret < 0) ? -1 : 0; } int rk_bt_start_discovery(unsigned int mseconds, RK_BT_SCAN_TYPE scan_type) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } pr_info("%s, tid(%lu)\n", __func__, pthread_self()); return bt_start_discovery(mseconds, scan_type); } int rk_bt_cancel_discovery() { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } pr_info("%s, tid(%lu)\n", __func__, pthread_self()); return bt_cancel_discovery(RK_BT_DISC_STOPPED_BY_USER); } bool rk_bt_is_discovering() { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return false; } return bt_is_scaning(); } int rk_bt_get_scaned_devices(RkBtScanedDevice **dev_list, int *count) { *count = 0; if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return bt_get_scaned_devices(dev_list, count, false); } int rk_bt_free_scaned_devices(RkBtScanedDevice *dev_list) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return bt_free_scaned_devices(dev_list); } void rk_bt_display_devices() { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return; } bt_display_devices(); } void rk_bt_display_paired_devices() { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return; } bt_display_paired_devices(); } int rk_bt_get_eir_data(char *address, char *eir_data, int len) { if (!bt_is_open()) { pr_err("bt isn't open, please open\n"); return -1; } return bt_get_eir_data(address, eir_data, len); } int rk_bt_pair_by_addr(char *addr) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return pair_by_addr(addr); } int rk_bt_unpair_by_addr(char *addr) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return unpair_by_addr(addr); } int rk_bt_set_device_name(char *name) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return bt_set_device_name(name); } int rk_bt_get_device_name(char *name, int len) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return bt_get_device_name(name, len); } int rk_bt_get_device_addr(char *addr, int len) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return bt_get_device_addr(addr, len); } int rk_bt_get_paired_devices(RkBtScanedDevice **dev_list, int *count) { *count = 0; if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return bt_get_scaned_devices(dev_list, count, true); } int rk_bt_free_paired_devices(RkBtScanedDevice *dev_list) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return bt_free_scaned_devices(dev_list); } RK_BT_PLAYROLE_TYPE rk_bt_get_playrole_by_addr(char *addr) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return bt_get_playrole_by_addr(addr); } int rk_bt_set_visibility(const int visiable, const int connectable) { if (visiable && connectable) { exec_command_system("hciconfig hci0 piscan"); return 0; } exec_command_system("hciconfig hci0 noscan"); usleep(20000);//20ms if (visiable) exec_command_system("hciconfig hci0 iscan"); if (connectable) exec_command_system("hciconfig hci0 pscan"); return 0; } void rk_bt_set_bsa_server_path(char *path) { pr_info("bluez don't support %s\n", __func__); } bool rk_bt_get_connected_properties(char *addr) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return false; } return get_device_connected_properties(addr); } int rk_bt_read_remote_device_name(char *addr, int transport) { pr_info("bluez don't support %s\n", __func__); return -1; } RK_BT_DEV_PLATFORM_TYPE rk_bt_get_dev_platform(char *addr) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } return (RK_BT_DEV_PLATFORM_TYPE)get_dev_platform(addr); } /***************************************************************** * Rockchip bluetooth hfp-hf api * *****************************************************************/ static int g_ba_hfp_client = -1; void rk_bt_hfp_register_callback(RK_BT_HFP_CALLBACK cb) { rfcomm_hfp_hf_regist_cb(cb); } int rk_bt_hfp_open() { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } if (bt_hfp_is_open()) { pr_err("%s: bt hfp has already been opened!!!\n", __func__); return -1; } if(bt_source_is_open()) { pr_info("%s: bt source has been opened, close bt source", __func__); rk_bt_source_close(); } if (bt_interface(BT_HFP_OPEN, NULL) < 0) { bt_control.is_hfp_open = false; return -1; } g_ba_hfp_client = bluealsa_open("hci0"); if (g_ba_hfp_client < 0) { pr_err("%s: bt hfp connect to bluealsa server failed!", __func__); return -1; } rfcomm_listen_ba_msg_start(); bt_control.is_hfp_open = true; reconn_last_devices(BT_DEVICES_HFP); return 0; } int rk_bt_hfp_sink_open(void) { if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } /* Already in sink mode or hfp mode? */ if (bt_sink_is_open() || bt_hfp_is_open()) { pr_err("\"rk_bt_sink_open\" or \"rk_bt_hfp_open\" is called before calling this interface." "This situation is not allowed."); return -1; } if(bt_source_is_open()) { pr_info("%s: bt source has been opened, close bt source", __func__); rk_bt_source_close(); } if (bt_interface(BT_HFP_SINK_OPEN, NULL) < 0) { bt_control.is_a2dp_sink_open = false; bt_control.is_hfp_open = false; return -1; } g_ba_hfp_client = bluealsa_open("hci0"); if (g_ba_hfp_client < 0) { pr_err("%s: bt hfp connect to bluealsa server failed!", __func__); return -1; } rfcomm_listen_ba_msg_start(); bt_control.is_a2dp_sink_open = true; bt_control.is_hfp_open = true; return 0; } static int bt_hal_hfp_close(bool disconnect) { rfcomm_listen_ba_msg_stop(); if (g_ba_hfp_client >= 0) { shutdown(g_ba_hfp_client, SHUT_RDWR); g_ba_hfp_client = -1; } if (!bt_hfp_is_open()) return -1; rfcomm_hfp_send_event(RK_BT_HFP_DISCONNECT_EVT, NULL); rfcomm_hfp_hf_regist_cb(NULL); bt_control.is_hfp_open = false; if (bt_sink_is_open()) return 0; if(disconnect) { if(!disconnect_current_devices()) sleep(3); } exec_command_system("hciconfig hci0 noscan"); kill_task("bluealsa-aplay"); kill_task("bluealsa"); return 0; } int rk_bt_hfp_close(void) { return bt_hal_hfp_close(true); } static char *build_rfcomm_command(const char *cmd) { static char command[512]; bool at; command[0] = '\0'; if (!(at = strncmp(cmd, "AT", 2) == 0)) strcpy(command, "\r\n"); strcat(command, cmd); strcat(command, "\r"); if (!at) strcat(command, "\n"); return command; } static int rk_bt_hfp_hp_send_cmd(char *cmd) { char dev_addr[18] = {0}; char result_buf[256] = {0}; int ret = 0; bdaddr_t ba_addr; char *start = NULL; if (g_ba_hfp_client <= 0) { pr_err("%s ba hfp client is not valid!", __func__); return -1; } exec_command("hcitool con", result_buf, sizeof(result_buf)); if ((start = strstr(result_buf, "ACL ")) != NULL) { start += 4; /* skip space */ memcpy(dev_addr, start, 17); } pr_err("%s send cmd:%s to addr:%s", __func__, cmd, dev_addr); ret = str2ba(dev_addr, &ba_addr); if (ret) { pr_err("%s no valid hfp connection!", __func__); return -1; } ret = bluealsa_send_rfcomm_command(g_ba_hfp_client, ba_addr, build_rfcomm_command(cmd)); if (ret) pr_err("%s ba hfp client cmd:/'%s/' failed!", __func__, cmd); return ret; } int rk_bt_hfp_pickup(void) { if(rk_bt_hfp_hp_send_cmd("ATA")) { pr_info("%s: send ATA cmd error\n", __func__); return -1; } return 0; } int rk_bt_hfp_hangup(void) { if(rk_bt_hfp_hp_send_cmd("AT+CHUP")) { pr_info("%s: send AT+CHUP cmd error\n", __func__); return -1; } return 0; } int rk_bt_hfp_dial_number(char *number) { char buf[256]; if(number == NULL || (strlen(number) == 0)) { pr_err("%s: empty number string\n", __func__); return -1; } if(strlen(number) > 250) { pr_err("%s: Invalid phone number(%s)\n", __func__, number); return -1; } memset(buf, 0, 256); sprintf(buf, "%s%s%s", "ATD", number, ";"); return rk_bt_hfp_hp_send_cmd(buf); } int rk_bt_hfp_redial(void) { return rk_bt_hfp_hp_send_cmd("AT+BLDN"); } int rk_bt_hfp_report_battery(int value) { int ret = 0; char at_cmd[100] = {0}; static int done = 0; if ((value < 0) || (value > 9)) { pr_info("%s: ERROR: Invalid value, should within [0, 9]\n", __func__); return -1; } if (done == 0) { ret = rk_bt_hfp_hp_send_cmd("AT+XAPL=ABCD-1234-0100,2"); done = 1; } if (ret == 0) { sprintf(at_cmd, "AT+IPHONEACCEV=1,1,%d", value); ret = rk_bt_hfp_hp_send_cmd(at_cmd); } return ret; } int rk_bt_hfp_set_volume(int volume) { int ret = 0; char at_cmd[100] = {0}; if (volume > 15) volume = 15; else if (volume < 0) volume = 0; sprintf(at_cmd, "AT+VGS=%d", volume); ret = rk_bt_hfp_hp_send_cmd(at_cmd); return ret; } void rk_bt_hfp_enable_cvsd(void) { //for compile pr_info("bluez don't support %s\n", __func__); } void rk_bt_hfp_disable_cvsd(void) { //for compile pr_info("bluez don't support %s\n", __func__); } int rk_bt_hfp_disconnect() { return disconnect_current_devices(); } /***************************************************************** * Rockchip bluetooth obex api * *****************************************************************/ static pthread_t g_obex_thread = 0; int rk_bt_obex_init(char *path) { char buf[100]; if (!bt_is_open()) { pr_info("%s: Please open bt!!!\n", __func__); return -1; } if (get_ps_pid("obexd")) { pr_info("%s: obexd has been opened\n", __func__); return 0; } if(!path) { pr_info("%s: error, path == NULL\n", __func__); return -1; } /* run obexd */ memset(buf, 0, 100); sprintf(buf, "%s%s%s", "/usr/libexec/bluetooth/obexd -d -n -l -a -r /", path, "/ &"); pr_info("%s: run obexd(%s)\n", __func__, buf); setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/var/run/dbus/system_bus_socket", 1); usleep(6000); if (run_task("obexd", buf)) { pr_err("%s: run obexd failed!\n", __func__); return -1; } return 0; } int rk_bt_obex_pbap_init() { if (!get_ps_pid("obexd")) { pr_info("%s: Please open obex!!!\n", __func__); return -1; } if(g_obex_thread) { pr_info("%s: g_obex_thread has been initialized\n", __func__); return -1; } /* Create thread to do connect task. */ if (pthread_create(&g_obex_thread, NULL, obex_main_thread, NULL)) { pr_info("%s: obex_main_thread thread create failed!\n", __func__); return -1; } pthread_setname_np(g_obex_thread, "obex_main_thread"); return 0; } void rk_bt_obex_register_status_cb(RK_BT_OBEX_STATE_CALLBACK cb) { obex_pbap_register_status_cb(cb); } int rk_bt_obex_pbap_connect(char *btaddr) { if(!g_obex_thread) { pr_err("%s: obex don't inited, please init\n", __func__); return -1; } if (!btaddr || (strlen(btaddr) < 17)) { pr_err("%s: Invalid address\n", __func__); return -1; } pr_info("[enter %s]\n", __func__); return obex_connect_pbap(btaddr); } int rk_bt_obex_pbap_get_vcf(char *dir_name, char *dir_file) { if(!g_obex_thread) { pr_err("%s: obex don't inited, please init\n", __func__); return -1; } if (!dir_name || !dir_file) { pr_err("%s: Invalid dir_name or dir_file\n", __func__); return -1; } pr_info("[enter %s]\n", __func__); return obex_get_pbap_pb(dir_name, dir_file); } int rk_bt_obex_pbap_disconnect(char *btaddr) { if(!g_obex_thread) { pr_err("%s: obex don't inited, please init\n", __func__); return -1; } pr_info("[enter %s]\n", __func__); return obex_disconnect(1, NULL); } int rk_bt_obex_pbap_deinit() { if(!g_obex_thread) { pr_info("%s: obex has been closed\n", __func__); return -1; } pr_info("[enter %s]\n", __func__); obex_quit(); pthread_join(g_obex_thread, NULL); g_obex_thread = 0; pr_info("[exit %s]\n", __func__); return 0; } int rk_bt_obex_deinit() { return kill_task("obexd"); } /***************************************************************** * Rockchip bluetooth pan api * *****************************************************************/ void rk_bt_pan_register_event_cb(RK_BT_PAN_EVENT_CALLBACK cb) { pr_info("bluez don't support %s\n", __func__); } int rk_bt_pan_open() { pr_info("bluez don't support %s\n", __func__); return -1; } int rk_bt_pan_close() { pr_info("bluez don't support %s\n", __func__); return -1; } int rk_bt_pan_connect(char *address) { pr_info("bluez don't support %s\n", __func__); return -1; } int rk_bt_pan_disconnect(char *address) { pr_info("bluez don't support %s\n", __func__); return -1; }