summaryrefslogtreecommitdiff
path: root/src/upio.c (plain)
blob: 3f73482b58a160b687aa2951f27ba1629761e32d
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 UPIODBG("%s: proc btwrite assertion, buffer: %c, timer_armed %d %d",
511 __FUNCTION__, buffer, lpm_proc_cb.btwrite_active, lpm_proc_cb.timer_created);
512
513 if (fd >= 0)
514 close(fd);
515#endif
516
517 break;
518
519 case UPIO_HOST_WAKE:
520 UPIODBG("upio_set: UPIO_HOST_WAKE");
521 break;
522 }
523}
524
525
526