/* * (C) Copyright 2008-2018 Fuzhou Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utility.h" #include "slog.h" #include "spp_server.h" #define SPP_SERVER_CHANNEL 1 typedef struct { int client_fd; int server_fd; pthread_t tid; int server_channel; bool server_running; RK_BT_SPP_STATE server_state; RK_BT_SPP_STATUS_CALLBACK state_cb; RK_BT_SPP_RECV_CALLBACK recv_cb; } spp_handler_t; static spp_handler_t g_spp_handler = { -1, -1, 0, 1, false, RK_BT_SPP_STATE_IDLE, NULL, NULL, }; static void spp_state_send(RK_BT_SPP_STATE state) { if (g_spp_handler.state_cb) g_spp_handler.state_cb(state); } static void close_cilent_fd() { if (g_spp_handler.client_fd >= 0) { shutdown(g_spp_handler.client_fd, SHUT_RDWR); g_spp_handler.client_fd = -1; } } static void close_server_fd() { if (g_spp_handler.server_fd >= 0) { shutdown(g_spp_handler.server_fd, SHUT_RDWR); g_spp_handler.server_fd = -1; } } static void *spp_server_thread(void *arg) { struct sockaddr_rc loc_addr = {0}, rem_addr = {0}; char buf[4096]; char rem_addr_str[20]; int bytes_read = 0; int opt = sizeof(rem_addr); int parame = 1; fd_set rfds; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 100000;/* 100ms */ g_spp_handler.server_fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if(g_spp_handler.server_fd < 0) { pr_err("[BT SPP] create socket error\n"); goto OUT; } /* Set address reuse */ setsockopt(g_spp_handler.server_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)¶me, sizeof(parame)); loc_addr.rc_family = AF_BLUETOOTH; loc_addr.rc_bdaddr = *BDADDR_ANY; loc_addr.rc_channel = g_spp_handler.server_channel; if(bind(g_spp_handler.server_fd, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) { pr_err("[BT SPP] bind socket error\n"); goto OUT; } if(listen(g_spp_handler.server_fd, 1) < 0) { pr_err("[BT SPP] listen error\n"); goto OUT; } g_spp_handler.server_running = true; while (g_spp_handler.server_running) { FD_ZERO(&rfds); FD_SET(g_spp_handler.server_fd, &rfds); if (select(g_spp_handler.server_fd + 1, &rfds, NULL, NULL, &tv) < 0) { pr_info("[BT SPP] server socket failed!\n"); goto OUT; } if (FD_ISSET(g_spp_handler.server_fd, &rfds) == 0) continue; g_spp_handler.client_fd = accept(g_spp_handler.server_fd, (struct sockaddr *)&rem_addr, &opt); if(g_spp_handler.client_fd < 0) { pr_err("[BT SPP] accept error\n"); goto OUT; } /* Get remote device addr */ memset(rem_addr_str, 0, sizeof(rem_addr_str)); ba2str(&rem_addr.rc_bdaddr, rem_addr_str); pr_info("[BT SPP] accepted connection from %s \n", rem_addr_str); g_spp_handler.server_state = RK_BT_SPP_STATE_CONNECT; spp_state_send(RK_BT_SPP_STATE_CONNECT); while(1) { memset(buf, 0, sizeof(buf)); bytes_read = read(g_spp_handler.client_fd, buf, sizeof(buf)); if (bytes_read <= 0) { close_cilent_fd(); g_spp_handler.server_state = RK_BT_SPP_STATE_DISCONNECT; spp_state_send(RK_BT_SPP_STATE_DISCONNECT); break; } if (g_spp_handler.recv_cb) g_spp_handler.recv_cb(buf, bytes_read); } } OUT: close_cilent_fd(); close_server_fd(); g_spp_handler.server_state = RK_BT_SPP_STATE_IDLE; pr_info("%s: Exit spp server thread!\n", __func__); return NULL; } int bt_spp_server_open() { int ret = 0; char cmd[128]; if (!g_spp_handler.tid) { memset(cmd, 0, 128); sprintf(cmd, "sdptool add --channel=%d SP", g_spp_handler.server_channel); exec_command_system(cmd); if(pthread_create(&g_spp_handler.tid, NULL, spp_server_thread, NULL)) { g_spp_handler.server_state = RK_BT_SPP_STATE_IDLE; pr_err("[BT SPP] create spp server thread failed!\n"); return -1; } pthread_setname_np(g_spp_handler.tid, "spp_server_thread"); } return 0; } void bt_spp_register_recv_callback(RK_BT_SPP_RECV_CALLBACK cb) { g_spp_handler.recv_cb = cb; } void bt_spp_register_status_callback(RK_BT_SPP_STATUS_CALLBACK cb) { g_spp_handler.state_cb = cb; } void bt_spp_server_close() { if(!g_spp_handler.server_running) return; pr_debug("[BT SPP] close start\n"); g_spp_handler.server_running = false; close_cilent_fd(); usleep(100000); //100ms close_server_fd(); //exec_command_system("sdptool del SP"); if (g_spp_handler.tid) { pthread_join(g_spp_handler.tid, NULL); g_spp_handler.tid = 0; } g_spp_handler.recv_cb = NULL; g_spp_handler.state_cb = NULL; g_spp_handler.server_state = RK_BT_SPP_STATE_IDLE; pr_debug("[BT SPP] close end\n"); } int bt_spp_write(char *data, int len) { int ret = 0; if (g_spp_handler.client_fd < 0) { pr_err("[BT SPP] write failed! ERROR:No connection is ready!\n"); return -1; } ret = write(g_spp_handler.client_fd, data, len); if (ret <= 0) { pr_err("[BT SPP] write failed! ERROR:%s\n", strerror(errno)); return ret; } return ret; } RK_BT_SPP_STATE bt_spp_get_status() { return g_spp_handler.server_state; } int bt_spp_set_channel(int channel) { if ((channel > 0) && (channel < 256)) g_spp_handler.server_channel = channel; else { pr_warning("[BT SPP] channel is not valid, use default channel(1)\n"); g_spp_handler.server_channel = SPP_SERVER_CHANNEL; } return 0; } int bt_spp_get_channel() { return g_spp_handler.server_channel; }