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