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