• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }