From: Uwe Hermann Date: Sat, 1 Dec 2012 18:35:19 +0000 (+0100) Subject: Rename tekpower-dmm to serial-dmm. X-Git-Tag: dsupstream~514 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=21a7f2692e09269edd872bca4e25a9d06b67c0e5;p=libsigrok.git Rename tekpower-dmm to serial-dmm. This is now a generic driver for multiple DMM "subdrivers" that use simple serial port protocols. --- diff --git a/configure.ac b/configure.ac index 8d87c98f..8efc8627 100644 --- a/configure.ac +++ b/configure.ac @@ -187,13 +187,13 @@ if test "x$HW_RADIOSHACK_DMM" = "xyes"; then AC_DEFINE(HAVE_HW_RADIOSHACK_DMM, 1, [RadioShack DMM support]) fi -AC_ARG_ENABLE(tekpower-dmm, AC_HELP_STRING([--enable-tekpower-dmm], - [enable TekPower DMM support [default=yes]]), - [HW_TEKPOWER_DMM="$enableval"], - [HW_TEKPOWER_DMM=yes]) -AM_CONDITIONAL(HW_TEKPOWER_DMM, test x$HW_TEKPOWER_DMM = xyes) -if test "x$HW_TEKPOWER_DMM" = "xyes"; then - AC_DEFINE(HAVE_HW_TEKPOWER_DMM, 1, [TekPower DMM support]) +AC_ARG_ENABLE(serial-dmm, AC_HELP_STRING([--enable-serial-dmm], + [enable serial DMM support [default=yes]]), + [HW_SERIAL_DMM="$enableval"], + [HW_SERIAL_DMM=yes]) +AM_CONDITIONAL(HW_SERIAL_DMM, test x$HW_SERIAL_DMM = xyes) +if test "x$HW_SERIAL_DMM" = "xyes"; then + AC_DEFINE(HAVE_HW_SERIAL_DMM, 1, [Serial DMM support]) fi AC_ARG_ENABLE(tondaj-sl-814, AC_HELP_STRING([--enable-tondaj-sl-814], @@ -314,7 +314,7 @@ AM_CONDITIONAL(NEED_SERIAL, \ -o "x$LA_LINK_MSO19" != xno \ -o "x$LA_OLS" != xno \ -o "x$HW_RADIOSHACK_DMM" != xno \ - -o "x$HW_TEKPOWER_DMM" != xno \ + -o "x$HW_SERIAL_DMM" != xno \ -o "x$HW_TONDAJ_SL_814" != xno \ ) @@ -370,7 +370,7 @@ AC_CONFIG_FILES([Makefile version.h hardware/Makefile hardware/link-mso19/Makefile hardware/openbench-logic-sniffer/Makefile hardware/radioshack-dmm/Makefile - hardware/tekpower-dmm/Makefile + hardware/serial-dmm/Makefile hardware/uni-t-dmm/Makefile hardware/zeroplus-logic-cube/Makefile input/Makefile @@ -415,7 +415,7 @@ echo " - Hantek DSO...................... $HW_HANTEK_DSO" echo " - Link MSO-19..................... $LA_LINK_MSO19" echo " - Openbench Logic Sniffer......... $LA_OLS" echo " - Radioshack DMM.................. $HW_RADIOSHACK_DMM" -echo " - TekPower DMM.................... $HW_TEKPOWER_DMM" +echo " - Serial DMM...................... $HW_SERIAL_DMM" echo " - Tondaj SL-814................... $HW_TONDAJ_SL_814" echo " - UNI-T DMM....................... $HW_UNI_T_DMM" echo " - victor-dmm...................... $HW_VICTOR_DMM" diff --git a/hardware/Makefile.am b/hardware/Makefile.am index 89da7bb0..89d46815 100644 --- a/hardware/Makefile.am +++ b/hardware/Makefile.am @@ -32,7 +32,7 @@ SUBDIRS = \ link-mso19 \ openbench-logic-sniffer \ radioshack-dmm \ - tekpower-dmm \ + serial-dmm \ tondaj-sl-814 \ uni-t-dmm \ victor-dmm \ @@ -93,8 +93,8 @@ if HW_RADIOSHACK_DMM libsigrokhardware_la_LIBADD += radioshack-dmm/libsigrokhwradioshackdmm.la endif -if HW_TEKPOWER_DMM -libsigrokhardware_la_LIBADD += tekpower-dmm/libsigrokhwtekpowerdmm.la +if HW_SERIAL_DMM +libsigrokhardware_la_LIBADD += serial-dmm/libsigrokhwserialdmm.la endif if HW_TONDAJ_SL_814 diff --git a/hardware/serial-dmm/Makefile.am b/hardware/serial-dmm/Makefile.am new file mode 100644 index 00000000..7fede239 --- /dev/null +++ b/hardware/serial-dmm/Makefile.am @@ -0,0 +1,33 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2012 Alexandru Gagniuc +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . +## + +if HW_SERIAL_DMM + +# Local lib, this is NOT meant to be installed! +noinst_LTLIBRARIES = libsigrokhwserialdmm.la + +libsigrokhwserialdmm_la_SOURCES = \ + api.c \ + protocol.c \ + protocol.h + +libsigrokhwserialdmm_la_CFLAGS = \ + -I$(top_srcdir) + +endif diff --git a/hardware/serial-dmm/api.c b/hardware/serial-dmm/api.c new file mode 100644 index 00000000..4293d635 --- /dev/null +++ b/hardware/serial-dmm/api.c @@ -0,0 +1,424 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2012 Bert Vermeulen + * Copyright (C) 2012 Alexandru Gagniuc + * Copyright (C) 2012 Uwe Hermann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" +#include "protocol.h" + +static const int hwopts[] = { + SR_HWOPT_CONN, + SR_HWOPT_SERIALCOMM, + 0, +}; + +static const int hwcaps[] = { + SR_HWCAP_MULTIMETER, + SR_HWCAP_LIMIT_SAMPLES, + SR_HWCAP_CONTINUOUS, + 0, +}; + +static const char *probe_names[] = { + "Probe", + NULL, +}; + +SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info; +static struct sr_dev_driver *di_tekpower_tp4000zc = &tekpower_tp4000zc_driver_info; + +/* After hw_init() this will point to a device-specific entry (see above). */ +static struct sr_dev_driver *di = NULL; + +SR_PRIV struct dmm_info dmms[] = { + { + "TekPower", "TP4000ZC", + "2400/8n1", 2400, + FS9721_PACKET_SIZE, + sr_fs9721_packet_valid, + sr_fs9721_parse, + dmm_details_tp4000zc, + }, +}; + +/* Properly close and free all devices. */ +static int clear_instances(void) +{ + struct sr_dev_inst *sdi; + struct drv_context *drvc; + struct dev_context *devc; + GSList *l; + + if (!(drvc = di->priv)) + return SR_OK; + + drvc = di->priv; + for (l = drvc->instances; l; l = l->next) { + if (!(sdi = l->data)) + continue; + if (!(devc = sdi->priv)) + continue; + sr_serial_dev_inst_free(devc->serial); + sr_dev_inst_free(sdi); + } + g_slist_free(drvc->instances); + drvc->instances = NULL; + + return SR_OK; +} + +static int hw_init(int dmm) +{ + struct drv_context *drvc; + + if (!(drvc = g_try_malloc0(sizeof(struct drv_context)))) { + sr_err("Driver context malloc failed."); + return SR_ERR_MALLOC; + } + + if (dmm == TEKPOWER_TP4000ZC) + di = di_tekpower_tp4000zc; + sr_dbg("Selected '%s' subdriver.", di->name); + + di->priv = drvc; + + return SR_OK; +} + +static int hw_init_tekpower_tp4000zc(void) +{ + return hw_init(TEKPOWER_TP4000ZC); +} + +static GSList *scan(const char *conn, const char *serialcomm, int dmm) +{ + struct sr_dev_inst *sdi; + struct drv_context *drvc; + struct dev_context *devc; + struct sr_probe *probe; + struct sr_serial_dev_inst *serial; + GSList *devices; + int dropped, ret; + size_t len; + uint8_t buf[128]; + + if (!(serial = sr_serial_dev_inst_new(conn, serialcomm))) + return NULL; + + if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK) + return NULL; + + sr_info("Probing port %s readonly.", conn); + + drvc = di->priv; + devices = NULL; + serial_flush(serial); + + /* + * There's no way to get an ID from the multimeter. It just sends data + * periodically, so the best we can do is check if the packets match + * the expected format. + */ + + /* Let's get a bit of data and see if we can find a packet. */ + len = sizeof(buf); + + ret = serial_stream_detect(serial, buf, &len, dmms[dmm].packet_size, + dmms[dmm].packet_valid, 1000, + dmms[dmm].baudrate); + if (ret != SR_OK) + goto scan_cleanup; + + /* + * If we dropped more than two packets worth of data, something is + * wrong. We shouldn't quit however, since the dropped bytes might be + * just zeroes at the beginning of the stream. Those can occur as a + * combination of the nonstandard cable that ships with this device and + * the serial port or USB to serial adapter. + */ + dropped = len - dmms[dmm].packet_size; + if (dropped > 2 * dmms[dmm].packet_size) + sr_warn("Had to drop too much data."); + + sr_info("Found device on port %s.", conn); + + if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, dmms[dmm].vendor, + dmms[dmm].device, ""))) + goto scan_cleanup; + + if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) { + sr_err("Device context malloc failed."); + goto scan_cleanup; + } + + devc->serial = serial; + + sdi->priv = devc; + sdi->driver = di; + if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1"))) + goto scan_cleanup; + sdi->probes = g_slist_append(sdi->probes, probe); + drvc->instances = g_slist_append(drvc->instances, sdi); + devices = g_slist_append(devices, sdi); + +scan_cleanup: + serial_close(serial); + + return devices; +} + +static GSList *hw_scan(GSList *options) +{ + struct sr_hwopt *opt; + GSList *l, *devices; + const char *conn, *serialcomm; + int dmm; + + conn = serialcomm = NULL; + for (l = options; l; l = l->next) { + opt = l->data; + switch (opt->hwopt) { + case SR_HWOPT_CONN: + conn = opt->value; + break; + case SR_HWOPT_SERIALCOMM: + serialcomm = opt->value; + break; + } + } + if (!conn) + return NULL; + + if (!strcmp(di->name, "tekpower-tp4000zc")) + dmm = 0; + + if (serialcomm) { + /* Use the provided comm specs. */ + devices = scan(conn, serialcomm, dmm); + } else { + /* Try the default. */ + devices = scan(conn, dmms[dmm].conn, dmm); + } + + return devices; +} + +static GSList *hw_dev_list(void) +{ + struct drv_context *drvc; + + drvc = di->priv; + + return drvc->instances; +} + +static int hw_dev_open(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + if (!(devc = sdi->priv)) { + sr_err("sdi->priv was NULL."); + return SR_ERR_BUG; + } + + if (serial_open(devc->serial, SERIAL_RDONLY) != SR_OK) + return SR_ERR; + + sdi->status = SR_ST_ACTIVE; + + return SR_OK; +} + +static int hw_dev_close(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + if (!(devc = sdi->priv)) { + sr_err("sdi->priv was NULL."); + return SR_ERR_BUG; + } + + if (devc->serial && devc->serial->fd != -1) { + serial_close(devc->serial); + sdi->status = SR_ST_INACTIVE; + } + + return SR_OK; +} + +static int hw_cleanup(void) +{ + clear_instances(); + + return SR_OK; +} + +static int hw_info_get(int info_id, const void **data, + const struct sr_dev_inst *sdi) +{ + (void)sdi; + + switch (info_id) { + case SR_DI_HWOPTS: + *data = hwopts; + break; + case SR_DI_HWCAPS: + *data = hwcaps; + break; + case SR_DI_NUM_PROBES: + *data = GINT_TO_POINTER(1); + break; + case SR_DI_PROBE_NAMES: + *data = probe_names; + break; + default: + return SR_ERR_ARG; + } + + return SR_OK; +} + +static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap, + const void *value) +{ + struct dev_context *devc; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR; + + if (!(devc = sdi->priv)) { + sr_err("sdi->priv was NULL."); + return SR_ERR_BUG; + } + + switch (hwcap) { + case SR_HWCAP_LIMIT_SAMPLES: + devc->limit_samples = *(const uint64_t *)value; + sr_dbg("Setting sample limit to %" PRIu64 ".", + devc->limit_samples); + break; + default: + sr_err("Unknown capability: %d.", hwcap); + return SR_ERR; + break; + } + + return SR_OK; +} + +static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, + void *cb_data) +{ + struct sr_datafeed_packet packet; + struct sr_datafeed_header header; + struct sr_datafeed_meta_analog meta; + struct dev_context *devc; + int (*receive_data)(int, int, void *) = NULL; + + if (!(devc = sdi->priv)) { + sr_err("sdi->priv was NULL."); + return SR_ERR_BUG; + } + + sr_dbg("Starting acquisition."); + + devc->cb_data = cb_data; + + /* + * Reset the number of samples to take. If we've already collected our + * quota, but we start a new session, and don't reset this, we'll just + * quit without acquiring any new samples. + */ + devc->num_samples = 0; + + /* Send header packet to the session bus. */ + sr_dbg("Sending SR_DF_HEADER."); + packet.type = SR_DF_HEADER; + packet.payload = (uint8_t *)&header; + header.feed_version = 1; + gettimeofday(&header.starttime, NULL); + sr_session_send(devc->cb_data, &packet); + + /* Send metadata about the SR_DF_ANALOG packets to come. */ + sr_dbg("Sending SR_DF_META_ANALOG."); + packet.type = SR_DF_META_ANALOG; + packet.payload = &meta; + meta.num_probes = 1; + sr_session_send(devc->cb_data, &packet); + + if (!strcmp(di->name, "tekpower-tp4000zc")) + receive_data = tekpower_tp4000zc_receive_data; + + /* Poll every 50ms, or whenever some data comes in. */ + sr_source_add(devc->serial->fd, G_IO_IN, 50, + receive_data, (void *)sdi); + + return SR_OK; +} + +static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) +{ + struct sr_datafeed_packet packet; + struct dev_context *devc; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR; + + if (!(devc = sdi->priv)) { + sr_err("sdi->priv was NULL."); + return SR_ERR_BUG; + } + + sr_dbg("Stopping acquisition."); + + sr_source_remove(devc->serial->fd); + hw_dev_close((struct sr_dev_inst *)sdi); + + /* Send end packet to the session bus. */ + sr_dbg("Sending SR_DF_END."); + packet.type = SR_DF_END; + sr_session_send(cb_data, &packet); + + return SR_OK; +} + +SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info = { + .name = "tekpower-tp4000zc", + .longname = "TekPower TP4000ZC", + .api_version = 1, + .init = hw_init_tekpower_tp4000zc, + .cleanup = hw_cleanup, + .scan = hw_scan, + .dev_list = hw_dev_list, + .dev_clear = clear_instances, + .dev_open = hw_dev_open, + .dev_close = hw_dev_close, + .info_get = hw_info_get, + .dev_config_set = hw_dev_config_set, + .dev_acquisition_start = hw_dev_acquisition_start, + .dev_acquisition_stop = hw_dev_acquisition_stop, + .priv = NULL, +}; diff --git a/hardware/serial-dmm/protocol.c b/hardware/serial-dmm/protocol.c new file mode 100644 index 00000000..eabca486 --- /dev/null +++ b/hardware/serial-dmm/protocol.c @@ -0,0 +1,146 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2012 Alexandru Gagniuc + * Copyright (C) 2012 Uwe Hermann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" +#include "protocol.h" + +static void log_dmm_packet(const uint8_t *buf) +{ + sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], + buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]); +} + +SR_PRIV void dmm_details_tp4000zc(struct sr_datafeed_analog *analog, void *info) +{ + struct fs9721_info *info_local; + + info_local = (struct fs9721_info *)info; + + /* User-defined FS9721_LP3 flag 'c2c1_10' means temperature. */ + if (info_local->is_c2c1_10) { + analog->mq = SR_MQ_TEMPERATURE; + /* No Kelvin or Fahrenheit from the device, just Celsius. */ + analog->unit = SR_UNIT_CELSIUS; + } +} + +static void handle_packet(const uint8_t *buf, struct dev_context *devc, + int dmm, void *info) +{ + float floatval; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog *analog; + + log_dmm_packet(buf); + + if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog)))) { + sr_err("Analog packet malloc failed."); + return; + } + + analog->num_samples = 1; + analog->mq = -1; + + dmms[dmm].packet_parse(buf, &floatval, analog, info); + analog->data = &floatval; + + dmms[dmm].dmm_details(analog, info); + + if (analog->mq != -1) { + /* Got a measurement. */ + packet.type = SR_DF_ANALOG; + packet.payload = analog; + sr_session_send(devc->cb_data, &packet); + devc->num_samples++; + } + + g_free(analog); +} + +static void handle_new_data(struct dev_context *devc, int dmm, void *info) +{ + int len, i, offset = 0; + + /* Try to get as much data as the buffer can hold. */ + len = DMM_BUFSIZE - devc->buflen; + len = serial_read(devc->serial, devc->buf + devc->buflen, len); + if (len < 1) { + sr_err("Serial port read error: %d.", len); + return; + } + devc->buflen += len; + + /* Now look for packets in that data. */ + while ((devc->buflen - offset) >= dmms[dmm].packet_size) { + if (dmms[dmm].packet_valid(devc->buf + offset)) { + handle_packet(devc->buf + offset, devc, dmm, info); + offset += dmms[dmm].packet_size; + } else { + offset++; + } + } + + /* If we have any data left, move it to the beginning of our buffer. */ + for (i = 0; i < devc->buflen - offset; i++) + devc->buf[i] = devc->buf[offset + i]; + devc->buflen -= offset; +} + +static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + + (void)fd; + + if (!(sdi = cb_data)) + return TRUE; + + if (!(devc = sdi->priv)) + return TRUE; + + if (revents == G_IO_IN) { + /* Serial data arrived. */ + handle_new_data(devc, dmm, info); + } + + if (devc->num_samples >= devc->limit_samples) { + sr_info("Requested number of samples reached, stopping."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } + + return TRUE; +} + +SR_PRIV int tekpower_tp4000zc_receive_data(int fd, int revents, void *cb_data) +{ + struct fs9721_info info; + + return receive_data(fd, revents, TEKPOWER_TP4000ZC, &info, cb_data); +} diff --git a/hardware/serial-dmm/protocol.h b/hardware/serial-dmm/protocol.h new file mode 100644 index 00000000..ba5eaa5d --- /dev/null +++ b/hardware/serial-dmm/protocol.h @@ -0,0 +1,73 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2012 Alexandru Gagniuc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBSIGROK_HARDWARE_SERIAL_DMM_PROTOCOL_H +#define LIBSIGROK_HARDWARE_SERIAL_DMM_PROTOCOL_H + +/* Message logging helpers with driver-specific prefix string. */ +#define DRIVER_LOG_DOMAIN "serial-dmm: " +#define sr_log(l, s, args...) sr_log(l, DRIVER_LOG_DOMAIN s, ## args) +#define sr_spew(s, args...) sr_spew(DRIVER_LOG_DOMAIN s, ## args) +#define sr_dbg(s, args...) sr_dbg(DRIVER_LOG_DOMAIN s, ## args) +#define sr_info(s, args...) sr_info(DRIVER_LOG_DOMAIN s, ## args) +#define sr_warn(s, args...) sr_warn(DRIVER_LOG_DOMAIN s, ## args) +#define sr_err(s, args...) sr_err(DRIVER_LOG_DOMAIN s, ## args) + +enum { + TEKPOWER_TP4000ZC, +}; + +struct dmm_info { + char *vendor; + char *device; + char *conn; + uint32_t baudrate; + int packet_size; + gboolean (*packet_valid)(const uint8_t *); + int (*packet_parse)(const uint8_t *, float *, + struct sr_datafeed_analog *, void *); + void (*dmm_details)(struct sr_datafeed_analog *, void *); +}; + +SR_PRIV struct dmm_info dmms[1]; + +#define DMM_BUFSIZE 256 + +/** Private, per-device-instance driver context. */ +struct dev_context { + /** The current sampling limit (in number of samples). */ + uint64_t limit_samples; + + /** Opaque pointer passed in by the frontend. */ + void *cb_data; + + /** The current number of already received samples. */ + uint64_t num_samples; + + struct sr_serial_dev_inst *serial; + + uint8_t buf[DMM_BUFSIZE]; + int bufoffset; + int buflen; +}; + +SR_PRIV int tekpower_tp4000zc_receive_data(int fd, int revents, void *cb_data); +SR_PRIV void dmm_details_tp4000zc(struct sr_datafeed_analog *analog, void *info); + +#endif diff --git a/hardware/tekpower-dmm/Makefile.am b/hardware/tekpower-dmm/Makefile.am deleted file mode 100644 index 5e7521b1..00000000 --- a/hardware/tekpower-dmm/Makefile.am +++ /dev/null @@ -1,33 +0,0 @@ -## -## This file is part of the sigrok project. -## -## Copyright (C) 2012 Alexandru Gagniuc -## -## This program is free software: you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation, either version 3 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program. If not, see . -## - -if HW_TEKPOWER_DMM - -# Local lib, this is NOT meant to be installed! -noinst_LTLIBRARIES = libsigrokhwtekpowerdmm.la - -libsigrokhwtekpowerdmm_la_SOURCES = \ - api.c \ - protocol.c \ - protocol.h - -libsigrokhwtekpowerdmm_la_CFLAGS = \ - -I$(top_srcdir) - -endif diff --git a/hardware/tekpower-dmm/api.c b/hardware/tekpower-dmm/api.c deleted file mode 100644 index 4293d635..00000000 --- a/hardware/tekpower-dmm/api.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * This file is part of the sigrok project. - * - * Copyright (C) 2012 Bert Vermeulen - * Copyright (C) 2012 Alexandru Gagniuc - * Copyright (C) 2012 Uwe Hermann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include "protocol.h" - -static const int hwopts[] = { - SR_HWOPT_CONN, - SR_HWOPT_SERIALCOMM, - 0, -}; - -static const int hwcaps[] = { - SR_HWCAP_MULTIMETER, - SR_HWCAP_LIMIT_SAMPLES, - SR_HWCAP_CONTINUOUS, - 0, -}; - -static const char *probe_names[] = { - "Probe", - NULL, -}; - -SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info; -static struct sr_dev_driver *di_tekpower_tp4000zc = &tekpower_tp4000zc_driver_info; - -/* After hw_init() this will point to a device-specific entry (see above). */ -static struct sr_dev_driver *di = NULL; - -SR_PRIV struct dmm_info dmms[] = { - { - "TekPower", "TP4000ZC", - "2400/8n1", 2400, - FS9721_PACKET_SIZE, - sr_fs9721_packet_valid, - sr_fs9721_parse, - dmm_details_tp4000zc, - }, -}; - -/* Properly close and free all devices. */ -static int clear_instances(void) -{ - struct sr_dev_inst *sdi; - struct drv_context *drvc; - struct dev_context *devc; - GSList *l; - - if (!(drvc = di->priv)) - return SR_OK; - - drvc = di->priv; - for (l = drvc->instances; l; l = l->next) { - if (!(sdi = l->data)) - continue; - if (!(devc = sdi->priv)) - continue; - sr_serial_dev_inst_free(devc->serial); - sr_dev_inst_free(sdi); - } - g_slist_free(drvc->instances); - drvc->instances = NULL; - - return SR_OK; -} - -static int hw_init(int dmm) -{ - struct drv_context *drvc; - - if (!(drvc = g_try_malloc0(sizeof(struct drv_context)))) { - sr_err("Driver context malloc failed."); - return SR_ERR_MALLOC; - } - - if (dmm == TEKPOWER_TP4000ZC) - di = di_tekpower_tp4000zc; - sr_dbg("Selected '%s' subdriver.", di->name); - - di->priv = drvc; - - return SR_OK; -} - -static int hw_init_tekpower_tp4000zc(void) -{ - return hw_init(TEKPOWER_TP4000ZC); -} - -static GSList *scan(const char *conn, const char *serialcomm, int dmm) -{ - struct sr_dev_inst *sdi; - struct drv_context *drvc; - struct dev_context *devc; - struct sr_probe *probe; - struct sr_serial_dev_inst *serial; - GSList *devices; - int dropped, ret; - size_t len; - uint8_t buf[128]; - - if (!(serial = sr_serial_dev_inst_new(conn, serialcomm))) - return NULL; - - if (serial_open(serial, SERIAL_RDONLY | SERIAL_NONBLOCK) != SR_OK) - return NULL; - - sr_info("Probing port %s readonly.", conn); - - drvc = di->priv; - devices = NULL; - serial_flush(serial); - - /* - * There's no way to get an ID from the multimeter. It just sends data - * periodically, so the best we can do is check if the packets match - * the expected format. - */ - - /* Let's get a bit of data and see if we can find a packet. */ - len = sizeof(buf); - - ret = serial_stream_detect(serial, buf, &len, dmms[dmm].packet_size, - dmms[dmm].packet_valid, 1000, - dmms[dmm].baudrate); - if (ret != SR_OK) - goto scan_cleanup; - - /* - * If we dropped more than two packets worth of data, something is - * wrong. We shouldn't quit however, since the dropped bytes might be - * just zeroes at the beginning of the stream. Those can occur as a - * combination of the nonstandard cable that ships with this device and - * the serial port or USB to serial adapter. - */ - dropped = len - dmms[dmm].packet_size; - if (dropped > 2 * dmms[dmm].packet_size) - sr_warn("Had to drop too much data."); - - sr_info("Found device on port %s.", conn); - - if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, dmms[dmm].vendor, - dmms[dmm].device, ""))) - goto scan_cleanup; - - if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) { - sr_err("Device context malloc failed."); - goto scan_cleanup; - } - - devc->serial = serial; - - sdi->priv = devc; - sdi->driver = di; - if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1"))) - goto scan_cleanup; - sdi->probes = g_slist_append(sdi->probes, probe); - drvc->instances = g_slist_append(drvc->instances, sdi); - devices = g_slist_append(devices, sdi); - -scan_cleanup: - serial_close(serial); - - return devices; -} - -static GSList *hw_scan(GSList *options) -{ - struct sr_hwopt *opt; - GSList *l, *devices; - const char *conn, *serialcomm; - int dmm; - - conn = serialcomm = NULL; - for (l = options; l; l = l->next) { - opt = l->data; - switch (opt->hwopt) { - case SR_HWOPT_CONN: - conn = opt->value; - break; - case SR_HWOPT_SERIALCOMM: - serialcomm = opt->value; - break; - } - } - if (!conn) - return NULL; - - if (!strcmp(di->name, "tekpower-tp4000zc")) - dmm = 0; - - if (serialcomm) { - /* Use the provided comm specs. */ - devices = scan(conn, serialcomm, dmm); - } else { - /* Try the default. */ - devices = scan(conn, dmms[dmm].conn, dmm); - } - - return devices; -} - -static GSList *hw_dev_list(void) -{ - struct drv_context *drvc; - - drvc = di->priv; - - return drvc->instances; -} - -static int hw_dev_open(struct sr_dev_inst *sdi) -{ - struct dev_context *devc; - - if (!(devc = sdi->priv)) { - sr_err("sdi->priv was NULL."); - return SR_ERR_BUG; - } - - if (serial_open(devc->serial, SERIAL_RDONLY) != SR_OK) - return SR_ERR; - - sdi->status = SR_ST_ACTIVE; - - return SR_OK; -} - -static int hw_dev_close(struct sr_dev_inst *sdi) -{ - struct dev_context *devc; - - if (!(devc = sdi->priv)) { - sr_err("sdi->priv was NULL."); - return SR_ERR_BUG; - } - - if (devc->serial && devc->serial->fd != -1) { - serial_close(devc->serial); - sdi->status = SR_ST_INACTIVE; - } - - return SR_OK; -} - -static int hw_cleanup(void) -{ - clear_instances(); - - return SR_OK; -} - -static int hw_info_get(int info_id, const void **data, - const struct sr_dev_inst *sdi) -{ - (void)sdi; - - switch (info_id) { - case SR_DI_HWOPTS: - *data = hwopts; - break; - case SR_DI_HWCAPS: - *data = hwcaps; - break; - case SR_DI_NUM_PROBES: - *data = GINT_TO_POINTER(1); - break; - case SR_DI_PROBE_NAMES: - *data = probe_names; - break; - default: - return SR_ERR_ARG; - } - - return SR_OK; -} - -static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap, - const void *value) -{ - struct dev_context *devc; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR; - - if (!(devc = sdi->priv)) { - sr_err("sdi->priv was NULL."); - return SR_ERR_BUG; - } - - switch (hwcap) { - case SR_HWCAP_LIMIT_SAMPLES: - devc->limit_samples = *(const uint64_t *)value; - sr_dbg("Setting sample limit to %" PRIu64 ".", - devc->limit_samples); - break; - default: - sr_err("Unknown capability: %d.", hwcap); - return SR_ERR; - break; - } - - return SR_OK; -} - -static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, - void *cb_data) -{ - struct sr_datafeed_packet packet; - struct sr_datafeed_header header; - struct sr_datafeed_meta_analog meta; - struct dev_context *devc; - int (*receive_data)(int, int, void *) = NULL; - - if (!(devc = sdi->priv)) { - sr_err("sdi->priv was NULL."); - return SR_ERR_BUG; - } - - sr_dbg("Starting acquisition."); - - devc->cb_data = cb_data; - - /* - * Reset the number of samples to take. If we've already collected our - * quota, but we start a new session, and don't reset this, we'll just - * quit without acquiring any new samples. - */ - devc->num_samples = 0; - - /* Send header packet to the session bus. */ - sr_dbg("Sending SR_DF_HEADER."); - packet.type = SR_DF_HEADER; - packet.payload = (uint8_t *)&header; - header.feed_version = 1; - gettimeofday(&header.starttime, NULL); - sr_session_send(devc->cb_data, &packet); - - /* Send metadata about the SR_DF_ANALOG packets to come. */ - sr_dbg("Sending SR_DF_META_ANALOG."); - packet.type = SR_DF_META_ANALOG; - packet.payload = &meta; - meta.num_probes = 1; - sr_session_send(devc->cb_data, &packet); - - if (!strcmp(di->name, "tekpower-tp4000zc")) - receive_data = tekpower_tp4000zc_receive_data; - - /* Poll every 50ms, or whenever some data comes in. */ - sr_source_add(devc->serial->fd, G_IO_IN, 50, - receive_data, (void *)sdi); - - return SR_OK; -} - -static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) -{ - struct sr_datafeed_packet packet; - struct dev_context *devc; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR; - - if (!(devc = sdi->priv)) { - sr_err("sdi->priv was NULL."); - return SR_ERR_BUG; - } - - sr_dbg("Stopping acquisition."); - - sr_source_remove(devc->serial->fd); - hw_dev_close((struct sr_dev_inst *)sdi); - - /* Send end packet to the session bus. */ - sr_dbg("Sending SR_DF_END."); - packet.type = SR_DF_END; - sr_session_send(cb_data, &packet); - - return SR_OK; -} - -SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info = { - .name = "tekpower-tp4000zc", - .longname = "TekPower TP4000ZC", - .api_version = 1, - .init = hw_init_tekpower_tp4000zc, - .cleanup = hw_cleanup, - .scan = hw_scan, - .dev_list = hw_dev_list, - .dev_clear = clear_instances, - .dev_open = hw_dev_open, - .dev_close = hw_dev_close, - .info_get = hw_info_get, - .dev_config_set = hw_dev_config_set, - .dev_acquisition_start = hw_dev_acquisition_start, - .dev_acquisition_stop = hw_dev_acquisition_stop, - .priv = NULL, -}; diff --git a/hardware/tekpower-dmm/protocol.c b/hardware/tekpower-dmm/protocol.c deleted file mode 100644 index eabca486..00000000 --- a/hardware/tekpower-dmm/protocol.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * This file is part of the sigrok project. - * - * Copyright (C) 2012 Alexandru Gagniuc - * Copyright (C) 2012 Uwe Hermann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include "protocol.h" - -static void log_dmm_packet(const uint8_t *buf) -{ - sr_dbg("DMM packet: %02x %02x %02x %02x %02x %02x %02x" - " %02x %02x %02x %02x %02x %02x %02x", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], - buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]); -} - -SR_PRIV void dmm_details_tp4000zc(struct sr_datafeed_analog *analog, void *info) -{ - struct fs9721_info *info_local; - - info_local = (struct fs9721_info *)info; - - /* User-defined FS9721_LP3 flag 'c2c1_10' means temperature. */ - if (info_local->is_c2c1_10) { - analog->mq = SR_MQ_TEMPERATURE; - /* No Kelvin or Fahrenheit from the device, just Celsius. */ - analog->unit = SR_UNIT_CELSIUS; - } -} - -static void handle_packet(const uint8_t *buf, struct dev_context *devc, - int dmm, void *info) -{ - float floatval; - struct sr_datafeed_packet packet; - struct sr_datafeed_analog *analog; - - log_dmm_packet(buf); - - if (!(analog = g_try_malloc0(sizeof(struct sr_datafeed_analog)))) { - sr_err("Analog packet malloc failed."); - return; - } - - analog->num_samples = 1; - analog->mq = -1; - - dmms[dmm].packet_parse(buf, &floatval, analog, info); - analog->data = &floatval; - - dmms[dmm].dmm_details(analog, info); - - if (analog->mq != -1) { - /* Got a measurement. */ - packet.type = SR_DF_ANALOG; - packet.payload = analog; - sr_session_send(devc->cb_data, &packet); - devc->num_samples++; - } - - g_free(analog); -} - -static void handle_new_data(struct dev_context *devc, int dmm, void *info) -{ - int len, i, offset = 0; - - /* Try to get as much data as the buffer can hold. */ - len = DMM_BUFSIZE - devc->buflen; - len = serial_read(devc->serial, devc->buf + devc->buflen, len); - if (len < 1) { - sr_err("Serial port read error: %d.", len); - return; - } - devc->buflen += len; - - /* Now look for packets in that data. */ - while ((devc->buflen - offset) >= dmms[dmm].packet_size) { - if (dmms[dmm].packet_valid(devc->buf + offset)) { - handle_packet(devc->buf + offset, devc, dmm, info); - offset += dmms[dmm].packet_size; - } else { - offset++; - } - } - - /* If we have any data left, move it to the beginning of our buffer. */ - for (i = 0; i < devc->buflen - offset; i++) - devc->buf[i] = devc->buf[offset + i]; - devc->buflen -= offset; -} - -static int receive_data(int fd, int revents, int dmm, void *info, void *cb_data) -{ - struct sr_dev_inst *sdi; - struct dev_context *devc; - - (void)fd; - - if (!(sdi = cb_data)) - return TRUE; - - if (!(devc = sdi->priv)) - return TRUE; - - if (revents == G_IO_IN) { - /* Serial data arrived. */ - handle_new_data(devc, dmm, info); - } - - if (devc->num_samples >= devc->limit_samples) { - sr_info("Requested number of samples reached, stopping."); - sdi->driver->dev_acquisition_stop(sdi, cb_data); - return TRUE; - } - - return TRUE; -} - -SR_PRIV int tekpower_tp4000zc_receive_data(int fd, int revents, void *cb_data) -{ - struct fs9721_info info; - - return receive_data(fd, revents, TEKPOWER_TP4000ZC, &info, cb_data); -} diff --git a/hardware/tekpower-dmm/protocol.h b/hardware/tekpower-dmm/protocol.h deleted file mode 100644 index e3722483..00000000 --- a/hardware/tekpower-dmm/protocol.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of the sigrok project. - * - * Copyright (C) 2012 Alexandru Gagniuc - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef LIBSIGROK_HARDWARE_TEKPOWER_DMM_PROTOCOL_H -#define LIBSIGROK_HARDWARE_TEKPOWER_DMM_PROTOCOL_H - -/* Message logging helpers with driver-specific prefix string. */ -#define DRIVER_LOG_DOMAIN "tekpower-dmm: " -#define sr_log(l, s, args...) sr_log(l, DRIVER_LOG_DOMAIN s, ## args) -#define sr_spew(s, args...) sr_spew(DRIVER_LOG_DOMAIN s, ## args) -#define sr_dbg(s, args...) sr_dbg(DRIVER_LOG_DOMAIN s, ## args) -#define sr_info(s, args...) sr_info(DRIVER_LOG_DOMAIN s, ## args) -#define sr_warn(s, args...) sr_warn(DRIVER_LOG_DOMAIN s, ## args) -#define sr_err(s, args...) sr_err(DRIVER_LOG_DOMAIN s, ## args) - -enum { - TEKPOWER_TP4000ZC, -}; - -struct dmm_info { - char *vendor; - char *device; - char *conn; - uint32_t baudrate; - int packet_size; - gboolean (*packet_valid)(const uint8_t *); - int (*packet_parse)(const uint8_t *, float *, - struct sr_datafeed_analog *, void *); - void (*dmm_details)(struct sr_datafeed_analog *, void *); -}; - -SR_PRIV struct dmm_info dmms[1]; - -#define DMM_BUFSIZE 256 - -/** Private, per-device-instance driver context. */ -struct dev_context { - /** The current sampling limit (in number of samples). */ - uint64_t limit_samples; - - /** Opaque pointer passed in by the frontend. */ - void *cb_data; - - /** The current number of already received samples. */ - uint64_t num_samples; - - struct sr_serial_dev_inst *serial; - - uint8_t buf[DMM_BUFSIZE]; - int bufoffset; - int buflen; -}; - -SR_PRIV int tekpower_tp4000zc_receive_data(int fd, int revents, void *cb_data); -SR_PRIV void dmm_details_tp4000zc(struct sr_datafeed_analog *analog, void *info); - -#endif diff --git a/hwdriver.c b/hwdriver.c index 729edc96..c377b2d5 100644 --- a/hwdriver.c +++ b/hwdriver.c @@ -113,7 +113,7 @@ extern SR_PRIV struct sr_dev_driver flukedmm_driver_info; #ifdef HAVE_HW_RADIOSHACK_DMM extern SR_PRIV struct sr_dev_driver radioshackdmm_driver_info; #endif -#ifdef HAVE_HW_TEKPOWER_DMM +#ifdef HAVE_HW_SERIAL_DMM extern SR_PRIV struct sr_dev_driver tekpower_tp4000zc_driver_info; #endif #ifdef HAVE_HW_UNI_T_DMM @@ -168,7 +168,7 @@ static struct sr_dev_driver *drivers_list[] = { #ifdef HAVE_HW_RADIOSHACK_DMM &radioshackdmm_driver_info, #endif -#ifdef HAVE_HW_TEKPOWER_DMM +#ifdef HAVE_HW_SERIAL_DMM &tekpower_tp4000zc_driver_info, #endif #ifdef HAVE_HW_UNI_T_DMM