]> sigrok.org Git - libsigrok.git/blob - hardware/serial-dmm/api.c
serial-dmm: Add Metex M-3640D support.
[libsigrok.git] / hardware / serial-dmm / api.c
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
5  * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
6  * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <glib.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <errno.h>
28 #include "libsigrok.h"
29 #include "libsigrok-internal.h"
30 #include "protocol.h"
31
32 static const int hwopts[] = {
33         SR_HWOPT_CONN,
34         SR_HWOPT_SERIALCOMM,
35         0,
36 };
37
38 static const int hwcaps[] = {
39         SR_HWCAP_MULTIMETER,
40         SR_HWCAP_LIMIT_SAMPLES,
41         SR_HWCAP_CONTINUOUS,
42         0,
43 };
44
45 static const char *probe_names[] = {
46         "Probe",
47         NULL,
48 };
49
50 SR_PRIV struct sr_dev_driver digitek_dt4000zc_driver_info;
51 SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info;
52 SR_PRIV struct sr_dev_driver metex_me31_driver_info;
53 SR_PRIV struct sr_dev_driver peaktech_3410_driver_info;
54 SR_PRIV struct sr_dev_driver mastech_mas345_driver_info;
55 SR_PRIV struct sr_dev_driver va_va18b_driver_info;
56 SR_PRIV struct sr_dev_driver metex_m3640d_driver_info;
57
58 static struct sr_dev_driver *di_dt4000zc = &digitek_dt4000zc_driver_info;
59 static struct sr_dev_driver *di_tp4000zc = &tekpower_tp4000zc_driver_info;
60 static struct sr_dev_driver *di_me31 = &metex_me31_driver_info;
61 static struct sr_dev_driver *di_3410 = &peaktech_3410_driver_info;
62 static struct sr_dev_driver *di_mas345 = &mastech_mas345_driver_info;
63 static struct sr_dev_driver *di_va18b = &va_va18b_driver_info;
64 static struct sr_dev_driver *di_m3640d = &metex_m3640d_driver_info;
65
66 /* After hw_init() this will point to a device-specific entry (see above). */
67 static struct sr_dev_driver *di = NULL;
68
69 SR_PRIV struct dmm_info dmms[] = {
70         {
71                 "Digitek", "DT4000ZC", "2400/8n1", 2400,
72                 FS9721_PACKET_SIZE, NULL,
73                 sr_fs9721_packet_valid, sr_fs9721_parse,
74                 dmm_details_dt4000zc,
75         },
76         {
77                 "TekPower", "TP4000ZC", "2400/8n1", 2400,
78                 FS9721_PACKET_SIZE, NULL,
79                 sr_fs9721_packet_valid, sr_fs9721_parse,
80                 dmm_details_tp4000zc,
81         },
82         {
83                 "Metex", "ME-31", "600/7n2/rts=0/dtr=1", 600,
84                 METEX14_PACKET_SIZE, sr_metex14_packet_request,
85                 sr_metex14_packet_valid, sr_metex14_parse,
86                 NULL,
87         },
88         {
89                 "Peaktech", "3410", "600/7n2/rts=0/dtr=1", 600,
90                 METEX14_PACKET_SIZE, sr_metex14_packet_request,
91                 sr_metex14_packet_valid, sr_metex14_parse,
92                 NULL,
93         },
94         {
95                 "MASTECH", "MAS345", "600/7n2/rts=0/dtr=1", 600,
96                 METEX14_PACKET_SIZE, sr_metex14_packet_request,
97                 sr_metex14_packet_valid, sr_metex14_parse,
98                 NULL,
99         },
100         {
101                 "V&A", "VA18B", "2400/8n1", 2400,
102                 FS9721_PACKET_SIZE, NULL,
103                 sr_fs9721_packet_valid, sr_fs9721_parse,
104                 dmm_details_va18b,
105         },
106         {
107                 "Metex", "M-3640D", "1200/7n2/rts=0/dtr=1", 1200,
108                 METEX14_PACKET_SIZE, sr_metex14_packet_request,
109                 sr_metex14_packet_valid, sr_metex14_parse,
110                 NULL,
111         },
112 };
113
114 /* Properly close and free all devices. */
115 static int clear_instances(void)
116 {
117         struct sr_dev_inst *sdi;
118         struct drv_context *drvc;
119         struct dev_context *devc;
120         GSList *l;
121
122         if (!(drvc = di->priv))
123                 return SR_OK;
124
125         drvc = di->priv;
126         for (l = drvc->instances; l; l = l->next) {
127                 if (!(sdi = l->data))
128                         continue;
129                 if (!(devc = sdi->priv))
130                         continue;
131                 sr_serial_dev_inst_free(devc->serial);
132                 sr_dev_inst_free(sdi);
133         }
134         g_slist_free(drvc->instances);
135         drvc->instances = NULL;
136
137         return SR_OK;
138 }
139
140 static int hw_init(int dmm)
141 {
142         struct drv_context *drvc;
143
144         if (!(drvc = g_try_malloc0(sizeof(struct drv_context)))) {
145                 sr_err("Driver context malloc failed.");
146                 return SR_ERR_MALLOC;
147         }
148
149         if (dmm == DIGITEK_DT4000ZC)
150                 di = di_dt4000zc;
151         if (dmm == TEKPOWER_TP4000ZC)
152                 di = di_tp4000zc;
153         if (dmm == METEX_ME31)
154                 di = di_me31;
155         if (dmm == PEAKTECH_3410)
156                 di = di_3410;
157         if (dmm == MASTECH_MAS345)
158                 di = di_mas345;
159         if (dmm == VA_VA18B)
160                 di = di_va18b;
161         if (dmm == METEX_M3640D)
162                 di = di_m3640d;
163         sr_dbg("Selected '%s' subdriver.", di->name);
164
165         di->priv = drvc;
166
167         return SR_OK;
168 }
169
170 static int hw_init_digitek_dt4000zc(void)
171 {
172         return hw_init(DIGITEK_DT4000ZC);
173 }
174
175 static int hw_init_tekpower_tp4000zc(void)
176 {
177         return hw_init(TEKPOWER_TP4000ZC);
178 }
179
180 static int hw_init_metex_me31(void)
181 {
182         return hw_init(METEX_ME31);
183 }
184
185 static int hw_init_peaktech_3410(void)
186 {
187         return hw_init(PEAKTECH_3410);
188 }
189
190 static int hw_init_mastech_mas345(void)
191 {
192         return hw_init(MASTECH_MAS345);
193 }
194
195 static int hw_init_va_va18b(void)
196 {
197         return hw_init(VA_VA18B);
198 }
199
200 static int hw_init_metex_m3640d(void)
201 {
202         return hw_init(METEX_M3640D);
203 }
204
205 static GSList *scan(const char *conn, const char *serialcomm, int dmm)
206 {
207         struct sr_dev_inst *sdi;
208         struct drv_context *drvc;
209         struct dev_context *devc;
210         struct sr_probe *probe;
211         struct sr_serial_dev_inst *serial;
212         GSList *devices;
213         int dropped, ret;
214         size_t len;
215         uint8_t buf[128];
216
217         if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
218                 return NULL;
219
220         if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
221                 return NULL;
222
223         sr_info("Probing port %s.", conn);
224
225         drvc = di->priv;
226         devices = NULL;
227         serial_flush(serial);
228
229         /*
230          * There's no way to get an ID from the multimeter. It just sends data
231          * periodically, so the best we can do is check if the packets match
232          * the expected format.
233          */
234
235         /* Let's get a bit of data and see if we can find a packet. */
236         len = sizeof(buf);
237
238         /* Request a packet if the DMM requires this. */
239         if (dmms[dmm].packet_request) {
240                 if ((ret = dmms[dmm].packet_request(serial)) < 0) {
241                         sr_err("Failed to request packet: %d.", ret);
242                         return FALSE;
243                 }
244         }
245
246         ret = serial_stream_detect(serial, buf, &len, dmms[dmm].packet_size,
247                                    dmms[dmm].packet_valid, 1000,
248                                    dmms[dmm].baudrate);
249         if (ret != SR_OK)
250                 goto scan_cleanup;
251
252         /*
253          * If we dropped more than two packets worth of data, something is
254          * wrong. We shouldn't quit however, since the dropped bytes might be
255          * just zeroes at the beginning of the stream. Those can occur as a
256          * combination of the nonstandard cable that ships with this device and
257          * the serial port or USB to serial adapter.
258          */
259         dropped = len - dmms[dmm].packet_size;
260         if (dropped > 2 * dmms[dmm].packet_size)
261                 sr_warn("Had to drop too much data.");
262
263         sr_info("Found device on port %s.", conn);
264
265         if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, dmms[dmm].vendor,
266                                     dmms[dmm].device, "")))
267                 goto scan_cleanup;
268
269         if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
270                 sr_err("Device context malloc failed.");
271                 goto scan_cleanup;
272         }
273
274         devc->serial = serial;
275
276         sdi->priv = devc;
277         sdi->driver = di;
278         if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
279                 goto scan_cleanup;
280         sdi->probes = g_slist_append(sdi->probes, probe);
281         drvc->instances = g_slist_append(drvc->instances, sdi);
282         devices = g_slist_append(devices, sdi);
283
284 scan_cleanup:
285         serial_close(serial);
286
287         return devices;
288 }
289
290 static GSList *hw_scan(GSList *options)
291 {
292         struct sr_hwopt *opt;
293         GSList *l, *devices;
294         const char *conn, *serialcomm;
295         int dmm;
296
297         conn = serialcomm = NULL;
298         for (l = options; l; l = l->next) {
299                 opt = l->data;
300                 switch (opt->hwopt) {
301                 case SR_HWOPT_CONN:
302                         conn = opt->value;
303                         break;
304                 case SR_HWOPT_SERIALCOMM:
305                         serialcomm = opt->value;
306                         break;
307                 }
308         }
309         if (!conn)
310                 return NULL;
311
312         if (!strcmp(di->name, "digitek-dt4000zc"))
313                 dmm = 0;
314         if (!strcmp(di->name, "tekpower-tp4000zc"))
315                 dmm = 1;
316         if (!strcmp(di->name, "metex-me31"))
317                 dmm = 2;
318         if (!strcmp(di->name, "peaktech-3410"))
319                 dmm = 3;
320         if (!strcmp(di->name, "mastech-mas345"))
321                 dmm = 4;
322         if (!strcmp(di->name, "va-va18b"))
323                 dmm = 5;
324         if (!strcmp(di->name, "metex-m3640d"))
325                 dmm = 6;
326
327         if (serialcomm) {
328                 /* Use the provided comm specs. */
329                 devices = scan(conn, serialcomm, dmm);
330         } else {
331                 /* Try the default. */
332                 devices = scan(conn, dmms[dmm].conn, dmm);
333         }
334
335         return devices;
336 }
337
338 static GSList *hw_dev_list(void)
339 {
340         struct drv_context *drvc;
341
342         drvc = di->priv;
343
344         return drvc->instances;
345 }
346
347 static int hw_dev_open(struct sr_dev_inst *sdi)
348 {
349         struct dev_context *devc;
350
351         if (!(devc = sdi->priv)) {
352                 sr_err("sdi->priv was NULL.");
353                 return SR_ERR_BUG;
354         }
355
356         if (serial_open(devc->serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
357                 return SR_ERR;
358
359         sdi->status = SR_ST_ACTIVE;
360
361         return SR_OK;
362 }
363
364 static int hw_dev_close(struct sr_dev_inst *sdi)
365 {
366         struct dev_context *devc;
367
368         if (!(devc = sdi->priv)) {
369                 sr_err("sdi->priv was NULL.");
370                 return SR_ERR_BUG;
371         }
372
373         if (devc->serial && devc->serial->fd != -1) {
374                 serial_close(devc->serial);
375                 sdi->status = SR_ST_INACTIVE;
376         }
377
378         return SR_OK;
379 }
380
381 static int hw_cleanup(void)
382 {
383         clear_instances();
384
385         return SR_OK;
386 }
387
388 static int hw_info_get(int info_id, const void **data,
389                        const struct sr_dev_inst *sdi)
390 {
391         (void)sdi;
392
393         switch (info_id) {
394         case SR_DI_HWOPTS:
395                 *data = hwopts;
396                 break;
397         case SR_DI_HWCAPS:
398                 *data = hwcaps;
399                 break;
400         case SR_DI_NUM_PROBES:
401                 *data = GINT_TO_POINTER(1);
402                 break;
403         case SR_DI_PROBE_NAMES:
404                 *data = probe_names;
405                 break;
406         default:
407                 return SR_ERR_ARG;
408         }
409
410         return SR_OK;
411 }
412
413 static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap,
414                              const void *value)
415 {
416         struct dev_context *devc;
417
418         if (sdi->status != SR_ST_ACTIVE)
419                 return SR_ERR;
420
421         if (!(devc = sdi->priv)) {
422                 sr_err("sdi->priv was NULL.");
423                 return SR_ERR_BUG;
424         }
425
426         switch (hwcap) {
427         case SR_HWCAP_LIMIT_SAMPLES:
428                 devc->limit_samples = *(const uint64_t *)value;
429                 sr_dbg("Setting sample limit to %" PRIu64 ".",
430                        devc->limit_samples);
431                 break;
432         default:
433                 sr_err("Unknown capability: %d.", hwcap);
434                 return SR_ERR;
435                 break;
436         }
437
438         return SR_OK;
439 }
440
441 static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
442                                     void *cb_data)
443 {
444         struct sr_datafeed_packet packet;
445         struct sr_datafeed_header header;
446         struct sr_datafeed_meta_analog meta;
447         struct dev_context *devc;
448         int (*receive_data)(int, int, void *) = NULL;
449
450         if (!(devc = sdi->priv)) {
451                 sr_err("sdi->priv was NULL.");
452                 return SR_ERR_BUG;
453         }
454
455         sr_dbg("Starting acquisition.");
456
457         devc->cb_data = cb_data;
458
459         /*
460          * Reset the number of samples to take. If we've already collected our
461          * quota, but we start a new session, and don't reset this, we'll just
462          * quit without acquiring any new samples.
463          */
464         devc->num_samples = 0;
465
466         /* Send header packet to the session bus. */
467         sr_dbg("Sending SR_DF_HEADER.");
468         packet.type = SR_DF_HEADER;
469         packet.payload = (uint8_t *)&header;
470         header.feed_version = 1;
471         gettimeofday(&header.starttime, NULL);
472         sr_session_send(devc->cb_data, &packet);
473
474         /* Send metadata about the SR_DF_ANALOG packets to come. */
475         sr_dbg("Sending SR_DF_META_ANALOG.");
476         packet.type = SR_DF_META_ANALOG;
477         packet.payload = &meta;
478         meta.num_probes = 1;
479         sr_session_send(devc->cb_data, &packet);
480
481         if (!strcmp(di->name, "digitek-dt4000zc"))
482                 receive_data = digitek_dt4000zc_receive_data;
483         if (!strcmp(di->name, "tekpower-tp4000zc"))
484                 receive_data = tekpower_tp4000zc_receive_data;
485         if (!strcmp(di->name, "metex-me31"))
486                 receive_data = metex_me31_receive_data;
487         if (!strcmp(di->name, "peaktech-3410"))
488                 receive_data = peaktech_3410_receive_data;
489         if (!strcmp(di->name, "mastech-mas345"))
490                 receive_data = mastech_mas345_receive_data;
491         if (!strcmp(di->name, "va-va18b"))
492                 receive_data = va_va18b_receive_data;
493         if (!strcmp(di->name, "metex-m3640d"))
494                 receive_data = metex_m3640d_receive_data;
495
496         /* Poll every 50ms, or whenever some data comes in. */
497         sr_source_add(devc->serial->fd, G_IO_IN, 50,
498                       receive_data, (void *)sdi);
499
500         return SR_OK;
501 }
502
503 static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
504 {
505         struct sr_datafeed_packet packet;
506         struct dev_context *devc;
507
508         if (sdi->status != SR_ST_ACTIVE)
509                 return SR_ERR;
510
511         if (!(devc = sdi->priv)) {
512                 sr_err("sdi->priv was NULL.");
513                 return SR_ERR_BUG;
514         }
515
516         sr_dbg("Stopping acquisition.");
517
518         sr_source_remove(devc->serial->fd);
519         hw_dev_close((struct sr_dev_inst *)sdi);
520
521         /* Send end packet to the session bus. */
522         sr_dbg("Sending SR_DF_END.");
523         packet.type = SR_DF_END;
524         sr_session_send(cb_data, &packet);
525
526         return SR_OK;
527 }
528
529 SR_PRIV struct sr_dev_driver digitek_dt4000zc_driver_info = {
530         .name = "digitek-dt4000zc",
531         .longname = "Digitek DT4000ZC",
532         .api_version = 1,
533         .init = hw_init_digitek_dt4000zc,
534         .cleanup = hw_cleanup,
535         .scan = hw_scan,
536         .dev_list = hw_dev_list,
537         .dev_clear = clear_instances,
538         .dev_open = hw_dev_open,
539         .dev_close = hw_dev_close,
540         .info_get = hw_info_get,
541         .dev_config_set = hw_dev_config_set,
542         .dev_acquisition_start = hw_dev_acquisition_start,
543         .dev_acquisition_stop = hw_dev_acquisition_stop,
544         .priv = NULL,
545 };
546
547 SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info = {
548         .name = "tekpower-tp4000zc",
549         .longname = "TekPower TP4000ZC",
550         .api_version = 1,
551         .init = hw_init_tekpower_tp4000zc,
552         .cleanup = hw_cleanup,
553         .scan = hw_scan,
554         .dev_list = hw_dev_list,
555         .dev_clear = clear_instances,
556         .dev_open = hw_dev_open,
557         .dev_close = hw_dev_close,
558         .info_get = hw_info_get,
559         .dev_config_set = hw_dev_config_set,
560         .dev_acquisition_start = hw_dev_acquisition_start,
561         .dev_acquisition_stop = hw_dev_acquisition_stop,
562         .priv = NULL,
563 };
564
565 SR_PRIV struct sr_dev_driver metex_me31_driver_info = {
566         .name = "metex-me31",
567         .longname = "Metex ME-31",
568         .api_version = 1,
569         .init = hw_init_metex_me31,
570         .cleanup = hw_cleanup,
571         .scan = hw_scan,
572         .dev_list = hw_dev_list,
573         .dev_clear = clear_instances,
574         .dev_open = hw_dev_open,
575         .dev_close = hw_dev_close,
576         .info_get = hw_info_get,
577         .dev_config_set = hw_dev_config_set,
578         .dev_acquisition_start = hw_dev_acquisition_start,
579         .dev_acquisition_stop = hw_dev_acquisition_stop,
580         .priv = NULL,
581 };
582
583 SR_PRIV struct sr_dev_driver peaktech_3410_driver_info = {
584         .name = "peaktech-3410",
585         .longname = "PeakTech 3410",
586         .api_version = 1,
587         .init = hw_init_peaktech_3410,
588         .cleanup = hw_cleanup,
589         .scan = hw_scan,
590         .dev_list = hw_dev_list,
591         .dev_clear = clear_instances,
592         .dev_open = hw_dev_open,
593         .dev_close = hw_dev_close,
594         .info_get = hw_info_get,
595         .dev_config_set = hw_dev_config_set,
596         .dev_acquisition_start = hw_dev_acquisition_start,
597         .dev_acquisition_stop = hw_dev_acquisition_stop,
598         .priv = NULL,
599 };
600
601 SR_PRIV struct sr_dev_driver mastech_mas345_driver_info = {
602         .name = "mastech-mas345",
603         .longname = "MASTECH MAS345",
604         .api_version = 1,
605         .init = hw_init_mastech_mas345,
606         .cleanup = hw_cleanup,
607         .scan = hw_scan,
608         .dev_list = hw_dev_list,
609         .dev_clear = clear_instances,
610         .dev_open = hw_dev_open,
611         .dev_close = hw_dev_close,
612         .info_get = hw_info_get,
613         .dev_config_set = hw_dev_config_set,
614         .dev_acquisition_start = hw_dev_acquisition_start,
615         .dev_acquisition_stop = hw_dev_acquisition_stop,
616         .priv = NULL,
617 };
618
619 SR_PRIV struct sr_dev_driver va_va18b_driver_info = {
620         .name = "va-va18b",
621         .longname = "V&A VA18B",
622         .api_version = 1,
623         .init = hw_init_va_va18b,
624         .cleanup = hw_cleanup,
625         .scan = hw_scan,
626         .dev_list = hw_dev_list,
627         .dev_clear = clear_instances,
628         .dev_open = hw_dev_open,
629         .dev_close = hw_dev_close,
630         .info_get = hw_info_get,
631         .dev_config_set = hw_dev_config_set,
632         .dev_acquisition_start = hw_dev_acquisition_start,
633         .dev_acquisition_stop = hw_dev_acquisition_stop,
634         .priv = NULL,
635 };
636
637 SR_PRIV struct sr_dev_driver metex_m3640d_driver_info = {
638         .name = "metex-m3640d",
639         .longname = "Metex M-3640D",
640         .api_version = 1,
641         .init = hw_init_metex_m3640d,
642         .cleanup = hw_cleanup,
643         .scan = hw_scan,
644         .dev_list = hw_dev_list,
645         .dev_clear = clear_instances,
646         .dev_open = hw_dev_open,
647         .dev_close = hw_dev_close,
648         .info_get = hw_info_get,
649         .dev_config_set = hw_dev_config_set,
650         .dev_acquisition_start = hw_dev_acquisition_start,
651         .dev_acquisition_stop = hw_dev_acquisition_stop,
652         .priv = NULL,
653 };