summaryrefslogtreecommitdiff
path: root/session.c
blob: 386fb710f5cb7a73506a15e5ea047a7b4f715cbb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
/*
 * This file is part of the libsigrokdecode project.
 *
 * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
 * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <config.h>
#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
#include "libsigrokdecode.h"
#include <inttypes.h>
#include <glib.h>

/**
 * @file
 *
 * Session handling.
 */

/**
 * @defgroup grp_session Session handling
 *
 * Starting and handling decoding sessions.
 *
 * @{
 */

/** @cond PRIVATE */

SRD_PRIV GSList *sessions = NULL;
SRD_PRIV int max_session_id = -1;

/** @endcond */

/**
 * Create a decoding session.
 *
 * A session holds all decoder instances, their stack relationships and
 * output callbacks.
 *
 * @param sess A pointer which will hold a pointer to a newly
 *             initialized session on return. Must not be NULL.
 *
 * @return SRD_OK upon success, a (negative) error code otherwise.
 *
 * @since 0.3.0
 */
SRD_API int srd_session_new(struct srd_session **sess)
{
	if (!sess)
		return SRD_ERR_ARG;

	*sess = g_malloc(sizeof(struct srd_session));
	(*sess)->session_id = ++max_session_id;
	(*sess)->di_list = (*sess)->callbacks = NULL;

	/* Keep a list of all sessions, so we can clean up as needed. */
	sessions = g_slist_append(sessions, *sess);

	srd_dbg("Creating session %d.", (*sess)->session_id);

	return SRD_OK;
}

/**
 * Start a decoding session.
 *
 * Decoders, instances and stack must have been prepared beforehand,
 * and all SRD_CONF parameters set.
 *
 * @param sess The session to start. Must not be NULL.
 *
 * @return SRD_OK upon success, a (negative) error code otherwise.
 *
 * @since 0.3.0
 */
SRD_API int srd_session_start(struct srd_session *sess)
{
	GSList *d;
	struct srd_decoder_inst *di;
	int ret;

	if (!sess)
		return SRD_ERR_ARG;

	srd_dbg("Calling start() of all instances in session %d.", sess->session_id);

	/* Run the start() method of all decoders receiving frontend data. */
	ret = SRD_OK;
	for (d = sess->di_list; d; d = d->next) {
		di = d->data;
		if ((ret = srd_inst_start(di)) != SRD_OK)
			break;
	}

	return ret;
}

static int srd_inst_send_meta(struct srd_decoder_inst *di, int key,
		GVariant *data)
{
	PyObject *py_ret;
	GSList *l;
	struct srd_decoder_inst *next_di;
	int ret;
	PyGILState_STATE gstate;

	if (key != SRD_CONF_SAMPLERATE)
		/* This is the only key we pass on to the decoder for now. */
		return SRD_OK;

	gstate = PyGILState_Ensure();

	if (PyObject_HasAttrString(di->py_inst, "metadata")) {
		py_ret = PyObject_CallMethod(di->py_inst, "metadata", "lK",
				(long)SRD_CONF_SAMPLERATE,
				(unsigned long long)g_variant_get_uint64(data));
		Py_XDECREF(py_ret);
	}

	PyGILState_Release(gstate);

	/* Push metadata to all the PDs stacked on top of this one. */
	for (l = di->next_di; l; l = l->next) {
		next_di = l->data;
		if ((ret = srd_inst_send_meta(next_di, key, data)) != SRD_OK)
			return ret;
	}

	return SRD_OK;
}

/**
 * Set a metadata configuration key in a session.
 *
 * @param sess The session to configure. Must not be NULL.
 * @param key The configuration key (SRD_CONF_*).
 * @param data The new value for the key, as a GVariant with GVariantType
 *             appropriate to that key. A floating reference can be passed
 *             in; its refcount will be sunk and unreferenced after use.
 *
 * @return SRD_OK upon success, a (negative) error code otherwise.
 *
 * @since 0.3.0
 */
