]> sigrok.org Git - libsigrok.git/blob - hardware/genericdmm/api.c
8f33226329895810c1b1fd380f9c18c357131ba9
[libsigrok.git] / hardware / genericdmm / api.c
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
5  * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  */
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include "libsigrok.h"
26 #include "libsigrok-internal.h"
27 #include "genericdmm.h"
28
29
30 extern SR_PRIV struct dmmchip dmmchip_fs9922;
31
32 static struct dev_profile dev_profiles[] = {
33         { "victor-70c", "Victor", "70C", &dmmchip_fs9922,
34                 0x1244, 0xd237, DMM_TRANSPORT_USBHID },
35         { "mastech-va18b", "Mastech", "VA18B", NULL, 0, 0, DMM_TRANSPORT_SERIAL},
36 };
37
38 static const int hwcaps[] = {
39         SR_HWCAP_MULTIMETER,
40         SR_HWCAP_LIMIT_SAMPLES,
41         SR_HWCAP_LIMIT_MSEC,
42         SR_HWCAP_CONTINUOUS,
43         SR_HWCAP_MODEL,
44         SR_HWCAP_CONN,
45         SR_HWCAP_SERIALCOMM,
46         0,
47 };
48
49 static const char *probe_names[] = {
50         "Probe",
51         NULL,
52 };
53
54 /* TODO need a way to keep these local to the static library */
55 SR_PRIV GSList *genericdmm_dev_insts = NULL;
56 SR_PRIV libusb_context *genericdmm_usb_context = NULL;
57
58
59 static int hw_init(const char *devinfo)
60 {
61         struct sr_dev_inst *sdi;
62         struct context *ctx;
63         int devcnt = 0;
64
65         /* Avoid compiler warnings. */
66         (void)devinfo;
67
68         if (libusb_init(&genericdmm_usb_context) != 0) {
69                 sr_err("genericdmm: Failed to initialize USB.");
70                 return 0;
71         }
72
73         if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
74                 sr_err("genericdmm: ctx malloc failed.");
75                 return 0;
76         }
77
78         devcnt = g_slist_length(genericdmm_dev_insts);
79         if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_ACTIVE, "Generic DMM",
80                         NULL, NULL))) {
81                 sr_err("genericdmm: sr_dev_inst_new returned NULL.");
82                 return 0;
83         }
84         sdi->priv = ctx;
85         genericdmm_dev_insts = g_slist_append(genericdmm_dev_insts, sdi);
86
87         /* Always initialized just one device instance. */
88         return 0;
89 }
90
91 static int hw_dev_open(int dev_index)
92 {
93         struct sr_dev_inst *sdi;
94         struct context *ctx;
95
96         if (!(sdi = sr_dev_inst_get(genericdmm_dev_insts, dev_index))) {
97                 sr_err("genericdmm: sdi was NULL.");
98                 return SR_ERR_BUG;
99         }
100
101         if (!(ctx = sdi->priv)) {
102                 sr_err("genericdmm: sdi->priv was NULL.");
103                 return SR_ERR_BUG;
104         }
105
106         sr_dbg("genericdmm: Opening serial port '%s'.", ctx->serial->port);
107
108         switch (ctx->profile->transport) {
109         case DMM_TRANSPORT_USBHID:
110                 /* TODO */
111                 break;
112         case DMM_TRANSPORT_SERIAL:
113                 /* TODO: O_NONBLOCK? */
114                 ctx->serial->fd = serial_open(ctx->serial->port, O_RDWR | O_NONBLOCK);
115                 if (ctx->serial->fd == -1) {
116                         sr_err("genericdmm: Couldn't open serial port '%s'.",
117                                ctx->serial->port);
118                         return SR_ERR;
119                 }
120                 //      serial_set_params(ctx->serial->fd, 2400, 8, 0, 1, 2);
121                 break;
122         default:
123                 sr_err("No transport set.");
124         }
125
126         return SR_OK;
127 }
128
129 static int hw_dev_close(int dev_index)
130 {
131         struct sr_dev_inst *sdi;
132         struct context *ctx;
133
134         if (!(sdi = sr_dev_inst_get(genericdmm_dev_insts, dev_index))) {
135                 sr_err("genericdmm: %s: sdi was NULL.", __func__);
136                 return SR_ERR_BUG;
137         }
138
139         if (!(ctx = sdi->priv)) {
140                 sr_err("genericdmm: %s: sdi->priv was NULL.", __func__);
141                 return SR_ERR_BUG;
142         }
143
144         /* TODO: Check for != NULL. */
145
146         switch (ctx->profile->transport) {
147         case DMM_TRANSPORT_USBHID:
148                 /* TODO */
149                 break;
150         case DMM_TRANSPORT_SERIAL:
151                 if (ctx->serial && ctx->serial->fd != -1) {
152                         serial_close(ctx->serial->fd);
153                         ctx->serial->fd = -1;
154                         sdi->status = SR_ST_INACTIVE;
155                 }
156                 break;
157         }
158
159         return SR_OK;
160 }
161
162 static int hw_cleanup(void)
163 {
164         GSList *l;
165         struct sr_dev_inst *sdi;
166         struct context *ctx;
167
168         /* Properly close and free all devices. */
169         for (l = genericdmm_dev_insts; l; l = l->next) {
170                 if (!(sdi = l->data)) {
171                         /* Log error, but continue cleaning up the rest. */
172                         sr_err("genericdmm: sdi was NULL, continuing.");
173                         continue;
174                 }
175                 if (!(ctx = sdi->priv)) {
176                         /* Log error, but continue cleaning up the rest. */
177                         sr_err("genericdmm: sdi->priv was NULL, continuing.");
178                         continue;
179                 }
180
181                 if (ctx->profile) {
182                         switch (ctx->profile->transport) {
183                         case DMM_TRANSPORT_USBHID:
184                                 /* TODO */
185                                 break;
186                         case DMM_TRANSPORT_SERIAL:
187                                 if (ctx->serial && ctx->serial->fd != -1)
188                                         serial_close(ctx->serial->fd);
189                                 sr_serial_dev_inst_free(ctx->serial);
190                                 break;
191                         }
192                 }
193
194                 sr_dev_inst_free(sdi);
195         }
196
197         g_slist_free(genericdmm_dev_insts);
198         genericdmm_dev_insts = NULL;
199
200         if (genericdmm_usb_context)
201                 libusb_exit(genericdmm_usb_context);
202
203         return SR_OK;
204 }
205
206 static const void *hw_dev_info_get(int dev_index, int dev_info_id)
207 {
208         struct sr_dev_inst *sdi;
209         struct context *ctx;
210         const void *info;
211
212         if (!(sdi = sr_dev_inst_get(genericdmm_dev_insts, dev_index))) {
213                 sr_err("genericdmm: sdi was NULL.");
214                 return NULL;
215         }
216
217         if (!(ctx = sdi->priv)) {
218                 sr_err("genericdmm: sdi->priv was NULL.");
219                 return NULL;
220         }
221
222                 sr_spew("genericdmm: dev_index %d, dev_info_id %d.",
223                                 dev_index, dev_info_id);
224
225         switch (dev_info_id) {
226         case SR_DI_INST:
227                 info = sdi;
228                 sr_spew("genericdmm: Returning sdi.");
229                 break;
230         case SR_DI_NUM_PROBES:
231                 info = GINT_TO_POINTER(1);
232                 sr_spew("genericdmm: Returning number of probes: 1.");
233                 break;
234         case SR_DI_PROBE_NAMES:
235                 info = probe_names;
236                 sr_spew("genericdmm: Returning probenames.");
237                 break;
238         case SR_DI_CUR_SAMPLERATE:
239                 /* TODO get rid of this */
240                 info = NULL;
241                 sr_spew("genericdmm: Returning samplerate: 0.");
242                 break;
243         default:
244                 /* Unknown device info ID. */
245                 sr_err("genericdmm: Unknown device info ID: %d.", dev_info_id);
246                 info = NULL;
247                 break;
248         }
249
250         return info;
251 }
252
253 static int hw_dev_status_get(int dev_index)
254 {
255         struct sr_dev_inst *sdi;
256
257         if (!(sdi = sr_dev_inst_get(genericdmm_dev_insts, dev_index))) {
258                 sr_err("genericdmm: sdi was NULL, device not found.");
259                 return SR_ST_NOT_FOUND;
260         }
261
262         sr_dbg("genericdmm: Returning status: %d.", sdi->status);
263
264         return sdi->status;
265 }
266
267 static const int *hw_hwcap_get_all(void)
268 {
269         sr_spew("genericdmm: Returning list of device capabilities.");
270
271         return hwcaps;
272 }
273
274 static int parse_conn_vidpid(struct sr_dev_inst *sdi, const char *conn)
275 {
276         struct context *ctx;
277         libusb_device **devlist;
278         struct libusb_device_descriptor des;
279         GRegex *reg;
280         GMatchInfo *match;
281         int vid, pid, found, err, i;
282         char *vidstr, *pidstr;
283
284         found = FALSE;
285
286         reg = g_regex_new(DMM_CONN_USB_VIDPID, 0, 0, NULL);
287         if (g_regex_match(reg, conn, 0, &match)) {
288                 /* Extract VID. */
289                 if (!(vidstr = g_match_info_fetch(match, 0))) {
290                         sr_err("failed to fetch VID from regex");
291                         goto err;
292                 }
293                 vid = strtoul(vidstr, NULL, 16);
294                 g_free(vidstr);
295                 if (vid > 0xffff) {
296                         sr_err("invalid VID");
297                         goto err;
298                 }
299
300                 /* Extract PID. */
301                 if (!(pidstr = g_match_info_fetch(match, 0))) {
302                         sr_err("failed to fetch PID from regex");
303                         goto err;
304                 }
305                 pid = strtoul(pidstr, NULL, 16);
306                 g_free(pidstr);
307                 if (pid > 0xffff) {
308                         sr_err("invalid PID");
309                         goto err;
310                 }
311
312                 /* Looks like a valid VID:PID, but is it connected? */
313                 libusb_get_device_list(genericdmm_usb_context, &devlist);
314                 for (i = 0; devlist[i]; i++) {
315                         if ((err = libusb_get_device_descriptor(devlist[i], &des))) {
316                                 sr_err("genericdmm: failed to get device descriptor: %d", err);
317                                 goto err;
318                         }
319
320                         if (des.idVendor == vid && des.idProduct == pid) {
321                                 ctx = sdi->priv;
322                                 ctx->usb = sr_usb_dev_inst_new(
323                                                 libusb_get_bus_number(devlist[i]),
324                                                 libusb_get_device_address(devlist[i]), NULL);
325                                 found = TRUE;
326                                 break;
327                         }
328                 }
329                 libusb_free_device_list(devlist, 1);
330         }
331
332 err:
333         if (match)
334                 g_match_info_unref(match);
335         g_regex_unref(reg);
336
337         return found;
338 }
339
340 static int parse_conn_busaddr(struct sr_dev_inst *sdi, const char *conn)
341 {
342         struct context *ctx;
343         libusb_device **devlist;
344         struct libusb_device_descriptor des;
345         GRegex *reg;
346         GMatchInfo *match;
347         int bus, addr, found, err, i;
348         char *busstr, *addrstr;
349
350         found = FALSE;
351
352         reg = g_regex_new(DMM_CONN_USB_BUSADDR, 0, 0, NULL);
353         if (g_regex_match(reg, conn, 0, &match)) {
354                 /* Extract bus. */
355                 if (!(busstr = g_match_info_fetch(match, 0))) {
356                         sr_err("failed to fetch bus from regex");
357                         goto err;
358                 }
359                 bus = strtoul(busstr, NULL, 16);
360                 g_free(busstr);
361                 if (bus > 64) {
362                         sr_err("invalid bus");
363                         goto err;
364                 }
365
366                 /* Extract address. */
367                 if (!(addrstr = g_match_info_fetch(match, 0))) {
368                         sr_err("failed to fetch address from regex");
369                         goto err;
370                 }
371                 addr = strtoul(addrstr, NULL, 16);
372                 g_free(addrstr);
373                 if (addr > 127) {
374                         sr_err("invalid address");
375                         goto err;
376                 }
377
378                 /* Looks like a valid bus/address, but is it connected? */
379                 libusb_get_device_list(genericdmm_usb_context, &devlist);
380                 for (i = 0; devlist[i]; i++) {
381                         if ((err = libusb_get_device_descriptor(devlist[i], &des))) {
382                                 sr_err("genericdmm: failed to get device descriptor: %d", err);
383                                 goto err;
384                         }
385
386                         if (libusb_get_bus_number(devlist[i]) == bus
387                                         && libusb_get_device_address(devlist[i]) == addr) {
388                                 ctx = sdi->priv;
389                                 ctx->usb = sr_usb_dev_inst_new(bus, addr, NULL);
390                                 found = TRUE;
391                                 break;
392                         }
393                 }
394                 libusb_free_device_list(devlist, 1);
395         }
396
397 err:
398         if (match)
399                 g_match_info_unref(match);
400         g_regex_unref(reg);
401
402         return found;
403 }
404
405 static int parse_conn_serial(struct sr_dev_inst *sdi, const char *conn)
406 {
407         int found;
408
409         found = FALSE;
410
411         /* TODO */
412
413         return found;
414 }
415
416 static int parse_conn(struct sr_dev_inst *sdi, const char *conn)
417 {
418
419         if (parse_conn_vidpid(sdi, conn))
420                 return SR_OK;
421
422         if (parse_conn_busaddr(sdi, conn))
423                 return SR_OK;
424
425         if (parse_conn_serial(sdi, conn))
426                 return SR_OK;
427
428         sr_err("Invalid connection specification");
429
430         return SR_ERR;
431 }
432
433 static int parse_serialcomm(struct sr_dev_inst *sdi, const char *conn)
434 {
435
436         /* TODO */
437         /* set ctx->serial_* */
438
439         return SR_OK;
440 }
441
442 static int hw_dev_config_set(int dev_index, int hwcap, const void *value)
443 {
444         struct sr_dev_inst *sdi;
445         struct context *ctx;
446         int i;
447
448         if (!(sdi = sr_dev_inst_get(genericdmm_dev_insts, dev_index))) {
449                 sr_err("genericdmm: sdi was NULL.");
450                 return SR_ERR_BUG;
451         }
452
453         if (!(ctx = sdi->priv)) {
454                 sr_err("genericdmm: sdi->priv was NULL.");
455                 return SR_ERR_BUG;
456         }
457
458         sr_spew("genericdmm: dev_index %d, hwcap %d.", dev_index, hwcap);
459
460         switch (hwcap) {
461         case SR_HWCAP_LIMIT_MSEC:
462                 if (*(const uint64_t *)value == 0) {
463                         sr_err("genericdmm: LIMIT_MSEC can't be 0.");
464                         return SR_ERR;
465                 }
466                 ctx->limit_msec = *(const uint64_t *)value;
467                 sr_dbg("genericdmm: Setting LIMIT_MSEC to %" PRIu64 ".",
468                        ctx->limit_msec);
469                 break;
470         case SR_HWCAP_LIMIT_SAMPLES:
471                 ctx->limit_samples = *(const uint64_t *)value;
472                 sr_dbg("genericdmm: Setting LIMIT_SAMPLES to %" PRIu64 ".",
473                        ctx->limit_samples);
474                 break;
475         case SR_HWCAP_MODEL:
476                 for (i = 0; dev_profiles[i].model; i++) {
477                         if (!strcasecmp(dev_profiles[i].model, value)) {
478                                 ctx->profile = &dev_profiles[i];
479                                 /* Frontends access these fields directly, so we
480                                  * need to copy them over. */
481                                 sdi->vendor = g_strdup(dev_profiles[i].vendor);
482                                 sdi->model = g_strdup(dev_profiles[i].model);
483                                 /* This is the first time we actually know which
484                                  * DMM chip we're talking to, so let's init
485                                  * anything specific to it now */
486                                 if (ctx->profile->chip->init)
487                                         if (ctx->profile->chip->init(ctx) != SR_OK)
488                                                 return SR_ERR;
489                                 break;
490                         }
491                 }
492                 if (!ctx->profile) {
493                         sr_err("unknown model %s", value);
494                         return SR_ERR;
495                 }
496                 break;
497         case SR_HWCAP_CONN:
498                 if (parse_conn(sdi, value) != SR_OK)
499                         return SR_ERR_ARG;
500                 break;
501         case SR_HWCAP_SERIALCOMM:
502                 if (parse_serialcomm(sdi, value) != SR_OK)
503                         return SR_ERR_ARG;
504                 break;
505         default:
506                 sr_err("genericdmm: Unknown capability: %d.", hwcap);
507                 return SR_ERR;
508                 break;
509         }
510
511         return SR_OK;
512 }
513
514 static int receive_data(int fd, int revents, void *cb_data)
515 {
516         struct sr_dev_inst *sdi;
517         struct context *ctx;
518
519         if (!(sdi = cb_data))
520                 return FALSE;
521
522         if (!(ctx = sdi->priv))
523                 return FALSE;
524
525         if (revents != G_IO_IN) {
526                 sr_err("genericdmm: No data?");
527                 return FALSE;
528         }
529
530         switch (ctx->profile->transport) {
531         case DMM_TRANSPORT_USBHID:
532                 /* TODO */
533                 break;
534         case DMM_TRANSPORT_SERIAL:
535                 /* TODO */
536                 break;
537         }
538
539         return TRUE;
540 }
541
542 static int hw_dev_acquisition_start(int dev_index, void *cb_data)
543 {
544         struct sr_datafeed_packet packet;
545         struct sr_datafeed_header header;
546         struct sr_datafeed_meta_analog meta;
547         struct sr_dev_inst *sdi;
548         struct context *ctx;
549
550         if (!(sdi = sr_dev_inst_get(genericdmm_dev_insts, dev_index))) {
551                 sr_err("genericdmm: sdi was NULL.");
552                 return SR_ERR_BUG;
553         }
554
555         if (!(ctx = sdi->priv)) {
556                 sr_err("genericdmm: sdi->priv was NULL.");
557                 return SR_ERR_BUG;
558         }
559
560         sr_dbg("genericdmm: Starting acquisition.");
561
562         ctx->cb_data = cb_data;
563
564         /* Send header packet to the session bus. */
565         sr_dbg("genericdmm: Sending SR_DF_HEADER.");
566         packet.type = SR_DF_HEADER;
567         packet.payload = (uint8_t *)&header;
568         header.feed_version = 1;
569         gettimeofday(&header.starttime, NULL);
570         sr_session_send(ctx->cb_data, &packet);
571
572         /* Send metadata about the SR_DF_ANALOG packets to come. */
573         sr_dbg("genericdmm: Sending SR_DF_META_ANALOG.");
574         packet.type = SR_DF_META_ANALOG;
575         packet.payload = &meta;
576         meta.num_probes = 1;
577         sr_session_send(ctx->cb_data, &packet);
578
579         /* Hook up a proxy handler to receive data from the device. */
580         switch (ctx->profile->transport) {
581         case DMM_TRANSPORT_USBHID:
582                 /* TODO libusb FD setup */
583                 break;
584         case DMM_TRANSPORT_SERIAL:
585                 /* TODO serial FD setup */
586                 // sr_source_add(ctx->serial->fd, G_IO_IN, -1, receive_data, sdi);
587                 break;
588         }
589
590         return SR_OK;
591 }
592
593 static int hw_dev_acquisition_stop(int dev_index, void *cb_data)
594 {
595         struct sr_datafeed_packet packet;
596
597         /* Avoid compiler warnings. */
598         (void)dev_index;
599
600         sr_dbg("genericdmm: Stopping acquisition.");
601
602         /* Send end packet to the session bus. */
603         sr_dbg("genericdmm: Sending SR_DF_END.");
604         packet.type = SR_DF_END;
605         sr_session_send(cb_data, &packet);
606
607         return SR_OK;
608 }
609
610 SR_PRIV struct sr_dev_driver genericdmm_driver_info = {
611         .name = "genericdmm",
612         .longname = "Generic DMM",
613         .api_version = 1,
614         .init = hw_init,
615         .cleanup = hw_cleanup,
616         .dev_open = hw_dev_open,
617         .dev_close = hw_dev_close,
618         .dev_info_get = hw_dev_info_get,
619         .dev_status_get = hw_dev_status_get,
620         .hwcap_get_all = hw_hwcap_get_all,
621         .dev_config_set = hw_dev_config_set,
622         .dev_acquisition_start = hw_dev_acquisition_start,
623         .dev_acquisition_stop = hw_dev_acquisition_stop,
624 };