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