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