blob: 8c87793f311bbb41cb642a881292f702045f7583
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 | #include "sysbridge.h" |
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 | sz = -1;//initial |
152 | if (is_rfkill_disabled()) |
153 | return -1; |
154 | |
155 | for (id = 0; ; id++) |
156 | { |
157 | |
158 | snprintf(path, sizeof(path), "/sys/class/rfkill/rfkill%d/type", id); |
159 | fd = open(path, O_RDONLY); |
160 | if (fd < 0) |
161 | { |
162 | ALOGE("init_rfkill : open(%s) failed: %s (%d)\n", \ |
163 | path, strerror(errno), errno); |
164 | ALOGE("open failed,use syscontrol\n"); |
165 | sz = amSystemWriteGetProperty(path,&buf); |
166 | goto end; |
167 | //return -1; |
168 | } |
169 | |
170 | sz = read(fd, &buf, sizeof(buf)); |
171 | close(fd); |
172 | end:if (sz >= 9 && memcmp(buf, "bluetooth", 9) == 0) |
173 | { |
174 | ALOGE("break"); |
175 | rfkill_id = id; |
176 | break; |
177 | } |
178 | else if (sz == -1) |
179 | { |
180 | ALOGE("init_rfkill : open(%s) failed: %s (%d)\n", \ |
181 | path, strerror(errno), errno); |
182 | return -1; |
183 | } |
184 | } |
185 | |
186 | asprintf(&rfkill_state_path, "/sys/class/rfkill/rfkill%d/state", rfkill_id); |
187 | return 0; |
188 | } |
189 | |
190 | /***************************************************************************** |
191 | ** LPM Static Functions |
192 | *****************************************************************************/ |
193 | |
194 | #if (BT_WAKE_VIA_PROC == TRUE) |
195 | /******************************************************************************* |
196 | ** |
197 | ** Function proc_btwrite_timeout |
198 | ** |
199 | ** Description Timeout thread of proc/.../btwrite assertion holding timer |
200 | ** |
201 | ** Returns None |
202 | ** |
203 | *******************************************************************************/ |
204 | static void proc_btwrite_timeout(union sigval arg) |
205 | { |
206 | UPIODBG("..%s..", __FUNCTION__); |
207 | lpm_proc_cb.btwrite_active = FALSE; |
208 | /* drive LPM down; this timer should fire only when BT is awake; */ |
209 | upio_set(UPIO_BT_WAKE, UPIO_DEASSERT, 1); |
210 | } |
211 | |
212 | /****************************************************************************** |
213 | ** |
214 | ** Function upio_start_stop_timer |
215 | ** |
216 | ** Description Arm user space timer in case lpm is left asserted |
217 | ** |
218 | ** Returns None |
219 | ** |
220 | *****************************************************************************/ |
221 | void upio_start_stop_timer(int action) { |
222 | struct itimerspec ts; |
223 | |
224 | if (action == UPIO_ASSERT) { |
225 | lpm_proc_cb.btwrite_active = TRUE; |
226 | if (lpm_proc_cb.timer_created == TRUE) { |
227 | ts.it_value.tv_sec = PROC_BTWRITE_TIMER_TIMEOUT_MS/1000; |
228 | ts.it_value.tv_nsec = 1000000*(PROC_BTWRITE_TIMER_TIMEOUT_MS%1000); |
229 | ts.it_interval.tv_sec = 0; |
230 | ts.it_interval.tv_nsec = 0; |
231 | } |
232 | } else { |
233 | /* unarm timer if writing 0 to lpm; reduce unnecessary user space wakeup */ |
234 | memset(&ts, 0, sizeof(ts)); |
235 | } |
236 | |
237 | if (timer_settime(lpm_proc_cb.timer_id, 0, &ts, 0) == 0) { |
238 | UPIODBG("%s : timer_settime success", __FUNCTION__); |
239 | } else { |
240 | UPIODBG("%s : timer_settime failed", __FUNCTION__); |
241 | } |
242 | } |
243 | #endif |
244 | |
245 | /***************************************************************************** |
246 | ** UPIO Interface Functions |
247 | *****************************************************************************/ |
248 | |
249 | /******************************************************************************* |
250 | ** |
251 | ** Function upio_init |
252 | ** |
253 | ** Description Initialization |
254 | ** |
255 | ** Returns None |
256 | ** |
257 | *******************************************************************************/ |
258 | void upio_init(void) |
259 | { |
260 | memset(upio_state, UPIO_UNKNOWN, UPIO_MAX_COUNT); |
261 | #if (BT_WAKE_VIA_PROC == TRUE) |
262 | memset(&lpm_proc_cb, 0, sizeof(vnd_lpm_proc_cb_t)); |
263 | #endif |
264 | } |
265 | |
266 | /******************************************************************************* |
267 | ** |
268 | ** Function upio_cleanup |
269 | ** |
270 | ** Description Clean up |
271 | ** |
272 | ** Returns None |
273 | ** |
274 | *******************************************************************************/ |
275 | void upio_cleanup(void) |
276 | { |
277 | #if (BT_WAKE_VIA_PROC == TRUE) |
278 | if (lpm_proc_cb.timer_created == TRUE) |
279 | timer_delete(lpm_proc_cb.timer_id); |
280 | |
281 | lpm_proc_cb.timer_created = FALSE; |
282 | #endif |
283 | } |
284 | |
285 | /******************************************************************************* |
286 | ** |
287 | ** Function upio_set_bluetooth_power |
288 | ** |
289 | ** Description Interact with low layer driver to set Bluetooth power |
290 | ** on/off. |
291 | ** |
292 | ** Returns 0 : SUCCESS or Not-Applicable |
293 | ** <0 : ERROR |
294 | ** |
295 | *******************************************************************************/ |
296 | int upio_set_bluetooth_power(int on) |
297 | { |
298 | int sz; |
299 | int fd = -1; |
300 | int ret = -1; |
301 | char buffer = '0'; |
302 | |
303 | switch(on) |
304 | { |
305 | case UPIO_BT_POWER_OFF: |
306 | buffer = '0'; |
307 | break; |
308 | |
309 | case UPIO_BT_POWER_ON: |
310 | buffer = '1'; |
311 | break; |
312 | } |
313 | |
314 | if (is_emulator_context()) |
315 | { |
316 | /* if new value is same as current, return -1 */ |
317 | if (bt_emul_enable == on) |
318 | return ret; |
319 | |
320 | UPIODBG("set_bluetooth_power [emul] %d", on); |
321 | |
322 | bt_emul_enable = on; |
323 | return 0; |
324 | } |
325 | |
326 | /* check if we have rfkill interface */ |
327 | if (is_rfkill_disabled()) |
328 | return 0; |
329 | |
330 | if (rfkill_id == -1) |
331 | { |
332 | if (init_rfkill()) |
333 | return ret; |
334 | } |
335 | |
336 | fd = open(rfkill_state_path, O_WRONLY); |
337 | |
338 | if (fd < 0) |
339 | { |
340 | ALOGE("set_bluetooth_power : open(%s) for write failed: %s (%d)", |
341 | rfkill_state_path, strerror(errno), errno); |
342 | sz = amSystemWriteSetProperty(rfkill_state_path, &buffer, 1); |
343 | goto last; |
344 | //return ret; |
345 | } |
346 | |
347 | sz = write(fd, &buffer, 1); |
348 | if (fd >= 0) |
349 | close(fd); |
350 | last: if (sz < 0) { |
351 | ALOGE("set_bluetooth_power : write(%s) failed: %s (%d)", |
352 | rfkill_state_path, strerror(errno),errno); |
353 | } |
354 | else |
355 | ret = 0; |
356 | |
357 | return ret; |
358 | } |
359 | |
360 | |
361 | /******************************************************************************* |
362 | ** |
363 | ** Function upio_set |
364 | ** |
365 | ** Description Set i/o based on polarity |
366 | ** |
367 | ** Returns None |
368 | ** |
369 | *******************************************************************************/ |
370 | void upio_set(uint8_t pio, uint8_t action, uint8_t polarity) |
371 | { |
372 | int rc; |
373 | #if (BT_WAKE_VIA_PROC == TRUE) |
374 | int fd = -1; |
375 | char buffer; |
376 | #endif |
377 | |
378 | UPIODBG("%s : pio %d action %d, polarity %d", __FUNCTION__, pio, action, polarity); |
379 | |
380 | switch (pio) |
381 | { |
382 | case UPIO_LPM_MODE: |
383 | if (upio_state[UPIO_LPM_MODE] == action) |
384 | { |
385 | UPIODBG("LPM is %s already", lpm_mode[action]); |
386 | return; |
387 | } |
388 | |
389 | upio_state[UPIO_LPM_MODE] = action; |
390 | |
391 | #if (BT_WAKE_VIA_PROC == TRUE) |
392 | fd = open(VENDOR_LPM_PROC_NODE, O_WRONLY); |
393 | |
394 | if (fd < 0) |
395 | { |
396 | ALOGE("upio_set : open(%s) for write failed: %s (%d)", |
397 | VENDOR_LPM_PROC_NODE, strerror(errno), errno); |
398 | return; |
399 | } |
400 | |
401 | if (action == UPIO_ASSERT) |
402 | { |
403 | buffer = '1'; |
404 | } |
405 | else |
406 | { |
407 | buffer = '0'; |
408 | |
409 | // delete btwrite assertion holding timer |
410 | if (lpm_proc_cb.timer_created == TRUE) |
411 | { |
412 | timer_delete(lpm_proc_cb.timer_id); |
413 | lpm_proc_cb.timer_created = FALSE; |
414 | } |
415 | } |
416 | |
417 | if (write(fd, &buffer, 1) < 0) |
418 | { |
419 | ALOGE("upio_set : write(%s) failed: %s (%d)", |
420 | VENDOR_LPM_PROC_NODE, strerror(errno),errno); |
421 | } |
422 | #if (PROC_BTWRITE_TIMER_TIMEOUT_MS != 0) |
423 | else |
424 | { |
425 | if (action == UPIO_ASSERT) |
426 | { |
427 | // create btwrite assertion holding timer |
428 | if (lpm_proc_cb.timer_created == FALSE) |
429 | { |
430 | int status; |
431 | struct sigevent se; |
432 | |
433 | se.sigev_notify = SIGEV_THREAD; |
434 | se.sigev_value.sival_ptr = &lpm_proc_cb.timer_id; |
435 | se.sigev_notify_function = proc_btwrite_timeout; |
436 | se.sigev_notify_attributes = NULL; |
437 | |
438 | status = timer_create(CLOCK_MONOTONIC, &se, |
439 | &lpm_proc_cb.timer_id); |
440 | |
441 | if (status == 0) |
442 | lpm_proc_cb.timer_created = TRUE; |
443 | } |
444 | } |
445 | } |
446 | #endif |
447 | |
448 | if (fd >= 0) |
449 | close(fd); |
450 | #endif |
451 | break; |
452 | |
453 | case UPIO_BT_WAKE: |
454 | if (upio_state[UPIO_BT_WAKE] == action) |
455 | { |
456 | UPIODBG("BT_WAKE is %s already", lpm_state[action]); |
457 | |
458 | #if (BT_WAKE_VIA_PROC == TRUE) |
459 | if (lpm_proc_cb.btwrite_active == TRUE) |
460 | /* |
461 | * The proc btwrite node could have not been updated for |
462 | * certain time already due to heavy downstream path flow. |
463 | * In this case, we want to explicity touch proc btwrite |
464 | * node to keep the bt_wake assertion in the LPM kernel |
465 | * driver. The current kernel bluesleep LPM code starts |
466 | * a 10sec internal in-activity timeout timer before it |
467 | * attempts to deassert BT_WAKE line. |
468 | */ |
469 | return; |
470 | #else |
471 | return; |
472 | #endif |
473 | } |
474 | |
475 | upio_state[UPIO_BT_WAKE] = action; |
476 | |
477 | #if (BT_WAKE_VIA_USERIAL_IOCTL == TRUE) |
478 | |
479 | userial_vendor_ioctl( ( (action==UPIO_ASSERT) ? \ |
480 | USERIAL_OP_ASSERT_BT_WAKE : USERIAL_OP_DEASSERT_BT_WAKE),\ |
481 | NULL); |
482 | |
483 | #elif (BT_WAKE_VIA_PROC == TRUE) |
484 | |
485 | /* |
486 | * Kick proc btwrite node only at UPIO_ASSERT |
487 | */ |
488 | #if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == FALSE) |
489 | if (action == UPIO_DEASSERT) |
490 | return; |
491 | #endif |
492 | fd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY); |
493 | |
494 | if (fd < 0) |
495 | { |
496 | ALOGE("upio_set : open(%s) for write failed: %s (%d)", |
497 | VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno); |
498 | return; |
499 | } |
500 | #if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == TRUE) |
501 | if (action == UPIO_DEASSERT) |
502 | buffer = '0'; |
503 | else |
504 | #endif |
505 | buffer = '1'; |
506 | |
507 | if (write(fd, &buffer, 1) < 0) |
508 | { |
509 | ALOGE("upio_set : write(%s) failed: %s (%d)", |
510 | VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno); |
511 | } |
512 | #if (PROC_BTWRITE_TIMER_TIMEOUT_MS != 0) |
513 | else |
514 | { |
515 | /* arm user space timer based on action */ |
516 | upio_start_stop_timer(action); |
517 | } |
518 | #endif |
519 | |
520 | UPIODBG("%s: proc btwrite assertion, buffer: %c, timer_armed %d %d", |
521 | __FUNCTION__, buffer, lpm_proc_cb.btwrite_active, lpm_proc_cb.timer_created); |
522 | |
523 | if (fd >= 0) |
524 | close(fd); |
525 | #endif |
526 | |
527 | break; |
528 | |
529 | case UPIO_HOST_WAKE: |
530 | UPIODBG("upio_set: UPIO_HOST_WAKE"); |
531 | break; |
532 | } |
533 | } |
534 | |
535 | |
536 |