From: lelazary Date: Sat, 5 Jan 2013 01:03:20 +0000 (-0800) Subject: Rewrote the link-mso19.c into api and protocol. Still need to test and cleanup some... X-Git-Tag: dsupstream~373 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=df92e5cf6b1caad0d1d43eb890d84af1ef79cd18;p=libsigrok.git Rewrote the link-mso19.c into api and protocol. Still need to test and cleanup some more --- diff --git a/hardware/link-mso19/api.c b/hardware/link-mso19/api.c new file mode 100644 index 00000000..5dd5045d --- /dev/null +++ b/hardware/link-mso19/api.c @@ -0,0 +1,538 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010-2012 Bert Vermeulen + * + * 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 "protocol.h" + +#define SERIALCOMM "460800/8n1" //Default communication params +#define SERIALCONN "/dev/ttyUSB0" //Default communication params + +static const int hwcaps[] = { + SR_HWCAP_LOGIC_ANALYZER, + SR_HWCAP_SAMPLERATE, +// SR_HWCAP_CAPTURE_RATIO, + SR_HWCAP_LIMIT_SAMPLES, +// SR_HWCAP_RLE, + 0, +}; + +/* + * Probes are numbered 0 to 7. + * + * See also: http://www.linkinstruments.com/images/mso19_1113.gif + */ +SR_PRIV const char *mso19_probe_names[NUM_PROBES + 1] = { + "0", "1", "2", "3", "4", "5", "6", "7", NULL +}; + +/*supported samplerates */ +static const struct sr_samplerates samplerates = { + SR_HZ(100), + SR_HZ(200), + SR_HZ(500), + SR_KHZ(1), + SR_KHZ(2), + SR_KHZ(5), + SR_KHZ(10), + SR_KHZ(20), + SR_KHZ(50), + SR_KHZ(100), + SR_KHZ(200), + SR_KHZ(500), + SR_MHZ(1), + SR_MHZ(2), + SR_MHZ(5), + SR_MHZ(10), + SR_MHZ(20), + SR_MHZ(50), + SR_MHZ(100), + SR_MHZ(200), + NULL, +}; + +SR_PRIV struct sr_dev_driver link_mso19_driver_info; +static struct sr_dev_driver *di = &link_mso19_driver_info; + +static int hw_init(struct sr_context *sr_ctx) +{ + printf("Init driver\n"); + + struct drv_context *drvc; + + if (!(drvc = g_try_malloc0(sizeof(struct drv_context)))) { + sr_err("Driver context malloc failed."); + return SR_ERR_MALLOC; + } + drvc->sr_ctx = sr_ctx; + di->priv = drvc; + + return SR_OK; +} + +static GSList *hw_scan(GSList *options) +{ + //struct sr_hwopt *opt; + //struct sr_probe *probe; + //GPollFD probefd; + //int ret, i; + //char buf[8]; + //struct udev *udev; + int i; + + (void)options; + GSList *devices = NULL; + + sr_info("Checking for link mso19\n"); + + const char* conn = NULL; + const char* serialcomm = NULL; + GSList *l; + for (l = options; l; l = l->next) { + struct sr_hwopt* opt = l->data; + switch (opt->hwopt) { + case SR_HWOPT_CONN: + conn = opt->value; + break; + case SR_HWOPT_SERIALCOMM: + serialcomm = opt->value; + break; + } + } + if (!conn) + conn = SERIALCONN; + if (serialcomm == NULL) + serialcomm = SERIALCOMM; + + struct udev *udev = udev_new(); + if (!udev) { + sr_err("Failed to initialize udev."); + } + struct udev_enumerate *enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "usb-serial"); + udev_enumerate_scan_devices(enumerate); + struct udev_list_entry *devs = udev_enumerate_get_list_entry(enumerate); + struct udev_list_entry *dev_list_entry; + for (dev_list_entry = devs; + dev_list_entry != NULL; + dev_list_entry = udev_list_entry_get_next(dev_list_entry)) + { + const char *syspath = udev_list_entry_get_name(dev_list_entry); + struct udev_device *dev = udev_device_new_from_syspath(udev, syspath); + const char *sysname = udev_device_get_sysname(dev); + struct udev_device *parent = udev_device_get_parent_with_subsystem_devtype( + dev, "usb", "usb_device"); + + if (!parent) { + sr_err("Unable to find parent usb device for %s", + sysname); + continue; + } + + const char *idVendor = udev_device_get_sysattr_value(parent, "idVendor"); + const char *idProduct = udev_device_get_sysattr_value(parent, "idProduct"); + if (strcmp(USB_VENDOR, idVendor) + || strcmp(USB_PRODUCT, idProduct)) + continue; + + const char* iSerial = udev_device_get_sysattr_value(parent, "serial"); + const char* iProduct = udev_device_get_sysattr_value(parent, "product"); + + char path[32]; + snprintf(path, sizeof(path), "/dev/%s", sysname); + + size_t s = strcspn(iProduct, " "); + char product[32]; + char manufacturer[32]; + if (s > sizeof(product) || + strlen(iProduct) - s > sizeof(manufacturer)) { + sr_err("Could not parse iProduct: %s.", iProduct); + continue; + } + strncpy(product, iProduct, s); + product[s] = 0; + strcpy(manufacturer, iProduct + s); + + //Create the device context and set its params + struct dev_context *devc; + if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) { + sr_err("Device context malloc failed."); + return devices; + } + + if (mso_parse_serial(iSerial, iProduct, devc) != SR_OK) { + sr_err("Invalid iSerial: %s.", iSerial); + g_free(devc); + return devices; + } + + char hwrev[32]; + sprintf(hwrev, "r%d", devc->hwrev); + devc->ctlbase1 = 0; + devc->protocol_trigger.spimode = 0; + for (i = 0; i < 4; i++) { + devc->protocol_trigger.word[i] = 0; + devc->protocol_trigger.mask[i] = 0xff; + } + + if (!(devc->serial = sr_serial_dev_inst_new(conn, serialcomm))) + { + g_free(devc); + return devices; + } + + struct sr_dev_inst *sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, + manufacturer, product, hwrev); + + if (!sdi) { + sr_err("Unable to create device instance for %s", + sysname); + sr_dev_inst_free(sdi); + g_free(devc); + return devices; + } + + //sdi->index = 0; + sdi->driver = di; + sdi->priv = devc; + //sdi->model = " + //sdi->version = "Testing1234"; + //struct sr_probe *probe; + //sdi->probes = g_slist_append(sdi->probes, probe); + + printf("Add the context\n"); + //Add the driver + struct drv_context *drvc = di->priv; + drvc->instances = g_slist_append(drvc->instances, sdi); + devices = g_slist_append(devices, sdi); + } + + printf("Return devices\n"); + return devices; +} + +static GSList *hw_dev_list(void) +{ + printf("Dev list\n"); + struct drv_context *drvc; + + drvc = di->priv; + + return drvc->instances; +} + +static int hw_dev_open(struct sr_dev_inst *sdi) +{ + printf("Dev opewn\n"); + struct dev_context *devc; + + devc = sdi->priv; + + if (serial_open(devc->serial, SERIAL_RDWR) != SR_OK) + return SR_ERR; + + sdi->status = SR_ST_ACTIVE; + + /* FIXME: discard serial buffer */ + mso_check_trigger(devc->serial, &devc->trigger_state); + sr_dbg("Trigger state: 0x%x.", devc->trigger_state); + + int ret = mso_reset_adc(sdi); + if (ret != SR_OK) + return ret; + + mso_check_trigger(devc->serial, &devc->trigger_state); + sr_dbg("Trigger state: 0x%x.", devc->trigger_state); + + // ret = mso_reset_fsm(sdi); + // if (ret != SR_OK) + // return ret; + // return SR_ERR; + + return SR_OK; +} + +static int hw_dev_close(struct sr_dev_inst *sdi) +{ + printf("dev close\n"); + struct dev_context *devc; + + devc = sdi->priv; + + if (devc->serial && devc->serial->fd != -1) { + serial_close(devc->serial); + sdi->status = SR_ST_INACTIVE; + } + + return SR_OK; +} + +static int hw_cleanup(void) +{ + printf("*Dev clearup\n"); + GSList *l; + struct sr_dev_inst *sdi; + struct drv_context *drvc; + struct dev_context *devc; + int ret = SR_OK; + + if (!(drvc = di->priv)) + return SR_OK; + + /* Properly close and free all devices. */ + for (l = drvc->instances; l; l = l->next) { + if (!(sdi = l->data)) { + /* Log error, but continue cleaning up the rest. */ + sr_err("%s: sdi was NULL, continuing", __func__); + ret = SR_ERR_BUG; + continue; + } + if (!(devc = sdi->priv)) { + /* Log error, but continue cleaning up the rest. */ + sr_err("%s: sdi->priv was NULL, continuing", __func__); + ret = SR_ERR_BUG; + continue; + } + hw_dev_close(sdi); + sr_serial_dev_inst_free(devc->serial); + sr_dev_inst_free(sdi); + } + g_slist_free(drvc->instances); + drvc->instances = NULL; + + return ret; +} + +static int hw_info_get(int info_id, const void **data, + const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + printf("Get info\n"); + + switch (info_id) { + case SR_DI_HWCAPS: + *data = hwcaps; + break; + case SR_DI_NUM_PROBES: + *data = GINT_TO_POINTER(1); + break; + case SR_DI_PROBE_NAMES: + *data = mso19_probe_names; + break; + case SR_DI_SAMPLERATES: + *data = &samplerates; + break; + case SR_DI_TRIGGER_TYPES: + *data = (char *)TRIGGER_TYPES; + break; + case SR_DI_CUR_SAMPLERATE: + if (sdi) { + devc = sdi->priv; + *data = &devc->cur_rate; + } else + return SR_ERR; + 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; + int ret; + const uint64_t *tmp_u64; + + printf("Config set\n"); + devc = sdi->priv; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR; + + switch (hwcap) { + case SR_HWCAP_SAMPLERATE: + return mso_configure_rate(sdi, *(const uint64_t *) value); + ret = SR_OK; + break; + case SR_HWCAP_LIMIT_SAMPLES: + ret = SR_OK; + break; + case SR_HWCAP_CAPTURE_RATIO: + ret = SR_OK; + break; + case SR_HWCAP_RLE: + ret = SR_OK; + break; + default: + ret = SR_ERR; + } + + return ret; +} + +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_logic meta; + struct dev_context *devc; + uint32_t trigger_config[4]; + uint32_t data; + uint16_t readcount, delaycount; + uint8_t changrp_mask; + int num_channels; + int i; + int ret = SR_ERR; + + + printf("Accquistion start\n"); + devc = sdi->priv; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR; + + //TODO if (ols_configure_probes(sdi) != SR_OK) { + //TODO sr_err("Failed to configure probes."); + //TODO return SR_ERR; + //TODO } + + /* + * Enable/disable channel groups in the flag register according to the + * probe mask. Calculate this here, because num_channels is needed + * to limit readcount. + */ + //changrp_mask = 0; + //num_channels = 0; + //for (i = 0; i < 4; i++) { + // if (devc->probe_mask & (0xff << (i * 8))) { + // changrp_mask |= (1 << i); + // num_channels++; + // } + //} + + /* FIXME: No need to do full reconfigure every time */ +// ret = mso_reset_fsm(sdi); +// if (ret != SR_OK) +// return ret; + + /* FIXME: ACDC Mode */ + devc->ctlbase1 &= 0x7f; +// devc->ctlbase1 |= devc->acdcmode; + + ret = mso_configure_rate(sdi, devc->cur_rate); + if (ret != SR_OK) + return ret; + + /* set dac offset */ + ret = mso_dac_out(sdi, devc->dac_offset); + if (ret != SR_OK) + return ret; + + ret = mso_configure_threshold_level(sdi); + if (ret != SR_OK) + return ret; + + ret = mso_configure_trigger(sdi); + if (ret != SR_OK) + return ret; + + /* FIXME: trigger_position */ + + + /* END of config hardware part */ + + /* with trigger */ + ret = mso_arm(sdi); + if (ret != SR_OK) + return ret; + + /* without trigger */ +// ret = mso_force_capture(sdi); +// if (ret != SR_OK) +// return ret; + + /* Start acquisition on the device. */ + mso_check_trigger(sdi, &devc->trigger_state); + ret = mso_check_trigger(sdi, NULL); + if (ret != SR_OK) + return ret; + + sr_source_add(devc->serial->fd, G_IO_IN, -1, mso_receive_data, cb_data); + + if (!(packet = g_try_malloc(sizeof(struct sr_datafeed_packet)))) { + sr_err("Datafeed packet malloc failed."); + return SR_ERR_MALLOC; + } + + if (!(header = g_try_malloc(sizeof(struct sr_datafeed_header)))) { + sr_err("Datafeed header malloc failed."); + g_free(packet); + return SR_ERR_MALLOC; + } + + packet->type = SR_DF_HEADER; + packet->payload = (unsigned char *)header; + header->feed_version = 1; + gettimeofday(&header->starttime, NULL); + sr_session_send(cb_data, packet); + + packet->type = SR_DF_META_LOGIC; + packet->payload = &meta; + meta.samplerate = devc->cur_rate; + meta.num_probes = NUM_PROBES; + sr_session_send(cb_data, packet); + + g_free(header); + g_free(packet); + + return SR_OK; +} + +/* TODO: This stops acquisition on ALL devices, ignoring dev_index. */ +static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) +{ + printf("Accuqstion stop\n"); + /* Avoid compiler warnings. */ + (void)cb_data; + + stop_acquisition(sdi); + + return SR_OK; +} + +SR_PRIV struct sr_dev_driver link_mso19_driver_info = { + .name = "link-mso19", + .longname = "Link Instruments MSO-19", + .api_version = 1, + .init = hw_init, + .cleanup = hw_cleanup, + .scan = hw_scan, + .dev_list = hw_dev_list, + .dev_clear = hw_cleanup, + .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/link-mso19/protocol.c b/hardware/link-mso19/protocol.c new file mode 100644 index 00000000..5c879dd1 --- /dev/null +++ b/hardware/link-mso19/protocol.c @@ -0,0 +1,271 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010-2012 Bert Vermeulen + * + * 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 "protocol.h" + +extern SR_PRIV struct sr_dev_driver link_mso19_driver_info; +static struct sr_dev_driver *di = &link_mso19_driver_info; + +SR_PRIV int mso_parse_serial(const char *iSerial, const char *iProduct, + struct dev_context *devc) +{ + unsigned int u1, u2, u3, u4, u5, u6; + + iProduct = iProduct; + /* FIXME: This code is in the original app, but I think its + * used only for the GUI */ + /* if (strstr(iProduct, "REV_02") || strstr(iProduct, "REV_03")) + devc->num_sample_rates = 0x16; + else + devc->num_sample_rates = 0x10; */ + + + /* parse iSerial */ + if (iSerial[0] != '4' || sscanf(iSerial, "%5u%3u%3u%1u%1u%6u", + &u1, &u2, &u3, &u4, &u5, &u6) != 6) + return SR_ERR; + devc->hwmodel = u4; + devc->hwrev = u5; + devc->serial = u6; + devc->vbit = u1 / 10000; + if (devc->vbit == 0) + devc->vbit = 4.19195; + devc->dac_offset = u2; + if (devc->dac_offset == 0) + devc->dac_offset = 0x1ff; + devc->offset_range = u3; + if (devc->offset_range == 0) + devc->offset_range = 0x17d; + + /* + * FIXME: There is more code on the original software to handle + * bigger iSerial strings, but as I can't test on my device + * I will not implement it yet + */ + + return SR_OK; +} + +SR_PRIV int mso_send_control_message(struct sr_serial_dev_inst *serial, + uint16_t payload[], int n) +{ + int i, w, ret, s = n * 2 + sizeof(mso_head) + sizeof(mso_foot); + char *p, *buf; + + ret = SR_ERR; + + if (serial->fd < 0) + goto ret; + + if (!(buf = g_try_malloc(s))) { + sr_err("Failed to malloc message buffer."); + ret = SR_ERR_MALLOC; + goto ret; + } + + p = buf; + memcpy(p, mso_head, sizeof(mso_head)); + p += sizeof(mso_head); + + for (i = 0; i < n; i++) { + *(uint16_t *) p = htons(payload[i]); + p += 2; + } + memcpy(p, mso_foot, sizeof(mso_foot)); + + w = 0; + while (w < s) { + ret = serial_write(serial, buf + w, s - w); + if (ret < 0) { + ret = SR_ERR; + goto free; + } + w += ret; + } + ret = SR_OK; +free: + g_free(buf); +ret: + return ret; +} + +SR_PRIV int mso_reset_adc(struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + uint16_t ops[2]; + + ops[0] = mso_trans(REG_CTL1, (devc->ctlbase1 | BIT_CTL1_RESETADC)); + ops[1] = mso_trans(REG_CTL1, devc->ctlbase1); + devc->ctlbase1 |= BIT_CTL1_ADC_UNKNOWN4; + + sr_dbg("Requesting ADC reset."); + return mso_send_control_message(devc->serial, ARRAY_AND_SIZE(ops)); +} + +SR_PRIV void stop_acquisition(const struct sr_dev_inst *sdi) +{ + struct sr_datafeed_packet packet; + struct dev_context *devc; + + devc = sdi->priv; + sr_source_remove(devc->serial->fd); + + /* Terminate session */ + packet.type = SR_DF_END; + sr_session_send(sdi, &packet); +} + +SR_PRIV int mso_clkrate_out(struct sr_serial_dev_inst *serial, uint16_t val) +{ + uint16_t ops[] = { + mso_trans(REG_CLKRATE1, (val >> 8) & 0xff), + mso_trans(REG_CLKRATE2, val & 0xff), + }; + + sr_dbg("Setting clkrate word to 0x%x.", val); + return mso_send_control_message(serial, ARRAY_AND_SIZE(ops)); +} + +SR_PRIV int mso_configure_rate(struct sr_dev_inst *sdi, uint32_t rate) +{ + struct dev_context *devc = sdi->priv; + unsigned int i; + int ret = SR_ERR; + + for (i = 0; i < ARRAY_SIZE(rate_map); i++) { + if (rate_map[i].rate == rate) { + devc->ctlbase2 = rate_map[i].slowmode; + ret = mso_clkrate_out(sdi, rate_map[i].val); + if (ret == SR_OK) + devc->cur_rate = rate; + return ret; + } + } + return ret; +} + + + + + +SR_PRIV int mso_check_trigger(struct sr_serial_dev_inst *serial, uint8_t *info) +{ + uint16_t ops[] = { mso_trans(REG_TRIGGER, 0) }; + int ret; + + sr_dbg("Requesting trigger state."); + ret = mso_send_control_message(serial, ARRAY_AND_SIZE(ops)); + if (info == NULL || ret != SR_OK) + return ret; + + + uint8_t buf = 0; + if (serial_read(serial, &buf, 1) != 1) /* FIXME: Need timeout */ + ret = SR_ERR; + *info = buf; + + sr_dbg("Trigger state is: 0x%x.", *info); + return ret; +} + +SR_PRIV int mso_receive_data(int fd, int revents, void *cb_data) +{ + + struct sr_datafeed_packet packet; + struct sr_datafeed_logic logic; + struct sr_dev_inst *sdi; + struct drv_context *drvc; + struct dev_context *devc; + GSList *l; + int num_channels, offset, i, j; + unsigned char byte; + + drvc = di->priv; + + /* Find this device's devc struct by its fd. */ + devc = NULL; + for (l = drvc->instances; l; l = l->next) { + sdi = l->data; + devc = sdi->priv; + if (devc->serial->fd == fd) + break; + devc = NULL; + } + if (!devc) + /* Shouldn't happen. */ + return TRUE; + + (void)revents; + + uint8_t in[1024]; + size_t s = serial_read(devc->serial, in, sizeof(in)); + if (s <= 0) + return FALSE; + + /* No samples */ + if (devc->trigger_state != MSO_TRIGGER_DATAREADY) { + devc->trigger_state = in[0]; + if (devc->trigger_state == MSO_TRIGGER_DATAREADY) { + mso_read_buffer(sdi); + devc->buffer_n = 0; + } else { + mso_check_trigger(devc->serial, NULL); + } + return FALSE; + } + + /* the hardware always dumps 1024 samples, 24bits each */ + if (devc->buffer_n < 3072) { + memcpy(devc->buffer + devc->buffer_n, in, s); + devc->buffer_n += s; + } + if (devc->buffer_n < 3072) + return FALSE; + + /* do the conversion */ + uint8_t logic_out[1024]; + double analog_out[1024]; + for (i = 0; i < 1024; i++) { + /* FIXME: Need to do conversion to mV */ + analog_out[i] = (devc->buffer[i * 3] & 0x3f) | + ((devc->buffer[i * 3 + 1] & 0xf) << 6); + logic_out[i] = ((devc->buffer[i * 3 + 1] & 0x30) >> 4) | + ((devc->buffer[i * 3 + 2] & 0x3f) << 2); + } + + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + logic.length = 1024; + logic.unitsize = 1; + logic.data = logic_out; + sr_session_send(cb_data, &packet); + + // Dont bother fixing this yet, keep it "old style" + /* + packet.type = SR_DF_ANALOG; + packet.length = 1024; + packet.unitsize = sizeof(double); + packet.payload = analog_out; + sr_session_send(ctx->session_dev_id, &packet); + */ + + packet.type = SR_DF_END; + sr_session_send(devc->session_dev_id, &packet); + +} diff --git a/hardware/link-mso19/protocol.h b/hardware/link-mso19/protocol.h new file mode 100644 index 00000000..74a9afdd --- /dev/null +++ b/hardware/link-mso19/protocol.h @@ -0,0 +1,235 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010-2012 Bert Vermeulen + * + * 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_LINK_MSO19_PROTOCOL_H +#define LIBSIGROK_HARDWARE_LINK_MSO19_PROTOCOL_H + +#define USB_VENDOR "3195" +#define USB_PRODUCT "f190" + +#include +#include +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" + +/* Message logging helpers with driver-specific prefix string. */ +#define DRIVER_LOG_DOMAIN "mso19: " +#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) + +#define NUM_PROBES 8 +#define NUM_TRIGGER_STAGES 4 +#define TRIGGER_TYPES "01" +#define SERIAL_SPEED B115200 +#define CLOCK_RATE SR_MHZ(100) +#define MIN_NUM_SAMPLES 4 + + +///* Bitmasks for CMD_FLAGS */ +//#define FLAG_DEMUX 0x01 +//#define FLAG_FILTER 0x02 +//#define FLAG_CHANNELGROUP_1 0x04 +//#define FLAG_CHANNELGROUP_2 0x08 +//#define FLAG_CHANNELGROUP_3 0x10 +//#define FLAG_CHANNELGROUP_4 0x20 +//#define FLAG_CLOCK_EXTERNAL 0x40 +//#define FLAG_CLOCK_INVERTED 0x80 +//#define FLAG_RLE 0x0100 + +#define MSO_TRIGGER_UNKNOWN '!' +#define MSO_TRIGGER_UNKNOWN1 '1' +#define MSO_TRIGGER_UNKNOWN2 '2' +#define MSO_TRIGGER_UNKNOWN3 '3' +#define MSO_TRIGGER_WAIT '4' +#define MSO_TRIGGER_FIRED '5' +#define MSO_TRIGGER_DATAREADY '6' + +/* Structure for the pattern generator state */ +struct mso_patgen { + /* Pattern generator clock config */ + uint16_t clock; + /* Buffer start address */ + uint16_t start; + /* Buffer end address */ + uint16_t end; + /* Pattern generator config */ + uint8_t config; + /* Samples buffer */ + uint8_t buffer[1024]; + /* Input/output configuration for the samples buffer (?)*/ + uint8_t io[1024]; + /* Number of loops for the pattern generator */ + uint8_t loops; + /* Bit enable mask for the I/O lines */ + uint8_t mask; +}; + +/* Data structure for the protocol trigger state */ +struct mso_prototrig { + /* Word match buffer */ + uint8_t word[4]; + /* Masks for the wordmatch buffer */ + uint8_t mask[4]; + /* SPI mode 0, 1, 2, 3. Set to 0 for I2C */ + uint8_t spimode; +}; + +/* Private, per-device-instance driver context. */ +struct dev_context { + /* info */ + uint8_t hwmodel; + uint8_t hwrev; + struct sr_serial_dev_inst *serial; +// uint8_t num_sample_rates; + /* calibration */ + double vbit; + uint16_t dac_offset; + uint16_t offset_range; + /* register cache */ + uint8_t ctlbase1; + uint8_t ctlbase2; + /* state */ + uint8_t la_threshold; + uint64_t cur_rate; + uint8_t dso_probe_attn; + uint8_t trigger_chan; + uint8_t trigger_slope; + uint8_t trigger_outsrc; + uint8_t trigger_state; + uint8_t la_trigger; + uint8_t la_trigger_mask; + double dso_trigger_voltage; + uint16_t dso_trigger_width; + struct mso_prototrig protocol_trigger; + void *session_dev_id; + uint16_t buffer_n; + char buffer[4096]; +}; + +SR_PRIV int mso_parse_serial(const char *iSerial, const char *iProduct, + struct dev_context *ctx); +SR_PRIV int mso_check_trigger(struct sr_serial_dev_inst *serial, uint8_t *info); +SR_PRIV int mso_reset_adc(struct sr_dev_inst *sdi); +SR_PRIV int mso_clkrate_out(struct sr_serial_dev_inst *serial, uint16_t val); +SR_PRIV int mso_configure_rate(struct sr_dev_inst *sdi, uint32_t rate); +SR_PRIV int mso_receive_data(int fd, int revents, void *cb_data); +SR_PRIV void stop_acquisition(const struct sr_dev_inst *sdi); + +/////////////////////// +// + +/* serial protocol */ +#define mso_trans(a, v) \ + (((v) & 0x3f) | (((v) & 0xc0) << 6) | (((a) & 0xf) << 8) | \ + ((~(v) & 0x20) << 1) | ((~(v) & 0x80) << 7)) + +SR_PRIV static const char mso_head[] = { 0x40, 0x4c, 0x44, 0x53, 0x7e }; +SR_PRIV static const char mso_foot[] = { 0x7e }; + +/* bank agnostic registers */ +#define REG_CTL2 15 + +/* bank 0 registers */ +#define REG_BUFFER 1 +#define REG_TRIGGER 2 +#define REG_CLKRATE1 9 +#define REG_CLKRATE2 10 +#define REG_DAC1 12 +#define REG_DAC2 13 +/* possibly bank agnostic: */ +#define REG_CTL1 14 + +/* bank 2 registers (SPI/I2C protocol trigger) */ +#define REG_PT_WORD(x) (x) +#define REG_PT_MASK(x) (x+4) +#define REG_PT_SPIMODE 8 + +/* bits - REG_CTL1 */ +#define BIT_CTL1_RESETFSM (1 << 0) +#define BIT_CTL1_ARM (1 << 1) +#define BIT_CTL1_ADC_UNKNOWN4 (1 << 4) /* adc enable? */ +#define BIT_CTL1_RESETADC (1 << 6) +#define BIT_CTL1_LED (1 << 7) + +/* bits - REG_CTL2 */ +#define BITS_CTL2_BANK(x) (x & 0x3) +#define BIT_CTL2_SLOWMODE (1 << 5) + +struct rate_map { + uint32_t rate; + uint16_t val; + uint8_t slowmode; +}; + +static struct rate_map rate_map[] = { + { SR_MHZ(200), 0x0205, 0 }, + { SR_MHZ(100), 0x0105, 0 }, + { SR_MHZ(50), 0x0005, 0 }, + { SR_MHZ(20), 0x0303, 0 }, + { SR_MHZ(10), 0x0308, 0 }, + { SR_MHZ(5), 0x030c, 0 }, + { SR_MHZ(2), 0x0330, 0 }, + { SR_MHZ(1), 0x0362, 0 }, + { SR_KHZ(500), 0x03c6, 0 }, + { SR_KHZ(200), 0x07f2, 0 }, + { SR_KHZ(100), 0x0fe6, 0 }, + { SR_KHZ(50), 0x1fce, 0 }, + { SR_KHZ(20), 0x4f86, 0 }, + { SR_KHZ(10), 0x9f0e, 0 }, + { SR_KHZ(5), 0x03c7, 0x20 }, + { SR_KHZ(2), 0x07f3, 0x20 }, + { SR_KHZ(1), 0x0fe7, 0x20 }, + { 500, 0x1fcf, 0x20 }, + { 200, 0x4f87, 0x20 }, + { 100, 0x9f0f, 0x20 }, +}; + +/* FIXME: Determine corresponding voltages */ +static uint16_t la_threshold_map[] = { + 0x8600, + 0x8770, + 0x88ff, + 0x8c70, + 0x8eff, + 0x8fff, +}; + + +//SR_PRIV extern const char *ols_probe_names[NUM_PROBES + 1]; +// +//SR_PRIV int send_shortcommand(struct sr_serial_dev_inst *serial, +// uint8_t command); +//SR_PRIV int send_longcommand(struct sr_serial_dev_inst *serial, +// uint8_t command, uint32_t data); +//SR_PRIV int ols_configure_probes(const struct sr_dev_inst *sdi); +//SR_PRIV uint32_t reverse16(uint32_t in); +//SR_PRIV uint32_t reverse32(uint32_t in); +//SR_PRIV struct dev_context *ols_dev_new(void); +//SR_PRIV struct sr_dev_inst *get_metadata(struct sr_serial_dev_inst *serial); +//SR_PRIV int ols_set_samplerate(const struct sr_dev_inst *sdi, +// uint64_t samplerate, +// const struct sr_samplerates *samplerates); +//SR_PRIV int ols_receive_data(int fd, int revents, void *cb_data); + +#endif