]>
Commit | Line | Data |
---|---|---|
e6b15cb5 SB |
1 | /* |
2 | * This file is part of the libsigrok project. | |
3 | * | |
4 | * Copyright (C) 2013 Bert Vermeulen <bert@biot.com> | |
5 | * Copyright (C) 2015 Stefan Brüns <stefan.bruens@rwth-aachen.de> | |
6 | * | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation, either version 3 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include <config.h> | |
22 | #include <sys/types.h> | |
23 | #include <sys/stat.h> | |
24 | #include <unistd.h> | |
25 | #include <ctype.h> | |
26 | #include <string.h> | |
27 | #include <stdint.h> | |
28 | #include <libsigrok/libsigrok.h> | |
29 | #include "libsigrok-internal.h" | |
30 | ||
31 | #define LOG_PREFIX "input/raw_analog" | |
32 | ||
33 | /* How many bytes at a time to process and send to the session bus. */ | |
34 | #define CHUNK_SIZE 4096 | |
35 | #define DEFAULT_NUM_CHANNELS 1 | |
36 | #define DEFAULT_SAMPLERATE 0 | |
37 | ||
38 | struct context { | |
39 | gboolean started; | |
40 | int fmt_index; | |
41 | uint64_t samplerate; | |
42 | int samplesize; | |
43 | struct sr_datafeed_packet packet; | |
44 | struct sr_datafeed_analog analog; | |
45 | struct sr_analog_encoding encoding; | |
46 | struct sr_analog_meaning meaning; | |
47 | struct sr_analog_spec spec; | |
48 | }; | |
49 | ||
50 | struct sample_format { | |
51 | const char const* fmt_name; | |
52 | // same as struct sr_analog_encoding | |
53 | uint8_t unitsize; | |
54 | gboolean is_signed; | |
55 | gboolean is_float; | |
56 | gboolean is_bigendian; | |
57 | }; | |
58 | ||
59 | static const struct sample_format const sample_formats[] = | |
60 | { | |
61 | { "S8", 1, TRUE, FALSE, FALSE }, | |
62 | { "U8", 1, FALSE, FALSE, FALSE }, | |
63 | { "S16_LE", 2, TRUE, FALSE, FALSE }, | |
64 | { "U16_LE", 2, FALSE, FALSE, FALSE }, | |
65 | { "S16_BE", 2, TRUE, FALSE, TRUE }, | |
66 | { "U16_BE", 2, FALSE, FALSE, TRUE }, | |
67 | { "S32_LE", 4, TRUE, FALSE, FALSE }, | |
68 | { "U32_LE", 4, FALSE, FALSE, FALSE }, | |
69 | { "S32_BE", 4, TRUE, FALSE, TRUE }, | |
70 | { "U32_BE", 4, FALSE, FALSE, TRUE }, | |
71 | { "FLOAT_LE", 4, TRUE, TRUE, FALSE }, | |
72 | { "FLOAT_BE", 4, TRUE, TRUE, TRUE }, | |
73 | { "FLOAT64_LE", 8, TRUE, TRUE, FALSE }, | |
74 | { "FLOAT64_BE", 8, TRUE, TRUE, TRUE }, | |
75 | }; | |
76 | ||
77 | static int parse_format_string(const char *format) | |
78 | { | |
79 | int num_formats = sizeof(sample_formats)/ sizeof(sample_formats[0]); | |
80 | for (int i = 0; i < num_formats; i++) { | |
81 | if (!strcmp(format, sample_formats[i].fmt_name)) | |
82 | return i; | |
83 | } | |
84 | ||
85 | return -1; | |
86 | } | |
87 | ||
88 | static void init_context(struct context *inc, const struct sample_format *fmt, GSList *channels) | |
89 | { | |
90 | inc->packet.type = SR_DF_ANALOG; | |
91 | inc->packet.payload = &inc->analog; | |
92 | ||
93 | inc->analog.data = NULL; | |
94 | inc->analog.num_samples = 0; | |
95 | inc->analog.encoding = &inc->encoding; | |
96 | inc->analog.meaning = &inc->meaning; | |
97 | inc->analog.spec = &inc->spec; | |
98 | ||
99 | inc->encoding.unitsize = fmt->unitsize; | |
100 | inc->encoding.is_signed = fmt->is_signed; | |
101 | inc->encoding.is_float = fmt->is_float; | |
102 | inc->encoding.is_bigendian = fmt->is_bigendian; | |
103 | inc->encoding.digits = 0; | |
104 | inc->encoding.is_digits_decimal = TRUE; | |
105 | inc->encoding.scale.p = 1; | |
106 | inc->encoding.scale.q = 1; | |
107 | inc->encoding.offset.p = 0; | |
108 | inc->encoding.offset.q = 1; | |
109 | ||
110 | inc->meaning.mq = 0; | |
111 | inc->meaning.unit = 0; | |
112 | inc->meaning.mqflags = 0; | |
113 | inc->meaning.channels = channels; | |
114 | ||
115 | inc->spec.spec_digits = 0; | |
116 | } | |
117 | ||
118 | static int init(struct sr_input *in, GHashTable *options) | |
119 | { | |
120 | struct context *inc; | |
121 | int num_channels; | |
122 | char channelname[8]; | |
123 | const char *format; | |
124 | int fmt_index; | |
125 | ||
126 | num_channels = g_variant_get_int32(g_hash_table_lookup(options, "numchannels")); | |
127 | if (num_channels < 1) { | |
128 | sr_err("Invalid value for numchannels: must be at least 1."); | |
129 | return SR_ERR_ARG; | |
130 | } | |
131 | ||
132 | format = g_variant_get_string(g_hash_table_lookup(options, "format"), NULL); | |
133 | if ((fmt_index = parse_format_string(format)) == -1) { | |
134 | GString *formats = g_string_sized_new(200); | |
135 | int num_formats = sizeof(sample_formats)/ sizeof(sample_formats[0]); | |
136 | for (int i = 0; i < num_formats; i++) | |
137 | g_string_append_printf(formats, "%s ", sample_formats[i].fmt_name); | |
138 | sr_err("Invalid format '%s': must be one of: %s.", | |
139 | format, formats->str); | |
140 | g_string_free(formats, TRUE); | |
141 | return SR_ERR_ARG; | |
142 | } | |
143 | ||
144 | in->sdi = g_malloc0(sizeof(struct sr_dev_inst)); | |
145 | in->priv = inc = g_malloc0(sizeof(struct context)); | |
146 | ||
147 | for (int i = 0; i < num_channels; i++) { | |
148 | snprintf(channelname, 8, "CH%d", i + 1); | |
149 | sr_channel_new(in->sdi, i, SR_CHANNEL_ANALOG, TRUE, channelname); | |
150 | } | |
151 | ||
152 | inc->samplerate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate")); | |
153 | inc->samplesize = sample_formats[fmt_index].unitsize * num_channels; | |
154 | init_context(inc, &sample_formats[fmt_index], in->sdi->channels); | |
155 | ||
156 | return SR_OK; | |
157 | } | |
158 | ||
159 | static int process_buffer(struct sr_input *in) | |
160 | { | |
161 | struct context *inc; | |
162 | struct sr_datafeed_meta meta; | |
163 | struct sr_datafeed_packet packet; | |
164 | struct sr_config *src; | |
165 | unsigned int offset, chunk_size; | |
166 | ||
167 | inc = in->priv; | |
168 | if (!inc->started) { | |
169 | std_session_send_df_header(in->sdi, LOG_PREFIX); | |
170 | ||
171 | if (inc->samplerate) { | |
172 | packet.type = SR_DF_META; | |
173 | packet.payload = &meta; | |
174 | src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate)); | |
175 | meta.config = g_slist_append(NULL, src); | |
176 | sr_session_send(in->sdi, &packet); | |
177 | g_slist_free(meta.config); | |
178 | sr_config_free(src); | |
179 | } | |
180 | ||
181 | inc->started = TRUE; | |
182 | } | |
183 | ||
184 | /* Round down to the last channels * unitsize boundary. */ | |
185 | inc->analog.num_samples = CHUNK_SIZE / inc->samplesize; | |
186 | chunk_size = inc->analog.num_samples * inc->samplesize; | |
187 | offset = 0; | |
188 | ||
189 | while ((offset + chunk_size) < in->buf->len) { | |
190 | inc->analog.data = in->buf->str + offset; | |
191 | sr_session_send(in->sdi, &inc->packet); | |
192 | offset += chunk_size; | |
193 | } | |
194 | ||
195 | inc->analog.num_samples = (in->buf->len - offset) / inc->samplesize; | |
196 | chunk_size = inc->analog.num_samples * inc->samplesize; | |
197 | if (chunk_size > 0) { | |
198 | inc->analog.data = in->buf->str + offset; | |
199 | sr_session_send(in->sdi, &inc->packet); | |
200 | offset += chunk_size; | |
201 | } | |
202 | ||
203 | if ((unsigned int)offset < in->buf->len) { | |
204 | /* | |
205 | * The incoming buffer wasn't processed completely. Stash | |
206 | * the leftover data for next time. | |
207 | */ | |
208 | g_string_erase(in->buf, 0, offset); | |
209 | } else { | |
210 | g_string_truncate(in->buf, 0); | |
211 | } | |
212 | ||
213 | return SR_OK; | |
214 | } | |
215 | ||
216 | static int receive(struct sr_input *in, GString *buf) | |
217 | { | |
218 | int ret; | |
219 | ||
220 | g_string_append_len(in->buf, buf->str, buf->len); | |
221 | ||
222 | if (!in->sdi_ready) { | |
223 | /* sdi is ready, notify frontend. */ | |
224 | in->sdi_ready = TRUE; | |
225 | return SR_OK; | |
226 | } | |
227 | ||
228 | ret = process_buffer(in); | |
229 | ||
230 | return ret; | |
231 | } | |
232 | ||
233 | static int end(struct sr_input *in) | |
234 | { | |
235 | struct sr_datafeed_packet packet; | |
236 | struct context *inc; | |
237 | int ret; | |
238 | ||
239 | if (in->sdi_ready) | |
240 | ret = process_buffer(in); | |
241 | else | |
242 | ret = SR_OK; | |
243 | ||
244 | inc = in->priv; | |
245 | if (inc->started) { | |
246 | packet.type = SR_DF_END; | |
247 | sr_session_send(in->sdi, &packet); | |
248 | } | |
249 | ||
250 | return ret; | |
251 | } | |
252 | ||
253 | static struct sr_option options[] = { | |
254 | { "numchannels", "Number of channels", "Number of channels", NULL, NULL }, | |
255 | { "samplerate", "Sample rate", "Sample rate", NULL, NULL }, | |
256 | { "format", "Format", "Numeric format", NULL, NULL }, | |
257 | ALL_ZERO | |
258 | }; | |
259 | ||
260 | static const struct sr_option *get_options(void) | |
261 | { | |
262 | int num_formats = sizeof(sample_formats)/ sizeof(sample_formats[0]); | |
263 | if (!options[0].def) { | |
264 | options[0].def = g_variant_ref_sink(g_variant_new_int32(DEFAULT_NUM_CHANNELS)); | |
265 | options[1].def = g_variant_ref_sink(g_variant_new_uint64(DEFAULT_SAMPLERATE)); | |
266 | options[2].def = g_variant_ref_sink(g_variant_new_string(sample_formats[0].fmt_name)); | |
267 | for (int i = 0; i < num_formats; i++) { | |
268 | options[2].values = g_slist_append(options[2].values, | |
269 | g_variant_ref_sink(g_variant_new_string(sample_formats[i].fmt_name))); | |
270 | } | |
271 | } | |
272 | ||
273 | return options; | |
274 | } | |
275 | ||
276 | static void cleanup(struct sr_input *in) | |
277 | { | |
278 | struct context *inc; | |
279 | ||
280 | inc = in->priv; | |
281 | g_variant_unref(options[0].def); | |
282 | g_variant_unref(options[1].def); | |
283 | g_variant_unref(options[2].def); | |
284 | g_slist_free_full(options[2].values, (GDestroyNotify)g_variant_unref); | |
285 | g_free(inc); | |
286 | in->priv = NULL; | |
287 | } | |
288 | ||
289 | SR_PRIV struct sr_input_module input_raw_analog = { | |
290 | .id = "raw_analog", | |
291 | .name = "RAW analog", | |
292 | .desc = "analog signals without header", | |
293 | .exts = (const char*[]){"raw", "bin", NULL}, | |
294 | .options = get_options, | |
295 | .init = init, | |
296 | .receive = receive, | |
297 | .end = end, | |
298 | .cleanup = cleanup, | |
299 | }; |