libsigrokdecode unreleased development snapshot
sigrok protocol decoding library
Loading...
Searching...
No Matches
irmp-main-sharedlib.c
Go to the documentation of this file.
1/*
2 * irmp-main-sharedlib.c
3 *
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>
7 *
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.
12 */
13
14/*
15 * Declare the library's public API first. Prove it's consistent and
16 * complete as a standalone header file.
17 */
18#include "irmp-main-sharedlib.h"
19
20#include <errno.h>
21#include <glib.h>
22#include <Python.h>
23#include <stdlib.h>
24#include <string.h>
25
26/*
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.
30 *
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.
36 */
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"
48
49/*
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.
53 *
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).
99 */
100
101#ifndef ARRAY_SIZE
102# define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
103#endif
104
105static int irmp_lib_initialized;
106static size_t irmp_lib_client_id;
107static GMutex irmp_lib_mutex;
108
109struct irmp_instance {
110 size_t client_id;
111 GMutex *mutex;
112};
113
114static void irmp_lib_autoinit(void)
115{
116 if (irmp_lib_initialized)
117 return;
118
119 irmp_lib_client_id = 0;
120 g_mutex_init(&irmp_lib_mutex);
121
122 irmp_lib_initialized = 1;
123}
124
125static size_t irmp_next_client_id(void)
126{
127 size_t id;
128
129 do {
130 id = ++irmp_lib_client_id;
131 } while (!id);
132
133 return id;
134}
135
136IRMP_DLLEXPORT struct irmp_instance *irmp_instance_alloc(void)
137{
138 struct irmp_instance *inst;
139
140 irmp_lib_autoinit();
141
142 inst = g_malloc0(sizeof(*inst));
143 if (!inst)
144 return NULL;
145
146 inst->client_id = irmp_next_client_id();
147 inst->mutex = &irmp_lib_mutex;
148
149 return inst;
150}
151
152IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state)
153{
154
155 irmp_lib_autoinit();
156
157 if (!state)
158 return;
159
160 g_free(state);
161}
162
163IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state)
164{
165
166 irmp_lib_autoinit();
167
168 return state ? state->client_id : 0;
169}
170
171IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait)
172{
173 int rc;
174 PyGILState_STATE pyst;
175
176 irmp_lib_autoinit();
177
178 if (!state || !state->mutex)
179 return -EINVAL;
180
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);
188 }
189 Py_END_ALLOW_THREADS
190 PyGILState_Release(pyst);
191 if (rc != 0)
192 return rc;
193
194 return 0;
195}
196
197IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state)
198{
199
200 irmp_lib_autoinit();
201
202 if (!state || !state->mutex)
203 return;
204
205 g_mutex_unlock(state->mutex);
206}
207
208static uint32_t s_end_sample;
209
211{
212 return F_INTERRUPTS;
213}
214
216{
217 size_t i;
218 IRMP_DATA data;
219
220 /*
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.
224 */
225 IRMP_PIN = 0xff;
226 i = F_INTERRUPTS;
227 while (i-- > 0) {
228 (void)irmp_ISR();
229 }
230 (void)irmp_get_data(&data);
231
232 time_counter = 0;
233 s_startBitSample = 0;
234 s_curSample = 0;
235 s_end_sample = 0;
236
237 /*
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.
246 */
247 silent = 1;
248 verbose = 0;
249}
250
252{
253 int ret;
254
255 IRMP_PIN = sample ? 0xff : 0x00;
256 ret = irmp_ISR() ? 1 : 0;
257 s_end_sample = s_curSample++;
258 return ret;
259}
260
262{
263 IRMP_DATA d;
264
265 if (!irmp_get_data(&d))
266 return 0;
267
268 data->address = d.address;
269 data->command = d.command;
270 data->protocol = d.protocol;
272 data->flags = d.flags;
273 data->start_sample = s_startBitSample;
274 data->end_sample = s_end_sample;
275 return 1;
276}
277
278#if WITH_IRMP_DETECT_BUFFER
279IRMP_DLLEXPORT struct irmp_result_data irmp_detect_buffer(const uint8_t *buff, size_t len)
280{
281 struct irmp_result_data ret;
282
283 memset(&ret, 0, sizeof(ret));
284 while (s_curSample < len) {
285 if (irmp_add_one_sample(buff[s_curSample])) {
287 return ret;
288 }
289 }
290 return ret;
291}
292#endif
293
295{
296 const char *name;
297
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;
304}
305
306static __attribute__((constructor)) void init(void)
307{
308 irmp_lib_autoinit();
309}
IRMP_DLLEXPORT int irmp_add_one_sample(int sample)
Feed an individual sample to the detector.
IRMP_DLLEXPORT void irmp_reset_state(void)
Reset internal decoder state.
IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state)
Get the client ID of an IRMP decoder core instance.
IRMP_DLLEXPORT const char * irmp_get_protocol_name(uint32_t protocol)
Resolve the protocol identifer to the protocol's name.
#define ARRAY_SIZE(x)
IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state)
Release a decoder instance.
IRMP_DLLEXPORT uint32_t irmp_get_sample_rate(void)
Query the IRMP library's configured sample rate.
IRMP_DLLEXPORT struct irmp_instance * irmp_instance_alloc(void)
Allocate a decoder instance.
IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait)
Acquire a decoder instance's lock.
IRMP_DLLEXPORT int irmp_get_result_data(struct irmp_result_data *data)
Query result data after detection succeeded.
IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state)
Release a decoder instance's lock.
#define IRMP_DLLEXPORT
uint_fast8_t irmp_get_data(IRMP_DATA *irmp_data_p)
Definition irmp.c:2412
uint_fast8_t irmp_ISR(void)
Definition irmp.c:2990
IR decoder result data at the library's public API.
const char * protocol_name
!< protocol, e.g.
uint32_t address
!< name of the protocol
uint32_t command
!< address
uint32_t start_sample
!< flags currently only repetition (bit 0)
uint32_t flags
!< command
uint32_t end_sample
!< the sampleindex there the detected command started