sae_j1850_vpw: rewrite decoder to improve usability and maintenance
[libsigrokdecode/gsi.git] / irmp / irmp-main-sharedlib.c
blob4d02460014baa4687429bc8cd7a79ad0261c8247
1 /*
2 * irmp-main-sharedlib.c
4 * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
5 * Copyright (c) 2009-2019 René Staffen - r.staffen(at)gmx.de
6 * Copyright (c) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
15 * Declare the library's public API first. Prove it's consistent and
16 * complete as a standalone header file.
18 #include "irmp-main-sharedlib.h"
20 #include <errno.h>
21 #include <glib.h>
22 #include <Python.h>
23 #include <stdlib.h>
24 #include <string.h>
27 * Include the IRMP core logic. This approach is required because of
28 * static variables which hold internal state. The core logic started
29 * as an MCU project where resources are severely constrained.
31 * This libsigrokdecode incarnation of IRMP will always be used in the
32 * UNIX_OR_WINDOWS configuration. But libtool(1) breaks the upstream
33 * logic's platform detection. Check reliably available conditions here
34 * and provide expected symbols to the library, to reduce changes to the
35 * upstream project.
37 #if defined _WIN32
38 # if !defined WIN32
39 # define WIN32
40 # endif
41 #else
42 # if !defined unix
43 # define unix
44 # endif
45 #endif
46 #include "irmp.h"
47 #include "irmp.c"
50 * The remaining source code implements the PC library, which accepts
51 * sample data from API callers, and provides detector results as they
52 * become available after seeing input data.
54 * TODO items, known constraints
55 * - Counters in the IRMP core logic and the library wrapper are 32bit
56 * only. In the strictest sense they only need to cover the span of
57 * an IR frame. In the PC side library case they need to cover "a
58 * detection phase", which happens to be under calling applications'
59 * control. The library shall not mess with the core's internal state,
60 * and may even not be able to reliably tell whether detection of a
61 * frame started in the core. Fortunately the 32bit counters only roll
62 * over after some 2.5 days at the highest available sample rate. So
63 * this limitation is not a blocker.
64 * - The IRMP core keeps internal state in global variables. Which is
65 * appropriate for MCU configurations. For the PC library use case
66 * this constraint prevents concurrency, only a single data stream
67 * can get processed at any time. This limitation can get addressed
68 * later, making the flexible and featureful IRMP detection available
69 * in the first place is considered highly desirable, and is a great
70 * improvement in itself.
71 * - The detection of IR frames from buffered data is both limited and
72 * complicated at the same time. The routine re-uses the caller's
73 * buffer _and_ internal state across multiple calls. Thus windowed
74 * operation over a larger set of input data is not available. The
75 * API lacks a flag for failed detection, thus applications need to
76 * guess from always returned payload data.
77 * - Is it worth adding a "detection in progress" query to the API? Is
78 * the information available to the library wrapper, and reliable?
79 * Shall applications be able to "poll" the started, and completed
80 * state for streamed operation including periodic state resets which
81 * won't interfere with pending detection? (It's assumed that this
82 * is only required when feeding single values in individual calls is
83 * found to be rather expensive.
84 * - Some of the result data reflects the core's internal presentation
85 * while there is no declaration in the library's API. This violates
86 * API layers, and needs to get addressed properly.
87 * - The IRMP core logic (strictly speaking the specific details of
88 * preprocessor symbol arrangements in the current implementation)
89 * appears to assume either to run on an MCU and capture IR signals
90 * from hardware pins, falling back to AVR if no other platform got
91 * detected. Or assumes to run on a (desktop) PC, and automatically
92 * enables ANALYZE mode, which results in lots of stdio traffic that
93 * is undesirable for application code which uses the shared library
94 * for strict detection purposes but no further analysis or research.
95 * It's a pity that turning off ANALYZE switches to MCU mode, and that
96 * keeping ANALYZE enabled but silencing the output is rather messy
97 * and touches the innards of the core logic (the irmp.c source file
98 * and its dependency header files).
101 #ifndef ARRAY_SIZE
102 # define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
103 #endif
105 static int irmp_lib_initialized;
106 static size_t irmp_lib_client_id;
107 static GMutex irmp_lib_mutex;
109 struct irmp_instance {
110 size_t client_id;
111 GMutex *mutex;
114 static void irmp_lib_autoinit(void)
116 if (irmp_lib_initialized)
117 return;
119 irmp_lib_client_id = 0;
120 g_mutex_init(&irmp_lib_mutex);
122 irmp_lib_initialized = 1;
125 static size_t irmp_next_client_id(void)
127 size_t id;
129 do {
130 id = ++irmp_lib_client_id;
131 } while (!id);
133 return id;
136 IRMP_DLLEXPORT struct irmp_instance *irmp_instance_alloc(void)
138 struct irmp_instance *inst;
140 irmp_lib_autoinit();
142 inst = g_malloc0(sizeof(*inst));
143 if (!inst)
144 return NULL;
146 inst->client_id = irmp_next_client_id();
147 inst->mutex = &irmp_lib_mutex;
149 return inst;
152 IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state)
155 irmp_lib_autoinit();
157 if (!state)
158 return;
160 g_free(state);
163 IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state)
166 irmp_lib_autoinit();
168 return state ? state->client_id : 0;
171 IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait)
173 int rc;
174 PyGILState_STATE pyst;
176 irmp_lib_autoinit();
178 if (!state || !state->mutex)
179 return -EINVAL;
181 pyst = PyGILState_Ensure();
182 Py_BEGIN_ALLOW_THREADS
183 if (wait) {
184 g_mutex_lock(state->mutex);
185 rc = 0;
186 } else {
187 rc = g_mutex_trylock(state->mutex);
189 Py_END_ALLOW_THREADS
190 PyGILState_Release(pyst);
191 if (rc != 0)
192 return rc;
194 return 0;
197 IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state)
200 irmp_lib_autoinit();
202 if (!state || !state->mutex)
203 return;
205 g_mutex_unlock(state->mutex);
208 static uint32_t s_end_sample;
210 IRMP_DLLEXPORT uint32_t irmp_get_sample_rate(void)
212 return F_INTERRUPTS;
215 IRMP_DLLEXPORT void irmp_reset_state(void)
217 size_t i;
218 IRMP_DATA data;
221 * Provide the equivalent of 1s idle input signal level. Then
222 * drain any potentially accumulated result data. This clears
223 * the internal decoder state.
225 IRMP_PIN = 0xff;
226 i = F_INTERRUPTS;
227 while (i-- > 0) {
228 (void)irmp_ISR();
230 (void)irmp_get_data(&data);
232 time_counter = 0;
233 s_startBitSample = 0;
234 s_curSample = 0;
235 s_end_sample = 0;
238 * TODO This is not the most appropriate location to control the
239 * core logic's verbosity. But out of the public set of library
240 * routines this call is closest to some initialization routine.
241 * The query for compile time parameter values is optional, the
242 * state reset is not. Multiple verbosity setup activities in
243 * the same program lifetime won't harm. This HACK is clearly
244 * preferrable over more fiddling with core logic innards, or
245 * the introduction of yet another DLL routine.
247 silent = 1;
248 verbose = 0;
251 IRMP_DLLEXPORT int irmp_add_one_sample(int sample)
253 int ret;
255 IRMP_PIN = sample ? 0xff : 0x00;
256 ret = irmp_ISR() ? 1 : 0;
257 s_end_sample = s_curSample++;
258 return ret;
261 IRMP_DLLEXPORT int irmp_get_result_data(struct irmp_result_data *data)
263 IRMP_DATA d;
265 if (!irmp_get_data(&d))
266 return 0;
268 data->address = d.address;
269 data->command = d.command;
270 data->protocol = d.protocol;
271 data->protocol_name = irmp_get_protocol_name(d.protocol);
272 data->flags = d.flags;
273 data->start_sample = s_startBitSample;
274 data->end_sample = s_end_sample;
275 return 1;
278 #if WITH_IRMP_DETECT_BUFFER
279 IRMP_DLLEXPORT struct irmp_result_data irmp_detect_buffer(const uint8_t *buff, size_t len)
281 struct irmp_result_data ret;
283 memset(&ret, 0, sizeof(ret));
284 while (s_curSample < len) {
285 if (irmp_add_one_sample(buff[s_curSample])) {
286 irmp_get_result_data(&ret);
287 return ret;
290 return ret;
292 #endif
294 IRMP_DLLEXPORT const char *irmp_get_protocol_name(uint32_t protocol)
296 const char *name;
298 if (protocol >= ARRAY_SIZE(irmp_protocol_names))
299 return "unknown";
300 name = irmp_protocol_names[protocol];
301 if (!name || !*name)
302 return "unknown";
303 return name;
306 static __attribute__((constructor)) void init(void)
308 irmp_lib_autoinit();