]>
Commit | Line | Data |
---|---|---|
b1fa9aac GS |
1 | /* |
2 | * This file is part of the libsigrok project. | |
3 | * | |
4 | * Copyright (C) 2023 Gerhard Sittig <gerhard.sittig@gmx.net> | |
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 | ||
18baeeed GS |
20 | /* |
21 | * Juntek JDS6600 is a DDS signal generator. | |
22 | * Often rebranded, goes by different names, among them Joy-IT JDS6600. | |
23 | * | |
24 | * This driver was built using Kristoff Bonne's knowledge as seen in his | |
25 | * MIT licensed Python code for JDS6600 control. For details see the | |
26 | * https://github.com/on1arf/jds6600_python repository. | |
27 | * | |
28 | * Supported features: | |
29 | * - Model detection, which determines the upper output frequency limit | |
30 | * (15..60MHz models exist). | |
31 | * - Assumes exactly two channels. Other models were not seen out there. | |
32 | * - Per channel configuration of: Waveform, output frequency, amplitude, | |
33 | * offset, duty cycle. | |
34 | * - Phase between channels is a global property and affects multiple | |
35 | * channels at the same time (their relation to each other). | |
36 | * | |
37 | * TODO | |
38 | * - Add support for the frequency measurement and/or the counter. This | |
39 | * feature's availability may depend on or interact with the state of | |
40 | * other generator channels. Needs consideration of constraints. | |
1711287e GS |
41 | * - Add support for "modes" (sweep, pulse, burst; modulation if the |
42 | * device supports it). | |
18baeeed GS |
43 | * - Add support for download/upload of arbitrary waveforms. This needs |
44 | * infrastructure in common libsigrok code as well as in applications. | |
45 | * At the moment "blob transfer" (waveform upload/download) appears to | |
46 | * not be supported. | |
47 | * - Re-consider parameter value ranges. Frequency depends on the model. | |
1711287e GS |
48 | * Amplitude depends on the model and frequencies. Can be -20..+20, |
49 | * or -10..+10, or -5..+5. Could be affected by offsets and further | |
50 | * get clipped. This implementation caps application's input to the | |
51 | * -20..+20 range, and sends the set request to the device. If any | |
52 | * further transformation happens in the device then applications | |
53 | * need to read back, this library driver doesn't. | |
18baeeed GS |
54 | * |
55 | * Implementation details: | |
56 | * - Communicates via USB CDC at 115200/8n1 (virtual COM port). | |
57 | * - Requests are in text format. Start with a ':' colon, followed by a | |
58 | * single letter instruction opcode, followed by a number which either | |
59 | * addresses a parameter (think hardware register) or storage slot for | |
60 | * an arbitrary waveform. Can be followed by an '=' equals sign and a | |
61 | * value. Multiple values are comma separated. The line may end in a | |
62 | * '.' period. Several end-of-line conventions are supported by the | |
63 | * devices' firmware versions, LF and CR/LF are reported to work. | |
64 | * - Responses also are in text format. Start with a ':' colon, followed | |
65 | * by an instruction letter, followed by a number (a parameter index, | |
66 | * or a waveform index), followed by '=' equal sign and one or more | |
67 | * values. Optionally ending in a '.' period. And ending in the | |
1711287e GS |
68 | * firmware's end-of-line. Read responses will have this format. |
69 | * Responses to write requests might just have the ":ok." literal. | |
18baeeed | 70 | * - There are four instructions: 'r' to read and 'w' to write parameters |
1711287e GS |
71 | * (think "hardware registers", optionaly multi-valued), 'a' to write |
72 | * and 'b' to read arbitrary waveform data (sequence of sample values). | |
18baeeed GS |
73 | * - Am not aware of a vendor's documentation for the protocol. Joy-IT |
74 | * provides the JT-JDS6600-Communication-protocol.pdf document which | |
75 | * leaves a lot of questions. This sigrok driver implementation used | |
76 | * a lot of https://github.com/on1arf/jds6600_python knowledge for | |
77 | * the initial version (MIT licenced Python code by Kristoff Bonne). | |
78 | * - The requests take effect when sent from application code. While | |
79 | * the requests remain uneffective when typed in interactive terminal | |
80 | * sessions. Though there are ":ok" responses, the action would not | |
81 | * happen in the device. It's assumed to be a firmware implementation | |
82 | * constraint that is essential to keep in mind. | |
83 | * - The right hand side of write requests or read responses can carry | |
84 | * any number of values, both numbers and text, integers and floats. | |
85 | * Still some of the parameters (voltages, times, frequencies) come in | |
86 | * interesting formats. A floating point "mantissa" and an integer code | |
87 | * for scaling the value. Not an exponent, but some kind of index. In | |
88 | * addition to an open coded "fixed point" style multiplier that is | |
89 | * implied and essential, but doesn't show on the wire. Interpretation | |
90 | * of responses and phrasing of values in requests is arbitrary, this | |
91 | * "black magic" was found by local experimentation (reading back the | |
92 | * values which were configured by local UI interaction). | |
1711287e GS |
93 | * - Communication is more reliable when the host unconditionally sends |
94 | * "function codes" (register and waveform indices) in two-digit form. | |
95 | * Device firmware might implement rather specific assumptions. | |
96 | * - Semantics of the right hand side in :rNN= and :bNN= read requests | |
97 | * is uncertain. Just passing 0 in all situations worked in a local | |
98 | * setup. As did omitting the value during interactive exploration. | |
99 | * | |
100 | * Example requests and responses. | |
101 | * - Get model identification (max output frequency) | |
102 | * TX text: --> :r00=0. | |
103 | * TX bytes: --> 3a 72 30 30 3d 30 2e 0d 0a | |
104 | * RX bytes: <-- 3a 72 30 30 3d 36 30 2e 0d 0a | |
105 | * RX text: <-- :r00=60. | |
106 | * - Get all channels' enabled state | |
107 | * TX text: --> :r20=0. | |
108 | * TX bytes: --> 3a 72 32 30 3d 30 2e 0d 0a | |
109 | * RX bytes: <-- 3a 72 32 30 3d 31 2c 31 2e 0d 0a | |
110 | * RX text: <-- :r20=1,1. | |
111 | * - Get first channel's waveform selection | |
112 | * TX text: --> :r21=0. | |
113 | * TX bytes: --> 3a 72 32 31 3d 30 2e 0d 0a | |
114 | * RX bytes: <-- 3a 72 32 31 3d 31 30 33 2e 0d 0a | |
115 | * RX text: <-- :r21=103. | |
116 | * - Set second channel's output frequency | |
117 | * TX text: --> :w24=1234500,0. | |
118 | * TX bytes: --> 3a 77 32 34 3d 31 32 33 34 35 30 30 2c 30 2e 0d 0a | |
119 | * RX bytes: <-- 3a 6f 6b 0d 0a | |
120 | * RX text: <-- :ok | |
121 | * - Read arbitrary waveform number 13 | |
122 | * TX text: --> :b13=0. | |
123 | * TX bytes: --> 3a 62 31 33 3d 30 2e 0d 0a | |
124 | * RX bytes: <-- 3a 62 31 33 3d 34 30 39 35 2c 34 30 39 35 2c ... 2c 34 30 39 35 2c 34 30 39 35 2c 0d 0a | |
125 | * RX text: <-- :b13=4095,4095,...,4095,4095, | |
18baeeed GS |
126 | */ |
127 | ||
128 | #include "config.h" | |
129 | ||
130 | #include <glib.h> | |
131 | #include <math.h> | |
132 | #include <string.h> | |
133 | ||
b1fa9aac GS |
134 | #include "protocol.h" |
135 | ||
1711287e | 136 | #define WITH_SERIAL_RAW_DUMP 0 /* Includes EOL and non-printables. */ |
18baeeed GS |
137 | #define WITH_ARBWAVE_DOWNLOAD 0 /* Development HACK */ |
138 | ||
139 | /* | |
140 | * The firmware's maximum response length. Seen when an arbitrary | |
141 | * waveform gets retrieved. Carries 2048 samples in the 0..4095 range. | |
142 | * Plus some decoration around that data. | |
143 | * :b01=4095,4095,...,4095,<CRLF> | |
144 | */ | |
145 | #define MAX_RSP_LENGTH (8 + 2048 * 5) | |
146 | ||
147 | /* Times are in milliseconds. */ | |
148 | #define DELAY_AFTER_WRITE 10 | |
149 | #define DELAY_AFTER_FLASH 100 | |
150 | #define TIMEOUT_READ_CHUNK 20 | |
151 | #define TIMEOUT_IDENTIFY 200 | |
152 | ||
153 | /* Instruction codes. Read/write parameters/waveforms. */ | |
154 | #define INSN_WRITE_PARA 'w' | |
155 | #define INSN_READ_PARA 'r' | |
156 | #define INSN_WRITE_WAVE 'a' | |
157 | #define INSN_READ_WAVE 'b' | |
158 | ||
159 | /* Indices for "register access". */ | |
160 | enum param_index { | |
161 | IDX_DEVICE_TYPE = 0, | |
162 | IDX_SERIAL_NUMBER = 1, | |
163 | IDX_CHANNELS_ENABLE = 20, | |
164 | IDX_WAVEFORM_CH1 = 21, | |
165 | IDX_WAVEFORM_CH2 = 22, | |
166 | IDX_FREQUENCY_CH1 = 23, | |
167 | IDX_FREQUENCY_CH2 = 24, | |
168 | IDX_AMPLITUDE_CH1 = 25, | |
169 | IDX_AMPLITUDE_CH2 = 26, | |
170 | IDX_OFFSET_CH1 = 27, | |
171 | IDX_OFFSET_CH2 = 28, | |
172 | IDX_DUTYCYCLE_CH1 = 29, | |
173 | IDX_DUTYCYCLE_CH2 = 30, | |
174 | IDX_PHASE_CHANNELS = 31, | |
175 | IDX_ACTION = 32, | |
176 | IDX_MODE = 33, | |
177 | IDX_INPUT_COUPLING = 36, | |
178 | IDX_MEASURE_GATE = 37, | |
179 | IDX_MEASURE_MODE = 38, | |
180 | IDX_COUNTER_RESET = 39, | |
181 | IDX_SWEEP_STARTFREQ = 40, | |
182 | IDX_SWEEP_ENDFREQ = 41, | |
183 | IDX_SWEEP_TIME = 42, | |
184 | IDX_SWEEP_DIRECTION = 43, | |
185 | IDX_SWEEP_MODE = 44, | |
186 | IDX_PULSE_WIDTH = 45, | |
187 | IDX_PULSE_PERIOD = 46, | |
188 | IDX_PULSE_OFFSET = 47, | |
189 | IDX_PULSE_AMPLITUDE = 48, | |
190 | IDX_BURST_COUNT = 49, | |
191 | IDX_BURST_MODE = 50, | |
192 | IDX_SYSTEM_SOUND = 51, | |
193 | IDX_SYSTEM_BRIGHTNESS = 52, | |
194 | IDX_SYSTEM_LANGUAGE = 53, | |
195 | IDX_SYSTEM_SYNC = 54, /* "Tracking" channels? */ | |
196 | IDX_SYSTEM_ARBMAX = 55, | |
197 | IDX_PROFILE_SAVE = 70, | |
198 | IDX_PROFILE_LOAD = 71, | |
199 | IDX_PROFILE_CLEAR = 72, | |
200 | IDX_COUNTER_VALUE = 80, | |
201 | IDX_MEAS_VALUE_FREQLOW = 81, | |
202 | IDX_MEAS_VALUE_FREQHI = 82, | |
203 | IDX_MEAS_VALUE_WIDTHHI = 83, | |
204 | IDX_MEAS_VALUE_WIDTHLOW = 84, | |
205 | IDX_MEAS_VALUE_PERIOD = 85, | |
206 | IDX_MEAS_VALUE_DUTYCYCLE = 86, | |
207 | IDX_MEAS_VALUE_U1 = 87, | |
208 | IDX_MEAS_VALUE_U2 = 88, | |
209 | IDX_MEAS_VALUE_U3 = 89, | |
210 | }; | |
211 | ||
212 | /* Firmware's codes for waveform selection. */ | |
213 | enum waveform_index_t { | |
214 | /* 17 pre-defined waveforms. */ | |
215 | WAVE_SINE = 0, | |
216 | WAVE_SQUARE = 1, | |
217 | WAVE_PULSE = 2, | |
218 | WAVE_TRIANGLE = 3, | |
219 | WAVE_PARTIAL_SINE = 4, | |
220 | WAVE_CMOS = 5, | |
221 | WAVE_DC = 6, | |
222 | WAVE_HALF_WAVE = 7, | |
223 | WAVE_FULL_WAVE = 8, | |
224 | WAVE_POS_LADDER = 9, | |
225 | WAVE_NEG_LADDER = 10, | |
226 | WAVE_NOISE = 11, | |
227 | WAVE_EXP_RISE = 12, | |
228 | WAVE_EXP_DECAY = 13, | |
229 | WAVE_MULTI_TONE = 14, | |
230 | WAVE_SINC = 15, | |
231 | WAVE_LORENZ = 16, | |
232 | WAVES_COUNT_BUILTIN, | |
233 | /* Up to 60 arbitrary waveforms. */ | |
234 | WAVES_ARB_BASE = 100, | |
235 | WAVE_ARB01 = WAVES_ARB_BASE + 1, | |
236 | /* ... */ | |
237 | WAVE_ARB60 = WAVES_ARB_BASE + 60, | |
238 | WAVES_PAST_LAST_ARB, | |
239 | }; | |
240 | #define WAVES_COUNT_ARBITRARY (WAVES_PAST_LAST_ARB - WAVE_ARB01) | |
241 | ||
242 | static const char *waveform_names[] = { | |
243 | [WAVE_SINE] = "sine", | |
244 | [WAVE_SQUARE] = "square", | |
245 | [WAVE_PULSE] = "pulse", | |
246 | [WAVE_TRIANGLE] = "triangle", | |
247 | [WAVE_PARTIAL_SINE] = "partial-sine", | |
248 | [WAVE_CMOS] = "cmos", | |
249 | [WAVE_DC] = "dc", | |
250 | [WAVE_HALF_WAVE] = "half-wave", | |
251 | [WAVE_FULL_WAVE] = "full-wave", | |
252 | [WAVE_POS_LADDER] = "pos-ladder", | |
253 | [WAVE_NEG_LADDER] = "neg-ladder", | |
254 | [WAVE_NOISE] = "noise", | |
255 | [WAVE_EXP_RISE] = "exp-rise", | |
256 | [WAVE_EXP_DECAY] = "exp-decay", | |
257 | [WAVE_MULTI_TONE] = "multi-tone", | |
258 | [WAVE_SINC] = "sinc", | |
259 | [WAVE_LORENZ] = "lorenz", | |
260 | }; | |
261 | #define WAVEFORM_ARB_NAME_FMT "arb-%02zu" | |
262 | ||
1711287e GS |
263 | static void log_raw_bytes(const char *caption, GString *buff) |
264 | { | |
265 | GString *text; | |
266 | ||
267 | if (!WITH_SERIAL_RAW_DUMP) | |
268 | return; | |
269 | if (sr_log_loglevel_get() < SR_LOG_SPEW) | |
270 | return; | |
271 | ||
272 | if (!caption) | |
273 | caption = ""; | |
274 | text = sr_hexdump_new((const uint8_t *)buff->str, buff->len); | |
275 | sr_spew("%s%s", caption, text->str); | |
276 | sr_hexdump_free(text); | |
277 | } | |
278 | ||
18baeeed GS |
279 | /* |
280 | * Writes a text line to the serial port. Normalizes end-of-line | |
281 | * including trailing period. | |
1711287e GS |
282 | * |
283 | * Accepts: | |
284 | * ":r01=0.<CR><LF>" | |
285 | * ":r01=0." | |
286 | * ":r01=0<LF>" | |
287 | * ":r01=0" | |
288 | * Normalizes to: | |
289 | * ":r01=0.<CR><LF>" | |
18baeeed GS |
290 | */ |
291 | static int serial_send_textline(const struct sr_dev_inst *sdi, | |
292 | GString *s, unsigned int delay_ms) | |
293 | { | |
294 | struct sr_serial_dev_inst *conn; | |
295 | const char *rdptr; | |
296 | size_t padlen, rdlen, wrlen; | |
297 | int ret; | |
298 | ||
299 | if (!sdi) | |
300 | return SR_ERR_ARG; | |
301 | conn = sdi->conn; | |
302 | if (!conn) | |
303 | return SR_ERR_ARG; | |
304 | if (!s) | |
305 | return SR_ERR_ARG; | |
306 | ||
1711287e GS |
307 | /* |
308 | * Trim surrounding whitespace. Normalize to canonical format. | |
309 | * Make sure there is enough room for the period and CR/LF | |
310 | * (and NUL termination). Use a glib API that's easy to adjust | |
311 | * the padded length of. Performance is not a priority here. | |
312 | */ | |
18baeeed GS |
313 | padlen = 4; |
314 | while (padlen--) | |
315 | g_string_append_c(s, '\0'); | |
316 | rdptr = sr_text_trim_spaces(s->str); | |
317 | rdlen = strlen(rdptr); | |
318 | if (rdlen && rdptr[rdlen - 1] == '.') | |
319 | rdlen--; | |
320 | g_string_set_size(s, rdlen); | |
321 | g_string_append_c(s, '.'); | |
1711287e | 322 | sr_spew("serial TX text: --> %s", rdptr); |
18baeeed GS |
323 | g_string_append_c(s, '\r'); |
324 | g_string_append_c(s, '\n'); | |
325 | rdlen = strlen(rdptr); | |
1711287e | 326 | log_raw_bytes("serial TX bytes: --> ", s); |
18baeeed GS |
327 | |
328 | /* Handle chunked writes, check for transmission errors. */ | |
329 | while (rdlen) { | |
330 | ret = serial_write_blocking(conn, rdptr, rdlen, 0); | |
331 | if (ret < 0) | |
332 | return SR_ERR_IO; | |
333 | wrlen = (size_t)ret; | |
334 | if (wrlen > rdlen) | |
335 | wrlen = rdlen; | |
336 | rdptr += wrlen; | |
337 | rdlen -= wrlen; | |
338 | } | |
339 | ||
340 | if (delay_ms) | |
341 | g_usleep(delay_ms * 1000); | |
342 | ||
343 | return SR_OK; | |
344 | } | |
345 | ||
346 | /* | |
347 | * Reads a text line from the serial port. Assumes that only a single | |
348 | * response text line is in flight (does not handle the case of more | |
349 | * receive data following after the first EOL). Transparently deals | |
350 | * with trailing period and end-of-line, so callers need not bother. | |
351 | * | |
352 | * Checks plausibility when the caller specifies conditions to check. | |
353 | * Optionally returns references (and lengths) to the response's RHS. | |
354 | * That's fine because data resides in a caller provided buffer. | |
355 | */ | |
356 | static int serial_recv_textline(const struct sr_dev_inst *sdi, | |
357 | GString *s, unsigned int delay_ms, unsigned int timeout_ms, | |
358 | gboolean *is_ok, char wants_insn, size_t wants_index, | |
359 | char **rhs_start, size_t *rhs_length) | |
360 | { | |
361 | struct sr_serial_dev_inst *ser; | |
362 | char *rdptr; | |
363 | size_t rdlen, got; | |
364 | int ret; | |
365 | guint64 now_us, deadline_us; | |
366 | gboolean has_timedout; | |
367 | char *eol_pos, *endptr; | |
368 | char got_insn; | |
369 | unsigned long got_index; | |
370 | ||
371 | if (is_ok) | |
372 | *is_ok = FALSE; | |
373 | if (rhs_start) | |
374 | *rhs_start = NULL; | |
375 | if (rhs_length) | |
376 | *rhs_length = 0; | |
377 | ||
378 | if (!sdi) | |
379 | return SR_ERR_ARG; | |
380 | ser = sdi->conn; | |
381 | if (!ser) | |
382 | return SR_ERR_ARG; | |
1711287e GS |
383 | if (!s) |
384 | return SR_ERR_ARG; | |
18baeeed GS |
385 | |
386 | g_string_set_size(s, MAX_RSP_LENGTH); | |
387 | g_string_truncate(s, 0); | |
388 | ||
389 | /* Arrange for overall receive timeout when caller specified. */ | |
390 | now_us = deadline_us = 0; | |
391 | if (timeout_ms) { | |
392 | now_us = g_get_monotonic_time(); | |
393 | deadline_us = now_us; | |
394 | deadline_us += timeout_ms * 1000; | |
395 | } | |
396 | ||
397 | rdptr = s->str; | |
398 | rdlen = s->allocated_len - 1 - s->len; | |
399 | while (rdlen) { | |
400 | /* Get another chunk of receive data. Check for EOL. */ | |
401 | ret = serial_read_blocking(ser, rdptr, rdlen, delay_ms); | |
402 | if (ret < 0) | |
403 | return SR_ERR_IO; | |
404 | got = (size_t)ret; | |
405 | if (got > rdlen) | |
406 | got = rdlen; | |
407 | rdptr[got] = '\0'; | |
408 | eol_pos = strchr(rdptr, '\n'); | |
409 | rdptr += got; | |
410 | rdlen -= got; | |
1711287e | 411 | g_string_set_size(s, s->len + got); |
18baeeed GS |
412 | /* Check timeout expiration upon empty reception. */ |
413 | has_timedout = FALSE; | |
414 | if (timeout_ms && !got) { | |
415 | now_us = g_get_monotonic_time(); | |
416 | if (now_us >= deadline_us) | |
417 | has_timedout = TRUE; | |
418 | } | |
419 | if (!eol_pos) { | |
420 | if (has_timedout) | |
421 | break; | |
422 | continue; | |
423 | } | |
1711287e | 424 | log_raw_bytes("serial RX bytes: <-- ", s); |
18baeeed GS |
425 | |
426 | /* Normalize the received text line. */ | |
427 | *eol_pos++ = '\0'; | |
428 | rdptr = s->str; | |
429 | (void)sr_text_trim_spaces(rdptr); | |
430 | rdlen = strlen(rdptr); | |
1711287e | 431 | sr_spew("serial RX text: <-- %s", rdptr); |
18baeeed GS |
432 | if (rdlen && rdptr[rdlen - 1] == '.') |
433 | rdptr[--rdlen] = '\0'; | |
434 | ||
435 | /* Check conditions as requested by the caller. */ | |
436 | if (is_ok || wants_insn || rhs_start) { | |
437 | if (*rdptr != ':') { | |
438 | sr_dbg("serial read, colon missing"); | |
439 | return SR_ERR_DATA; | |
440 | } | |
441 | rdptr++; | |
442 | rdlen--; | |
443 | } | |
444 | /* | |
445 | * The check for 'ok' is terminal. Does not combine with | |
446 | * responses which carry payload data on their RHS. | |
447 | */ | |
448 | if (is_ok) { | |
449 | *is_ok = strcmp(rdptr, "ok") == 0; | |
450 | sr_dbg("serial read, 'ok' check %d", *is_ok); | |
451 | return *is_ok ? SR_OK : SR_ERR_DATA; | |
452 | } | |
453 | /* | |
454 | * Conditional strict checks for caller's expected fields. | |
455 | * Unconditional weaker checks for general structure. | |
456 | */ | |
457 | if (wants_insn && *rdptr != wants_insn) { | |
458 | sr_dbg("serial read, unexpected insn"); | |
459 | return SR_ERR_DATA; | |
460 | } | |
461 | got_insn = *rdptr++; | |
462 | switch (got_insn) { | |
463 | case INSN_WRITE_PARA: | |
464 | case INSN_READ_PARA: | |
465 | case INSN_WRITE_WAVE: | |
466 | case INSN_READ_WAVE: | |
467 | /* EMPTY */ | |
468 | break; | |
469 | default: | |
470 | sr_dbg("serial read, unknown insn %c", got_insn); | |
471 | return SR_ERR_DATA; | |
472 | } | |
473 | endptr = NULL; | |
474 | ret = sr_atoul_base(rdptr, &got_index, &endptr, 10); | |
475 | if (ret != SR_OK || !endptr) | |
476 | return SR_ERR_DATA; | |
477 | if (wants_index && got_index != wants_index) { | |
1711287e | 478 | sr_dbg("serial read, unexpected index %lu", got_index); |
18baeeed GS |
479 | return SR_ERR_DATA; |
480 | } | |
481 | rdptr = endptr; | |
482 | if (rhs_start || rhs_length) { | |
483 | if (*rdptr != '=') { | |
484 | sr_dbg("serial read, equals sign missing"); | |
485 | return SR_ERR_DATA; | |
486 | } | |
487 | } | |
488 | if (*rdptr) | |
489 | rdptr++; | |
490 | ||
491 | /* Response is considered plausible here. */ | |
492 | if (rhs_start) | |
493 | *rhs_start = rdptr; | |
494 | if (rhs_length) | |
495 | *rhs_length = strlen(rdptr); | |
496 | return SR_OK; | |
497 | } | |
1711287e GS |
498 | log_raw_bytes("serial RX bytes: <-- ", s); |
499 | sr_dbg("serial read, unterminated response, discarded"); | |
18baeeed GS |
500 | |
501 | sr_dbg("serial read, no EOL seen"); | |
502 | return SR_ERR_DATA; | |
503 | } | |
504 | ||
505 | /* Formatting helpers for request construction. */ | |
506 | ||
507 | static void append_insn_read_para(GString *s, char insn, size_t idx) | |
508 | { | |
1711287e | 509 | g_string_append_printf(s, ":%c%02zu=0", insn, idx & 0xff); |
18baeeed GS |
510 | } |
511 | ||
512 | static void append_insn_write_para_va(GString *s, char insn, size_t idx, | |
513 | const char *fmt, va_list args) ATTR_FMT_PRINTF(4, 0); | |
514 | static void append_insn_write_para_va(GString *s, char insn, size_t idx, | |
515 | const char *fmt, va_list args) | |
516 | { | |
1711287e | 517 | g_string_append_printf(s, ":%c%02zu=", insn, idx & 0xff); |
18baeeed GS |
518 | g_string_append_vprintf(s, fmt, args); |
519 | } | |
520 | ||
521 | static void append_insn_write_para_dots(GString *s, char insn, size_t idx, | |
522 | const char *fmt, ...) ATTR_FMT_PRINTF(4, 5); | |
523 | static void append_insn_write_para_dots(GString *s, char insn, size_t idx, | |
524 | const char *fmt, ...) | |
525 | { | |
526 | va_list args; | |
527 | ||
528 | va_start(args, fmt); | |
529 | append_insn_write_para_va(s, insn, idx, fmt, args); | |
530 | va_end(args); | |
531 | } | |
532 | ||
533 | /* | |
534 | * Turn comma separators into whitespace. Simplifies the interpretation | |
535 | * of multi-value response payloads. Also replaces any trailing period | |
536 | * in case callers kept one in the receive buffer. | |
537 | */ | |
538 | static void replace_separators(char *s) | |
539 | { | |
540 | ||
541 | while (s && *s) { | |
542 | if (s[0] == ',') { | |
543 | *s++ = ' '; | |
544 | continue; | |
545 | } | |
546 | if (s[0] == '.' && s[1] == '\0') { | |
547 | *s++ = ' '; | |
548 | continue; | |
549 | } | |
550 | s++; | |
551 | } | |
552 | } | |
553 | ||
554 | /* | |
555 | * Convenience to interpret responses' values. Also concentrates the | |
556 | * involved magic and simplifies diagnostics. It's essential to apply | |
557 | * implicit multipliers, and to properly combine multiple fields into | |
558 | * the resulting parameter's value (think scaling and offsetting). | |
559 | */ | |
560 | ||
561 | static const double scales_freq[] = { | |
562 | 1, 1, 1, 1e-3, 1e-6, | |
563 | }; | |
564 | ||
565 | static int parse_freq_text(char *s, double *value) | |
566 | { | |
567 | char *word; | |
568 | int ret; | |
569 | double dvalue; | |
570 | unsigned long scale; | |
571 | ||
572 | replace_separators(s); | |
573 | ||
574 | /* First word is a mantissa, in centi-Hertz. :-O */ | |
575 | word = sr_text_next_word(s, &s); | |
576 | ret = sr_atod(word, &dvalue); | |
577 | if (ret != SR_OK) | |
578 | return ret; | |
579 | ||
580 | /* Next word is an encoded scaling factor. */ | |
581 | word = sr_text_next_word(s, &s); | |
582 | ret = sr_atoul_base(word, &scale, NULL, 10); | |
583 | if (ret != SR_OK) | |
584 | return ret; | |
1711287e | 585 | sr_spew("parse freq, mant %f, scale %lu", dvalue, scale); |
18baeeed GS |
586 | if (scale >= ARRAY_SIZE(scales_freq)) |
587 | return SR_ERR_DATA; | |
588 | ||
589 | /* Do scale the mantissa's value. */ | |
590 | dvalue /= 100.0; | |
591 | dvalue /= scales_freq[scale]; | |
1711287e | 592 | sr_spew("parse freq, value %f", dvalue); |
18baeeed GS |
593 | |
594 | if (value) | |
595 | *value = dvalue; | |
596 | return SR_OK; | |
597 | } | |
598 | ||
599 | static int parse_volt_text(char *s, double *value) | |
600 | { | |
601 | int ret; | |
602 | double dvalue; | |
603 | ||
604 | /* Single value, in units of mV. */ | |
605 | ret = sr_atod(s, &dvalue); | |
606 | if (ret != SR_OK) | |
607 | return ret; | |
1711287e | 608 | sr_spew("parse volt, mant %f", dvalue); |
18baeeed | 609 | dvalue /= 1000.0; |
1711287e | 610 | sr_spew("parse volt, value %f", dvalue); |
18baeeed GS |
611 | |
612 | if (value) | |
613 | *value = dvalue; | |
614 | return SR_OK; | |
615 | } | |
616 | ||
617 | static int parse_bias_text(char *s, double *value) | |
618 | { | |
619 | int ret; | |
620 | double dvalue; | |
621 | ||
622 | /* | |
623 | * Single value, in units of 10mV with a 10V offset. Capped to | |
624 | * the +9.99V..-9.99V range. The Joy-IT PDF is a little weird | |
625 | * suggesting that ":w27=9999." translates to 9.99 volts. | |
626 | */ | |
627 | ret = sr_atod(s, &dvalue); | |
628 | if (ret != SR_OK) | |
629 | return ret; | |
1711287e | 630 | sr_spew("parse bias, mant %f", dvalue); |
18baeeed GS |
631 | dvalue /= 100.0; |
632 | dvalue -= 10.0; | |
633 | if (dvalue >= 9.99) | |
634 | dvalue = 9.99; | |
635 | if (dvalue <= -9.99) | |
636 | dvalue = -9.99; | |
1711287e | 637 | sr_spew("parse bias, value %f", dvalue); |
18baeeed GS |
638 | |
639 | if (value) | |
640 | *value = dvalue; | |
641 | return SR_OK; | |
642 | } | |
643 | ||
644 | static int parse_duty_text(char *s, double *value) | |
645 | { | |
646 | int ret; | |
647 | double dvalue; | |
648 | ||
649 | /* | |
650 | * Single value, in units of 0.1% (permille). | |
651 | * Scale to the 0.0..1.0 range. | |
652 | */ | |
653 | ret = sr_atod(s, &dvalue); | |
654 | if (ret != SR_OK) | |
655 | return ret; | |
1711287e | 656 | sr_spew("parse duty, mant %f", dvalue); |
18baeeed | 657 | dvalue /= 1000.0; |
1711287e | 658 | sr_spew("parse duty, value %f", dvalue); |
18baeeed GS |
659 | |
660 | if (value) | |
661 | *value = dvalue; | |
662 | return SR_OK; | |
663 | } | |
664 | ||
665 | static int parse_phase_text(char *s, double *value) | |
666 | { | |
667 | int ret; | |
668 | double dvalue; | |
669 | ||
670 | /* Single value, in units of deci-degrees. */ | |
671 | ret = sr_atod(s, &dvalue); | |
672 | if (ret != SR_OK) | |
673 | return ret; | |
1711287e | 674 | sr_spew("parse phase, mant %f", dvalue); |
18baeeed | 675 | dvalue /= 10.0; |
1711287e | 676 | sr_spew("parse phase, value %f", dvalue); |
18baeeed GS |
677 | |
678 | if (value) | |
679 | *value = dvalue; | |
680 | return SR_OK; | |
681 | } | |
682 | ||
683 | /* | |
684 | * Convenience to generate request presentations. Also concentrates the | |
685 | * involved magic and simplifies diagnostics. It's essential to apply | |
686 | * implicit multipliers, and to properly create all request fields that | |
687 | * communicate a value to the device's firmware (think scale and offset). | |
688 | */ | |
689 | ||
690 | static void write_freq_text(GString *s, double freq) | |
691 | { | |
692 | unsigned long scale_idx; | |
693 | const char *text_pos; | |
694 | ||
1711287e | 695 | sr_spew("write freq, value %f", freq); |
18baeeed GS |
696 | text_pos = &s->str[s->len]; |
697 | ||
698 | /* | |
699 | * First word is mantissa in centi-Hertz. Second word is a | |
700 | * scaling factor code. Keep scaling simple, always scale | |
701 | * by a factor of 1.0. | |
702 | */ | |
703 | scale_idx = 0; | |
704 | freq *= scales_freq[scale_idx]; | |
705 | freq *= 100.0; | |
706 | ||
1711287e | 707 | g_string_append_printf(s, "%.0f,%lu", freq, scale_idx); |
18baeeed GS |
708 | sr_spew("write freq, text %s", text_pos); |
709 | } | |
710 | ||
711 | static void write_volt_text(GString *s, double volt) | |
712 | { | |
713 | const char *text_pos; | |
714 | ||
1711287e | 715 | sr_spew("write volt, value %f", volt); |
18baeeed GS |
716 | text_pos = &s->str[s->len]; |
717 | ||
718 | /* | |
719 | * Single value in units of 1mV. | |
720 | * Limit input values to the 0..+20 range. This writer is only | |
721 | * used by the amplitude setter. | |
722 | */ | |
723 | if (volt > 20.0) | |
724 | volt = 20.0; | |
725 | if (volt < 0.0) | |
726 | volt = 0.0; | |
727 | volt *= 1000.0; | |
1711287e | 728 | g_string_append_printf(s, "%.0f", volt); |
18baeeed GS |
729 | sr_spew("write volt, text %s", text_pos); |
730 | } | |
731 | ||
732 | static void write_bias_text(GString *s, double volt) | |
733 | { | |
734 | const char *text_pos; | |
735 | ||
1711287e | 736 | sr_spew("write bias, value %f", volt); |
18baeeed GS |
737 | text_pos = &s->str[s->len]; |
738 | ||
739 | /* | |
740 | * Single value in units of 10mV with a 10V offset. Capped to | |
741 | * the +9.99..-9.99 range. | |
742 | */ | |
743 | if (volt > 9.99) | |
744 | volt = 9.99; | |
745 | if (volt < -9.99) | |
746 | volt = -9.99; | |
747 | volt += 10.0; | |
748 | volt *= 100.0; | |
749 | ||
1711287e | 750 | g_string_append_printf(s, "%.0f", volt); |
18baeeed GS |
751 | sr_spew("write bias, text %s", text_pos); |
752 | } | |
753 | ||
754 | static void write_duty_text(GString *s, double duty) | |
755 | { | |
756 | const char *text_pos; | |
757 | ||
1711287e | 758 | sr_spew("write duty, value %f", duty); |
18baeeed GS |
759 | text_pos = &s->str[s->len]; |
760 | ||
761 | /* | |
762 | * Single value in units of 0.1% (permille). Capped to the | |
763 | * 0.0..1.0 range. | |
764 | */ | |
765 | if (duty < 0.0) | |
766 | duty = 0.0; | |
767 | if (duty > 1.0) | |
768 | duty = 1.0; | |
769 | duty *= 1000.0; | |
770 | ||
1711287e | 771 | g_string_append_printf(s, "%.0f", duty); |
18baeeed GS |
772 | sr_spew("write duty, text %s", text_pos); |
773 | } | |
774 | ||
775 | static void write_phase_text(GString *s, double phase) | |
776 | { | |
777 | const char *text_pos; | |
778 | ||
1711287e | 779 | sr_spew("write phase, value %f", phase); |
18baeeed GS |
780 | text_pos = &s->str[s->len]; |
781 | ||
782 | /* | |
783 | * Single value in units of deci-degrees. | |
784 | * Kept to the 0..360 range by means of a modulo operation. | |
785 | */ | |
786 | phase = fmod(phase, 360.0); | |
787 | phase *= 10.0; | |
788 | ||
1711287e | 789 | g_string_append_printf(s, "%.0f", phase); |
18baeeed GS |
790 | sr_spew("write phase, text %s", text_pos); |
791 | } | |
792 | ||
793 | /* | |
794 | * Convenience communication wrapper. Re-uses a buffer in devc, which | |
795 | * simplifies resource handling in error paths. Sends a parameter-less | |
796 | * read-request. Then receives a response which can carry values. | |
797 | */ | |
798 | static int quick_send_read_then_recv(const struct sr_dev_inst *sdi, | |
799 | char insn, size_t idx, | |
800 | unsigned int read_timeout_ms, | |
801 | char **rhs_start, size_t *rhs_length) | |
802 | { | |
803 | struct dev_context *devc; | |
804 | GString *s; | |
805 | int ret; | |
806 | ||
807 | if (!sdi) | |
808 | return SR_ERR_ARG; | |
809 | devc = sdi->priv; | |
810 | if (!devc) | |
811 | return SR_ERR_ARG; | |
812 | if (!devc->quick_req) | |
813 | devc->quick_req = g_string_sized_new(MAX_RSP_LENGTH); | |
814 | s = devc->quick_req; | |
815 | ||
816 | g_string_truncate(s, 0); | |
817 | append_insn_read_para(s, insn, idx); | |
818 | ret = serial_send_textline(sdi, s, DELAY_AFTER_WRITE); | |
819 | if (ret != SR_OK) | |
820 | return ret; | |
821 | ||
822 | ret = serial_recv_textline(sdi, s, | |
823 | TIMEOUT_READ_CHUNK, read_timeout_ms, | |
824 | NULL, insn, idx, rhs_start, rhs_length); | |
825 | if (ret != SR_OK) | |
826 | return ret; | |
827 | ||
828 | return SR_OK; | |
829 | } | |
830 | ||
831 | /* | |
832 | * Convenience communication wrapper, re-uses a buffer in devc. Sends a | |
833 | * write-request with parameters. Then receives an "ok" style response. | |
834 | * Had to put the request details after the response related parameters | |
835 | * because of the va_list API. | |
836 | */ | |
837 | static int quick_send_write_then_recv_ok(const struct sr_dev_inst *sdi, | |
838 | unsigned int read_timeout_ms, gboolean *is_ok, | |
839 | char insn, size_t idx, const char *fmt, ...) ATTR_FMT_PRINTF(6, 7); | |
840 | static int quick_send_write_then_recv_ok(const struct sr_dev_inst *sdi, | |
841 | unsigned int read_timeout_ms, gboolean *is_ok, | |
842 | char insn, size_t idx, const char *fmt, ...) | |
843 | { | |
844 | struct dev_context *devc; | |
845 | GString *s; | |
846 | va_list args; | |
847 | int ret; | |
848 | gboolean ok; | |
849 | ||
850 | if (!sdi) | |
851 | return SR_ERR_ARG; | |
852 | devc = sdi->priv; | |
853 | if (!devc) | |
854 | return SR_ERR_ARG; | |
855 | if (!devc->quick_req) | |
856 | devc->quick_req = g_string_sized_new(MAX_RSP_LENGTH); | |
857 | s = devc->quick_req; | |
858 | ||
859 | g_string_truncate(s, 0); | |
860 | va_start(args, fmt); | |
861 | append_insn_write_para_va(s, insn, idx, fmt, args); | |
862 | va_end(args); | |
863 | ret = serial_send_textline(sdi, s, DELAY_AFTER_WRITE); | |
864 | if (ret != SR_OK) | |
865 | return ret; | |
866 | ||
867 | ret = serial_recv_textline(sdi, s, | |
868 | TIMEOUT_READ_CHUNK, read_timeout_ms, | |
869 | &ok, '\0', 0, NULL, NULL); | |
870 | if (is_ok) | |
871 | *is_ok = ok; | |
872 | if (ret != SR_OK) | |
873 | return ret; | |
874 | ||
875 | return SR_OK; | |
876 | } | |
877 | ||
878 | /* | |
879 | * High level getters/setters for device properties. | |
880 | * To be used by the api.c config get/set infrastructure. | |
881 | */ | |
882 | ||
883 | SR_PRIV int jds6600_get_chans_enable(const struct sr_dev_inst *sdi) | |
884 | { | |
885 | struct dev_context *devc; | |
886 | int ret; | |
887 | char *rdptr, *word, *endptr; | |
888 | struct devc_dev *device; | |
889 | struct devc_chan *chans; | |
890 | size_t idx; | |
891 | unsigned long on; | |
892 | ||
893 | devc = sdi->priv; | |
894 | if (!devc) | |
895 | return SR_ERR_ARG; | |
896 | ||
897 | /* Transmit the request, receive the response. */ | |
898 | ret = quick_send_read_then_recv(sdi, | |
899 | INSN_READ_PARA, IDX_CHANNELS_ENABLE, | |
900 | 0, &rdptr, NULL); | |
901 | if (ret != SR_OK) | |
902 | return ret; | |
903 | sr_dbg("get enabled, response text: %s", rdptr); | |
904 | ||
905 | /* Interpret the response (multiple values, boolean). */ | |
906 | replace_separators(rdptr); | |
907 | device = &devc->device; | |
908 | chans = devc->channel_config; | |
909 | for (idx = 0; idx < device->channel_count_gen; idx++) { | |
910 | word = sr_text_next_word(rdptr, &rdptr); | |
911 | if (!word || !*word) | |
912 | return SR_ERR_DATA; | |
913 | endptr = NULL; | |
914 | ret = sr_atoul_base(word, &on, &endptr, 10); | |
915 | if (ret != SR_OK || !endptr || *endptr) | |
916 | return SR_ERR_DATA; | |
917 | chans[idx].enabled = on; | |
918 | } | |
919 | ||
920 | return SR_OK; | |
921 | } | |
922 | ||
923 | SR_PRIV int jds6600_get_waveform(const struct sr_dev_inst *sdi, size_t ch_idx) | |
924 | { | |
925 | struct dev_context *devc; | |
926 | int ret; | |
927 | char *rdptr, *endptr; | |
928 | struct devc_wave *waves; | |
929 | struct devc_chan *chan; | |
930 | unsigned long code; | |
931 | size_t idx; | |
932 | ||
933 | if (!sdi) | |
934 | return SR_ERR_ARG; | |
935 | devc = sdi->priv; | |
936 | if (!devc) | |
937 | return SR_ERR_ARG; | |
938 | waves = &devc->waveforms; | |
939 | if (ch_idx >= ARRAY_SIZE(devc->channel_config)) | |
940 | return SR_ERR_ARG; | |
941 | chan = &devc->channel_config[ch_idx]; | |
942 | ||
943 | /* Transmit the request, receive the response. */ | |
944 | ret = quick_send_read_then_recv(sdi, | |
945 | INSN_READ_PARA, IDX_WAVEFORM_CH1 + ch_idx, | |
946 | 0, &rdptr, NULL); | |
947 | if (ret != SR_OK) | |
948 | return ret; | |
949 | sr_dbg("get waveform, response text: %s", rdptr); | |
950 | ||
951 | /* | |
952 | * Interpret the response (integer value, waveform code). | |
953 | * Lookup the firmware's code for that waveform in the | |
954 | * list of user perceivable names for waveforms. | |
955 | */ | |
956 | endptr = NULL; | |
957 | ret = sr_atoul_base(rdptr, &code, &endptr, 10); | |
958 | if (ret != SR_OK) | |
959 | return SR_ERR_DATA; | |
960 | for (idx = 0; idx < waves->names_count; idx++) { | |
961 | if (code != waves->fw_codes[idx]) | |
962 | continue; | |
963 | chan->waveform_code = code; | |
964 | chan->waveform_index = idx; | |
965 | sr_dbg("get waveform, code %lu, idx %zu, name %s", | |
966 | code, idx, waves->names[idx]); | |
967 | return SR_OK; | |
968 | } | |
969 | ||
970 | return SR_ERR_DATA; | |
971 | } | |
972 | ||
973 | #if WITH_ARBWAVE_DOWNLOAD | |
974 | /* | |
975 | * Development HACK. Get a waveform from the device. Uncertain where to | |
976 | * dump it though. Have yet to identify a sigrok API for waveforms. | |
977 | */ | |
978 | static int jds6600_get_arb_waveform(const struct sr_dev_inst *sdi, size_t idx) | |
979 | { | |
980 | struct dev_context *devc; | |
981 | struct devc_wave *waves; | |
982 | int ret; | |
983 | char *rdptr, *word, *endptr; | |
984 | size_t sample_count; | |
985 | unsigned long value; | |
986 | ||
987 | if (!sdi) | |
988 | return SR_ERR_ARG; | |
989 | devc = sdi->priv; | |
990 | if (!devc) | |
991 | return SR_ERR_ARG; | |
992 | waves = &devc->waveforms; | |
993 | ||
994 | if (idx >= waves->arbitrary_count) | |
995 | return SR_ERR_ARG; | |
996 | ||
997 | /* Transmit the request, receive the response. */ | |
998 | ret = quick_send_read_then_recv(sdi, | |
999 | INSN_READ_WAVE, idx, | |
1000 | 0, &rdptr, NULL); | |
1001 | if (ret != SR_OK) | |
1002 | return ret; | |
1003 | sr_dbg("get arb wave, response text: %s", rdptr); | |
1004 | ||
1005 | /* Extract the sequence of samples for the waveform. */ | |
1006 | replace_separators(rdptr); | |
1007 | sample_count = 0; | |
1008 | while (rdptr && *rdptr) { | |
1009 | word = sr_text_next_word(rdptr, &rdptr); | |
1010 | if (!word) | |
1011 | break; | |
1012 | endptr = NULL; | |
1013 | ret = sr_atoul_base(word, &value, &endptr, 10); | |
1014 | if (ret != SR_OK || !endptr || *endptr) { | |
1015 | sr_dbg("get arb wave, conv error: %s", word); | |
1016 | return SR_ERR_DATA; | |
1017 | } | |
1018 | sample_count++; | |
1019 | } | |
1020 | sr_dbg("get arb wave, samples count: %zu", sample_count); | |
1021 | ||
1022 | return SR_OK; | |
1023 | } | |
1024 | #endif | |
1025 | ||
1026 | SR_PRIV int jds6600_get_frequency(const struct sr_dev_inst *sdi, size_t ch_idx) | |
1027 | { | |
1028 | struct dev_context *devc; | |
1029 | struct devc_chan *chan; | |
1030 | int ret; | |
1031 | char *rdptr; | |
1032 | double freq; | |
1033 | ||
1034 | devc = sdi->priv; | |
1035 | if (!devc) | |
1036 | return SR_ERR_ARG; | |
1037 | if (ch_idx >= ARRAY_SIZE(devc->channel_config)) | |
1038 | return SR_ERR_ARG; | |
1039 | chan = &devc->channel_config[ch_idx]; | |
1040 | ||
1041 | /* Transmit the request, receive the response. */ | |
1042 | ret = quick_send_read_then_recv(sdi, | |
1043 | INSN_READ_PARA, IDX_FREQUENCY_CH1 + ch_idx, | |
1044 | 0, &rdptr, NULL); | |
1045 | if (ret != SR_OK) | |
1046 | return ret; | |
1047 | sr_dbg("get frequency, response text: %s", rdptr); | |
1048 | ||
1049 | /* Interpret the response (value and scale, frequency). */ | |
1050 | ret = parse_freq_text(rdptr, &freq); | |
1051 | if (ret != SR_OK) | |
1052 | return SR_ERR_DATA; | |
1711287e | 1053 | sr_dbg("get frequency, value %f", freq); |
18baeeed GS |
1054 | chan->output_frequency = freq; |
1055 | return SR_OK; | |
1056 | } | |
1057 | ||
1058 | SR_PRIV int jds6600_get_amplitude(const struct sr_dev_inst *sdi, size_t ch_idx) | |
1059 | { | |
1060 | struct dev_context *devc; | |
1061 | struct devc_chan *chan; | |
1062 | int ret; | |
1063 | char *rdptr; | |
1064 | double amp; | |
1065 | ||
1066 | devc = sdi->priv; | |
1067 | if (!devc) | |
1068 | return SR_ERR_ARG; | |
1069 | if (ch_idx >= ARRAY_SIZE(devc->channel_config)) | |
1070 | return SR_ERR_ARG; | |
1071 | chan = &devc->channel_config[ch_idx]; | |
1072 | ||
1073 | /* Transmit the request, receive the response. */ | |
1074 | ret = quick_send_read_then_recv(sdi, | |
1075 | INSN_READ_PARA, IDX_AMPLITUDE_CH1 + ch_idx, | |
1076 | 0, &rdptr, NULL); | |
1077 | if (ret != SR_OK) | |
1078 | return ret; | |
1079 | sr_dbg("get amplitude, response text: %s", rdptr); | |
1080 | ||
1081 | /* Interpret the response (single value, a voltage). */ | |
1082 | ret = parse_volt_text(rdptr, &); | |
1083 | if (ret != SR_OK) | |
1084 | return SR_ERR_DATA; | |
1711287e | 1085 | sr_dbg("get amplitude, value %f", amp); |
18baeeed GS |
1086 | chan->amplitude = amp; |
1087 | return SR_OK; | |
1088 | } | |
1089 | ||
1090 | SR_PRIV int jds6600_get_offset(const struct sr_dev_inst *sdi, size_t ch_idx) | |
1091 | { | |
1092 | struct dev_context *devc; | |
1093 | struct devc_chan *chan; | |
1094 | int ret; | |
1095 | char *rdptr; | |
1096 | double off; | |
1097 | ||
1098 | devc = sdi->priv; | |
1099 | if (!devc) | |
1100 | return SR_ERR_ARG; | |
1101 | if (ch_idx >= ARRAY_SIZE(devc->channel_config)) | |
1102 | return SR_ERR_ARG; | |
1103 | chan = &devc->channel_config[ch_idx]; | |
1104 | ||
1105 | /* Transmit the request, receive the response. */ | |
1106 | ret = quick_send_read_then_recv(sdi, | |
1107 | INSN_READ_PARA, IDX_OFFSET_CH1 + ch_idx, | |
1108 | 0, &rdptr, NULL); | |
1109 | if (ret != SR_OK) | |
1110 | return ret; | |
1111 | sr_dbg("get offset, response text: %s", rdptr); | |
1112 | ||
1113 | /* Interpret the response (single value, an offset). */ | |
1114 | ret = parse_bias_text(rdptr, &off); | |
1115 | if (ret != SR_OK) | |
1116 | return SR_ERR_DATA; | |
1711287e | 1117 | sr_dbg("get offset, value %f", off); |
18baeeed GS |
1118 | chan->offset = off; |
1119 | return SR_OK; | |
1120 | } | |
1121 | ||
1122 | SR_PRIV int jds6600_get_dutycycle(const struct sr_dev_inst *sdi, size_t ch_idx) | |
b1fa9aac | 1123 | { |
b1fa9aac | 1124 | struct dev_context *devc; |
18baeeed GS |
1125 | struct devc_chan *chan; |
1126 | int ret; | |
1127 | char *rdptr; | |
1128 | double duty; | |
b1fa9aac | 1129 | |
18baeeed GS |
1130 | devc = sdi->priv; |
1131 | if (!devc) | |
1132 | return SR_ERR_ARG; | |
1133 | if (ch_idx >= ARRAY_SIZE(devc->channel_config)) | |
1134 | return SR_ERR_ARG; | |
1135 | chan = &devc->channel_config[ch_idx]; | |
1136 | ||
1137 | /* Transmit the request, receive the response. */ | |
1138 | ret = quick_send_read_then_recv(sdi, | |
1139 | INSN_READ_PARA, IDX_DUTYCYCLE_CH1 + ch_idx, | |
1140 | 0, &rdptr, NULL); | |
1141 | if (ret != SR_OK) | |
1142 | return ret; | |
1143 | sr_dbg("get duty cycle, response text: %s", rdptr); | |
1144 | ||
1145 | /* Interpret the response (single value, a percentage). */ | |
1146 | ret = parse_duty_text(rdptr, &duty); | |
1147 | if (ret != SR_OK) | |
1148 | return SR_ERR_DATA; | |
1711287e | 1149 | sr_dbg("get duty cycle, value %f", duty); |
18baeeed GS |
1150 | chan->dutycycle = duty; |
1151 | return SR_OK; | |
1152 | } | |
1153 | ||
1154 | SR_PRIV int jds6600_get_phase_chans(const struct sr_dev_inst *sdi) | |
1155 | { | |
1156 | struct dev_context *devc; | |
1157 | int ret; | |
1158 | char *rdptr; | |
1159 | double phase; | |
1160 | ||
1161 | devc = sdi->priv; | |
1162 | if (!devc) | |
1163 | return SR_ERR_ARG; | |
1164 | ||
1165 | /* Transmit the request, receive the response. */ | |
1166 | ret = quick_send_read_then_recv(sdi, | |
1167 | INSN_READ_PARA, IDX_PHASE_CHANNELS, | |
1168 | 0, &rdptr, NULL); | |
1169 | if (ret != SR_OK) | |
1170 | return ret; | |
1171 | sr_dbg("get phase, response text: %s", rdptr); | |
1172 | ||
1173 | /* Interpret the response (single value, an angle). */ | |
1174 | ret = parse_phase_text(rdptr, &phase); | |
1175 | if (ret != SR_OK) | |
1176 | return SR_ERR_DATA; | |
1711287e | 1177 | sr_dbg("get phase, value %f", phase); |
18baeeed GS |
1178 | devc->channels_phase = phase; |
1179 | return SR_OK; | |
1180 | } | |
1181 | ||
1182 | SR_PRIV int jds6600_set_chans_enable(const struct sr_dev_inst *sdi) | |
1183 | { | |
1184 | struct dev_context *devc; | |
1185 | struct devc_chan *chans; | |
1186 | GString *en_text; | |
1187 | size_t idx; | |
1188 | int ret; | |
b1fa9aac | 1189 | |
b1fa9aac | 1190 | if (!sdi) |
18baeeed GS |
1191 | return SR_ERR_ARG; |
1192 | devc = sdi->priv; | |
1193 | if (!devc) | |
1194 | return SR_ERR_ARG; | |
1195 | ||
1196 | /* Transmit the request, receive an "ok" style response. */ | |
1197 | chans = devc->channel_config; | |
1198 | en_text = g_string_sized_new(20); | |
1199 | for (idx = 0; idx < devc->device.channel_count_gen; idx++) { | |
1200 | if (en_text->len) | |
1201 | g_string_append_c(en_text, ','); | |
1202 | g_string_append_c(en_text, chans[idx].enabled ? '1' : '0'); | |
1203 | } | |
1204 | sr_dbg("set enabled, request text: %s", en_text->str); | |
1205 | ret = quick_send_write_then_recv_ok(sdi, 0, NULL, | |
1206 | INSN_WRITE_PARA, IDX_CHANNELS_ENABLE, "%s", en_text->str); | |
1207 | g_string_free(en_text, 20); | |
1208 | if (ret != SR_OK) | |
1209 | return ret; | |
1210 | ||
1211 | return SR_OK; | |
1212 | } | |
b1fa9aac | 1213 | |
18baeeed GS |
1214 | SR_PRIV int jds6600_set_waveform(const struct sr_dev_inst *sdi, size_t ch_idx) |
1215 | { | |
1216 | struct dev_context *devc; | |
1217 | struct devc_chan *chan; | |
1218 | int ret; | |
1219 | ||
1220 | if (!sdi) | |
1221 | return SR_ERR_ARG; | |
b1fa9aac GS |
1222 | devc = sdi->priv; |
1223 | if (!devc) | |
18baeeed GS |
1224 | return SR_ERR_ARG; |
1225 | if (ch_idx >= devc->device.channel_count_gen) | |
1226 | return SR_ERR_ARG; | |
1227 | chan = &devc->channel_config[ch_idx]; | |
1228 | ||
1229 | /* Transmit the request, receive an "ok" style response. */ | |
1230 | ret = quick_send_write_then_recv_ok(sdi, 0, NULL, | |
1231 | INSN_WRITE_PARA, IDX_WAVEFORM_CH1 + ch_idx, | |
1232 | "%" PRIu32, chan->waveform_code); | |
1233 | if (ret != SR_OK) | |
1234 | return ret; | |
1235 | ||
1236 | return SR_OK; | |
1237 | } | |
1238 | ||
1239 | #if WITH_ARBWAVE_DOWNLOAD | |
1240 | /* | |
1241 | * Development HACK. Send a waveform to the device. Uncertain where | |
1242 | * to get it from though. Just generate some stupid pattern that's | |
1243 | * seen on the LCD later. | |
1244 | * | |
1245 | * Local experiments suggest that writing another waveform after having | |
1246 | * written one earlier results in the next waveform to become mangled. | |
1247 | * It appears to start with an all-bits-set pattern for a remarkable | |
1248 | * number of samples, before the actually written pattern is seen. Some | |
1249 | * delay after reception of the ":ok" response may be required to avoid | |
1250 | * this corruption. | |
1251 | */ | |
b1fa9aac | 1252 | |
18baeeed GS |
1253 | /* Stupid creation of one sample value. Gets waveform index and sample count. */ |
1254 | static uint16_t make_sample(size_t wave, size_t curr, size_t total) | |
1255 | { | |
1256 | uint16_t max_value, high_value, low_value; | |
1257 | size_t ival, high_width; | |
1258 | gboolean is_high; | |
1259 | ||
1260 | /* Get the waveform's amplitudes. */ | |
1261 | max_value = 4096; | |
1262 | high_value = max_value / (wave + 3); | |
1263 | high_value = max_value - high_value; | |
1264 | low_value = max_value - high_value; | |
1265 | ||
1266 | /* Get pulses' total interval, high and low half-periods. */ | |
1267 | ival = (total - 10) / wave; | |
1268 | high_width = ival / 2; | |
1269 | ||
1270 | /* Check location in the current period. */ | |
1271 | curr %= ival; | |
1272 | is_high = curr <= high_width; | |
1273 | return is_high ? high_value : low_value; | |
1274 | } | |
1275 | ||
1276 | /* Creation and download of the sequence of samples. */ | |
1277 | static int jds6600_set_arb_waveform(const struct sr_dev_inst *sdi, size_t idx) | |
1278 | { | |
1279 | struct dev_context *devc; | |
1280 | struct devc_wave *waves; | |
1281 | GString *wave_text; | |
1282 | size_t samples_total, samples_curr; | |
1283 | uint16_t value; | |
1284 | gboolean ok; | |
1285 | int ret; | |
1286 | ||
1287 | if (!sdi) | |
1288 | return SR_ERR_ARG; | |
1289 | devc = sdi->priv; | |
1290 | if (!devc) | |
1291 | return SR_ERR_ARG; | |
1292 | waves = &devc->waveforms; | |
1293 | ||
1294 | if (idx >= waves->arbitrary_count) | |
1295 | return SR_ERR_ARG; | |
1296 | ||
1297 | /* Construct a pattern that depends on the waveform index. */ | |
1298 | wave_text = g_string_sized_new(MAX_RSP_LENGTH); | |
1299 | samples_total = 2048; | |
1300 | samples_curr = 0; | |
1301 | for (samples_curr = 0; samples_curr < samples_total; samples_curr++) { | |
1302 | value = make_sample(idx, samples_curr, samples_total); | |
1303 | if (samples_curr) | |
1304 | g_string_append_c(wave_text, ','); | |
1305 | g_string_append_printf(wave_text, "%" PRIu16, value); | |
b1fa9aac | 1306 | } |
18baeeed GS |
1307 | sr_dbg("set arb wave, request text: %s", wave_text->str); |
1308 | ||
1309 | /* Transmit the request, receive an "ok" style response. */ | |
1310 | ret = quick_send_write_then_recv_ok(sdi, 0, &ok, | |
1311 | INSN_WRITE_WAVE, idx, "%s", wave_text->str); | |
1312 | if (ret != SR_OK) | |
1313 | return ret; | |
1314 | sr_dbg("set arb wave, response ok: %d", ok); | |
1315 | ||
1316 | if (DELAY_AFTER_FLASH) | |
1317 | g_usleep(DELAY_AFTER_FLASH * 1000); | |
1318 | ||
1319 | return SR_OK; | |
1320 | } | |
1321 | #endif | |
1322 | ||
1323 | SR_PRIV int jds6600_set_frequency(const struct sr_dev_inst *sdi, size_t ch_idx) | |
1324 | { | |
1325 | struct dev_context *devc; | |
1326 | struct devc_chan *chan; | |
1327 | double freq; | |
1328 | GString *freq_text; | |
1329 | int ret; | |
1330 | ||
1331 | if (!sdi) | |
1332 | return SR_ERR_ARG; | |
1333 | devc = sdi->priv; | |
1334 | if (!devc) | |
1335 | return SR_ERR_ARG; | |
1336 | if (ch_idx >= devc->device.channel_count_gen) | |
1337 | return SR_ERR_ARG; | |
1338 | chan = &devc->channel_config[ch_idx]; | |
1339 | ||
1340 | /* Limit input values to the range supported by the model. */ | |
1341 | freq = chan->output_frequency; | |
1342 | if (freq < 0.01) | |
1343 | freq = 0.01; | |
1344 | if (freq > devc->device.max_output_frequency) | |
1345 | freq = devc->device.max_output_frequency; | |
1346 | ||
1347 | /* Transmit the request, receive an "ok" style response. */ | |
1348 | freq_text = g_string_sized_new(32); | |
1349 | write_freq_text(freq_text, freq); | |
1350 | ret = quick_send_write_then_recv_ok(sdi, 0, NULL, | |
1351 | INSN_WRITE_PARA, IDX_FREQUENCY_CH1 + ch_idx, | |
1352 | "%s", freq_text->str); | |
1353 | g_string_free(freq_text, TRUE); | |
1354 | if (ret != SR_OK) | |
1355 | return ret; | |
1356 | ||
1357 | return SR_OK; | |
1358 | } | |
1359 | ||
1360 | SR_PRIV int jds6600_set_amplitude(const struct sr_dev_inst *sdi, size_t ch_idx) | |
1361 | { | |
1362 | struct dev_context *devc; | |
1363 | struct devc_chan *chan; | |
1364 | GString *volt_text; | |
1365 | int ret; | |
1366 | ||
1367 | if (!sdi) | |
1368 | return SR_ERR_ARG; | |
1369 | devc = sdi->priv; | |
1370 | if (!devc) | |
1371 | return SR_ERR_ARG; | |
1372 | if (ch_idx >= devc->device.channel_count_gen) | |
1373 | return SR_ERR_ARG; | |
1374 | chan = &devc->channel_config[ch_idx]; | |
1375 | ||
1376 | /* Transmit the request, receive an "ok" style response. */ | |
1377 | volt_text = g_string_sized_new(32); | |
1378 | write_volt_text(volt_text, chan->amplitude); | |
1379 | ret = quick_send_write_then_recv_ok(sdi, 0, NULL, | |
1380 | INSN_WRITE_PARA, IDX_AMPLITUDE_CH1 + ch_idx, | |
1381 | "%s", volt_text->str); | |
1382 | g_string_free(volt_text, TRUE); | |
1383 | if (ret != SR_OK) | |
1384 | return ret; | |
1385 | ||
1386 | return SR_OK; | |
1387 | } | |
1388 | ||
1389 | SR_PRIV int jds6600_set_offset(const struct sr_dev_inst *sdi, size_t ch_idx) | |
1390 | { | |
1391 | struct dev_context *devc; | |
1392 | struct devc_chan *chan; | |
1393 | GString *volt_text; | |
1394 | int ret; | |
1395 | ||
1396 | if (!sdi) | |
1397 | return SR_ERR_ARG; | |
1398 | devc = sdi->priv; | |
1399 | if (!devc) | |
1400 | return SR_ERR_ARG; | |
1401 | if (ch_idx >= devc->device.channel_count_gen) | |
1402 | return SR_ERR_ARG; | |
1403 | chan = &devc->channel_config[ch_idx]; | |
1404 | ||
1405 | /* Transmit the request, receive an "ok" style response. */ | |
1406 | volt_text = g_string_sized_new(32); | |
1407 | write_bias_text(volt_text, chan->offset); | |
1408 | ret = quick_send_write_then_recv_ok(sdi, 0, NULL, | |
1409 | INSN_WRITE_PARA, IDX_OFFSET_CH1 + ch_idx, | |
1410 | "%s", volt_text->str); | |
1411 | g_string_free(volt_text, TRUE); | |
1412 | if (ret != SR_OK) | |
1413 | return ret; | |
1414 | ||
1415 | return SR_OK; | |
1416 | } | |
1417 | ||
1418 | SR_PRIV int jds6600_set_dutycycle(const struct sr_dev_inst *sdi, size_t ch_idx) | |
1419 | { | |
1420 | struct dev_context *devc; | |
1421 | struct devc_chan *chan; | |
1422 | GString *duty_text; | |
1423 | int ret; | |
1424 | ||
1425 | if (!sdi) | |
1426 | return SR_ERR_ARG; | |
1427 | devc = sdi->priv; | |
1428 | if (!devc) | |
1429 | return SR_ERR_ARG; | |
1430 | if (ch_idx >= devc->device.channel_count_gen) | |
1431 | return SR_ERR_ARG; | |
1432 | chan = &devc->channel_config[ch_idx]; | |
1433 | ||
1434 | /* Transmit the request, receive an "ok" style response. */ | |
1435 | duty_text = g_string_sized_new(32); | |
1436 | write_duty_text(duty_text, chan->dutycycle); | |
1437 | ret = quick_send_write_then_recv_ok(sdi, 0, NULL, | |
1438 | INSN_WRITE_PARA, IDX_DUTYCYCLE_CH1 + ch_idx, | |
1439 | "%s", duty_text->str); | |
1440 | g_string_free(duty_text, TRUE); | |
1441 | if (ret != SR_OK) | |
1442 | return ret; | |
1443 | ||
1444 | return SR_OK; | |
1445 | } | |
1446 | ||
1447 | SR_PRIV int jds6600_set_phase_chans(const struct sr_dev_inst *sdi) | |
1448 | { | |
1449 | struct dev_context *devc; | |
1450 | GString *phase_text; | |
1451 | int ret; | |
1452 | ||
1453 | if (!sdi) | |
1454 | return SR_ERR_ARG; | |
1455 | devc = sdi->priv; | |
1456 | if (!devc) | |
1457 | return SR_ERR_ARG; | |
1458 | ||
1459 | /* Transmit the request, receive an "ok" style response. */ | |
1460 | phase_text = g_string_sized_new(32); | |
1461 | write_phase_text(phase_text, devc->channels_phase); | |
1462 | ret = quick_send_write_then_recv_ok(sdi, 0, NULL, | |
1463 | INSN_WRITE_PARA, IDX_PHASE_CHANNELS, | |
1464 | "%s", phase_text->str); | |
1465 | g_string_free(phase_text, TRUE); | |
1466 | if (ret != SR_OK) | |
1467 | return ret; | |
1468 | ||
1469 | return SR_OK; | |
1470 | } | |
1471 | ||
1472 | /* | |
1473 | * High level helpers for the scan/probe phase. Identify the attached | |
1474 | * device and synchronize to its current state and its capabilities. | |
1475 | */ | |
1476 | ||
1477 | SR_PRIV int jds6600_identify(struct sr_dev_inst *sdi) | |
1478 | { | |
1479 | struct dev_context *devc; | |
1480 | int ret; | |
1481 | char *rdptr, *endptr; | |
1482 | unsigned long devtype; | |
1483 | ||
1484 | (void)append_insn_write_para_dots; | |
1485 | ||
1486 | if (!sdi) | |
1487 | return SR_ERR_ARG; | |
1488 | devc = sdi->priv; | |
1489 | if (!devc) | |
1490 | return SR_ERR_ARG; | |
1491 | ||
1492 | /* Transmit "read device type" request, receive the response. */ | |
1493 | ret = quick_send_read_then_recv(sdi, | |
1494 | INSN_READ_PARA, IDX_DEVICE_TYPE, | |
1495 | TIMEOUT_IDENTIFY, &rdptr, NULL); | |
1496 | if (ret != SR_OK) | |
1497 | return ret; | |
1498 | sr_dbg("identify, device type '%s'", rdptr); | |
1499 | ||
1500 | /* Interpret the response (integer value, max freq). */ | |
1501 | endptr = NULL; | |
1502 | ret = sr_atoul_base(rdptr, &devtype, &endptr, 10); | |
1503 | if (ret != SR_OK || !endptr) | |
1504 | return SR_ERR_DATA; | |
1505 | devc->device.device_type = devtype; | |
1506 | ||
1507 | /* Transmit "read serial number" request. receive response. */ | |
1508 | ret = quick_send_read_then_recv(sdi, | |
1509 | INSN_READ_PARA, IDX_SERIAL_NUMBER, | |
1510 | 0, &rdptr, NULL); | |
1511 | if (ret != SR_OK) | |
1512 | return ret; | |
1513 | sr_dbg("identify, serial number '%s'", rdptr); | |
1514 | ||
1515 | /* Keep the response (in string format, some serial number). */ | |
1516 | devc->device.serial_number = g_strdup(rdptr); | |
1517 | ||
1518 | return SR_OK; | |
1519 | } | |
1520 | ||
1521 | SR_PRIV int jds6600_setup_devc(struct sr_dev_inst *sdi) | |
1522 | { | |
1523 | struct dev_context *devc; | |
1524 | size_t alloc_count, assign_idx, idx; | |
1525 | struct devc_dev *device; | |
1526 | struct devc_wave *waves; | |
1527 | enum waveform_index_t code; | |
1528 | char *name; | |
1529 | int ret; | |
1530 | ||
1531 | if (!sdi) | |
1532 | return SR_ERR_ARG; | |
1533 | devc = sdi->priv; | |
1534 | if (!devc) | |
1535 | return SR_ERR_ARG; | |
1536 | ||
1537 | /* | |
1538 | * Derive maximum output frequency from detected device type. | |
1539 | * Open coded generator channel count. | |
1540 | */ | |
1541 | device = &devc->device; | |
1542 | if (!device->device_type) | |
1543 | return SR_ERR_DATA; | |
1544 | device->max_output_frequency = device->device_type; | |
1545 | device->max_output_frequency *= SR_MHZ(1); | |
1546 | device->channel_count_gen = MAX_GEN_CHANNELS; | |
1547 | ||
1548 | /* Construct the list of waveform names and their codes. */ | |
1549 | waves = &devc->waveforms; | |
1550 | waves->builtin_count = WAVES_COUNT_BUILTIN; | |
1551 | waves->arbitrary_count = WAVES_COUNT_ARBITRARY; | |
1552 | alloc_count = waves->builtin_count; | |
1553 | alloc_count += waves->arbitrary_count; | |
1554 | waves->names_count = alloc_count; | |
1555 | waves->fw_codes = g_malloc0(alloc_count * sizeof(waves->fw_codes[0])); | |
1556 | alloc_count++; | |
1557 | waves->names = g_malloc0(alloc_count * sizeof(waves->names[0])); | |
1711287e GS |
1558 | if (!waves->names || !waves->fw_codes) { |
1559 | g_free(waves->names); | |
1560 | g_free(waves->fw_codes); | |
18baeeed | 1561 | return SR_ERR_MALLOC; |
1711287e | 1562 | } |
18baeeed GS |
1563 | assign_idx = 0; |
1564 | for (idx = 0; idx < waves->builtin_count; idx++) { | |
1565 | code = idx; | |
1566 | name = g_strdup(waveform_names[idx]); | |
1567 | waves->fw_codes[assign_idx] = code; | |
1568 | waves->names[assign_idx] = name; | |
1569 | assign_idx++; | |
1570 | } | |
1571 | for (idx = 0; idx < waves->arbitrary_count; idx++) { | |
1572 | code = WAVE_ARB01 + idx; | |
1573 | name = g_strdup_printf(WAVEFORM_ARB_NAME_FMT, idx + 1); | |
1574 | waves->fw_codes[assign_idx] = code; | |
1575 | waves->names[assign_idx] = name; | |
1576 | assign_idx++; | |
1577 | } | |
1578 | waves->names[assign_idx] = NULL; | |
1579 | ||
1580 | /* | |
1581 | * Populate internal channel configuration details from the | |
1582 | * device's current state. Emit a series of queries which | |
1583 | * update internal knowledge. | |
1711287e GS |
1584 | * |
1585 | * Implementation detail: Channel count is low, all parameters | |
1586 | * are simple scalars. Communication cycles are few, while we | |
1587 | * still are in the scan/probe phase and successfully verified | |
1588 | * the device to respond. Disconnects and other exceptional | |
1589 | * conditions are extremely unlikely. Not checking every getter | |
1590 | * call's return value is acceptable here. | |
18baeeed GS |
1591 | */ |
1592 | ret = SR_OK; | |
1593 | ret |= jds6600_get_chans_enable(sdi); | |
1594 | for (idx = 0; idx < device->channel_count_gen; idx++) { | |
1595 | ret |= jds6600_get_waveform(sdi, idx); | |
1596 | ret |= jds6600_get_frequency(sdi, idx); | |
1597 | ret |= jds6600_get_amplitude(sdi, idx); | |
1598 | ret |= jds6600_get_offset(sdi, idx); | |
1599 | ret |= jds6600_get_dutycycle(sdi, idx); | |
1711287e GS |
1600 | if (ret != SR_OK) |
1601 | break; | |
18baeeed GS |
1602 | } |
1603 | ret |= jds6600_get_phase_chans(sdi); | |
18baeeed GS |
1604 | if (ret != SR_OK) |
1605 | return SR_ERR_DATA; | |
1606 | ||
1607 | #if WITH_ARBWAVE_DOWNLOAD | |
1608 | /* | |
1609 | * Development HACK, to see how waveform upload works. | |
1610 | * How to forward the data to the application? Or the | |
1611 | * sigrok session actually? Provide these as acquisition | |
1612 | * results? | |
1613 | */ | |
1614 | ret |= jds6600_get_arb_waveform(sdi, 13); | |
1615 | if (ret != SR_OK) | |
1616 | return SR_ERR_DATA; | |
1617 | ret |= jds6600_set_arb_waveform(sdi, 12); | |
1618 | ret |= jds6600_set_arb_waveform(sdi, 13); | |
1619 | if (ret != SR_OK) | |
1620 | return SR_ERR_DATA; | |
1621 | #endif | |
b1fa9aac | 1622 | |
18baeeed | 1623 | return SR_OK; |
b1fa9aac | 1624 | } |