SRD_API int srd_session_metadata_set(struct srd_session *sess, int key,
		GVariant *data)
{
	GSList *l;
	int ret;

	if (!sess)
		return SRD_ERR_ARG;

	if (!key) {
		srd_err("Invalid key.");
		return SRD_ERR_ARG;
	}

	if (!data) {
		srd_err("Invalid value.");
		return SRD_ERR_ARG;
	}

	/* Hardcoded to samplerate/uint64 for now. */

	if (key != SRD_CONF_SAMPLERATE) {
		srd_err("Unknown config key %d.", key);
		return SRD_ERR_ARG;
	}
	if (!g_variant_is_of_type(data, G_VARIANT_TYPE_UINT64)) {
		srd_err("Invalid value type: expected uint64, got %s",
				g_variant_get_type_string(data));
		return SRD_ERR_ARG;
	}

	srd_dbg("Setting session %d samplerate to %"G_GUINT64_FORMAT".",
			sess->session_id, g_variant_get_uint64(data));

	ret = SRD_OK;
	for (l = sess->di_list; l; l = l->next) {
		if ((ret = srd_inst_send_meta(l->data, key, data)) != SRD_OK)
			break;
	}

	g_variant_unref(data);

	return ret;
}

/**
 * Send a chunk of logic sample data to a running decoder session.
 *
 * If no channel map has been set up, the logic samples must be arranged
 * in channel order, in the least amount of space possible. The default
 * channel set consists of all required channels + all optional channels.
 *
 * The size of a sample in inbuf is 'unitsize' bytes. If no channel map
 * has been configured, it is the minimum number of bytes needed to store
 * the default channels.
 *
 * The calls to this function must provide the samples that shall be
 * used by the protocol decoder
 *  - in the correct order ([...]5, 6, 4, 7, 8[...] is a bug),
 *  - starting from sample zero (2, 3, 4, 5, 6[...] is a bug),
 *  - consecutively, with no gaps (0, 1, 2, 4, 5[...] is a bug).
 *
 * The start- and end-sample numbers are absolute sample numbers (relative
 * to the start of the whole capture/file/stream), i.e. they are not relative
 * sample numbers within the chunk specified by 'inbuf' and 'inbuflen'.
 *
 * Correct example (4096 samples total, 4 chunks @ 1024 samples each):
 *   srd_session_send(s, 0,    1023, inbuf, 1024, 1);
 *   srd_session_send(s, 1024, 2047, inbuf, 1024, 1);
 *   srd_session_send(s, 2048, 3071, inbuf, 1024, 1);
 *   srd_session_send(s, 3072, 4095, inbuf, 1024, 1);
 *
 * The chunk size ('inbuflen') can be arbitrary and can differ between calls.
 *
 * Correct example (4096 samples total, 7 chunks @ various samples each):
 *   srd_session_send(s, 0,    1023, inbuf, 1024, 1);
 *   srd_session_send(s, 1024, 1123, inbuf,  100, 1);
 *   srd_session_send(s, 1124, 1423, inbuf,  300, 1);
 *   srd_session_send(s, 1424, 1642, inbuf,  219, 1);
 *   srd_session_send(s, 1643, 2047, inbuf,  405, 1);
 *   srd_session_send(s, 2048, 3071, inbuf, 1024, 1);
 *   srd_session_send(s, 3072, 4095, inbuf, 1024, 1);
 *
 * INCORRECT example (4096 samples total, 4 chunks @ 1024 samples each, but
 * the start- and end-samplenumbers are not absolute):
 *   srd_session_send(s, 0,    1023, inbuf, 1024, 1);
 *   srd_session_send(s, 0,    1023, inbuf, 1024, 1);
 *   srd_session_send(s, 0,    1023, inbuf, 1024, 1);
 *   srd_session_send(s, 0,    1023, inbuf, 1024, 1);
 *
 * @param sess The session to use. Must not be NULL.
 * @param abs_start_samplenum The absolute starting sample number for the
 *              buffer's sample set, relative to the start of capture.
 * @param abs_end_samplenum The absolute ending sample number for the
 *              buffer's sample set, relative to the start of capture.
 * @param inbuf Pointer to sample data. Must not be NULL.
 * @param inbuflen Length in bytes of the buffer. Must be > 0.
 * @param unitsize The number of bytes per sample. Must be > 0.
 *
 * @return SRD_OK upon success, a (negative) error code otherwise.
 *
 * @since 0.4.0
 */
