userial.c revision ead3cde4bac0c3e32cd31f149093f004eef8ceeb
1/****************************************************************************** 2 * 3 * Copyright (C) 2009-2012 Broadcom Corporation 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at: 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ******************************************************************************/ 18 19/****************************************************************************** 20 * 21 * Filename: userial.c 22 * 23 * Description: Contains open/read/write/close functions on serial port 24 * 25 ******************************************************************************/ 26 27#define LOG_TAG "bt_userial" 28 29#include <utils/Log.h> 30#include <pthread.h> 31#include <fcntl.h> 32#include <errno.h> 33#include <stdio.h> 34#include <sys/socket.h> 35#include "bt_hci_bdroid.h" 36#include "userial.h" 37#include "utils.h" 38#include "bt_vendor_lib.h" 39#include <sys/prctl.h> 40 41/****************************************************************************** 42** Constants & Macros 43******************************************************************************/ 44 45#ifndef USERIAL_DBG 46#define USERIAL_DBG FALSE 47#endif 48 49#if (USERIAL_DBG == TRUE) 50#define USERIALDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);} 51#else 52#define USERIALDBG(param, ...) {} 53#endif 54 55#ifndef ENABLE_USERIAL_TIMING_LOGS 56#define ENABLE_USERIAL_TIMING_LOGS FALSE 57#endif 58 59#define MAX_SERIAL_PORT (USERIAL_PORT_3 + 1) 60#define READ_LIMIT (BTHC_USERIAL_READ_MEM_SIZE - BT_HC_HDR_SIZE) 61 62enum { 63 USERIAL_RX_EXIT, 64 USERIAL_RX_FLOW_OFF, 65 USERIAL_RX_FLOW_ON 66}; 67 68/****************************************************************************** 69** Externs 70******************************************************************************/ 71 72extern bt_vendor_interface_t *bt_vnd_if; 73 74/****************************************************************************** 75** Local type definitions 76******************************************************************************/ 77 78typedef struct 79{ 80 int fd; 81 uint8_t port; 82 pthread_t read_thread; 83 BUFFER_Q rx_q; 84 HC_BT_HDR *p_rx_hdr; 85} tUSERIAL_CB; 86 87/****************************************************************************** 88** Static variables 89******************************************************************************/ 90 91static tUSERIAL_CB userial_cb; 92static volatile uint8_t userial_running = 0; 93 94/****************************************************************************** 95** Static functions 96******************************************************************************/ 97 98#if defined(ENABLE_USERIAL_TIMING_LOGS) && (ENABLE_USERIAL_TIMING_LOGS==TRUE) 99 100static void log_userial_tx_timing(int len) 101{ 102 #define USEC_PER_SEC 1000000L 103 static struct timespec prev = {0, 0}; 104 struct timespec now, diff; 105 unsigned int diff_us = 0; 106 unsigned int now_us = 0; 107 108 clock_gettime(CLOCK_MONOTONIC, &now); 109 now_us = now.tv_sec*USEC_PER_SEC + now.tv_nsec/1000; 110 diff_us = (now.tv_sec - prev.tv_sec) * USEC_PER_SEC + (now.tv_nsec - prev.tv_nsec)/1000; 111 112 ALOGW("[userial] ts %08d diff : %08d len %d", now_us, diff_us, 113 len); 114 115 prev = now; 116} 117 118#endif 119 120 121/***************************************************************************** 122** Socket signal functions to wake up userial_read_thread for termination 123** 124** creating an unnamed pair of connected sockets 125** - signal_fds[0]: join fd_set in select call of userial_read_thread 126** - signal_fds[1]: trigger from userial_close 127*****************************************************************************/ 128static int signal_fds[2]={0,1}; 129static uint8_t rx_flow_on = TRUE; 130static inline int create_signal_fds(fd_set* set) 131{ 132 if(signal_fds[0]==0 && socketpair(AF_UNIX, SOCK_STREAM, 0, signal_fds)<0) 133 { 134 ALOGE("create_signal_sockets:socketpair failed, errno: %d", errno); 135 return -1; 136 } 137 FD_SET(signal_fds[0], set); 138 return signal_fds[0]; 139} 140static inline int send_wakeup_signal(char sig_cmd) 141{ 142 return send(signal_fds[1], &sig_cmd, sizeof(sig_cmd), 0); 143} 144static inline char reset_signal() 145{ 146 char sig_recv = -1; 147 recv(signal_fds[0], &sig_recv, sizeof(sig_recv), MSG_WAITALL); 148 return sig_recv; 149} 150static inline int is_signaled(fd_set* set) 151{ 152 return FD_ISSET(signal_fds[0], set); 153} 154 155 156/******************************************************************************* 157** 158** Function select_read 159** 160** Description check if fd is ready for reading and listen for termination 161** signal. need to use select in order to avoid collision 162** between read and close on the same fd 163** 164** Returns -1: termination 165** >=0: numbers of bytes read back from fd 166** 167*******************************************************************************/ 168static int select_read(int fd, uint8_t *pbuf, int len) 169{ 170 fd_set input; 171 int n = 0, ret = -1; 172 char reason = 0; 173 174 while (userial_running) 175 { 176 /* Initialize the input fd set */ 177 FD_ZERO(&input); 178 if (rx_flow_on == TRUE) 179 { 180 FD_SET(fd, &input); 181 } 182 int fd_max = create_signal_fds(&input); 183 fd_max = fd_max > fd ? fd_max : fd; 184 185 /* Do the select */ 186 n = select(fd_max+1, &input, NULL, NULL, NULL); 187 if(is_signaled(&input)) 188 { 189 reason = reset_signal(); 190 if (reason == USERIAL_RX_EXIT) 191 { 192 USERIALDBG("RX termination"); 193 return -1; 194 } 195 else if (reason == USERIAL_RX_FLOW_OFF) 196 { 197 USERIALDBG("RX flow OFF"); 198 rx_flow_on = FALSE; 199 } 200 else if (reason == USERIAL_RX_FLOW_ON) 201 { 202 USERIALDBG("RX flow ON"); 203 rx_flow_on = TRUE; 204 } 205 } 206 207 if (n > 0) 208 { 209 /* We might have input */ 210 if (FD_ISSET(fd, &input)) 211 { 212 ret = read(fd, pbuf, (size_t)len); 213 if (0 == ret) 214 ALOGW( "read() returned 0!" ); 215 216 return ret; 217 } 218 } 219 else if (n < 0) 220 ALOGW( "select() Failed"); 221 else if (n == 0) 222 ALOGW( "Got a select() TIMEOUT"); 223 224 } 225 226 return ret; 227} 228 229/******************************************************************************* 230** 231** Function userial_read_thread 232** 233** Description 234** 235** Returns void * 236** 237*******************************************************************************/ 238static void *userial_read_thread(void *arg) 239{ 240 int rx_length = 0; 241 HC_BT_HDR *p_buf = NULL; 242 uint8_t *p; 243 244 USERIALDBG("Entering userial_read_thread()"); 245 prctl(PR_SET_NAME, (unsigned long)"userial_read", 0, 0, 0); 246 247 rx_flow_on = TRUE; 248 userial_running = 1; 249 250 while (userial_running) 251 { 252 if (bt_hc_cbacks) 253 { 254 p_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc( \ 255 BTHC_USERIAL_READ_MEM_SIZE); 256 } 257 else 258 p_buf = NULL; 259 260 if (p_buf != NULL) 261 { 262 p_buf->offset = 0; 263 p_buf->layer_specific = 0; 264 265 p = (uint8_t *) (p_buf + 1); 266 rx_length = select_read(userial_cb.fd, p, READ_LIMIT); 267 } 268 else 269 { 270 rx_length = 0; 271 utils_delay(100); 272 ALOGW("userial_read_thread() failed to gain buffers"); 273 continue; 274 } 275 276 277 if (rx_length > 0) 278 { 279 p_buf->len = (uint16_t)rx_length; 280 utils_enqueue(&(userial_cb.rx_q), p_buf); 281 bthc_signal_event(HC_EVENT_RX); 282 } 283 else /* either 0 or < 0 */ 284 { 285 ALOGW("select_read return size <=0:%d, exiting userial_read_thread",\ 286 rx_length); 287 /* if we get here, we should have a buffer */ 288 bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1)); 289 /* negative value means exit thread */ 290 break; 291 } 292 } /* for */ 293 294 userial_running = 0; 295 USERIALDBG("Leaving userial_read_thread()"); 296 pthread_exit(NULL); 297 298 return NULL; // Compiler friendly 299} 300 301 302/***************************************************************************** 303** Userial API Functions 304*****************************************************************************/ 305 306/******************************************************************************* 307** 308** Function userial_init 309** 310** Description Initializes the userial driver 311** 312** Returns TRUE/FALSE 313** 314*******************************************************************************/ 315uint8_t userial_init(void) 316{ 317 USERIALDBG("userial_init"); 318 memset(&userial_cb, 0, sizeof(tUSERIAL_CB)); 319 userial_cb.fd = -1; 320 utils_queue_init(&(userial_cb.rx_q)); 321 return TRUE; 322} 323 324 325/******************************************************************************* 326** 327** Function userial_open 328** 329** Description Open Bluetooth device with the port ID 330** 331** Returns TRUE/FALSE 332** 333*******************************************************************************/ 334uint8_t userial_open(uint8_t port) 335{ 336 struct sched_param param; 337 int policy, result; 338 pthread_attr_t thread_attr; 339 int fd_array[CH_MAX]; 340 341 USERIALDBG("userial_open(port:%d)", port); 342 343 if (userial_running) 344 { 345 /* Userial is open; close it first */ 346 userial_close(); 347 utils_delay(50); 348 } 349 350 if (port >= MAX_SERIAL_PORT) 351 { 352 ALOGE("Port > MAX_SERIAL_PORT"); 353 return FALSE; 354 } 355 356 /* Calling vendor-specific part */ 357 if (bt_vnd_if) 358 { 359 result = bt_vnd_if->op(BT_VND_OP_USERIAL_OPEN, &fd_array); 360 361 if (result != 1) 362 { 363 ALOGE("userial_open: wrong numbers of open fd in vendor lib [%d]!", 364 result); 365 ALOGE("userial_open: HCI UART expects only one open fd"); 366 bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL); 367 return FALSE; 368 } 369 370 userial_cb.fd = fd_array[0]; 371 } 372 else 373 { 374 ALOGE("userial_open: missing vendor lib interface !!!"); 375 ALOGE("userial_open: unable to open UART port"); 376 return FALSE; 377 } 378 379 if (userial_cb.fd == -1) 380 { 381 ALOGE("userial_open: failed to open UART port"); 382 return FALSE; 383 } 384 385 USERIALDBG( "fd = %d", userial_cb.fd); 386 387 userial_cb.port = port; 388 389 pthread_attr_init(&thread_attr); 390 391 if (pthread_create(&(userial_cb.read_thread), &thread_attr, \ 392 userial_read_thread, NULL) != 0 ) 393 { 394 ALOGE("pthread_create failed!"); 395 return FALSE; 396 } 397 398 if(pthread_getschedparam(userial_cb.read_thread, &policy, ¶m)==0) 399 { 400 policy = BTHC_LINUX_BASE_POLICY; 401#if (BTHC_LINUX_BASE_POLICY!=SCHED_NORMAL) 402 param.sched_priority = BTHC_USERIAL_READ_THREAD_PRIORITY; 403#endif 404 result = pthread_setschedparam(userial_cb.read_thread, policy, ¶m); 405 if (result != 0) 406 { 407 ALOGW("userial_open: pthread_setschedparam failed (%s)", \ 408 strerror(result)); 409 } 410 } 411 412 return TRUE; 413} 414 415/******************************************************************************* 416** 417** Function userial_read 418** 419** Description Read data from the userial port 420** 421** Returns Number of bytes actually read from the userial port and 422** copied into p_data. This may be less than len. 423** 424*******************************************************************************/ 425uint16_t userial_read(uint16_t msg_id, uint8_t *p_buffer, uint16_t len) 426{ 427 uint16_t total_len = 0; 428 uint16_t copy_len = 0; 429 uint8_t *p_data = NULL; 430 431 do 432 { 433 if(userial_cb.p_rx_hdr != NULL) 434 { 435 p_data = ((uint8_t *)(userial_cb.p_rx_hdr + 1)) + \ 436 (userial_cb.p_rx_hdr->offset); 437 438 if((userial_cb.p_rx_hdr->len) <= (len - total_len)) 439 copy_len = userial_cb.p_rx_hdr->len; 440 else 441 copy_len = (len - total_len); 442 443 memcpy((p_buffer + total_len), p_data, copy_len); 444 445 total_len += copy_len; 446 447 userial_cb.p_rx_hdr->offset += copy_len; 448 userial_cb.p_rx_hdr->len -= copy_len; 449 450 if(userial_cb.p_rx_hdr->len == 0) 451 { 452 if (bt_hc_cbacks) 453 bt_hc_cbacks->dealloc((TRANSAC) userial_cb.p_rx_hdr, \ 454 (char *) (userial_cb.p_rx_hdr+1)); 455 456 userial_cb.p_rx_hdr = NULL; 457 } 458 } 459 460 if(userial_cb.p_rx_hdr == NULL) 461 { 462 userial_cb.p_rx_hdr=(HC_BT_HDR *)utils_dequeue(&(userial_cb.rx_q)); 463 } 464 } while ((userial_cb.p_rx_hdr != NULL) && (total_len < len)); 465 466 return total_len; 467} 468 469/******************************************************************************* 470** 471** Function userial_write 472** 473** Description Write data to the userial port 474** 475** Returns Number of bytes actually written to the userial port. This 476** may be less than len. 477** 478*******************************************************************************/ 479uint16_t userial_write(uint16_t msg_id, uint8_t *p_data, uint16_t len) 480{ 481 int ret, total = 0; 482 483 while(len != 0) 484 { 485#if defined(ENABLE_USERIAL_TIMING_LOGS) && (ENABLE_USERIAL_TIMING_LOGS==TRUE) 486 log_userial_tx_timing(len); 487#endif 488 ret = write(userial_cb.fd, p_data+total, len); 489 total += ret; 490 len -= ret; 491 } 492 493 return ((uint16_t)total); 494} 495 496/******************************************************************************* 497** 498** Function userial_close 499** 500** Description Close the userial port 501** 502** Returns None 503** 504*******************************************************************************/ 505void userial_close(void) 506{ 507 int result; 508 TRANSAC p_buf; 509 510 USERIALDBG("userial_close(fd:%d)", userial_cb.fd); 511 512 if (userial_running) 513 send_wakeup_signal(USERIAL_RX_EXIT); 514 515 if ((result=pthread_join(userial_cb.read_thread, NULL)) < 0) 516 ALOGE( "pthread_join() FAILED result:%d", result); 517 518 /* Calling vendor-specific part */ 519 if (bt_vnd_if) 520 bt_vnd_if->op(BT_VND_OP_USERIAL_CLOSE, NULL); 521 522 userial_cb.fd = -1; 523 524 if (bt_hc_cbacks) 525 { 526 while ((p_buf = utils_dequeue (&(userial_cb.rx_q))) != NULL) 527 { 528 bt_hc_cbacks->dealloc(p_buf, (char *) ((HC_BT_HDR *)p_buf+1)); 529 } 530 } 531} 532 533/******************************************************************************* 534** 535** Function userial_ioctl 536** 537** Description ioctl inteface 538** 539** Returns None 540** 541*******************************************************************************/ 542void userial_ioctl(userial_ioctl_op_t op, void *p_data) 543{ 544 switch(op) 545 { 546 case USERIAL_OP_RXFLOW_ON: 547 if (userial_running) 548 send_wakeup_signal(USERIAL_RX_FLOW_ON); 549 break; 550 551 case USERIAL_OP_RXFLOW_OFF: 552 if (userial_running) 553 send_wakeup_signal(USERIAL_RX_FLOW_OFF); 554 break; 555 556 case USERIAL_OP_INIT: 557 default: 558 break; 559 } 560} 561 562