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 */
88 typedef 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
95 static vnd_lpm_proc_cb_t lpm_proc_cb;
96 #endif
97
98 /******************************************************************************
99 ** Static variables
100 ******************************************************************************/
101
102 static uint8_t upio_state[UPIO_MAX_COUNT];
103 static int rfkill_id = -1;
104 static int bt_emul_enable = 0;
105 static char rfkill_state_path[128];
106
107 /******************************************************************************
108 ** Static functions
109 ******************************************************************************/
110
111 /* for friendly debugging outpout string */
112 static char *lpm_mode[] = {
113 "UNKNOWN",
114 "disabled",
115 "enabled"
116 };
117
118 static char *lpm_state[] = {
119 "UNKNOWN",
120 "de-asserted",
121 "asserted"
122 };
123
124 /*****************************************************************************
125 ** Bluetooth On/Off Static Functions
126 *****************************************************************************/
is_emulator_context(void)127 static int is_emulator_context(void)
128 {
129 return 0;
130 }
131
is_rfkill_disabled(void)132 static int is_rfkill_disabled(void)
133 {
134 return UPIO_BT_POWER_OFF;
135 }
136
init_rfkill(void)137 static 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 *******************************************************************************/
proc_btwrite_timeout(union sigval arg)182 static 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 *****************************************************************************/
upio_start_stop_timer(int action)199 void 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 *******************************************************************************/
upio_init(void)238 void 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 *******************************************************************************/
upio_cleanup(void)255 void 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 *******************************************************************************/
upio_set_bluetooth_power(int on)276 int 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 *******************************************************************************/
upio_set(uint8_t pio,uint8_t action,uint8_t polarity)348 void 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 UPIODBG("%s : pio %d action %d, polarity %d", __FUNCTION__, pio, action, polarity);
356 switch (pio) {
357 case UPIO_LPM_MODE:
358 if (upio_state[UPIO_LPM_MODE] == action) {
359 UPIODBG("LPM is %s already", lpm_mode[action]);
360 return;
361 }
362 upio_state[UPIO_LPM_MODE] = action;
363 #if (BT_WAKE_VIA_PROC == TRUE)
364 fd = open(VENDOR_LPM_PROC_NODE, O_WRONLY);
365 if (fd < 0) {
366 LOGE("upio_set : open(%s) for write failed: %s (%d)",
367 VENDOR_LPM_PROC_NODE, strerror(errno), errno);
368 return;
369 }
370 if (action == UPIO_ASSERT) {
371 buffer = '1';
372 } else {
373 buffer = '0';
374 // delete btwrite assertion holding timer
375 if (lpm_proc_cb.timer_created == TRUE) {
376 timer_delete(lpm_proc_cb.timer_id);
377 lpm_proc_cb.timer_created = FALSE;
378 }
379 }
380 if (write(fd, &buffer, 1) < 0) {
381 LOGE("upio_set : write(%s) failed: %s (%d)",
382 VENDOR_LPM_PROC_NODE, strerror(errno), errno);
383 #if (PROC_BTWRITE_TIMER_TIMEOUT_MS != 0)
384 } else {
385 if (action == UPIO_ASSERT) {
386 // create btwrite assertion holding timer
387 if (lpm_proc_cb.timer_created == FALSE) {
388 int status;
389 struct sigevent se;
390 se.sigev_notify = SIGEV_THREAD;
391 se.sigev_value.sival_ptr = &lpm_proc_cb.timer_id;
392 se.sigev_notify_function = proc_btwrite_timeout;
393 se.sigev_notify_attributes = NULL;
394 status = timer_create(CLOCK_MONOTONIC, &se,
395 &lpm_proc_cb.timer_id);
396 if (status == 0)
397 lpm_proc_cb.timer_created = TRUE;
398 }
399 }
400 }
401 #else
402 }
403 #endif
404 if (fd >= 0)
405 close(fd);
406 #endif
407 break;
408 case UPIO_BT_WAKE:
409 if (upio_state[UPIO_BT_WAKE] == action) {
410 UPIODBG("BT_WAKE is %s already", lpm_state[action]);
411 #if (BT_WAKE_VIA_PROC == TRUE)
412 if (lpm_proc_cb.btwrite_active == TRUE) {
413 /*
414 * The proc btwrite node could have not been updated for
415 * certain time already due to heavy downstream path flow.
416 * In this case, we want to explicity touch proc btwrite
417 * node to keep the bt_wake assertion in the LPM kernel
418 * driver. The current kernel bluesleep LPM code starts
419 * a 10sec internal in-activity timeout timer before it
420 * attempts to deassert BT_WAKE line.
421 */
422 return;
423 }
424 #else
425 return;
426 #endif
427 }
428 upio_state[UPIO_BT_WAKE] = action;
429 #if (BT_WAKE_VIA_USERIAL_IOCTL == TRUE)
430 userial_vendor_ioctl(( (action == UPIO_ASSERT) ? \
431 USERIAL_OP_ASSERT_BT_WAKE : USERIAL_OP_DEASSERT_BT_WAKE), \
432 NULL);
433 #elif (BT_WAKE_VIA_PROC == TRUE)
434 /*
435 * Kick proc btwrite node only at UPIO_ASSERT
436 */
437 #if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == FALSE)
438 if (action == UPIO_DEASSERT)
439 return;
440 #endif
441 fd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY);
442 if (fd < 0) {
443 LOGE("upio_set : open(%s) for write failed: %s (%d)",
444 VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno);
445 return;
446 }
447 #if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == TRUE)
448 if (action == UPIO_DEASSERT) {
449 buffer = '0';
450 } else {
451 #endif
452 buffer = '1';
453 #if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == TRUE)
454 }
455 #endif
456 if (write(fd, &buffer, 1) < 0) {
457 LOGE("upio_set : write(%s) failed: %s (%d)",
458 VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno);
459 #if (PROC_BTWRITE_TIMER_TIMEOUT_MS != 0)
460 } else {
461 /* arm user space timer based on action */
462 upio_start_stop_timer(action);
463 }
464 #else
465 }
466 #endif
467
468 #if (BT_WAKE_VIA_PROC_NOTIFY_DEASSERT == TRUE)
469 lpm_proc_cb.btwrite_active = TRUE;
470 #endif
471 UPIODBG("%s: proc btwrite assertion, buffer: %c, timer_armed %d %d",
472 __FUNCTION__, buffer, lpm_proc_cb.btwrite_active, lpm_proc_cb.timer_created);
473
474 if (fd >= 0)
475 close(fd);
476 #endif
477 break;
478 case UPIO_HOST_WAKE:
479 UPIODBG("upio_set: UPIO_HOST_WAKE");
480 break;
481 default:
482 // nothing to do
483 break;
484 }
485 }