libsigrokdecode  unreleased development snapshot
sigrok protocol decoding library
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 
105 static int irmp_lib_initialized;
106 static size_t irmp_lib_client_id;
107 static GMutex irmp_lib_mutex;
108 
109 struct irmp_instance {
110  size_t client_id;
111  GMutex *mutex;
112 };
113 
114 static 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 
125 static 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 
136 IRMP_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 
152 IRMP_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 
163 IRMP_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 
171 IRMP_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 
197 IRMP_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 
208 static 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
279 IRMP_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])) {
286  irmp_get_result_data(&ret);
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 
306 static __attribute__((constructor)) void init(void)
307 {
308  irmp_lib_autoinit();
309 }
uint_fast8_t irmp_get_data(IRMP_DATA *irmp_data_p)
Definition: irmp.c:2412
IRMP_DLLEXPORT const char * irmp_get_protocol_name(uint32_t protocol)
Resolve the protocol identifer to the protocol&#39;s name.
uint32_t command
!< address
IR decoder result data at the library&#39;s public API.
IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state)
Release a decoder instance.
#define IRMP_DLLEXPORT
uint32_t flags
!< command
uint32_t address
!< name of the protocol
uint32_t start_sample
!< flags currently only repetition (bit 0)
IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait)
Acquire a decoder instance&#39;s lock.
IRMP_DLLEXPORT int irmp_add_one_sample(int sample)
Feed an individual sample to the detector.
uint_fast8_t irmp_ISR(void)
Definition: irmp.c:2990
uint32_t end_sample
!< the sampleindex there the detected command started
#define ARRAY_SIZE(x)
IRMP_DLLEXPORT uint32_t irmp_get_sample_rate(void)
Query the IRMP library&#39;s configured sample rate.
IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state)
Get the client ID of an IRMP decoder core instance.
IRMP_DLLEXPORT struct irmp_instance * irmp_instance_alloc(void)
Allocate a decoder instance.
IRMP_DLLEXPORT void irmp_reset_state(void)
Reset internal decoder state.
const char * protocol_name
!< protocol, e.g.
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&#39;s lock.