]> sigrok.org Git - libsigrok.git/blame - src/output/hex.c
demo: Support changing the amplitude of analog channels.
[libsigrok.git] / src / output / hex.c
CommitLineData
8d3af2e8
BV
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <stdlib.h>
21#include <string.h>
22#include <glib.h>
23#include "libsigrok.h"
24#include "libsigrok-internal.h"
25
26#define LOG_PREFIX "output/hex"
27
28#define DEFAULT_SAMPLES_PER_LINE 192
29
30struct context {
31 unsigned int num_enabled_channels;
32 int samples_per_line;
33 int bit_cnt;
34 int spl_cnt;
35 int trigger;
7bcbbfae 36 uint64_t samplerate;
8d3af2e8
BV
37 int *channel_index;
38 char **channel_names;
39 char **line_values;
40 uint8_t *sample_buf;
7bcbbfae 41 gboolean header_done;
8d3af2e8 42 GString **lines;
8d3af2e8
BV
43};
44
a755b0e1 45static int init(struct sr_output *o, GHashTable *options)
8d3af2e8
BV
46{
47 struct context *ctx;
48 struct sr_channel *ch;
49 GSList *l;
dba3e682
BV
50 GHashTableIter iter;
51 gpointer key, value;
8d3af2e8 52 unsigned int i, j;
a755b0e1 53 uint32_t spl;
8d3af2e8
BV
54
55 if (!o || !o->sdi)
56 return SR_ERR_ARG;
dba3e682
BV
57
58 spl = DEFAULT_SAMPLES_PER_LINE;
a755b0e1
BV
59 if (options) {
60 g_hash_table_iter_init(&iter, options);
61 while (g_hash_table_iter_next(&iter, &key, &value)) {
62 if (!strcmp(key, "width")) {
63 if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
64 sr_err("Invalid type for 'width' option.");
65 return SR_ERR_ARG;
66 }
67 if (!(spl = g_variant_get_uint32(value))) {
68 sr_err("Invalid width.");
69 return SR_ERR_ARG;
70 }
71 } else {
72 sr_err("Unknown option '%s'.", key);
dba3e682
BV
73 return SR_ERR_ARG;
74 }
dba3e682
BV
75 }
76 }
77
8d3af2e8
BV
78 ctx = g_malloc0(sizeof(struct context));
79 o->internal = ctx;
80 ctx->trigger = -1;
8d3af2e8
BV
81 ctx->samples_per_line = spl;
82
83 for (l = o->sdi->channels; l; l = l->next) {
84 ch = l->data;
85 if (ch->type != SR_CHANNEL_LOGIC)
86 continue;
87 if (!ch->enabled)
88 continue;
89 ctx->num_enabled_channels++;
90 }
91 ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels);
92 ctx->channel_names = g_malloc(sizeof(char *) * ctx->num_enabled_channels);
93 ctx->lines = g_malloc(sizeof(GString *) * ctx->num_enabled_channels);
94 ctx->sample_buf = g_malloc(ctx->num_enabled_channels);
95
96 j = 0;
97 for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
98 ch = l->data;
99 if (ch->type != SR_CHANNEL_LOGIC)
100 continue;
101 if (!ch->enabled)
102 continue;
103 ctx->channel_index[j] = ch->index;
104 ctx->channel_names[j] = ch->name;
105 ctx->lines[j] = g_string_sized_new(80);
106 ctx->sample_buf[j] = 0;
107 g_string_printf(ctx->lines[j], "%s:", ch->name);
108 j++;
109 }
110
7bcbbfae
BV
111 return SR_OK;
112}
113
a755b0e1 114static GString *gen_header(const struct sr_output *o)
7bcbbfae
BV
115{
116 struct context *ctx;
117 GVariant *gvar;
118 GString *header;
119 int num_channels;
120 char *samplerate_s;
121
122 ctx = o->internal;
123 if (ctx->samplerate == 0) {
124 if (sr_config_get(o->sdi->driver, o->sdi, NULL, SR_CONF_SAMPLERATE,
125 &gvar) == SR_OK) {
126 ctx->samplerate = g_variant_get_uint64(gvar);
127 g_variant_unref(gvar);
128 }
129 }
130
131 header = g_string_sized_new(512);
132 g_string_printf(header, "%s\n", PACKAGE_STRING);
8d3af2e8 133 num_channels = g_slist_length(o->sdi->channels);
7bcbbfae
BV
134 g_string_append_printf(header, "Acquisition with %d/%d channels",
135 ctx->num_enabled_channels, num_channels);
136 if (ctx->samplerate != 0) {
137 samplerate_s = sr_samplerate_string(ctx->samplerate);
138 g_string_append_printf(header, " at %s", samplerate_s);
8d3af2e8 139 g_free(samplerate_s);
8d3af2e8 140 }
7bcbbfae 141 g_string_append_printf(header, "\n");
8d3af2e8 142
7bcbbfae 143 return header;
8d3af2e8
BV
144}
145
a755b0e1 146static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
dba3e682 147 GString **out)
8d3af2e8 148{
7bcbbfae 149 const struct sr_datafeed_meta *meta;
8d3af2e8 150 const struct sr_datafeed_logic *logic;
7bcbbfae
BV
151 const struct sr_config *src;
152 GSList *l;
8d3af2e8
BV
153 struct context *ctx;
154 int idx, pos, offset;
155 uint64_t i, j;
156 gchar *p;
157
8d3af2e8
BV
158 *out = NULL;
159 if (!o || !o->sdi)
160 return SR_ERR_ARG;
161 if (!(ctx = o->internal))
162 return SR_ERR_ARG;
163
164 switch (packet->type) {
7bcbbfae
BV
165 case SR_DF_META:
166 meta = packet->payload;
167 for (l = meta->config; l; l = l->next) {
168 src = l->data;
169 if (src->key != SR_CONF_SAMPLERATE)
170 continue;
171 ctx->samplerate = g_variant_get_uint64(src->data);
172 }
173 break;
8d3af2e8
BV
174 case SR_DF_TRIGGER:
175 ctx->trigger = ctx->spl_cnt;
176 break;
177 case SR_DF_LOGIC:
7bcbbfae
BV
178 if (!ctx->header_done) {
179 *out = gen_header(o);
180 ctx->header_done = TRUE;
8d3af2e8
BV
181 } else
182 *out = g_string_sized_new(512);
183
184 logic = packet->payload;
185 for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
186 ctx->spl_cnt++;
187 pos = ctx->spl_cnt & 7;
188 for (j = 0; j < ctx->num_enabled_channels; j++) {
189 idx = ctx->channel_index[j];
190 p = logic->data + i + idx / 8;
191 ctx->sample_buf[j] <<= 1;
192 if (*p & (1 << (idx % 8)))
193 ctx->sample_buf[j] |= 1;
194 if (ctx->spl_cnt && pos == 0) {
195 /* Buffered a byte's worth, output hex. */
196 g_string_append_printf(ctx->lines[j], "%.2x ",
197 ctx->sample_buf[j]);
198 ctx->sample_buf[j] = 0;
199 }
200
201 if (ctx->spl_cnt == ctx->samples_per_line) {
202 /* Flush line buffers. */
203 g_string_append_len(*out, ctx->lines[j]->str, ctx->lines[j]->len);
204 g_string_append_c(*out, '\n');
205 if (j == ctx->num_enabled_channels - 1 && ctx->trigger > -1) {
206 offset = ctx->trigger + ctx->trigger / 8;
207 g_string_append_printf(*out, "T:%*s^ %d\n", offset, "", ctx->trigger);
208 ctx->trigger = -1;
209 }
210 g_string_printf(ctx->lines[j], "%s:", ctx->channel_names[j]);
211 }
212 }
213 if (ctx->spl_cnt == ctx->samples_per_line)
214 /* Line buffers were already flushed. */
215 ctx->spl_cnt = 0;
216 }
217 break;
218 case SR_DF_END:
219 if (ctx->spl_cnt) {
220 /* Line buffers need flushing. */
221 *out = g_string_sized_new(512);
222 for (i = 0; i < ctx->num_enabled_channels; i++) {
223 if (ctx->spl_cnt & 7)
224 g_string_append_printf(ctx->lines[i], "%.2x ",
225 ctx->sample_buf[i] << (8 - (ctx->spl_cnt & 7)));
226 g_string_append_len(*out, ctx->lines[i]->str, ctx->lines[i]->len);
227 g_string_append_c(*out, '\n');
228 }
229 }
230 break;
231 }
232
233 return SR_OK;
234}
235
236static int cleanup(struct sr_output *o)
237{
238 struct context *ctx;
239 unsigned int i;
240
241 if (!o)
242 return SR_ERR_ARG;
243
244 if (!(ctx = o->internal))
245 return SR_OK;
246
8d3af2e8
BV
247 g_free(ctx->channel_index);
248 g_free(ctx->sample_buf);
249 g_free(ctx->channel_names);
250 for (i = 0; i < ctx->num_enabled_channels; i++)
251 g_string_free(ctx->lines[i], TRUE);
252 g_free(ctx->lines);
8d3af2e8
BV
253 g_free(ctx);
254 o->internal = NULL;
255
256 return SR_OK;
257}
258
a755b0e1
BV
259static struct sr_option options[] = {
260 { "width", "Width", "Number of samples per line", NULL, NULL },
261 { 0 }
262};
263
264static struct sr_option *get_options(gboolean cached)
265{
266 if (cached)
267 return options;
268
269 options[0].def = g_variant_new_uint32(DEFAULT_SAMPLES_PER_LINE);
270 g_variant_ref_sink(options[0].def);
271
272 return options;
273}
274
275SR_PRIV struct sr_output_module output_hex = {
8d3af2e8 276 .id = "hex",
a755b0e1
BV
277 .name = "Hexadecimal",
278 .desc = "Hexadecimal digits",
279 .options = get_options,
8d3af2e8
BV
280 .init = init,
281 .receive = receive,
282 .cleanup = cleanup,
283};
284