xref: /btstack/platform/qt/btstack_run_loop_qt.cpp (revision 1b464e99afd70ddaf6b75be1ba7cc563a5f5dfd8)
1 /*
2  * Copyright (C) 2019 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "btstack_run_loop_qt.c"
39 
40 /*
41  *  btstack_run_loop_qt.c
42  */
43 
44 // enable POSIX functions (needed for -std=c99)
45 #define _POSIX_C_SOURCE 200809
46 
47 #include "btstack_run_loop_qt.h"
48 
49 #include "btstack_run_loop.h"
50 #include "btstack_run_loop_base.h"
51 #include "btstack_util.h"
52 #include "btstack_linked_list.h"
53 #include "btstack_debug.h"
54 
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <sys/select.h>
58 #include <sys/time.h>
59 #include <time.h>
60 #include <unistd.h>
61 
62 static void btstack_run_loop_qt_dump_timer(void);
63 
64 // start time. tv_usec/tv_nsec = 0
65 #ifdef _POSIX_MONOTONIC_CLOCK
66 // use monotonic clock if available
67 static struct timespec init_ts;
68 #else
69 // fallback to gettimeofday
70 static struct timeval init_tv;
71 #endif
72 
73 static BTstackRunLoopQt * btstack_run_loop_object;
74 
75 #ifdef _POSIX_MONOTONIC_CLOCK
76 /**
77  * @brief Returns the timespec which represents the time(stop - start). It might be negative
78  */
79 static void timespec_diff(struct timespec *start, struct timespec *stop, struct timespec *result){
80     result->tv_sec = stop->tv_sec - start->tv_sec;
81     if ((stop->tv_nsec - start->tv_nsec) < 0) {
82         result->tv_sec = stop->tv_sec - start->tv_sec - 1;
83         result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
84     } else {
85         result->tv_sec = stop->tv_sec - start->tv_sec;
86         result->tv_nsec = stop->tv_nsec - start->tv_nsec;
87     }
88 }
89 
90 /**
91  * @brief Convert timespec to miliseconds, might overflow
92  */
93 static uint64_t timespec_to_milliseconds(struct timespec *a){
94     uint64_t ret = 0;
95     uint64_t sec_val = (uint64_t)(a->tv_sec);
96     uint64_t nsec_val = (uint64_t)(a->tv_nsec);
97     ret = (sec_val*1000) + (nsec_val/1000000);
98     return ret;
99 }
100 
101 /**
102  * @brief Returns the milisecond value of (stop - start). Might overflow
103  */
104 static uint64_t timespec_diff_milis(struct timespec* start, struct timespec* stop){
105     struct timespec diff_ts;
106     timespec_diff(start, stop, &diff_ts);
107     return timespec_to_milliseconds(&diff_ts);
108 }
109 #endif
110 
111 /**
112  * @brief Queries the current time in ms since start
113  */
114 static uint32_t btstack_run_loop_qt_get_time_ms(void){
115     uint32_t time_ms;
116 #ifdef _POSIX_MONOTONIC_CLOCK
117     struct timespec now_ts;
118     clock_gettime(CLOCK_MONOTONIC, &now_ts);
119     time_ms = (uint32_t) timespec_diff_milis(&init_ts, &now_ts);
120 #else
121     struct timeval tv;
122     gettimeofday(&tv, NULL);
123     time_ms = (uint32_t) ((tv.tv_sec  - init_tv.tv_sec) * 1000) + (tv.tv_usec / 1000);
124 #endif
125     return time_ms;
126 }
127 
128 /**
129  * Execute run_loop
130  */
131 static void btstack_run_loop_qt_execute(void) {
132 }
133 
134 // set timer
135 static void btstack_run_loop_qt_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){
136     uint32_t time_ms = btstack_run_loop_qt_get_time_ms();
137     a->timeout = time_ms + timeout_in_ms;
138     log_debug("btstack_run_loop_qt_set_timer to %u ms (now %u, timeout %u)", a->timeout, time_ms, timeout_in_ms);
139 }
140 
141 /**
142  * Add timer to run_loop (keep list sorted)
143  */
144 static void btstack_run_loop_qt_add_timer(btstack_timer_source_t *ts){
145     uint32_t now = btstack_run_loop_qt_get_time_ms();
146     int32_t next_timeout_before = btstack_run_loop_base_get_time_until_timeout(now);
147     btstack_run_loop_base_add_timer(ts);
148     int32_t next_timeout_after = btstack_run_loop_base_get_time_until_timeout(now);
149     if (next_timeout_after >= 0 && (next_timeout_after != next_timeout_before)){
150         QTimer::singleShot((uint32_t)next_timeout_after, btstack_run_loop_object, SLOT(processTimers()));
151     }
152 }
153 
154 // BTstackRunLoopQt class implementation
155 void BTstackRunLoopQt::processTimers(){
156     uint32_t now = btstack_run_loop_qt_get_time_ms();
157     int32_t next_timeout_before = btstack_run_loop_base_get_time_until_timeout(now);
158     btstack_run_loop_base_process_timers(btstack_run_loop_qt_get_time_ms());
159     int32_t next_timeout_after = btstack_run_loop_base_get_time_until_timeout(now);
160     if (next_timeout_after >= 0 && (next_timeout_after != next_timeout_before)){
161         QTimer::singleShot((uint32_t)next_timeout_after, this, SLOT(processTimers()));
162     }
163 }
164 
165 static void btstack_run_loop_qt_init(void){
166 
167     btstack_run_loop_base_init();
168 
169     btstack_run_loop_object = new BTstackRunLoopQt();
170 
171 #ifdef _POSIX_MONOTONIC_CLOCK
172     clock_gettime(CLOCK_MONOTONIC, &init_ts);
173     init_ts.tv_nsec = 0;
174 #else
175     // just assume that we started at tv_usec == 0
176     gettimeofday(&init_tv, NULL);
177     init_tv.tv_usec = 0;
178 #endif
179 }
180 
181 static void btstack_run_loop_qt_dump_timer(void){
182     btstack_linked_item_t *it;
183     int i = 0;
184     for (it = (btstack_linked_item_t *) btstack_run_loop_base_timers; it ; it = it->next){
185         btstack_timer_source_t *ts = (btstack_timer_source_t*) it;
186         log_info("timer %u (%p): timeout %u\n", i, ts, ts->timeout);
187     }
188 }
189 
190 static const btstack_run_loop_t btstack_run_loop_qt = {
191     &btstack_run_loop_qt_init,
192     &btstack_run_loop_base_add_data_source,
193     &btstack_run_loop_base_remove_data_source,
194     &btstack_run_loop_base_enable_data_source_callbacks,
195     &btstack_run_loop_base_disable_data_source_callbacks,
196     &btstack_run_loop_qt_set_timer,
197     &btstack_run_loop_qt_add_timer,
198     &btstack_run_loop_base_remove_timer,
199     &btstack_run_loop_qt_execute,
200     &btstack_run_loop_qt_dump_timer,
201     &btstack_run_loop_qt_get_time_ms,
202 };
203 
204 /**
205  * Provide btstack_run_loop_posix instance
206  */
207 const btstack_run_loop_t * btstack_run_loop_qt_get_instance(void){
208     return &btstack_run_loop_qt;
209 }
210 
211