SRD_API int srd_session_send(struct srd_session *sess,
		uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
		const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize)
{
	GSList *d;
	int ret;

	if (!sess)
		return SRD_ERR_ARG;

	for (d = sess->di_list; d; d = d->next) {
		if ((ret = srd_inst_decode(d->data, abs_start_samplenum,
				abs_end_samplenum, inbuf, inbuflen, unitsize)) != SRD_OK)
			return ret;
	}

	return SRD_OK;
}

/**
 * Terminate currently executing decoders in a session, reset internal state.
 *
 * All decoder instances have their .wait() method terminated, which
 * shall terminate .decode() as well. Afterwards the decoders' optional
 * .reset() method gets executed.
 *
 * This routine allows callers to abort pending expensive operations,
 * when they are no longer interested in the decoders' results. Note
 * that the decoder state is lost and aborted work cannot resume.
 *
 * This routine also allows callers to re-use previously created decoder
 * stacks to process new input data which is not related to previously
 * processed input data. This avoids the necessity to re-construct the
 * decoder stack.
 *
 * @param sess The session in which to terminate decoders. Must not be NULL.
 *
 * @return SRD_OK upon success, a (negative) error code otherwise.
 *
 * @since 0.5.1
 */
SRD_API int srd_session_terminate_reset(struct srd_session *sess)
{
	GSList *d;
	int ret;

	if (!sess)
		return SRD_ERR_ARG;

	for (d = sess->di_list; d; d = d->next) {
		ret = srd_inst_terminate_reset(d->data);
		if (ret != SRD_OK)
			return ret;
	}

	return SRD_OK;
}

/**
 * Destroy a decoding session.
 *
 * All decoder instances and output callbacks are properly released.
 *
 * @param sess The session to be destroyed. Must not be NULL.
 *
 * @return SRD_OK upon success, a (negative) error code otherwise.
 *
 * @since 0.3.0
 */
SRD_API int srd_session_destroy(struct srd_session *sess)
{
	int session_id;

	if (!sess)
		return SRD_ERR_ARG;

	session_id = sess->session_id;
	if (sess->di_list)
		srd_inst_free_all(sess);
	if (sess->callbacks)
		g_slist_free_full(sess->callbacks, g_free);
	sessions = g_slist_remove(sessions, sess);
	g_free(sess);

	srd_dbg("Destroyed session %d.", session_id);

	return SRD_OK;
}

/**
 * Register/add a decoder output callback function.
 *
 * The function will be called when a protocol decoder sends output back
 * to the PD controller (except for Python objects, which only go up the
 * stack).
 *
 * @param sess The output session in which to register the callback.
 *             Must not be NULL.
 * @param output_type The output type this callback will receive. Only one
 *                    callback per output type can be registered.
 * @param cb The function to call. Must not be NULL.
 * @param cb_data Private data for the callback function. Can be NULL.
 *
 * @since 0.3.0
 */
SRD_API int srd_pd_output_callback_add(struct srd_session *sess,
		int output_type, srd_pd_output_callback cb, void *cb_data)
{
	struct srd_pd_callback *pd_cb;

	if (!sess)
		return SRD_ERR_ARG;

	srd_dbg("Registering new callback for output type %s.",
		output_type_name(output_type));

	pd_cb = g_malloc(sizeof(struct srd_pd_callback));
	pd_cb->output_type = output_type;
	pd_cb->cb = cb;
	pd_cb->cb_data = cb_data;
	sess->callbacks = g_slist_append(sess->callbacks, pd_cb);

	return SRD_OK;
}

/** @private */
SRD_PRIV struct srd_pd_callback *srd_pd_output_callback_find(
		struct srd_session *sess, int output_type)
{
	GSList *l;
	struct srd_pd_callback *tmp, *pd_cb;

	if (!sess)
		return NULL;

	pd_cb = NULL;
	for (l = sess->callbacks; l; l = l->next) {
		tmp = l->data;
		if (tmp->output_type == output_type) {
			pd_cb = tmp;
			break;
		}
	}

	return pd_cb;
}

/** @} */