diff --git a/src/hardware/hung-chang-dso-2100/api.c b/src/hardware/hung-chang-dso-2100/api.c index d3c3c286..1fd4df0b 100644 --- a/src/hardware/hung-chang-dso-2100/api.c +++ b/src/hardware/hung-chang-dso-2100/api.c @@ -17,28 +17,214 @@ * along with this program. If not, see . */ +#include +#include #include "protocol.h" SR_PRIV struct sr_dev_driver hung_chang_dso_2100_driver_info; +static const uint32_t scanopts[] = { + SR_CONF_CONN, +}; + +static const uint32_t drvopts[] = { + SR_CONF_OSCILLOSCOPE, +}; + +static const uint32_t devopts[] = { + SR_CONF_CONN | SR_CONF_GET, + SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_BUFFERSIZE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +}; + +static const uint32_t cgopts[] = { + SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_PROBE_FACTOR | SR_CONF_GET | SR_CONF_SET, +}; + +static const uint64_t samplerates[] = { + SR_MHZ(100), SR_MHZ(50), SR_MHZ(25), SR_MHZ(20), + SR_MHZ(10), SR_MHZ(5), SR_KHZ(2500), SR_MHZ(2), + SR_MHZ(1), SR_KHZ(500), SR_KHZ(250), SR_KHZ(200), + SR_KHZ(100), SR_KHZ(50), SR_KHZ(25), SR_KHZ(20), + SR_KHZ(10), SR_KHZ(5), SR_HZ(2500), SR_KHZ(2), + SR_KHZ(1), SR_HZ(500), SR_HZ(250), SR_HZ(200), + SR_HZ(100), SR_HZ(50), SR_HZ(25), SR_HZ(20) +}; + +/* must be in sync with readout_steps[] in protocol.c */ +static const uint64_t buffersizes[] = { + 2 * 500, 3 * 500, 4 * 500, 5 * 500, + 6 * 500, 7 * 500, 8 * 500, 9 * 500, 10 * 500, + 12 * 500 - 2, 14 * 500 - 2, 16 * 500 - 2, + 18 * 500 - 2, 20 * 500 - 2, 10240 - 2 +}; + +static const uint64_t vdivs[][2] = { + { 10, 1000 }, + { 20, 1000 }, + { 50, 1000 }, + { 100, 1000 }, + { 200, 1000 }, + { 500, 1000 }, + { 1, 1 }, + { 2, 1 }, + { 5, 1 }, +}; + +/* Bits 4 and 5 enable relays that add /10 filters to the chain + * Bits 0 and 1 select an output from a resistor array */ +static const uint8_t vdivs_map[] = { + 0x01, 0x02, 0x03, 0x21, 0x22, 0x23, 0x31, 0x32, 0x33 +}; + + +static const char *trigger_sources[] = { + "A", "B", "EXT" +}; + +static const uint8_t trigger_sources_map[] = { + 0x00, 0x80, 0x40 +}; + +static const char *trigger_slopes[] = { + "f", "r" +}; + +static const char *coupling[] = { + "DC", "AC", "GND" +}; + +static const uint8_t coupling_map[] = { + 0x00, 0x08, 0x04 +}; + static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx) { return std_init(sr_ctx, di, LOG_PREFIX); } +static GSList *scan_port(GSList *devices, struct sr_dev_driver *di, + struct parport *port) +{ + struct sr_dev_inst *sdi; + struct sr_channel *ch; + struct sr_channel_group *cg; + struct dev_context *devc; + struct drv_context *drvc; + int i; + + if (ieee1284_open(port, 0, &i) != E1284_OK) { + sr_err("Can't open parallel port %s", port->name); + goto fail1; + } + + if ((i & (CAP1284_RAW | CAP1284_BYTE)) != (CAP1284_RAW | CAP1284_BYTE)) { + sr_err("Parallel port %s does not provide low-level bidirection access", + port->name); + goto fail2; + } + + if (ieee1284_claim(port) != E1284_OK) { + sr_err("Parallel port %s already in use", port->name); + goto fail2; + } + + if (!hung_chang_dso_2100_check_id(port)) + goto fail3; + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("Hung-Chang"); + sdi->model = g_strdup("DSO-2100"); + sdi->driver = di; + drvc = di->context; + sdi->inst_type = 0; /* FIXME */ + sdi->conn = port; + ieee1284_ref(port); + + for (i = 0; i < NUM_CHANNELS; i++) { + cg = g_malloc0(sizeof(struct sr_channel_group)); + cg->name = g_strdup(trigger_sources[i]); + ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, FALSE, trigger_sources[i]); + cg->channels = g_slist_append(cg->channels, ch); + sdi->channel_groups = g_slist_append(sdi->channel_groups, cg); + } + + devc = g_malloc0(sizeof(struct dev_context)); + devc->enabled_channel = g_slist_append(NULL, NULL); + devc->channel = 0; + devc->rate = 0; + devc->probe[0] = 10; + devc->probe[1] = 10; + devc->cctl[0] = 0x31; /* 1V/div, DC coupling, trigger on channel A*/ + devc->cctl[1] = 0x31; /* 1V/div, DC coupling, no tv sync trigger */ + devc->edge = 0; + devc->tlevel = 0x80; + devc->pos[0] = 0x80; + devc->pos[1] = 0x80; + devc->offset[0] = 0x80; + devc->offset[1] = 0x80; + devc->gain[0] = 0x80; + devc->gain[1] = 0x80; + devc->frame_limit = 0; + devc->last_step = 0; /* buffersize = 1000 */ + sdi->priv = devc; + + drvc->instances = g_slist_append(drvc->instances, sdi); + devices = g_slist_append(devices, sdi); + +fail3: + ieee1284_release(port); +fail2: + ieee1284_close(port); +fail1: + return devices; +} + static GSList *scan(struct sr_dev_driver *di, GSList *options) { - struct drv_context *drvc; - GSList *devices; + struct parport_list ports; + struct sr_config *src; + const char *conn = NULL; + GSList *devices, *option; + gboolean port_found; + int i; - (void)options; + + for (option = options; option; option = option->next) { + src = option->data; + if (src->key == SR_CONF_CONN) { + conn = g_variant_get_string(src->data, NULL); + break; + } + } + + if (!conn) + return NULL; + + if (ieee1284_find_ports(&ports, 0) != E1284_OK) + return NULL; devices = NULL; - drvc = di->context; - drvc->instances = NULL; + port_found = FALSE; + for (i = 0; i < ports.portc; i++) + if (!strcmp(ports.portv[i]->name, conn)) { + port_found = TRUE; + devices = scan_port(devices, di, ports.portv[i]); + } - /* TODO: scan for devices, either based on a SR_CONF_CONN option - * or on a USB scan. */ + if (!port_found) { + sr_err("Parallel port %s not found. Valid names are:", conn); + for (i = 0; i < ports.portc; i++) + sr_err("\t%s", ports.portv[i]->name); + } + + ieee1284_free_ports(&ports); return devices; } @@ -48,28 +234,77 @@ static GSList *dev_list(const struct sr_dev_driver *di) return ((struct drv_context *)(di->context))->instances; } +static void clear_private(void *priv) +{ + struct dev_context *devc = priv; + + g_slist_free(devc->enabled_channel); +} + static int dev_clear(const struct sr_dev_driver *di) { - return std_dev_clear(di, NULL); + struct drv_context *drvc = di->context; + struct sr_dev_inst *sdi; + GSList *l; + + if (drvc) { + for (l = drvc->instances; l; l = l->next) { + sdi = l->data; + ieee1284_unref(sdi->conn); + } + } + + return std_dev_clear(di, clear_private); } static int dev_open(struct sr_dev_inst *sdi) { - (void)sdi; + struct dev_context *devc = sdi->priv; + int i; - /* TODO: get handle from sdi->conn and open it. */ + if (sdi->status != SR_ST_INACTIVE) + goto fail1; + + if (ieee1284_open(sdi->conn, 0, &i) != E1284_OK) + goto fail1; + + if (ieee1284_claim(sdi->conn) != E1284_OK) + goto fail2; + + if (ieee1284_data_dir(sdi->conn, 1) != E1284_OK) + goto fail3; + + if (hung_chang_dso_2100_move_to(sdi, 1)) + goto fail3; + + devc->samples = g_try_malloc(1000 * sizeof(*devc->samples)); + if (!devc->samples) + goto fail3; sdi->status = SR_ST_ACTIVE; return SR_OK; + +fail3: + hung_chang_dso_2100_reset_port(sdi->conn); + ieee1284_release(sdi->conn); +fail2: + ieee1284_close(sdi->conn); +fail1: + return SR_ERR; } static int dev_close(struct sr_dev_inst *sdi) { - (void)sdi; + struct dev_context *devc = sdi->priv; - /* TODO: get handle from sdi->conn and close it. */ + if (sdi->status != SR_ST_ACTIVE) + return SR_OK; + g_free(devc->samples); + hung_chang_dso_2100_reset_port(sdi->conn); + ieee1284_release(sdi->conn); + ieee1284_close(sdi->conn); sdi->status = SR_ST_INACTIVE; return SR_OK; @@ -77,27 +312,141 @@ static int dev_close(struct sr_dev_inst *sdi) static int cleanup(const struct sr_dev_driver *di) { - dev_clear(di); + struct drv_context *drvc = di->context; + int ret; - /* TODO: free other driver resources, if any. */ + ret = dev_clear(di); - return SR_OK; + g_free(drvc); + + return ret; +} + +static int find_in_array(GVariant *data, const GVariantType *type, + const void *arr, int n) +{ + const char * const *sarr; + const char *s; + const uint64_t *u64arr; + const uint8_t *u8arr; + uint64_t u64; + uint8_t u8; + int i; + + if (!g_variant_is_of_type(data, type)) + return -1; + + switch (g_variant_classify(data)) { + case G_VARIANT_CLASS_STRING: + s = g_variant_get_string(data, NULL); + sarr = arr; + + for (i = 0; i < n; i++) + if (!strcmp(s, sarr[i])) + return i; + break; + case G_VARIANT_CLASS_UINT64: + u64 = g_variant_get_uint64(data); + u64arr = arr; + + for (i = 0; i < n; i++) + if (u64 == u64arr[i]) + return i; + break; + case G_VARIANT_CLASS_BYTE: + u8 = g_variant_get_byte(data); + u8arr = arr; + + for (i = 0; i < n; i++) + if (u8 == u8arr[i]) + return i; + default: + break; + } + + return -1; +} + +static int reverse_map(uint8_t u, const uint8_t *arr, int n) +{ + GVariant *v = g_variant_new_byte(u); + int i = find_in_array(v, G_VARIANT_TYPE_BYTE, arr, n); + g_variant_unref(v); + return i; } static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; + struct dev_context *devc = sdi->priv; + struct parport *port; + int ret, i, ch = -1; - (void)sdi; - (void)data; - (void)cg; + if (cg) /* sr_config_get will validate cg using config_list */ + ch = ((struct sr_channel *)cg->channels->data)->index; ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_CONN: + port = sdi->conn; + *data = g_variant_new_string(port->name); + break; + case SR_CONF_LIMIT_FRAMES: + *data = g_variant_new_uint64(devc->frame_limit); + break; + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(samplerates[devc->rate]); + break; + case SR_CONF_TRIGGER_SOURCE: + i = reverse_map(devc->cctl[0] & 0xC0, trigger_sources_map, + ARRAY_SIZE(trigger_sources_map)); + if (i == -1) + ret = SR_ERR; + else + *data = g_variant_new_string(trigger_sources[i]); + break; + case SR_CONF_TRIGGER_SLOPE: + if (devc->edge >= ARRAY_SIZE(trigger_slopes)) + ret = SR_ERR; + else + *data = g_variant_new_string(trigger_slopes[devc->edge]); + break; + case SR_CONF_BUFFERSIZE: + *data = g_variant_new_uint64(buffersizes[devc->last_step]); + break; + case SR_CONF_VDIV: + if (ch == -1) { + ret = SR_ERR_CHANNEL_GROUP; + } else { + i = reverse_map(devc->cctl[ch] & 0x33, vdivs_map, + ARRAY_SIZE(vdivs_map)); + if (i == -1) + ret = SR_ERR; + else + *data = g_variant_new("(tt)", vdivs[i][0], + vdivs[i][1]); + } + break; + case SR_CONF_COUPLING: + if (ch == -1) { + ret = SR_ERR_CHANNEL_GROUP; + } else { + i = reverse_map(devc->cctl[ch] & 0x0C, coupling_map, + ARRAY_SIZE(coupling_map)); + if (i == -1) + ret = SR_ERR; + else + *data = g_variant_new_string(coupling[i]); + } + break; + case SR_CONF_PROBE_FACTOR: + if (ch == -1) + ret = SR_ERR_CHANNEL_GROUP; + else + *data = g_variant_new_uint64(devc->probe[ch]); + break; default: - return SR_ERR_NA; + ret = SR_ERR_NA; } return ret; @@ -106,17 +455,95 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; + struct dev_context *devc = sdi->priv; + int ret, i, ch = -1; + uint64_t u, v; - (void)data; - (void)cg; + if (cg) /* sr_config_set will validate cg using config_list */ + ch = ((struct sr_channel *)cg->channels->data)->index; if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_LIMIT_FRAMES: + devc->frame_limit = g_variant_get_uint64(data); + break; + case SR_CONF_SAMPLERATE: + i = find_in_array(data, G_VARIANT_TYPE_UINT64, + samplerates, ARRAY_SIZE(samplerates)); + if (i == -1) + ret = SR_ERR_ARG; + else + devc->rate = i; + break; + case SR_CONF_TRIGGER_SOURCE: + i = find_in_array(data, G_VARIANT_TYPE_STRING, + trigger_sources, ARRAY_SIZE(trigger_sources)); + if (i == -1) + ret = SR_ERR_ARG; + else + devc->cctl[0] = (devc->cctl[0] & 0x3F) + | trigger_sources_map[i]; + break; + case SR_CONF_TRIGGER_SLOPE: + i = find_in_array(data, G_VARIANT_TYPE_STRING, + trigger_slopes, ARRAY_SIZE(trigger_slopes)); + if (i == -1) + ret = SR_ERR_ARG; + else + devc->edge = i; + break; + case SR_CONF_BUFFERSIZE: + i = find_in_array(data, G_VARIANT_TYPE_UINT64, + buffersizes, ARRAY_SIZE(buffersizes)); + if (i == -1) + ret = SR_ERR_ARG; + else + devc->last_step = i; + break; + case SR_CONF_VDIV: + if (ch == -1) { + ret = SR_ERR_CHANNEL_GROUP; + } else if (!g_variant_is_of_type(data, G_VARIANT_TYPE("(tt)"))) { + ret = SR_ERR_ARG; + } else { + g_variant_get(data, "(tt)", &u, &v); + for (i = 0; i < (int)ARRAY_SIZE(vdivs); i++) + if (vdivs[i][0] == u && vdivs[i][1] == v) + break; + if (i == ARRAY_SIZE(vdivs)) + ret = SR_ERR_ARG; + else + devc->cctl[ch] = (devc->cctl[ch] & 0xCC) + | vdivs_map[i]; + } + break; + case SR_CONF_COUPLING: + if (ch == -1) { + ret = SR_ERR_CHANNEL_GROUP; + } else { + i = find_in_array(data, G_VARIANT_TYPE_STRING, + coupling, ARRAY_SIZE(coupling)); + if (i == -1) + ret = SR_ERR_ARG; + else + devc->cctl[ch] = (devc->cctl[ch] & 0xF3) + | coupling_map[i]; + } + break; + case SR_CONF_PROBE_FACTOR: + if (ch == -1) { + ret = SR_ERR_CHANNEL_GROUP; + } else { + u = g_variant_get_uint64(data); + if (!u) + ret = SR_ERR_ARG; + else + devc->probe[ch] = u; + } + break; default: ret = SR_ERR_NA; } @@ -124,50 +551,194 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd return ret; } +static int config_channel_set(const struct sr_dev_inst *sdi, + struct sr_channel *ch, + unsigned int changes) +{ + struct dev_context *devc = sdi->priv; + uint8_t v; + + if (changes & SR_CHANNEL_SET_ENABLED) { + if (ch->enabled) { + v = devc->channel | (1 << ch->index); + if (v & (v - 1)) + return SR_ERR; + devc->channel = v; + devc->enabled_channel->data = ch; + } else { + devc->channel &= ~(1 << ch->index); + } + } + return SR_OK; +} + +static int config_commit(const struct sr_dev_inst *sdi) +{ + uint8_t state = hung_chang_dso_2100_read_mbox(sdi->conn, 0.02); + int ret; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + switch (state) { + case 0x03: + case 0x14: + case 0x21: + /* we will travel the complete config path on our way to state 1 */ + break; + case 0x00: + state = 0x01; + default: + ret = hung_chang_dso_2100_move_to(sdi, 1); + if (ret != SR_OK) + return ret; + case 0x01: + hung_chang_dso_2100_write_mbox(sdi->conn, 4); + } + ret = hung_chang_dso_2100_move_to(sdi, 1); + if (ret != SR_OK) + return ret; + return hung_chang_dso_2100_move_to(sdi, state); +} + static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; + GVariantBuilder gvb; + GVariant *gvar, *rational[2]; + GSList *l; + int i; - (void)sdi; - (void)data; - (void)cg; - - ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_SCAN_OPTIONS: + case SR_CONF_DEVICE_OPTIONS: + break; + case SR_CONF_SAMPLERATE: + case SR_CONF_TRIGGER_SOURCE: + case SR_CONF_TRIGGER_SLOPE: + case SR_CONF_BUFFERSIZE: + if (!sdi || cg) + return SR_ERR_NA; + break; + case SR_CONF_VDIV: + case SR_CONF_COUPLING: + if (!sdi) + return SR_ERR_NA; + if (!cg) + return SR_ERR_CHANNEL_GROUP; + l = g_slist_find(sdi->channel_groups, cg); + if (!l) + return SR_ERR_ARG; + break; default: return SR_ERR_NA; } - return ret; + switch (key) { + case SR_CONF_SCAN_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t)); + break; + case SR_CONF_DEVICE_OPTIONS: + if (!sdi) + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t)); + else if (!cg) + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts, ARRAY_SIZE(devopts), sizeof(uint32_t)); + else + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + cgopts, ARRAY_SIZE(cgopts), sizeof(uint32_t)); + break; + case SR_CONF_SAMPLERATE: + g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); + gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), + samplerates, ARRAY_SIZE(samplerates), sizeof(uint64_t)); + g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar); + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_TRIGGER_SOURCE: + *data = g_variant_new_strv(trigger_sources, ARRAY_SIZE(trigger_sources)); + break; + case SR_CONF_TRIGGER_SLOPE: + *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes)); + break; + case SR_CONF_BUFFERSIZE: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64, + buffersizes, ARRAY_SIZE(buffersizes), sizeof(uint64_t)); + break; + case SR_CONF_VDIV: + g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < (int)ARRAY_SIZE(vdivs); i++) { + rational[0] = g_variant_new_uint64(vdivs[i][0]); + rational[1] = g_variant_new_uint64(vdivs[i][1]); + gvar = g_variant_new_tuple(rational, 2); + g_variant_builder_add_value(&gvb, gvar); + } + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_COUPLING: + *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling)); + break; + } + + return SR_OK; } static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) { - (void)sdi; - (void)cb_data; + struct dev_context *devc = sdi->priv; + int ret; if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; - /* TODO: configure hardware, reset acquisition state, set up - * callbacks and send header packet. */ + if (devc->channel) { + static const float res_array[] = {0.5, 1, 2, 5}; + static const uint8_t relays[] = {100, 10, 10, 1}; + devc->factor = devc->probe[devc->channel - 1] / 32.0; + devc->factor *= res_array[devc->cctl[devc->channel - 1] & 0x03]; + devc->factor /= relays[(devc->cctl[devc->channel - 1] >> 4) & 0x03]; + } + devc->frame = 0; + devc->cb_data = cb_data; + devc->state_known = TRUE; + devc->step = 0; + devc->adc2 = FALSE; + devc->retries = MAX_RETRIES; + + ret = hung_chang_dso_2100_move_to(sdi, 0x21); + if (ret != SR_OK) + return ret; + + std_session_send_df_header(cb_data, LOG_PREFIX); + + sr_session_source_add(sdi->session, 0, 0, 8, + hung_chang_dso_2100_poll, (void *)sdi); + + return SR_OK; +} + +SR_PRIV int hung_chang_dso_2100_dev_acquisition_stop(const struct sr_dev_inst *sdi, + void *cb_data) +{ + struct sr_datafeed_packet packet = { .type = SR_DF_END }; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + sr_session_send(cb_data, &packet); + sr_session_source_remove(sdi->session, 0); + + hung_chang_dso_2100_move_to(sdi, 1); return SR_OK; } static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) { - (void)cb_data; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; - - /* TODO: stop acquisition. */ - - return SR_OK; + return hung_chang_dso_2100_dev_acquisition_stop(sdi, cb_data); } SR_PRIV struct sr_dev_driver hung_chang_dso_2100_driver_info = { @@ -181,6 +752,8 @@ SR_PRIV struct sr_dev_driver hung_chang_dso_2100_driver_info = { .dev_clear = dev_clear, .config_get = config_get, .config_set = config_set, + .config_channel_set = config_channel_set, + .config_commit = config_commit, .config_list = config_list, .dev_open = dev_open, .dev_close = dev_close, diff --git a/src/hardware/hung-chang-dso-2100/protocol.c b/src/hardware/hung-chang-dso-2100/protocol.c index 1a5dd33d..6f997db0 100644 --- a/src/hardware/hung-chang-dso-2100/protocol.c +++ b/src/hardware/hung-chang-dso-2100/protocol.c @@ -17,14 +17,406 @@ * along with this program. If not, see . */ +#include #include "protocol.h" -SR_PRIV int hung_chang_dso_2100_receive_data(int fd, int revents, void *cb_data) +/* The firmware can be in the following states: + * 0x00 Temporary state during initialization + * Automatically transitions to state 0x01 + * 0x01 Idle, this state updates calibration caps + * Send 0x02 to go to state 0x21 + * Send 0x03 to go to state 0x03 + * Send 0x04 to go to state 0x14 + * 0x21 Trigger is armed, caps are _not_ updated + * Send 0x99 to check if trigger event occured + * if triggered, goes to state 0x03 + * else stays in state 0x21 + * Send 0xFE to generate artificial trigger event + * returns to state 0x21 + * but next 0x99 will succeed + * Send 0xFF to go to state 0x03 (abort capture) + * 0x03 Extracts two 500 sample subsets from the 5000 + * sample capture buffer for readout + * When reading samples, the FPGA starts at the + * first of the 1000 samples and automatically + * advances to the next. + * Send 0x04 to go to state 0x0F + * 0x14 Scroll acquisition mode, update calib caps + * When reading samples, the FPGA provides the + * current value of the ADCs + * Send 0xFF to go to state 0x0F + * 0x0F Send channel number (1 or 2) to go to next state + * There are actually two 0x0F states in series + * which both expect the channel number. + * If the values don't match, they are discarded. + * The next state 0x05 is entered anyway + * 0x05 Same as state 0x0F but expects sample rate index. + * The next state is 0x08 + * 0x08 Same as state 0x0F but expects step size + 1 for + * the second 500 sample subset + * The next state is 0x09 + * 0x09 Same as state 0x0F but expects step size + 1 for + * the first 500 sample subset + * The next state is 0x06 + * 0x06 Same as state 0x0F but expects vdiv and coupling + * configuration for the first channel and trigger + * source selection. + * (U46 in the schematics) + * The next state is 0x07 + * 0x07 Same as state 0x0F but expects vdiv and coupling + * configuration for the first channel and trigger + * type (edge, TV hsync, TV vsync). + * (U47 in the schematics) + * The next state is 0x0A + * 0x0A Same as state 0x0F but expects a parameter X + 1 + * that determines the offset of the second 500 sample + * subset + * Offset = 5 * X * step size for first subset + * The next state is 0x0B + * 0x0B Same as state 0x0F but expects the type of edge to + * trigger on (rising or falling) + * The next state is 0x0C + * 0x0C Same as state 0x0F but expects the calibration + * value for the first channel's position + * (POS1 in the schematics) + * The next state is 0x0D + * 0x0D Same as state 0x0F but expects the calibration + * value for the second channel's position + * (POS2 in the schematics) + * The next state is 0x0E + * 0x0E Same as state 0x0F but expects the trigger level + * (TRIGLEVEL in the schematics) + * Keep in mind that trigger sources are AC coupled + * The next state is 0x10 + * 0x10 Same as state 0x0F but expects the calibration + * value for the first channel's offset + * (OFFSET1 in the schematics) + * The next state is 0x11 + * 0x11 Same as state 0x0F but expects the calibration + * value for the first channel's gain + * (GAIN1 in the schematics) + * The next state is 0x12 + * 0x12 Same as state 0x0F but expects the calibration + * value for the second channel's offset + * (OFFSET2 in the schematics) + * The next state is 0x13 + * 0x13 Same as state 0x0F but expects the calibration + * value for the second channel's gain + * (GAIN2 in the schematics) + * The next state is 0x01 + * + * The Mailbox appears to be half duplex. + * If one side writes a byte into the mailbox, it + * reads 0 until the other side has written a byte. + * So you can't transfer 0. + * + * As the status signals are unconnected, the device is not + * IEEE1284 compliant and can't make use of EPP or ECP transfers. + * It drives the data lines when control is set to: + * 0 => Channel A data + * C1284_NAUTOFD => Channel B data + * C1284_NSELECTIN => Mailbox + * C1284_NSELECTIN | C1284_NAUTOFD => 0x55 + * + * It takes about 200ns for the data lines to become stable after + * the control lines have been changed. This driver assumes that + * parallel port access is slow enough to not require additional + * delays. + * + * Channel values in state 0x14 and the mailbox can change their + * value while they are selected, the latter of course only from + * 0 to a valid state. Beware of intermediate values. + * + * SRAM N layout (N = 1 or 2): + * 0x0000-0x13ff samples captured from ADC N + * 0x4000-0x41f3 bytes extracted from 0x6000 with step1 + * (both ADCs but only channel N) + * 0x41f4-0x43e7 bytes extracted from 0x6000+5*step1*shift + * with step2 (both ADCs but only channel N) + * 0x43e8-0x43ea {0x01, 0xfe, 0x80} + * 0x43eb-0x444e copy of bytes from 0x4320 + * 0x6000-0x7387 interleaved SRAM 1 and SRAM 2 bytes from + * 0x0001 to 0x09c5 after channel N was captured + * + * On a trigger event the FPGA directs the ADC samples to the region + * at 0x0000. The microcontroller then copies 5000 samples from 0x0001 + * to 0x6000. Each time state 0x03 is entered, the bytes from 0x4000 + * to 0x444e are filled and the start address for readout is reset to + * 0x4000. Readout will wrap around back to 0x4000 after reaching 0x7fff. + * + * As you can see from the layout, it was probably intended to capture + * 5000 samples for both probes before they are read out. We don't do that + * to be able to read the full 10k samples captured by the FPGA. It would + * be useless anyway if you don't capture repetitive signals. We're also + * not reading the two samples at 0x0000 to save a few milliseconds. + */ + +static const struct { + uint16_t num; + uint8_t step1; + uint8_t shift; + uint8_t interleave; +} readout_steps[] = { + { 1000, 1, 100, 0 }, + { 500, 100, 2, 0 }, + { 500, 100, 3, 0 }, + { 500, 100, 4, 0 }, + { 500, 100, 5, 0 }, + { 500, 100, 6, 0 }, + { 500, 100, 7, 0 }, + { 500, 100, 8, 0 }, + { 500, 100, 9, 0 }, + { 499, 212, 41, 1 }, + { 500, 157, 56, 1 }, + { 500, 247, 36, 1 }, + { 500, 232, 180, 1 }, + { 500, 230, 182, 1 }, + { 120, 212, 43, 1 } +}; + +SR_PRIV void hung_chang_dso_2100_reset_port(struct parport *port) { + ieee1284_write_control(port, + C1284_NSTROBE | C1284_NAUTOFD | C1284_NSELECTIN); + ieee1284_data_dir(port, 0); +} + +SR_PRIV gboolean hung_chang_dso_2100_check_id(struct parport *port) +{ + gboolean ret = FALSE; + + if (ieee1284_data_dir(port, 1) != E1284_OK) + goto fail; + + ieee1284_write_control(port, C1284_NSTROBE | C1284_NAUTOFD | C1284_NSELECTIN); + ieee1284_write_control(port, C1284_NAUTOFD | C1284_NSELECTIN); + + if (ieee1284_read_data(port) != 0x55) + goto fail; + + ret = TRUE; +fail: + hung_chang_dso_2100_reset_port(port); + + return ret; +} + +SR_PRIV void hung_chang_dso_2100_write_mbox(struct parport *port, uint8_t val) +{ + sr_dbg("mbox <= %X", val); + ieee1284_write_control(port, + C1284_NSTROBE | C1284_NINIT | C1284_NSELECTIN); + ieee1284_data_dir(port, 0); + ieee1284_write_data(port, val); + ieee1284_write_control(port, C1284_NINIT | C1284_NSELECTIN); + ieee1284_write_control(port, + C1284_NSTROBE | C1284_NINIT | C1284_NSELECTIN); + ieee1284_data_dir(port, 1); + ieee1284_write_control(port, + C1284_NSTROBE | C1284_NAUTOFD | C1284_NINIT | C1284_NSELECTIN); +} + +SR_PRIV uint8_t hung_chang_dso_2100_read_mbox(struct parport *port, float timeout) +{ + GTimer *timer = NULL; + uint8_t val; + + ieee1284_write_control(port, C1284_NSTROBE | C1284_NSELECTIN); + ieee1284_write_control(port, C1284_NSELECTIN); + + for (;;) { + if (ieee1284_read_data(port)) { + /* Always read the value a second time. + * The first one may be unstable. */ + val = ieee1284_read_data(port); + break; + } + if (!timer) { + timer = g_timer_new(); + } else if (g_timer_elapsed(timer, NULL) > timeout) { + val = 0; + break; + } + } + + ieee1284_write_control(port, C1284_NSTROBE | C1284_NSELECTIN); + ieee1284_write_control(port, + C1284_NSTROBE | C1284_NAUTOFD | C1284_NINIT | C1284_NSELECTIN); + + if (timer) + g_timer_destroy(timer); + sr_dbg("mbox == %X", val); + return val; +} + +SR_PRIV int hung_chang_dso_2100_move_to(const struct sr_dev_inst *sdi, uint8_t target) +{ + struct dev_context *devc = sdi->priv; + int timeout = 40; + uint8_t c; + + while (timeout--) { + c = hung_chang_dso_2100_read_mbox(sdi->conn, 0.1); + if (c == target) + return SR_OK; + + switch (c) { + case 0x00: + /* Can happen if someone wrote something into + * the mbox that was not expected by the uC. + * Alternating between 0xff and 4 helps in + * all states. */ + c = (timeout & 1) ? 0xFF : 0x04; + break; + case 0x01: + switch (target) { + case 0x21: c = 2; break; + case 0x03: c = 3; break; + default: c = 4; + } + break; + case 0x03: c = 4; break; + case 0x05: c = devc->rate + 1; break; + case 0x06: c = devc->cctl[0]; break; + case 0x07: c = devc->cctl[1]; break; + case 0x08: c = 1 /* step 2 */ + 1 ; break; + case 0x09: c = readout_steps[devc->step].step1 + 1; break; + case 0x0A: c = readout_steps[devc->step].shift + 1; break; + case 0x0B: c = devc->edge + 1; break; + case 0x0C: c = devc->pos[0]; break; + case 0x0D: c = devc->pos[1]; break; + case 0x0E: c = devc->tlevel; break; + case 0x0F: + if (!devc->channel) + c = 1; + else if (readout_steps[devc->step].interleave) + c = devc->adc2 ? 2 : 1; + else + c = devc->channel; + break; + case 0x10: c = devc->offset[0]; break; + case 0x11: c = devc->gain[0]; break; + case 0x12: c = devc->offset[1]; break; + case 0x13: c = devc->gain[1]; break; + case 0x14: + case 0x21: c = 0xFF; break; + default: + return SR_ERR_DATA; + } + hung_chang_dso_2100_write_mbox(sdi->conn, c); + } + return SR_ERR_TIMEOUT; +} + +static void skip_samples(struct parport *port, uint8_t ctrl, size_t num) +{ + while (num--) { + ieee1284_write_control(port, ctrl & ~C1284_NSTROBE); + ieee1284_write_control(port, ctrl); + } +} + +static void read_samples(struct parport *port, uint8_t ctrl, uint8_t *buf, size_t num, size_t stride) +{ + while (num--) { + ieee1284_write_control(port, ctrl & ~C1284_NSTROBE); + *buf = ieee1284_read_data(port); + buf += stride; + ieee1284_write_control(port, ctrl); + } +} + +static void push_samples(const struct sr_dev_inst *sdi, uint8_t *buf, size_t num) +{ + struct dev_context *devc = sdi->priv; + float *data = devc->samples; + struct sr_datafeed_analog analog = { + .channels = devc->enabled_channel, + .num_samples = num, + .mq = SR_MQ_VOLTAGE, + .unit = SR_UNIT_VOLT, + .mqflags = 0, + .data = data, + }; + struct sr_datafeed_packet packet = { + .type = SR_DF_ANALOG, + .payload = &analog, + }; + float factor = devc->factor; + + while (num--) + data[num] = (buf[num] - 0x80) * factor; + + sr_session_send(devc->cb_data, &packet); +} + +static int read_subframe(const struct sr_dev_inst *sdi, uint8_t *buf) +{ + struct dev_context *devc = sdi->priv; + uint8_t sig[3], ctrl; + unsigned int num; + gboolean interleave; + + interleave = readout_steps[devc->step].interleave; + ctrl = C1284_NSTROBE; + if ((interleave && devc->adc2) || (!interleave && devc->channel == 2)) + ctrl |= C1284_NAUTOFD; + + ieee1284_write_control(sdi->conn, ctrl); + num = readout_steps[devc->step].num; + if (num < 1000) + skip_samples(sdi->conn, ctrl, 1000 - num); + read_samples(sdi->conn, ctrl, buf + (devc->adc2 ? 1 : 0), num, + interleave ? 2 : 1); + read_samples(sdi->conn, ctrl, sig, 3, 1); + if (sig[0] != 0x01 || sig[1] != 0xfe || sig[2] != 0x80) { + if (--devc->retries) { + sr_dbg("Missing signature at end of buffer, %i tries remaining", + devc->retries); + return TRUE; + } else { + sr_err("Failed to read frame without transfer errors"); + devc->step = 0; + } + } else { + if (interleave && !devc->adc2) { + devc->adc2 = TRUE; + devc->retries = MAX_RETRIES; + return TRUE; + } else { + if (interleave) + num *= 2; + if (!devc->step) { + struct sr_datafeed_packet packet = { + .type = SR_DF_TRIGGER + }; + + push_samples(sdi, buf, 6); + sr_session_send(devc->cb_data, &packet); + buf += 6; + num -= 6; + } + push_samples(sdi, buf, num); + if (++devc->step > devc->last_step) + devc->step = 0; + } + } + + devc->adc2 = FALSE; + devc->retries = MAX_RETRIES; + + return devc->step > 0; +} + +SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data) +{ + struct sr_datafeed_packet packet = { .type = SR_DF_FRAME_BEGIN }; const struct sr_dev_inst *sdi; struct dev_context *devc; + uint8_t state, buf[1000]; (void)fd; + (void)revents; if (!(sdi = cb_data)) return TRUE; @@ -32,9 +424,40 @@ SR_PRIV int hung_chang_dso_2100_receive_data(int fd, int revents, void *cb_data) if (!(devc = sdi->priv)) return TRUE; - if (revents == G_IO_IN) { - /* TODO */ + if (devc->state_known) + hung_chang_dso_2100_write_mbox(sdi->conn, 0x99); + + state = hung_chang_dso_2100_read_mbox(sdi->conn, 0.00025); + devc->state_known = (state != 0x00); + + if (!devc->state_known || state == 0x21) + return TRUE; + + if (state != 0x03) { + sr_err("Unexpected state 0x%X while checking for trigger"); + return FALSE; } + sr_session_send(devc->cb_data, &packet); + + if (devc->channel) { + while (read_subframe(sdi, buf)) { + if (hung_chang_dso_2100_move_to(sdi, 1) != SR_OK) + break; + hung_chang_dso_2100_write_mbox(sdi->conn, 3); + g_usleep(1700); + if (hung_chang_dso_2100_read_mbox(sdi->conn, 0.02) != 0x03) + break; + } + } + + packet.type = SR_DF_FRAME_END; + sr_session_send(devc->cb_data, &packet); + + if (++devc->frame >= devc->frame_limit) + hung_chang_dso_2100_dev_acquisition_stop(sdi, devc->cb_data); + else + hung_chang_dso_2100_move_to(sdi, 0x21); + return TRUE; } diff --git a/src/hardware/hung-chang-dso-2100/protocol.h b/src/hardware/hung-chang-dso-2100/protocol.h index 4a24aebc..5636699a 100644 --- a/src/hardware/hung-chang-dso-2100/protocol.h +++ b/src/hardware/hung-chang-dso-2100/protocol.h @@ -26,19 +26,44 @@ #include "libsigrok-internal.h" #define LOG_PREFIX "hung-chang-dso-2100" +#define MAX_RETRIES 4 +#define NUM_CHANNELS 2 /** Private, per-device-instance driver context. */ struct dev_context { - /* Model-specific information */ - /* Acquisition settings */ + GSList *enabled_channel; + uint8_t channel; + uint8_t rate; + uint8_t cctl[2]; + uint8_t edge; + uint8_t tlevel; + uint8_t pos[2]; + uint8_t offset[2]; + uint8_t gain[2]; /* Operational state */ + uint64_t frame_limit; + uint64_t frame; + uint64_t probe[2]; + uint8_t step; + uint8_t last_step; + uint8_t retries; + gboolean adc2; /* Temporary state across callbacks */ - + void *cb_data; + float *samples; + float factor; + gboolean state_known; }; -SR_PRIV int hung_chang_dso_2100_receive_data(int fd, int revents, void *cb_data); +SR_PRIV void hung_chang_dso_2100_reset_port(struct parport *port); +SR_PRIV gboolean hung_chang_dso_2100_check_id(struct parport *port); +SR_PRIV void hung_chang_dso_2100_write_mbox(struct parport *port, uint8_t val); +SR_PRIV uint8_t hung_chang_dso_2100_read_mbox(struct parport *port, float timeout); +SR_PRIV int hung_chang_dso_2100_move_to(const struct sr_dev_inst *sdi, uint8_t target); +SR_PRIV int hung_chang_dso_2100_poll(int fd, int revents, void *cb_data); +SR_PRIV int hung_chang_dso_2100_dev_acquisition_stop(const struct sr_dev_inst *sdi, void *cb_data); #endif