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:      upio.c
22 *
23 *  Description:   Contains I/O functions, like
24 *                      rfkill control
25 *                      BT_WAKE/HOST_WAKE control
26 *
27 ******************************************************************************/
28
29#define LOG_TAG "bt_upio"
30
31#include <utils/Log.h>
32#include <fcntl.h>
33#include <errno.h>
34#include <string.h>
35#include <cutils/properties.h>
36#include "bt_vendor_brcm.h"
37#include "upio.h"
38#include "userial_vendor.h"
39
40/******************************************************************************
41**  Constants & Macros
42******************************************************************************/
43
44#ifndef UPIO_DBG
45#define UPIO_DBG FALSE
46#endif
47
48#if (UPIO_DBG == TRUE)
49#define UPIODBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
50#else
51#define UPIODBG(param, ...) {}
52#endif
53
54/******************************************************************************
55**  Local type definitions
56******************************************************************************/
57
58#if (BT_WAKE_VIA_PROC == TRUE)
59
60/* proc fs node for enable/disable lpm mode */
61#ifndef VENDOR_LPM_PROC_NODE
62#define VENDOR_LPM_PROC_NODE "/proc/bluetooth/sleep/lpm"
63#endif
64
65/* proc fs node for notifying write request */
66#ifndef VENDOR_BTWRITE_PROC_NODE
67#define VENDOR_BTWRITE_PROC_NODE "/proc/bluetooth/sleep/btwrite"
68#endif
69
70/*
71 * Maximum btwrite assertion holding time without consecutive btwrite kicking.
72 * This value is correlative(shorter) to the in-activity timeout period set in
73 * the bluesleep LPM code. The current value used in bluesleep is 10sec.
74 */
75#ifndef PROC_BTWRITE_TIMER_TIMEOUT_MS
76#define PROC_BTWRITE_TIMER_TIMEOUT_MS   8000
77#endif
78
79/* lpm proc control block */
80typedef struct
81{
82    uint8_t btwrite_active;
83    uint8_t timer_created;
84    timer_t timer_id;
85    uint32_t timeout_ms;
86} vnd_lpm_proc_cb_t;
87
88static vnd_lpm_proc_cb_t lpm_proc_cb;
89#endif
90
91/******************************************************************************
92**  Static variables
93******************************************************************************/
94
95static uint8_t upio_state[UPIO_MAX_COUNT];
96static int rfkill_id = -1;
97static int bt_emul_enable = 0;
98static char *rfkill_state_path = NULL;
99
100/******************************************************************************
101**  Static functions
102******************************************************************************/
103
104/* for friendly debugging outpout string */
105static char *lpm_mode[] = {
106    "UNKNOWN",
107    "disabled",
108    "enabled"
109};
110
111static char *lpm_state[] = {
112    "UNKNOWN",
113    "de-asserted",
114    "asserted"
115};
116
117/*****************************************************************************
118**   Bluetooth On/Off Static Functions
119*****************************************************************************/
120static int is_emulator_context(void)
121{
122    char value[PROPERTY_VALUE_MAX];
123
124    property_get("ro.kernel.qemu", value, "0");
125    UPIODBG("is_emulator_context : %s", value);
126    if (strcmp(value, "1") == 0) {
127        return 1;
128    }
129    return 0;
130}
131
132static int is_rfkill_disabled(void)
133{
134    char value[PROPERTY_VALUE_MAX];
135
136    property_get("ro.rfkilldisabled", value, "0");
137    UPIODBG("is_rfkill_disabled ? [%s]", value);
138
139    if (strcmp(value, "1") == 0) {
140        return UPIO_BT_POWER_ON;
141    }
142
143    return UPIO_BT_POWER_OFF;
144}
145
146static int init_rfkill()
147{
148    char path[64];
149    char buf[16];
150    int fd, sz, id;
151
152    if (is_rfkill_disabled())
153        return -1;
154
155    for (id = 0; ; id++)
156    {
157        snprintf(path, sizeof(path), "/sys/class/rfkill/rfkill%d/type", id);
158        fd = open(path, O_RDONLY);
159        if (fd < 0)
160        {
161            ALOGE("init_rfkill : open(%s) failed: %s (%d)\n", \
162                 path, strerror(errno), errno);
163            return -1;
164        }
165
166        sz = read(fd, &buf, sizeof(buf));
167        close(fd);
168
169        if (sz >= 9 && memcmp(buf, "bluetooth", 9) == 0)
170        {
171            rfkill_id = id;
172            break;
173        }
174    }
175
176    asprintf(&rfkill_state_path, "/sys/class/rfkill/rfkill%d/state", rfkill_id);
177    return 0;
178}
179
180/*****************************************************************************
181**   LPM Static Functions
182*****************************************************************************/
183
184#if (BT_WAKE_VIA_PROC == TRUE)
185/*******************************************************************************
186**
187** Function        proc_btwrite_timeout
188**
189** Description     Timeout thread of proc/.../btwrite assertion holding timer
190**
191** Returns         None
192**
193*******************************************************************************/
194static void proc_btwrite_timeout(union sigval arg)
195{
196    UPIODBG("..%s..", __FUNCTION__);
197    lpm_proc_cb.btwrite_active = FALSE;
198    /* drive LPM down; this timer should fire only when BT is awake; */
199    upio_set(UPIO_BT_WAKE, UPIO_DEASSERT, 1);
200}
201
202/******************************************************************************
203 **
204 ** Function      upio_start_stop_timer
205 **
206 ** Description   Arm user space timer in case lpm is left asserted
207 **
208 ** Returns       None
209 **
210 *****************************************************************************/
211void upio_start_stop_timer(int action) {
212    struct itimerspec ts;
213
214    if (action == UPIO_ASSERT) {
215        lpm_proc_cb.btwrite_active = TRUE;
216        if (lpm_proc_cb.timer_created == TRUE) {
217            ts.it_value.tv_sec = PROC_BTWRITE_TIMER_TIMEOUT_MS/1000;
218            ts.it_value.tv_nsec = 1000000*(PROC_BTWRITE_TIMER_TIMEOUT_MS%1000);
219            ts.it_interval.tv_sec = 0;
220            ts.it_interval.tv_nsec = 0;
221        }
222    } else {
223        /* unarm timer if writing 0 to lpm; reduce unnecessary user space wakeup */
224        memset(&ts, 0, sizeof(ts));
225    }
226
227    if (timer_settime(lpm_proc_cb.timer_id, 0, &ts, 0) == 0) {
228        UPIODBG("%s : timer_settime success", __FUNCTION__);
229    } else {
230        UPIODBG("%s : timer_settime failed", __FUNCTION__);
231    }
232}
233#endif
234
235/*****************************************************************************
236**   UPIO Interface Functions
237*****************************************************************************/
238
239/*******************************************************************************
240**
241** Function        upio_init
242**
243** Description     Initialization
244**
245** Returns         None
246**
247*******************************************************************************/
248void upio_init(void)
249{
250    memset(upio_state, UPIO_UNKNOWN, UPIO_MAX_COUNT);
251#if (BT_WAKE_VIA_PROC == TRUE)
252    memset(&lpm_proc_cb, 0, sizeof(vnd_lpm_proc_cb_t));
253#endif
254}
255
256/*******************************************************************************
257**
258** Function        upio_cleanup
259**
260** Description     Clean up
261**
262** Returns         None
263**
264*******************************************************************************/
265void upio_cleanup(void)
266{
267#if (BT_WAKE_VIA_PROC == TRUE)
268    if (lpm_proc_cb.timer_created == TRUE)
269        timer_delete(lpm_proc_cb.timer_id);
270
271    lpm_proc_cb.timer_created = FALSE;
272#endif
273}
274
275/*******************************************************************************
276**
277** Function        upio_set_bluetooth_power
278**
279** Description     Interact with low layer driver to set Bluetooth power
280**                 on/off.
281**
282** Returns         0  : SUCCESS or Not-Applicable
283**                 <0 : ERROR
284**
285*******************************************************************************/
286int upio_set_bluetooth_power(int on)
287{
288    int sz;
289    int fd = -1;
290    int ret = -1;
291    char buffer = '0';
292
293    switch(on)
294    {
295        case UPIO_BT_POWER_OFF:
296            buffer = '0';
297            break;
298
299        case UPIO_BT_POWER_ON:
300            buffer = '1';
301            break;
302    }
303
304    if (is_emulator_context())
305    {
306        /* if new value is same as current, return -1 */
307        if (bt_emul_enable == on)
308            return ret;
309
310        UPIODBG("set_bluetooth_power [emul] %d", on);
311
312        bt_emul_enable = on;
313        return 0;
314    }
315
316    /* check if we have rfkill interface */
317    if (is_rfkill_disabled())
318        return 0;
319
320    if (rfkill_id == -1)
321    {
322        if (init_rfkill())
323            return ret;
324    }
325
326    fd = open(rfkill_state_path, O_WRONLY);
327
328    if (fd < 0)
329    {
330        ALOGE("set_bluetooth_power : open(%s) for write failed: %s (%d)",
331            rfkill_state_path, strerror(errno), errno);
332        return ret;
333    }
334
335    sz = write(fd, &buffer, 1);
336
337    if (sz < 0) {
338        ALOGE("set_bluetooth_power : write(%s) failed: %s (%d)",
339            rfkill_state_path, strerror(errno),errno);
340    }
341    else
342        ret = 0;
343
344    if (fd >= 0)
345        close(fd);
346
347    return ret;
348}
349
350
351/*******************************************************************************
352**
353** Function        upio_set
354**
355** Description     Set i/o based on polarity
356**
357** Returns         None
358**
359*******************************************************************************/
360void upio_set(uint8_t pio, uint8_t action, uint8_t polarity)
361{
362    int rc;
363#if (BT_WAKE_VIA_PROC == TRUE)
364    int fd = -1;
365    char buffer;
366#endif
367
368    UPIODBG("%s : pio %d action %d, polarity %d", __FUNCTION__, pio, action, polarity);
369
370    switch (pio)
371    {
372        case UPIO_LPM_MODE:
373            if (upio_state[UPIO_LPM_MODE] == action)
374            {
375                UPIODBG("LPM is %s already", lpm_mode[action]);
376                return;
377            }
378
379            upio_state[UPIO_LPM_MODE] = action;
380
381#if (BT_WAKE_VIA_PROC == TRUE)
382            fd = open(VENDOR_LPM_PROC_NODE, O_WRONLY);
383
384            if (fd < 0)
385            {
386                ALOGE("upio_set : open(%s) for write failed: %s (%d)",
387                        VENDOR_LPM_PROC_NODE, strerror(errno), errno);
388                return;
389            }
390
391            if (action == UPIO_ASSERT)
392            {
393                buffer = '1';
394            }
395            else
396            {
397                buffer = '0';
398
399                // delete btwrite assertion holding timer
400                if (lpm_proc_cb.timer_created == TRUE)
401                {
402                    timer_delete(lpm_proc_cb.timer_id);
403                    lpm_proc_cb.timer_created = FALSE;
404                }
405            }
406
407            if (write(fd, &buffer, 1) < 0)
408            {
409                ALOGE("upio_set : write(%s) failed: %s (%d)",
410                        VENDOR_LPM_PROC_NODE, strerror(errno),errno);
411            }
412#if (PROC_BTWRITE_TIMER_TIMEOUT_MS != 0)
413            else
414            {
415                if (action == UPIO_ASSERT)
416                {
417                    // create btwrite assertion holding timer
418                    if (lpm_proc_cb.timer_created == FALSE)
419                    {
420                        int status;
421                        struct sigevent se;
422
423                        se.sigev_notify = SIGEV_THREAD;
424                        se.sigev_value.sival_ptr = &lpm_proc_cb.timer_id;
425                        se.sigev_notify_function = proc_btwrite_timeout;
426                        se.sigev_notify_attributes = NULL;
427
428                        status = timer_create(CLOCK_MONOTONIC, &se,
429                                                &lpm_proc_cb.timer_id);
430
431                        if (status == 0)
432                            lpm_proc_cb.timer_created = TRUE;
433                    }
434                }
435            }
436#endif
437
438            if (fd >= 0)
439                close(fd);
440#endif
441            break;
442
443        case UPIO_BT_WAKE:
444            if (upio_state[UPIO_BT_WAKE] == action)
445            {
446                UPIODBG("BT_WAKE is %s already", lpm_state[action]);
447
448#if (BT_WAKE_VIA_PROC == TRUE)
449                if (lpm_proc_cb.btwrite_active == TRUE)
450                    /*
451                     * The proc btwrite node could have not been updated for
452                     * certain time already due to heavy downstream path flow.
453                     * In this case, we want to explicity touch proc btwrite
454                     * node to keep the bt_wake assertion in the LPM kernel
455                     * driver. The current kernel bluesleep LPM code starts
456                     * a 10sec internal in-activity timeout timer before it
457                     * attempts to deassert BT_WAKE line.
458                     */
459                    return;
460#else
461                return;
462#endif
463            }
464
465            upio_state[UPIO_BT_WAKE] = action;
466
467#if (BT_WAKE_VIA_USERIAL_IOCTL == TRUE)
468
469            userial_vendor_ioctl( ( (action==UPIO_ASSERT) ? \
470                      USERIAL_OP_ASSERT_BT_WAKE : USERIAL_OP_DEASSERT_BT_WAKE),\
471                      NULL);
472
473#elif (BT_WAKE_VIA_PROC == TRUE)
474
475            /*
476             *  Kick proc btwrite node only at UPIO_ASSERT
477             */
478#if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == FALSE)
479            if (action == UPIO_DEASSERT)
480                return;
481#endif
482            fd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY);
483
484            if (fd < 0)
485            {
486                ALOGE("upio_set : open(%s) for write failed: %s (%d)",
487                        VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno);
488                return;
489            }
490#if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == TRUE)
491            if (action == UPIO_DEASSERT)
492                buffer = '0';
493            else
494#endif
495                buffer = '1';
496
497            if (write(fd, &buffer, 1) < 0)
498            {
499                ALOGE("upio_set : write(%s) failed: %s (%d)",
500                        VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno);
501            }
502#if (PROC_BTWRITE_TIMER_TIMEOUT_MS != 0)
503            else
504            {
505                /* arm user space timer based on action */
506                upio_start_stop_timer(action);
507            }
508#endif
509
510#if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == TRUE)
511            lpm_proc_cb.btwrite_active = TRUE;
512#endif
513
514            UPIODBG("%s: proc btwrite assertion, buffer: %c, timer_armed %d %d",
515                    __FUNCTION__, buffer, lpm_proc_cb.btwrite_active, lpm_proc_cb.timer_created);
516
517            if (fd >= 0)
518                close(fd);
519#endif
520
521            break;
522
523        case UPIO_HOST_WAKE:
524            UPIODBG("upio_set: UPIO_HOST_WAKE");
525            break;
526    }
527}
528
529
530