blob: 4940f8ebef4e0f1acc31d8aa10b71c4b2a84d2c1
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 */ |
79 | typedef 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 | |
87 | static vnd_lpm_proc_cb_t lpm_proc_cb; |
88 | #endif |
89 | |
90 | /****************************************************************************** |
91 | ** Static variables |
92 | ******************************************************************************/ |
93 | |
94 | static uint8_t upio_state[UPIO_MAX_COUNT]; |
95 | static int rfkill_id = -1; |
96 | static int bt_emul_enable = 0; |
97 | static char *rfkill_state_path = NULL; |
98 | |
99 | /****************************************************************************** |
100 | ** Static functions |
101 | ******************************************************************************/ |
102 | |
103 | /* for friendly debugging outpout string */ |
104 | static char *lpm_mode[] = { |
105 | "UNKNOWN", |
106 | "disabled", |
107 | "enabled" |
108 | }; |
109 | |
110 | static char *lpm_state[] = { |
111 | "UNKNOWN", |
112 | "de-asserted", |
113 | "asserted" |
114 | }; |
115 | |
116 | /***************************************************************************** |
117 | ** Bluetooth On/Off Static Functions |
118 | *****************************************************************************/ |
119 | static 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 | |
131 | static 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 | |
145 | static 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 | *******************************************************************************/ |
193 | static 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 | *******************************************************************************/ |
213 | void 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 | *******************************************************************************/ |
230 | void 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 | *******************************************************************************/ |
251 | int 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 | *******************************************************************************/ |
325 | void 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 | UPIODBG("LPM asserted"); |
357 | buffer = '1'; |
358 | } |
359 | else |
360 | { |
361 | UPIODBG("LPM deasserted"); |
362 | buffer = '0'; |
363 | |
364 | // delete btwrite assertion holding timer |
365 | if (lpm_proc_cb.timer_created == TRUE) |
366 | { |
367 | timer_delete(lpm_proc_cb.timer_id); |
368 | lpm_proc_cb.timer_created = FALSE; |
369 | } |
370 | } |
371 | |
372 | if (write(fd, &buffer, 1) < 0) |
373 | { |
374 | ALOGE("upio_set : write(%s) failed: %s (%d)", |
375 | VENDOR_LPM_PROC_NODE, strerror(errno),errno); |
376 | } |
377 | else |
378 | { |
379 | if (action == UPIO_ASSERT) |
380 | { |
381 | // create btwrite assertion holding timer |
382 | if (lpm_proc_cb.timer_created == FALSE) |
383 | { |
384 | int status; |
385 | struct sigevent se; |
386 | |
387 | se.sigev_notify = SIGEV_THREAD; |
388 | se.sigev_value.sival_ptr = &lpm_proc_cb.timer_id; |
389 | se.sigev_notify_function = proc_btwrite_timeout; |
390 | se.sigev_notify_attributes = NULL; |
391 | |
392 | status = timer_create(CLOCK_MONOTONIC, &se, |
393 | &lpm_proc_cb.timer_id); |
394 | |
395 | if (status == 0) |
396 | lpm_proc_cb.timer_created = TRUE; |
397 | } |
398 | } |
399 | } |
400 | |
401 | if (fd >= 0) |
402 | close(fd); |
403 | #endif |
404 | break; |
405 | |
406 | case UPIO_BT_WAKE: |
407 | |
408 | if (upio_state[UPIO_BT_WAKE] == action) |
409 | { |
410 | UPIODBG("BT_WAKE is %s already", lpm_state[action]); |
411 | |
412 | #if (BT_WAKE_VIA_PROC == TRUE) |
413 | if (lpm_proc_cb.btwrite_active == TRUE) |
414 | /* |
415 | * The proc btwrite node could have not been updated for |
416 | * certain time already due to heavy downstream path flow. |
417 | * In this case, we want to explicity touch proc btwrite |
418 | * node to keep the bt_wake assertion in the LPM kernel |
419 | * driver. The current kernel bluesleep LPM code starts |
420 | * a 10sec internal in-activity timeout timer before it |
421 | * attempts to deassert BT_WAKE line. |
422 | */ |
423 | #endif |
424 | return; |
425 | } |
426 | |
427 | upio_state[UPIO_BT_WAKE] = action; |
428 | |
429 | /**************************************** |
430 | * !!! TODO !!! |
431 | * |
432 | * === Custom Porting Required === |
433 | * |
434 | * Platform dependent user-to-kernel |
435 | * interface is required to set output |
436 | * state of physical BT_WAKE pin. |
437 | ****************************************/ |
438 | #if (BT_WAKE_VIA_USERIAL_IOCTL == TRUE) |
439 | userial_vendor_ioctl( ( (action==UPIO_ASSERT) ? \ |
440 | USERIAL_OP_ASSERT_BT_WAKE : USERIAL_OP_DEASSERT_BT_WAKE),\ |
441 | NULL); |
442 | |
443 | #elif (BT_WAKE_VIA_PROC == TRUE) |
444 | |
445 | /* |
446 | * Kick proc btwrite node only at UPIO_ASSERT |
447 | */ |
448 | if (action == UPIO_DEASSERT) |
449 | { |
450 | UPIODBG("btwake deassertion"); |
451 | return; |
452 | } |
453 | |
454 | fd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY); |
455 | |
456 | if (fd < 0) |
457 | { |
458 | ALOGE("upio_set : open(%s) for write failed: %s (%d)", |
459 | VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno); |
460 | return; |
461 | } |
462 | |
463 | buffer = '1'; |
464 | |
465 | if (write(fd, &buffer, 1) < 0) |
466 | { |
467 | ALOGE("upio_set : write(%s) failed: %s (%d)", |
468 | VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno); |
469 | } |
470 | else |
471 | { |
472 | lpm_proc_cb.btwrite_active = TRUE; |
473 | |
474 | if (lpm_proc_cb.timer_created == TRUE) |
475 | { |
476 | struct itimerspec ts; |
477 | |
478 | ts.it_value.tv_sec = PROC_BTWRITE_TIMER_TIMEOUT_MS/1000; |
479 | ts.it_value.tv_nsec = 1000*(PROC_BTWRITE_TIMER_TIMEOUT_MS%1000); |
480 | ts.it_interval.tv_sec = 0; |
481 | ts.it_interval.tv_nsec = 0; |
482 | |
483 | timer_settime(lpm_proc_cb.timer_id, 0, &ts, 0); |
484 | } |
485 | } |
486 | //ms_delay(10); |
487 | UPIODBG("proc btwrite assertion"); |
488 | |
489 | if (fd >= 0) |
490 | close(fd); |
491 | #endif |
492 | break; |
493 | |
494 | case UPIO_HOST_WAKE: |
495 | UPIODBG("upio_set: UPIO_HOST_WAKE"); |
496 | break; |
497 | } |
498 | } |
499 | |
500 | |
501 |