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"
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
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).
102 # define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
105 static int irmp_lib_initialized
;
106 static size_t irmp_lib_client_id
;
107 static GMutex irmp_lib_mutex
;
109 struct irmp_instance
{
114 static void irmp_lib_autoinit(void)
116 if (irmp_lib_initialized
)
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)
130 id
= ++irmp_lib_client_id
;
136 IRMP_DLLEXPORT
struct irmp_instance
*irmp_instance_alloc(void)
138 struct irmp_instance
*inst
;
142 inst
= g_malloc0(sizeof(*inst
));
146 inst
->client_id
= irmp_next_client_id();
147 inst
->mutex
= &irmp_lib_mutex
;
152 IRMP_DLLEXPORT
void irmp_instance_free(struct irmp_instance
*state
)
163 IRMP_DLLEXPORT
size_t irmp_instance_id(struct irmp_instance
*state
)
168 return state
? state
->client_id
: 0;
171 IRMP_DLLEXPORT
int irmp_instance_lock(struct irmp_instance
*state
, int wait
)
174 PyGILState_STATE pyst
;
178 if (!state
|| !state
->mutex
)
181 pyst
= PyGILState_Ensure();
182 Py_BEGIN_ALLOW_THREADS
184 g_mutex_lock(state
->mutex
);
187 rc
= g_mutex_trylock(state
->mutex
);
190 PyGILState_Release(pyst
);
197 IRMP_DLLEXPORT
void irmp_instance_unlock(struct irmp_instance
*state
)
202 if (!state
|| !state
->mutex
)
205 g_mutex_unlock(state
->mutex
);
208 static uint32_t s_end_sample
;
210 IRMP_DLLEXPORT
uint32_t irmp_get_sample_rate(void)
215 IRMP_DLLEXPORT
void irmp_reset_state(void)
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.
230 (void)irmp_get_data(&data
);
233 s_startBitSample
= 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.
251 IRMP_DLLEXPORT
int irmp_add_one_sample(int sample
)
255 IRMP_PIN
= sample
? 0xff : 0x00;
256 ret
= irmp_ISR() ? 1 : 0;
257 s_end_sample
= s_curSample
++;
261 IRMP_DLLEXPORT
int irmp_get_result_data(struct irmp_result_data
*data
)
265 if (!irmp_get_data(&d
))
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
;
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
);
294 IRMP_DLLEXPORT
const char *irmp_get_protocol_name(uint32_t protocol
)
298 if (protocol
>= ARRAY_SIZE(irmp_protocol_names
))
300 name
= irmp_protocol_names
[protocol
];
306 static __attribute__((constructor
)) void init(void)