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 */ |
80 | typedef 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 | |
88 | static vnd_lpm_proc_cb_t lpm_proc_cb; |
89 | #endif |
90 | |
91 | /****************************************************************************** |
92 | ** Static variables |
93 | ******************************************************************************/ |
94 | |
95 | static uint8_t upio_state[UPIO_MAX_COUNT]; |
96 | static int rfkill_id = -1; |
97 | static int bt_emul_enable = 0; |
98 | static char *rfkill_state_path = NULL; |
99 | |
100 | /****************************************************************************** |
101 | ** Static functions |
102 | ******************************************************************************/ |
103 | |
104 | /* for friendly debugging outpout string */ |
105 | static char *lpm_mode[] = { |
106 | "UNKNOWN", |
107 | "disabled", |
108 | "enabled" |
109 | }; |
110 | |
111 | static char *lpm_state[] = { |
112 | "UNKNOWN", |
113 | "de-asserted", |
114 | "asserted" |
115 | }; |
116 | |
117 | /***************************************************************************** |
118 | ** Bluetooth On/Off Static Functions |
119 | *****************************************************************************/ |
120 | static 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 | |
132 | static 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 | |
146 | static 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 | *******************************************************************************/ |
194 | static 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 | *****************************************************************************/ |
211 | void 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 | *******************************************************************************/ |
248 | void 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 | *******************************************************************************/ |
265 | void 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 | *******************************************************************************/ |
286 | int 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 | *******************************************************************************/ |
360 | void 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 |