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