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