X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=hardware%2Fcommon%2Fscpi_serial.c;h=feb3317651eb18f2d858b576bd242e4556c1e1d0;hb=43cd4637285833706f8a404ca027bcf0ee75b9ae;hp=3e1492522aea9ee36f8a6264128b54266a5e7a08;hpb=a1ff9c1897262faa3b284ea5bb82593c45de70d0;p=libsigrok.git diff --git a/hardware/common/scpi_serial.c b/hardware/common/scpi_serial.c index 3e149252..feb33176 100644 --- a/hardware/common/scpi_serial.c +++ b/hardware/common/scpi_serial.c @@ -22,139 +22,219 @@ #include "libsigrok-internal.h" #include +#include #include -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "scpi_serial: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) +#define LOG_PREFIX "scpi_serial" -#define SCPI_READ_RETRIES 100 -#define SCPI_READ_RETRY_TIMEOUT 10000 +#define BUFFER_SIZE 1024 -SR_PRIV int scpi_serial_open(void *priv) +struct scpi_serial { + struct sr_serial_dev_inst *serial; + char buffer[BUFFER_SIZE]; + size_t count; + size_t read; +}; + +static struct { + uint16_t vendor_id; + uint16_t product_id; + const char *serialcomm; +} scpi_serial_usb_ids[] = { + { 0x0403, 0xed72, "115200/8n1/flow=1" }, /* Hameg HO720 */ + { 0x0403, 0xed73, "115200/8n1/flow=1" }, /* Hameg HO730 */ +}; + +static GSList *scpi_serial_scan(struct drv_context *drvc) +{ + GSList *l, *r, *resources = NULL; + gchar *res; + unsigned i; + + (void)drvc; + + for (i = 0; i < ARRAY_SIZE(scpi_serial_usb_ids); i++) { + if ((l = sr_serial_find_usb(scpi_serial_usb_ids[i].vendor_id, + scpi_serial_usb_ids[i].product_id)) == NULL) + continue; + for (r = l; r; r = r->next) { + if (scpi_serial_usb_ids[i].serialcomm) + res = g_strdup_printf("%s:%s", (char *) r->data, + scpi_serial_usb_ids[i].serialcomm); + else + res = g_strdup(r->data); + resources = g_slist_append(resources, res); + } + g_slist_free_full(l, g_free); + } + + return resources; +} + +static int scpi_serial_dev_inst_new(void *priv, struct drv_context *drvc, + const char *resource, char **params, const char *serialcomm) +{ + struct scpi_serial *sscpi = priv; + + (void)drvc; + (void)params; + + if (!(sscpi->serial = sr_serial_dev_inst_new(resource, serialcomm))) + return SR_ERR; + + return SR_OK; +} + +static int scpi_serial_open(void *priv) { - struct sr_serial_dev_inst *serial = priv; + struct scpi_serial *sscpi = priv; + struct sr_serial_dev_inst *serial = sscpi->serial; + + if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK) + return SR_ERR; + + if (serial_flush(serial) != SR_OK) + return SR_ERR; + + sscpi->count = 0; + sscpi->read = 0; - return serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK); + return SR_OK; } -SR_PRIV int scpi_serial_source_add(void *priv, int events, int timeout, - sr_receive_data_callback_t cb, void *cb_data) +static int scpi_serial_source_add(struct sr_session *session, void *priv, + int events, int timeout, sr_receive_data_callback cb, void *cb_data) { - struct sr_serial_dev_inst *serial = priv; + struct scpi_serial *sscpi = priv; + struct sr_serial_dev_inst *serial = sscpi->serial; - return sr_source_add(serial->fd, events, timeout, cb, cb_data); + return serial_source_add(session, serial, events, timeout, cb, cb_data); } -SR_PRIV int scpi_serial_source_remove(void *priv) +static int scpi_serial_source_remove(struct sr_session *session, void *priv) { - struct sr_serial_dev_inst *serial = priv; + struct scpi_serial *sscpi = priv; + struct sr_serial_dev_inst *serial = sscpi->serial; - return sr_source_remove(serial->fd); + return serial_source_remove(session, serial); } -SR_PRIV int scpi_serial_send(void *priv, const char *command) +static int scpi_serial_send(void *priv, const char *command) { - int len, out; + int len, result, written; gchar *terminated_command; - struct sr_serial_dev_inst *serial = priv; + struct scpi_serial *sscpi = priv; + struct sr_serial_dev_inst *serial = sscpi->serial; terminated_command = g_strconcat(command, "\n", NULL); len = strlen(terminated_command); - out = serial_write(serial, terminated_command, len); - g_free(terminated_command); - - if (out != len) { - sr_dbg("Only sent %d/%d bytes of SCPI command: '%s'.", out, - len, command); - return SR_ERR; + written = 0; + while (written < len) { + result = serial_write(serial, terminated_command + written, len - written); + if (result < 0) { + sr_err("Error while sending SCPI command: '%s'.", command); + g_free(terminated_command); + return SR_ERR; + } + written += result; } + g_free(terminated_command); + sr_spew("Successfully sent SCPI command: '%s'.", command); return SR_OK; } -SR_PRIV int scpi_serial_receive(void *priv, char **scpi_response) +static int scpi_serial_read_begin(void *priv) +{ + (void) priv; + + return SR_OK; +} + +static int scpi_serial_read_data(void *priv, char *buf, int maxlen) { + struct scpi_serial *sscpi = priv; int len, ret; - char buf[256]; - unsigned int i; - GString *response; - struct sr_serial_dev_inst *serial = priv; - response = g_string_sized_new(1024); + len = BUFFER_SIZE - sscpi->count; - for (i = 0; i <= SCPI_READ_RETRIES; i++) { - while ((len = serial_read(serial, buf, sizeof(buf))) > 0) - response = g_string_append_len(response, buf, len); + /* Try to read new data into the buffer if there is space. */ + if (len > 0) { + ret = serial_read(sscpi->serial, sscpi->buffer + sscpi->read, + BUFFER_SIZE - sscpi->count); - if (response->len > 0 && - response->str[response->len-1] == '\n') { - sr_spew("Fetched full SCPI response."); - break; - } + if (ret < 0) + return ret; - g_usleep(SCPI_READ_RETRY_TIMEOUT); - } + sscpi->count += ret; - if (response->len == 0) { - sr_dbg("No SCPI response received."); - g_string_free(response, TRUE); - *scpi_response = NULL; - return SR_ERR; - } else if (response->str[response->len - 1] == '\n') { - /* - * The SCPI response contains a LF ('\n') at the end and we - * don't need this so replace it with a '\0' and decrement - * the length. - */ - response->str[--response->len] = '\0'; - ret = SR_OK; - } else { - sr_warn("Incomplete SCPI response received!"); - ret = SR_ERR; + if (ret > 0) + sr_spew("Read %d bytes into buffer.", ret); } - /* Minor optimization: steal the string instead of copying. */ - *scpi_response = response->str; + /* Return as many bytes as possible from buffer, excluding any trailing newline. */ + if (sscpi->read < sscpi->count) { + len = sscpi->count - sscpi->read; + if (len > maxlen) + len = maxlen; + if (sscpi->buffer[sscpi->read + len - 1] == '\n') + len--; + sr_spew("Returning %d bytes from buffer.", len); + memcpy(buf, sscpi->buffer + sscpi->read, len); + sscpi->read += len; + if (sscpi->read == BUFFER_SIZE) { + sr_spew("Resetting buffer."); + sscpi->count = 0; + sscpi->read = 0; + } + return len; + } - /* A SCPI response can be quite large, print at most 50 characters. */ - sr_dbg("SCPI response received (length %d): '%.50s'", - response->len, response->str); + return 0; +} - g_string_free(response, FALSE); +static int scpi_serial_read_complete(void *priv) +{ + struct scpi_serial *sscpi = priv; - return ret; + /* If the next character is a newline, discard it and report complete. */ + if (sscpi->read < sscpi->count && sscpi->buffer[sscpi->read] == '\n') { + sscpi->read++; + return 1; + } else { + return 0; + } } -SR_PRIV struct sr_scpi_dev_inst *scpi_serial_dev_inst_new(const char *port, - const char *serialcomm) +static int scpi_serial_close(void *priv) { - struct sr_scpi_dev_inst *scpi; - struct sr_serial_dev_inst *serial; + struct scpi_serial *sscpi = priv; - scpi = g_try_malloc(sizeof(struct sr_scpi_dev_inst)); + return serial_close(sscpi->serial); +} - if (!(serial = sr_serial_dev_inst_new(port, serialcomm))) - { - g_free(scpi); - return NULL; - } +static void scpi_serial_free(void *priv) +{ + struct scpi_serial *sscpi = priv; - scpi->open = scpi_serial_open; - scpi->source_add = scpi_serial_source_add; - scpi->source_remove = scpi_serial_source_remove; - scpi->send = scpi_serial_send; - scpi->receive = scpi_serial_receive; - scpi->read = serial_read; - scpi->close = serial_close; - scpi->free = sr_serial_dev_inst_free; - scpi->priv = serial; - - return scpi; + sr_serial_dev_inst_free(sscpi->serial); } + +SR_PRIV const struct sr_scpi_dev_inst scpi_serial_dev = { + .name = "serial", + .prefix = "", + .priv_size = sizeof(struct scpi_serial), + .scan = scpi_serial_scan, + .dev_inst_new = scpi_serial_dev_inst_new, + .open = scpi_serial_open, + .source_add = scpi_serial_source_add, + .source_remove = scpi_serial_source_remove, + .send = scpi_serial_send, + .read_begin = scpi_serial_read_begin, + .read_data = scpi_serial_read_data, + .read_complete = scpi_serial_read_complete, + .close = scpi_serial_close, + .free = scpi_serial_free, +};