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