1 /******************************************************************************
2  *
3  *  Copyright (C) 2014 Google, Inc.
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 #include <stdlib.h>
19 #include <string.h>
20 #include <stdbool.h>
21 #include "osi/alarm.h"
22 #include "osi/allocator.h"
23 #include "osi/list.h"
24 #include "esp_timer.h"
25 #include "btc/btc_task.h"
26 #include "btc/btc_alarm.h"
27 #include "osi/mutex.h"
28 #include "bt_common.h"
29 
30 typedef struct alarm_t {
31     /* timer id point to here */
32     esp_timer_handle_t alarm_hdl;
33     osi_alarm_callback_t cb;
34     void *cb_data;
35     int64_t deadline_us;
36 } osi_alarm_t;
37 
38 enum {
39     ALARM_STATE_IDLE,
40     ALARM_STATE_OPEN,
41 };
42 
43 static osi_mutex_t alarm_mutex;
44 static int alarm_state;
45 
46 #if (BT_BLE_DYNAMIC_ENV_MEMORY == FALSE)
47 static struct alarm_t alarm_cbs[ALARM_CBS_NUM];
48 #else
49 static struct alarm_t *alarm_cbs;
50 #endif
51 
52 static osi_alarm_err_t alarm_free(osi_alarm_t *alarm);
53 static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic);
54 
osi_alarm_create_mux(void)55 int osi_alarm_create_mux(void)
56 {
57     if (alarm_state != ALARM_STATE_IDLE) {
58         OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
59         return -1;
60     }
61     osi_mutex_new(&alarm_mutex);
62     return 0;
63 }
64 
osi_alarm_delete_mux(void)65 int osi_alarm_delete_mux(void)
66 {
67     if (alarm_state != ALARM_STATE_IDLE) {
68         OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
69         return -1;
70     }
71     osi_mutex_free(&alarm_mutex);
72     return 0;
73 }
74 
osi_alarm_init(void)75 void osi_alarm_init(void)
76 {
77     assert(alarm_mutex != NULL);
78 
79     osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
80     if (alarm_state != ALARM_STATE_IDLE) {
81         OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
82         goto end;
83     }
84 #if (BT_BLE_DYNAMIC_ENV_MEMORY == TRUE)
85     if ((alarm_cbs = (osi_alarm_t *)osi_malloc(sizeof(osi_alarm_t) * ALARM_CBS_NUM)) == NULL) {
86         OSI_TRACE_ERROR("%s, malloc failed\n", __func__);
87         goto end;
88     }
89 #endif
90 
91     memset(alarm_cbs, 0x00, sizeof(osi_alarm_t) * ALARM_CBS_NUM);
92     alarm_state = ALARM_STATE_OPEN;
93 
94 end:
95     osi_mutex_unlock(&alarm_mutex);
96 }
97 
osi_alarm_deinit(void)98 void osi_alarm_deinit(void)
99 {
100     assert(alarm_mutex != NULL);
101 
102     osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
103     if (alarm_state != ALARM_STATE_OPEN) {
104         OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
105         goto end;
106     }
107 
108     for (int i = 0; i < ALARM_CBS_NUM; i++) {
109         if (alarm_cbs[i].alarm_hdl != NULL) {
110             alarm_free(&alarm_cbs[i]);
111         }
112     }
113 
114 #if (BT_BLE_DYNAMIC_ENV_MEMORY == TRUE)
115     osi_free(alarm_cbs);
116     alarm_cbs = NULL;
117 #endif
118 
119     alarm_state = ALARM_STATE_IDLE;
120 
121 end:
122     osi_mutex_unlock(&alarm_mutex);
123 }
124 
alarm_cbs_lookfor_available(void)125 static struct alarm_t *alarm_cbs_lookfor_available(void)
126 {
127     int i;
128 
129     for (i = 0; i < ALARM_CBS_NUM; i++) {
130         if (alarm_cbs[i].alarm_hdl == NULL) { //available
131             OSI_TRACE_DEBUG("%s %d %p\n", __func__, i, &alarm_cbs[i]);
132             return &alarm_cbs[i];
133         }
134     }
135 
136     return NULL;
137 }
138 
alarm_cb_handler(struct alarm_t * alarm)139 static void alarm_cb_handler(struct alarm_t *alarm)
140 {
141     OSI_TRACE_DEBUG("TimerID %p\n", alarm);
142     if (alarm_state != ALARM_STATE_OPEN) {
143         OSI_TRACE_WARNING("%s, invalid state %d\n", __func__, alarm_state);
144         return;
145     }
146     btc_msg_t msg = {0};
147     btc_alarm_args_t arg;
148     msg.sig = BTC_SIG_API_CALL;
149     msg.pid = BTC_PID_ALARM;
150     arg.cb = alarm->cb;
151     arg.cb_data = alarm->cb_data;
152     btc_transfer_context(&msg, &arg, sizeof(btc_alarm_args_t), NULL);
153 }
154 
osi_alarm_new(const char * alarm_name,osi_alarm_callback_t callback,void * data,period_ms_t timer_expire)155 osi_alarm_t *osi_alarm_new(const char *alarm_name, osi_alarm_callback_t callback, void *data, period_ms_t timer_expire)
156 {
157     assert(alarm_mutex != NULL);
158 
159     struct alarm_t *timer_id = NULL;
160 
161     osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
162     if (alarm_state != ALARM_STATE_OPEN) {
163         OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
164         timer_id = NULL;
165         goto end;
166     }
167 
168     timer_id = alarm_cbs_lookfor_available();
169 
170     if (!timer_id) {
171         OSI_TRACE_ERROR("%s alarm_cbs exhausted\n", __func__);
172         timer_id = NULL;
173         goto end;
174     }
175 
176     esp_timer_create_args_t tca = {0};
177     tca.callback = (esp_timer_cb_t)alarm_cb_handler;
178     tca.arg = timer_id;
179     tca.dispatch_method = ESP_TIMER_TASK;
180     tca.name = alarm_name;
181 
182     timer_id->cb = callback;
183     timer_id->cb_data = data;
184     timer_id->deadline_us = 0;
185 
186     esp_err_t stat = esp_timer_create(&tca, &timer_id->alarm_hdl);
187     if (stat != ESP_OK) {
188         OSI_TRACE_ERROR("%s failed to create timer, err 0x%x\n", __func__, stat);
189         timer_id = NULL;
190         goto end;
191     }
192 
193 end:
194     osi_mutex_unlock(&alarm_mutex);
195     return timer_id;
196 }
197 
alarm_free(osi_alarm_t * alarm)198 static osi_alarm_err_t alarm_free(osi_alarm_t *alarm)
199 {
200     if (!alarm || alarm->alarm_hdl == NULL) {
201         OSI_TRACE_ERROR("%s null\n", __func__);
202         return OSI_ALARM_ERR_INVALID_ARG;
203     }
204     esp_timer_stop(alarm->alarm_hdl);
205     esp_err_t stat = esp_timer_delete(alarm->alarm_hdl);
206     if (stat != ESP_OK) {
207         OSI_TRACE_ERROR("%s failed to delete timer, err 0x%x\n", __func__, stat);
208         return OSI_ALARM_ERR_FAIL;
209     }
210 
211     memset(alarm, 0, sizeof(osi_alarm_t));
212     return OSI_ALARM_ERR_PASS;
213 }
214 
osi_alarm_free(osi_alarm_t * alarm)215 void osi_alarm_free(osi_alarm_t *alarm)
216 {
217     assert(alarm_mutex != NULL);
218 
219     osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
220     if (alarm_state != ALARM_STATE_OPEN) {
221         OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
222         goto end;
223     }
224     alarm_free(alarm);
225 
226 end:
227     osi_mutex_unlock(&alarm_mutex);
228     return;
229 }
230 
alarm_set(osi_alarm_t * alarm,period_ms_t timeout,bool is_periodic)231 static osi_alarm_err_t alarm_set(osi_alarm_t *alarm, period_ms_t timeout, bool is_periodic)
232 {
233     assert(alarm_mutex != NULL);
234 
235     osi_alarm_err_t ret = OSI_ALARM_ERR_PASS;
236     osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
237     if (alarm_state != ALARM_STATE_OPEN) {
238         OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
239         ret = OSI_ALARM_ERR_INVALID_STATE;
240         goto end;
241     }
242 
243     if (!alarm || alarm->alarm_hdl == NULL) {
244         OSI_TRACE_ERROR("%s null\n", __func__);
245         ret = OSI_ALARM_ERR_INVALID_ARG;
246         goto end;
247     }
248 
249     int64_t timeout_us = 1000 * (int64_t)timeout;
250     esp_err_t stat;
251     if (is_periodic) {
252         stat = esp_timer_start_periodic(alarm->alarm_hdl, (uint64_t)timeout_us);
253     } else {
254         stat = esp_timer_start_once(alarm->alarm_hdl, (uint64_t)timeout_us);
255     }
256     if (stat != ESP_OK) {
257         OSI_TRACE_ERROR("%s failed to start timer, err 0x%x\n", __func__, stat);
258         ret = OSI_ALARM_ERR_FAIL;
259         goto end;
260     }
261     alarm->deadline_us = is_periodic ? 0 : (timeout_us + esp_timer_get_time());
262 
263 end:
264     osi_mutex_unlock(&alarm_mutex);
265     return ret;
266 }
267 
osi_alarm_set(osi_alarm_t * alarm,period_ms_t timeout)268 osi_alarm_err_t osi_alarm_set(osi_alarm_t *alarm, period_ms_t timeout)
269 {
270     return alarm_set(alarm, timeout, FALSE);
271 }
272 
osi_alarm_set_periodic(osi_alarm_t * alarm,period_ms_t period)273 osi_alarm_err_t osi_alarm_set_periodic(osi_alarm_t *alarm, period_ms_t period)
274 {
275     return alarm_set(alarm, period, TRUE);
276 }
277 
osi_alarm_cancel(osi_alarm_t * alarm)278 osi_alarm_err_t osi_alarm_cancel(osi_alarm_t *alarm)
279 {
280     int ret = OSI_ALARM_ERR_PASS;
281     osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
282     if (alarm_state != ALARM_STATE_OPEN) {
283         OSI_TRACE_ERROR("%s, invalid state %d\n", __func__, alarm_state);
284         ret = OSI_ALARM_ERR_INVALID_STATE;
285         goto end;
286     }
287 
288     if (!alarm || alarm->alarm_hdl == NULL) {
289         OSI_TRACE_ERROR("%s null\n", __func__);
290         ret = OSI_ALARM_ERR_INVALID_ARG;
291         goto end;
292     }
293 
294     esp_err_t stat = esp_timer_stop(alarm->alarm_hdl);
295     if (stat != ESP_OK) {
296         OSI_TRACE_DEBUG("%s failed to stop timer, err 0x%x\n", __func__, stat);
297         ret = OSI_ALARM_ERR_FAIL;
298         goto end;
299     }
300 end:
301     osi_mutex_unlock(&alarm_mutex);
302     return ret;
303 }
304 
osi_alarm_get_remaining_ms(const osi_alarm_t * alarm)305 period_ms_t osi_alarm_get_remaining_ms(const osi_alarm_t *alarm)
306 {
307     assert(alarm_mutex != NULL);
308     int64_t dt_us = 0;
309 
310     osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT);
311     dt_us = alarm->deadline_us - esp_timer_get_time();
312     osi_mutex_unlock(&alarm_mutex);
313 
314     return (dt_us > 0) ? (period_ms_t)(dt_us / 1000) : 0;
315 }
316 
osi_time_get_os_boottime_ms(void)317 uint32_t osi_time_get_os_boottime_ms(void)
318 {
319     return (uint32_t)(esp_timer_get_time() / 1000);
320 }
321 
osi_alarm_is_active(osi_alarm_t * alarm)322 bool osi_alarm_is_active(osi_alarm_t *alarm)
323 {
324     assert(alarm != NULL);
325 
326     if (alarm->alarm_hdl != NULL) {
327         return esp_timer_is_active(alarm->alarm_hdl);
328     }
329 
330     return false;
331 }
332