]> sigrok.org Git - libsigrok.git/blob - src/hardware/gwinstek-gpd/api.c
gwinstek-gpd: Retry device identification on failure
[libsigrok.git] / src / hardware / gwinstek-gpd / api.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2018 Bastian Schmitz <bastian.schmitz@udo.edu>
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 <config.h>
21 #include <string.h>
22 #include "protocol.h"
23
24 #define IDN_RETRIES 3 /* at least 2 */
25
26 static const uint32_t scanopts[] = {
27         SR_CONF_CONN,
28         SR_CONF_SERIALCOMM,
29 };
30
31 static const uint32_t drvopts[] = {
32         SR_CONF_POWER_SUPPLY,
33 };
34
35 static const uint32_t devopts[] = {
36         SR_CONF_CONTINUOUS,
37         SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
38         SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
39         SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
40         SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
41 };
42
43 static const uint32_t devopts_cg[] = {
44         SR_CONF_VOLTAGE | SR_CONF_GET,
45         SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
46         SR_CONF_CURRENT | SR_CONF_GET,
47         SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
48 };
49
50 static const char *channel_modes[] = {
51         "Independent",
52 };
53
54 static const struct gpd_model models[] = {
55         { GPD_2303S, "GPD-2303S",
56                 CHANMODE_INDEPENDENT,
57                 2,
58                 {
59                         /* Channel 1 */
60                         { { 0, 30, 0.001 }, { 0, 3, 0.001 } },
61                         /* Channel 2 */
62                         { { 0, 30, 0.001 }, { 0, 3, 0.001 } },
63                 },
64         },
65         { GPD_3303S, "GPD-3303S",
66                 CHANMODE_INDEPENDENT,
67                 2,
68                 {
69                         /* Channel 1 */
70                         { { 0, 32, 0.001 }, { 0, 3.2, 0.001 } },
71                         /* Channel 2 */
72                         { { 0, 32, 0.001 }, { 0, 3.2, 0.001 } },
73                 },
74         },
75 };
76
77 static GSList *scan(struct sr_dev_driver *di, GSList *options)
78 {
79         const char *conn, *serialcomm;
80         const struct gpd_model *model;
81         const struct sr_config *src;
82         struct sr_channel *ch;
83         struct sr_channel_group *cg;
84         GSList *l;
85         struct sr_serial_dev_inst *serial;
86         struct sr_dev_inst *sdi;
87         char reply[100];
88         unsigned int i;
89         struct dev_context *devc;
90         char channel[10];
91         GRegex *regex;
92         GMatchInfo *match_info;
93         unsigned int cc_cv_ch1, cc_cv_ch2, track1, track2, beep, baud1, baud2;
94
95         serial = NULL;
96         match_info = NULL;
97         regex = NULL;
98         conn = NULL;
99         serialcomm = NULL;
100
101         for (l = options; l; l = l->next) {
102                 src = l->data;
103                 switch (src->key) {
104                 case SR_CONF_CONN:
105                         conn = g_variant_get_string(src->data, NULL);
106                         break;
107                 case SR_CONF_SERIALCOMM:
108                         serialcomm = g_variant_get_string(src->data, NULL);
109                         break;
110                 }
111         }
112
113         if (!conn)
114                 return NULL;
115         if (!serialcomm)
116                 serialcomm = "115200/8n1";
117         sr_info("Probing serial port %s @ %s", conn, serialcomm);
118         serial = sr_serial_dev_inst_new(conn, serialcomm);
119         if (serial_open(serial, SERIAL_RDWR) != SR_OK)
120                 return NULL;
121
122         /*
123          * Problem: we need to clear the GPD receive buffer before we
124          * can expect it to process commands correctly.
125          *
126          * Do not just send a newline, since that may cause it to
127          * execute a currently buffered command.
128          *
129          * Solution: Send identification request a few times.
130          * The first should corrupt any previous buffered command if present
131          * and respond with "Invalid Character." or respond directly with
132          * an identification string starting with "GW INSTEK"
133          */
134         for (i = 0; i<IDN_RETRIES; ++i) {
135                 /* Request the GPD to identify itself */
136                 gpd_send_cmd(serial, "*IDN?\n");
137                 if (gpd_receive_reply(serial, reply, sizeof(reply)) == SR_OK) {
138                         if (0 == strncmp(reply, "GW INSTEK", 9 )) {
139                                 break;
140                         }
141                 }
142         }
143         if (i == IDN_RETRIES) {
144                 sr_err("Device did not reply to identification request.");
145                 serial_flush(serial);
146                 goto error;
147         }
148
149         /*
150          * Returned identification string is for example:
151          * "GW INSTEK,GPD-2303S,SN:ER915277,V2.10"
152          */
153         regex = g_regex_new("GW INSTEK,(.+),SN:(.+),(V.+)", 0, 0, NULL);
154         if (!g_regex_match(regex, reply, 0, &match_info)) {
155                 sr_err("Unsupported model '%s'.", reply);
156                 goto error;
157         }
158
159         model = NULL;
160         for (i = 0; i < ARRAY_SIZE(models); i++) {
161                 if (!strcmp(g_match_info_fetch(match_info, 1), models[i].name)) {
162                         model = &models[i];
163                         break;
164                 }
165         }
166         if (!model) {
167                 sr_err("Unsupported model '%s'.", reply);
168                 goto error;
169         }
170
171         sr_info("Detected model '%s'.", model->name);
172
173         sdi = g_malloc0(sizeof(struct sr_dev_inst));
174         sdi->status = SR_ST_INACTIVE;
175         sdi->vendor = g_strdup("GW Instek");
176         sdi->model = g_strdup(model->name);
177         sdi->inst_type = SR_INST_SERIAL;
178         sdi->conn = serial;
179
180         for (i = 0; i < model->num_channels; i++) {
181                 snprintf(channel, sizeof(channel), "CH%d", i + 1);
182                 ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel);
183                 cg = g_malloc(sizeof(struct sr_channel_group));
184                 cg->name = g_strdup(channel);
185                 cg->channels = g_slist_append(NULL, ch);
186                 cg->priv = NULL;
187                 sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
188         }
189
190         devc = g_malloc0(sizeof(struct dev_context));
191         sr_sw_limits_init(&devc->limits);
192         devc->model = model;
193         devc->config = g_malloc0(sizeof(struct per_channel_config)
194                                  * model->num_channels);
195         sdi->priv = devc;
196
197         serial_flush(serial);
198         gpd_send_cmd(serial, "STATUS?\n");
199         gpd_receive_reply(serial, reply, sizeof(reply));
200
201         if (sscanf(reply, "%1u%1u%1u%1u%1u%1u%1u%1u", &cc_cv_ch1,
202                         &cc_cv_ch2, &track1, &track2, &beep,
203                         &devc->output_enabled, &baud1, &baud2) != 8) {
204                 /* old firmware (< 2.00?) responds with different format */
205                 if (sscanf(reply, "%1u %1u %1u %1u %1u X %1u X", &cc_cv_ch1,
206                            &cc_cv_ch2, &track1, &track2, &beep,
207                            &devc->output_enabled) != 6) {
208                         sr_err("Invalid reply to STATUS: '%s'.", reply);
209                         goto error;
210                 }
211                 /* ignore remaining two lines of status message */
212                 gpd_receive_reply(serial, reply, sizeof(reply));
213                 gpd_receive_reply(serial, reply, sizeof(reply));
214         }
215
216         for (i = 0; i < model->num_channels; ++i) {
217                 gpd_send_cmd(serial, "ISET%d?\n", i + 1);
218                 gpd_receive_reply(serial, reply, sizeof(reply));
219                 if (sscanf(reply, "%f", &devc->config[i].output_current_max) != 1) {
220                         sr_err("Invalid reply to ISETn?: '%s'.", reply);
221                         goto error;
222                 }
223
224                 gpd_send_cmd(serial, "VSET%d?\n", i + 1);
225                 gpd_receive_reply(serial, reply, sizeof(reply));
226                 if (sscanf(reply, "%f", &devc->config[i].output_voltage_max) != 1) {
227                         sr_err("Invalid reply to VSETn?: '%s'.", reply);
228                         goto error;
229                 }
230                 gpd_send_cmd(serial, "IOUT%d?\n", i + 1);
231                 gpd_receive_reply(serial, reply, sizeof(reply));
232                 if (sscanf(reply, "%f", &devc->config[i].output_current_last) != 1) {
233                         sr_err("Invalid reply to IOUTn?: '%s'.", reply);
234                         goto error;
235                 }
236                 gpd_send_cmd(serial, "VOUT%d?\n", i + 1);
237                 gpd_receive_reply(serial, reply, sizeof(reply));
238                 if (sscanf(reply, "%f", &devc->config[i].output_voltage_last) != 1) {
239                         sr_err("Invalid reply to VOUTn?: '%s'.", reply);
240                         goto error;
241                 }
242         }
243
244         serial_close(serial);
245
246         return std_scan_complete(di, g_slist_append(NULL, sdi));
247
248 error:
249         if (match_info)
250                 g_match_info_free(match_info);
251         if (regex)
252                 g_regex_unref(regex);
253         if (serial)
254                 serial_close(serial);
255
256         return NULL;
257 }
258
259 static int config_get(uint32_t key, GVariant **data,
260         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
261 {
262         int channel;
263         const struct dev_context *devc;
264         const struct sr_channel *ch;
265
266         if (!sdi)
267                 return SR_ERR_ARG;
268
269         devc = sdi->priv;
270
271         if (!cg) {
272                 switch (key) {
273                 case SR_CONF_LIMIT_SAMPLES:
274                 case SR_CONF_LIMIT_MSEC:
275                         return sr_sw_limits_config_get(&devc->limits, key, data);
276                 case SR_CONF_CHANNEL_CONFIG:
277                         *data = g_variant_new_string(
278                                 channel_modes[devc->channel_mode]);
279                         break;
280                 case SR_CONF_ENABLED:
281                         *data = g_variant_new_boolean(devc->output_enabled);
282                         break;
283                 default:
284                         return SR_ERR_NA;
285                 }
286         } else {
287                 ch = cg->channels->data;
288                 channel = ch->index;
289                 switch (key) {
290                 case SR_CONF_VOLTAGE:
291                         *data = g_variant_new_double(
292                                 devc->config[channel].output_voltage_last);
293                         break;
294                 case SR_CONF_VOLTAGE_TARGET:
295                         *data = g_variant_new_double(
296                                 devc->config[channel].output_voltage_max);
297                         break;
298                 case SR_CONF_CURRENT:
299                         *data = g_variant_new_double(
300                                 devc->config[channel].output_current_last);
301                         break;
302                 case SR_CONF_CURRENT_LIMIT:
303                         *data = g_variant_new_double(
304                                 devc->config[channel].output_current_max);
305                         break;
306                 default:
307                         return SR_ERR_NA;
308                 }
309         }
310
311         return SR_OK;
312 }
313
314 static int config_set(uint32_t key, GVariant *data,
315         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
316 {
317         int ret, channel;
318         const struct sr_channel *ch;
319         double dval;
320         gboolean bval;
321         struct dev_context *devc;
322
323         devc = sdi->priv;
324
325         ret = SR_OK;
326
327         switch (key) {
328         case SR_CONF_LIMIT_MSEC:
329         case SR_CONF_LIMIT_SAMPLES:
330                 return sr_sw_limits_config_set(&devc->limits, key, data);
331         case SR_CONF_ENABLED:
332                 bval = g_variant_get_boolean(data);
333                 gpd_send_cmd(sdi->conn, "OUT%c\n", bval ? '1' : '0');
334                 devc->output_enabled = bval;
335                 break;
336         case SR_CONF_VOLTAGE_TARGET:
337                 ch = cg->channels->data;
338                 channel = ch->index;
339                 dval = g_variant_get_double(data);
340                 if (dval < devc->model->channels[channel].voltage[0]
341                     || dval > devc->model->channels[channel].voltage[1])
342                         return SR_ERR_ARG;
343                 gpd_send_cmd(sdi->conn, "VSET%d:%05.3lf\n", channel + 1, dval);
344                 devc->config[channel].output_voltage_max = dval;
345                 break;
346         case SR_CONF_CURRENT_LIMIT:
347                 ch = cg->channels->data;
348                 channel = ch->index;
349                 dval = g_variant_get_double(data);
350                 if (dval < devc->model->channels[channel].current[0]
351                     || dval > devc->model->channels[channel].current[1])
352                         return SR_ERR_ARG;
353                 gpd_send_cmd(sdi->conn, "ISET%d:%05.3lf\n", channel + 1, dval);
354                 devc->config[channel].output_current_max = dval;
355                 break;
356         default:
357                 ret = SR_ERR_NA;
358                 break;
359         }
360
361         return ret;
362 }
363
364 static int config_list(uint32_t key, GVariant **data,
365         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
366 {
367         const struct dev_context *devc;
368         const struct sr_channel *ch;
369         int channel;
370
371         devc = (sdi) ? sdi->priv : NULL;
372
373         if (!cg) {
374                 switch (key) {
375                 case SR_CONF_SCAN_OPTIONS:
376                 case SR_CONF_DEVICE_OPTIONS:
377                         return STD_CONFIG_LIST(key, data, sdi, cg, scanopts,
378                                                drvopts, devopts);
379                 case SR_CONF_CHANNEL_CONFIG:
380                         *data = g_variant_new_strv(ARRAY_AND_SIZE(channel_modes));
381                         break;
382                 default:
383                         return SR_ERR_NA;
384                 }
385         } else {
386                 ch = cg->channels->data;
387                 channel = ch->index;
388
389                 switch (key) {
390                 case SR_CONF_DEVICE_OPTIONS:
391                         *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
392                         break;
393                 case SR_CONF_VOLTAGE_TARGET:
394                         *data = std_gvar_min_max_step_array(
395                                 devc->model->channels[channel].voltage);
396                         break;
397                 case SR_CONF_CURRENT_LIMIT:
398                         *data = std_gvar_min_max_step_array(
399                                 devc->model->channels[channel].current);
400                         break;
401                 default:
402                         return SR_ERR_NA;
403                 }
404         }
405
406         return SR_OK;
407 }
408
409 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
410 {
411         struct dev_context *devc;
412         struct sr_serial_dev_inst *serial;
413
414         devc = sdi->priv;
415
416         sr_sw_limits_acquisition_start(&devc->limits);
417         std_session_send_df_header(sdi);
418
419         devc->reply_pending = FALSE;
420         devc->req_sent_at = 0;
421         serial = sdi->conn;
422         serial_source_add(sdi->session, serial, G_IO_IN, 100,
423                           gpd_receive_data, (void *)sdi);
424
425         return SR_OK;
426 }
427
428 static struct sr_dev_driver gwinstek_gpd_driver_info = {
429         .name = "gwinstek-gpd",
430         .longname = "GW Instek GPD",
431         .api_version = 1,
432         .init = std_init,
433         .cleanup = std_cleanup,
434         .scan = scan,
435         .dev_list = std_dev_list,
436         .dev_clear = std_dev_clear,
437         .config_get = config_get,
438         .config_set = config_set,
439         .config_list = config_list,
440         .dev_open = std_serial_dev_open,
441         .dev_close = std_serial_dev_close,
442         .dev_acquisition_start = dev_acquisition_start,
443         .dev_acquisition_stop = std_serial_dev_acquisition_stop,
444         .context = NULL,
445 };
446 SR_REGISTER_DEV_DRIVER(gwinstek_gpd_driver_info);