From ca7d19b5c8cd6a4922218a323602347bd6f94c07 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Sun, 25 Jun 2017 20:38:05 +0200 Subject: [PATCH] saleae-logicpro: Initial implementation. The driver currently support only digital channels and a limited set of sample rates. --- configure.ac | 2 +- src/hardware/saleae-logicpro/api.c | 319 ++++++++++++- src/hardware/saleae-logicpro/protocol.c | 608 +++++++++++++++++++++++- src/hardware/saleae-logicpro/protocol.h | 28 +- 4 files changed, 919 insertions(+), 38 deletions(-) diff --git a/configure.ac b/configure.ac index c839e852..9a618769 100644 --- a/configure.ac +++ b/configure.ac @@ -264,7 +264,7 @@ SR_DRIVER([Pipistrello-OLS], [pipistrello-ols], [libftdi]) SR_DRIVER([Rigol DS], [rigol-ds]) SR_DRIVER([Rohde&Schwarz SME-0x], [rohde-schwarz-sme-0x], [libserialport]) SR_DRIVER([Saleae Logic16], [saleae-logic16], [libusb]) -SR_DRIVER([saleae-logicpro], [saleae-logicpro]) +SR_DRIVER([Saleae Logic Pro], [saleae-logicpro], [libusb]) SR_DRIVER([SCPI PPS], [scpi-pps]) SR_DRIVER([serial DMM], [serial-dmm], [libserialport]) SR_DRIVER([serial LCR], [serial-lcr], [libserialport]) diff --git a/src/hardware/saleae-logicpro/api.c b/src/hardware/saleae-logicpro/api.c index 96c04642..82105c14 100644 --- a/src/hardware/saleae-logicpro/api.c +++ b/src/hardware/saleae-logicpro/api.c @@ -18,25 +18,166 @@ */ #include +#include +#include #include "protocol.h" +#define BUF_COUNT (8) +#define BUF_SIZE (16*1024) +#define BUF_TIMEOUT (1000*1000) + SR_PRIV struct sr_dev_driver saleae_logicpro_driver_info; +static const uint32_t drvopts[] = { + SR_CONF_LOGIC_ANALYZER, +}; + +static const uint32_t scanopts[] = { + SR_CONF_CONN, +}; + +static const uint32_t devopts[] = { + SR_CONF_CONTINUOUS, + SR_CONF_CONN | SR_CONF_GET, + SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +}; + +static const char *channel_names[] = { + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15", +}; + +static const uint64_t samplerates[] = { + SR_MHZ(1), + SR_MHZ(2), + SR_KHZ(2500), + SR_MHZ(10), + SR_MHZ(25), + SR_MHZ(50), +}; + +static gboolean scan_firmware(libusb_device *dev) +{ + struct libusb_device_descriptor des; + struct libusb_device_handle *hdl; + gboolean ret; + unsigned char strdesc[64]; + + hdl = NULL; + ret = FALSE; + + libusb_get_device_descriptor(dev, &des); + + if (libusb_open(dev, &hdl) != 0) + goto out; + + if (libusb_get_string_descriptor_ascii(hdl, + des.iManufacturer, strdesc, sizeof(strdesc)) < 0) + goto out; + if (strcmp((const char *)strdesc, "Saleae")) + goto out; + + if (libusb_get_string_descriptor_ascii(hdl, + des.iProduct, strdesc, sizeof(strdesc)) < 0) + goto out; + if (strcmp((const char *)strdesc, "Logic Pro")) + goto out; + + ret = TRUE; + +out: + if (hdl) + libusb_close(hdl); + + return ret; +} + static GSList *scan(struct sr_dev_driver *di, GSList *options) { struct drv_context *drvc; - GSList *devices; - - (void)options; + struct dev_context *devc; + struct sr_dev_inst *sdi; + GSList *devices, *conn_devices; + libusb_device **devlist; + struct libusb_device_descriptor des; + const char *conn; + char connection_id[64]; devices = NULL; drvc = di->context; drvc->instances = NULL; - /* TODO: scan for devices, either based on a SR_CONF_CONN option - * or on a USB scan. */ + conn = NULL; + for (GSList *l = options; l; l = l->next) { + struct sr_config *src = l->data; - return devices; + switch (src->key) { + case SR_CONF_CONN: + conn = g_variant_get_string(src->data, NULL); + break; + } + } + + if (conn) + conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); + else + conn_devices = NULL; + + libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + for (unsigned int i = 0; devlist[i]; i++) { + if (conn) { + struct sr_usb_dev_inst *usb = NULL; + GSList *l; + + for (l = conn_devices; l; l = l->next) { + usb = l->data; + if (usb->bus == libusb_get_bus_number(devlist[i]) + && usb->address == libusb_get_device_address(devlist[i])) + break; + } + if (!l) + /* This device matched none of the ones that + * matched the conn specification. */ + continue; + } + + libusb_get_device_descriptor(devlist[i], &des); + + usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)); + + if (des.idVendor != 0x21a9 || des.idProduct != 0x1006) + continue; + + if (!scan_firmware(devlist[i])) { + sr_err("Found a Logic Pro device, but firmware is not loaded (use Saleae application)."); + continue; + } + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INITIALIZING; + sdi->vendor = g_strdup("Saleae"); + sdi->model = g_strdup("Logic Pro 16"); + sdi->connection_id = g_strdup(connection_id); + + for (unsigned int j = 0; j < ARRAY_SIZE(channel_names); j++) + sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE, + channel_names[j]); + + sr_dbg("Found a Logic Pro 16 device."); + sdi->status = SR_ST_INACTIVE; + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i]), NULL); + + devc = g_malloc0(sizeof(struct dev_context)); + sdi->priv = devc; + devices = g_slist_append(devices, sdi); + + } + libusb_free_device_list(devlist, 1); + g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); + + return std_scan_complete(di, devices); } static int dev_clear(const struct sr_dev_driver *di) @@ -46,9 +187,23 @@ static int dev_clear(const struct sr_dev_driver *di) static int dev_open(struct sr_dev_inst *sdi) { - (void)sdi; + struct sr_dev_driver *di = sdi->driver; + struct drv_context *drvc = di->context; + struct dev_context *devc = sdi->priv; + struct sr_usb_dev_inst *usb = sdi->conn; + int ret; - /* TODO: get handle from sdi->conn and open it. */ + if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK) + return SR_ERR; + + if ((ret = libusb_claim_interface(usb->devhdl, 0))) { + sr_err("Failed to claim interface: %s.", libusb_error_name(ret)); + return SR_ERR; + } + + /* configure default samplerate */ + if (devc->dig_samplerate == 0) + devc->dig_samplerate = samplerates[3]; sdi->status = SR_ST_ACTIVE; @@ -57,9 +212,9 @@ static int dev_open(struct sr_dev_inst *sdi) static int dev_close(struct sr_dev_inst *sdi) { - (void)sdi; + struct sr_usb_dev_inst *usb = sdi->conn; - /* TODO: get handle from sdi->conn and close it. */ + sr_usb_close(usb); sdi->status = SR_ST_INACTIVE; @@ -67,17 +222,28 @@ static int dev_close(struct sr_dev_inst *sdi) } static int config_get(uint32_t key, GVariant **data, - const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { + struct sr_usb_dev_inst *usb; + struct dev_context *devc; int ret; - (void)sdi; - (void)data; (void)cg; ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_CONN: + if (!sdi || !sdi->conn) + return SR_ERR_ARG; + usb = sdi->conn; + *data = g_variant_new_printf("%d.%d", usb->bus, usb->address); + break; + case SR_CONF_SAMPLERATE: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_uint64(devc->dig_samplerate); + break; default: return SR_ERR_NA; } @@ -86,19 +252,22 @@ static int config_get(uint32_t key, GVariant **data, } static int config_set(uint32_t key, GVariant *data, - const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { + struct dev_context *devc; int ret; - (void)data; (void)cg; if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; + devc = sdi->priv; ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_SAMPLERATE: + devc->dig_samplerate = g_variant_get_uint64(data); + break; default: ret = SR_ERR_NA; } @@ -107,17 +276,43 @@ static int config_set(uint32_t key, GVariant *data, } static int config_list(uint32_t key, GVariant **data, - const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { + GVariant *gvar; + GVariantBuilder gvb; int ret; (void)sdi; - (void)data; (void)cg; ret = SR_OK; switch (key) { - /* TODO */ + 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 { + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts, + ARRAY_SIZE(devopts), + 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; default: return SR_ERR_NA; } @@ -125,23 +320,101 @@ static int config_list(uint32_t key, GVariant **data, return ret; } +static void dev_acquisition_abort(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + unsigned int i; + + for (i = 0; i < devc->num_transfers; i++) { + if (devc->transfers[i]) + libusb_cancel_transfer(devc->transfers[i]); + } +} + +static int dev_acquisition_handle(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi = cb_data; + struct drv_context *drvc = sdi->driver->context; + struct timeval tv = {}; + + (void)fd; + (void)revents; + + libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv); + + return TRUE; +} + static int dev_acquisition_start(const struct sr_dev_inst *sdi) { + struct dev_context *devc = sdi->priv; + struct drv_context *drvc = sdi->driver->context; + struct libusb_transfer *transfer; + struct sr_usb_dev_inst *usb; + uint8_t *buf; + unsigned int i, 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. */ + ret = saleae_logicpro_init(sdi); + if (ret != SR_OK) + return ret; + + ret = saleae_logicpro_prepare(sdi); + if (ret != SR_OK) + return ret; + + usb = sdi->conn; + + devc->conv_buffer = g_malloc(CONV_BUFFER_SIZE); + + devc->num_transfers = BUF_COUNT; + devc->transfers = g_malloc0(sizeof(*devc->transfers) * BUF_COUNT); + for (i = 0; i < devc->num_transfers; i++) { + buf = g_malloc(BUF_SIZE); + transfer = libusb_alloc_transfer(0); + libusb_fill_bulk_transfer(transfer, usb->devhdl, + 2 | LIBUSB_ENDPOINT_IN, buf, BUF_SIZE, + saleae_logicpro_receive_data, (void *)sdi, BUF_TIMEOUT); + if ((ret = libusb_submit_transfer(transfer)) != 0) { + sr_err("Failed to submit transfer: %s.", + libusb_error_name(ret)); + libusb_free_transfer(transfer); + g_free(buf); + dev_acquisition_abort(sdi); + return SR_ERR; + } + devc->transfers[i] = transfer; + devc->submitted_transfers++; + } + + usb_source_add(sdi->session, drvc->sr_ctx, BUF_TIMEOUT, dev_acquisition_handle, (void *)sdi); + + std_session_send_df_header(sdi); + + saleae_logicpro_start(sdi); + if (ret != SR_OK) + return ret; return SR_OK; } static int dev_acquisition_stop(struct sr_dev_inst *sdi) { + struct dev_context *devc = sdi->priv; + struct drv_context *drvc = sdi->driver->context; + if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; - /* TODO: stop acquisition. */ + saleae_logicpro_stop(sdi); + + std_session_send_df_end(sdi); + + usb_source_remove(sdi->session, drvc->sr_ctx); + + g_free(devc->conv_buffer); return SR_OK; } diff --git a/src/hardware/saleae-logicpro/protocol.c b/src/hardware/saleae-logicpro/protocol.c index b5352f11..ff8634b9 100644 --- a/src/hardware/saleae-logicpro/protocol.c +++ b/src/hardware/saleae-logicpro/protocol.c @@ -18,24 +18,610 @@ */ #include +#include #include "protocol.h" -SR_PRIV int saleae_logicpro_receive_data(int fd, int revents, void *cb_data) +#define COMMAND_START_CAPTURE 0x01 +#define COMMAND_STOP_CAPTURE 0x02 +#define COMMAND_READ_EEPROM 0x07 +#define COMMAND_WRITE_REG 0x80 +#define COMMAND_READ_REG 0x81 +#define COMMAND_WRITE_I2C 0x87 +#define COMMAND_READ_I2C 0x88 +#define COMMAND_WAKE_I2C 0x89 +#define COMMAND_READ_FW_VER 0x8b + +#define REG_LED_RED 0x0f +#define REG_LED_GREEN 0x10 +#define REG_LED_BLUE 0x11 + +static void iterate_lfsr(const struct sr_dev_inst *sdi) { - const struct sr_dev_inst *sdi; - struct dev_context *devc; + struct dev_context *devc = sdi->priv; + uint32_t lfsr = devc->lfsr; + int i, max; - (void)fd; + max = (lfsr & 0x1f) + 34; + for (i = 0; i <= max; i++) { + lfsr = (lfsr >> 1) | \ + ((lfsr ^ \ + (lfsr >> 1) ^ \ + (lfsr >> 21) ^ \ + (lfsr >> 31) \ + ) << 31); + } + sr_dbg("Iterate 0x%08x -> 0x%08x", devc->lfsr, lfsr); + devc->lfsr = lfsr; +} - if (!(sdi = cb_data)) - return TRUE; +static void encrypt(const struct sr_dev_inst *sdi, const uint8_t *in, uint8_t *out, uint8_t len) +{ + struct dev_context *devc = sdi->priv; + uint32_t lfsr = devc->lfsr; + uint8_t value, mask; + int i; - if (!(devc = sdi->priv)) - return TRUE; + for (i = 0; i < len; i++) { + value = in[i]; + mask = lfsr >> (i%4*8); + if (i == 0) + value = (value & 0x28) | ((value ^ mask) & ~0x28); + else + value = value ^ mask; + out[i] = value; + } + iterate_lfsr(sdi); +} - if (revents == G_IO_IN) { - /* TODO */ +static void decrypt(const struct sr_dev_inst *sdi, uint8_t *data, uint8_t len) +{ + struct dev_context *devc = sdi->priv; + uint32_t lfsr = devc->lfsr; + int i; + + for (i = 0; i < len; i++) { + data[i] ^= (lfsr >> (i%4*8)); + } + iterate_lfsr(sdi); +} + +static int transact(const struct sr_dev_inst *sdi, + const uint8_t *req, uint8_t req_len, + uint8_t *rsp, uint8_t rsp_len) +{ + struct sr_usb_dev_inst *usb = sdi->conn; + uint8_t *req_enc; + uint8_t rsp_dummy[1] = {}; + int ret, xfer; + + if (req_len < 2 || req_len > 64 || rsp_len > 128 || + !req || (rsp_len > 0 && !rsp)) + return SR_ERR_ARG; + + req_enc = g_malloc(req_len); + encrypt(sdi, req, req_enc, req_len); + + ret = libusb_bulk_transfer(usb->devhdl, 1, req_enc, req_len, &xfer, 1000); + if (ret != 0) { + sr_dbg("Failed to send request 0x%02x: %s.", + req[1], libusb_error_name(ret)); + return SR_ERR; + } + if (xfer != req_len) { + sr_dbg("Failed to send request 0x%02x: incorrect length " + "%d != %d.", req[1], xfer, req_len); + return SR_ERR; } - return TRUE; + if (req[0] == 0x20) { // reseed + return SR_OK; + } else if (rsp_len == 0) { + rsp = rsp_dummy; + rsp_len = sizeof(rsp_dummy); + } + + ret = libusb_bulk_transfer(usb->devhdl, 0x80 | 1, rsp, rsp_len, + &xfer, 1000); + if (ret != 0) { + sr_dbg("Failed to receive response to request 0x%02x: %s.", + req[1], libusb_error_name(ret)); + return SR_ERR; + } + if (xfer != rsp_len) { + sr_dbg("Failed to receive response to request 0x%02x: " + "incorrect length %d != %d.", req[1], xfer, rsp_len); + return SR_ERR; + } + + decrypt(sdi, rsp, rsp_len); + + return SR_OK; +} + +static int reseed(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + uint8_t req[] = {0x20, 0x24, 0x4b, 0x35, 0x8e}; + + devc->lfsr = 0; + return transact(sdi, req, sizeof(req), NULL, 0); +} + +static int write_regs(const struct sr_dev_inst *sdi, uint8_t (*regs)[2], uint8_t cnt) +{ + uint8_t req[64]; + int i; + + if (cnt < 1 || cnt > 30) + return SR_ERR_ARG; + + req[0] = 0x00; + req[1] = COMMAND_WRITE_REG; + req[2] = cnt; + + for (i = 0; i < cnt; i++) { + req[3 + 2 * i] = regs[i][0]; + req[4 + 2 * i] = regs[i][1]; + } + + return transact(sdi, req, 3 + 2*cnt, NULL, 0); +} + +static int write_reg(const struct sr_dev_inst *sdi, + uint8_t address, uint8_t value) +{ + uint8_t regs[2] = {address, value}; + + return write_regs(sdi, ®s, 1); +} + +static int get_firmware_version(const struct sr_dev_inst *sdi) +{ + uint8_t req[2] = {0x00, COMMAND_READ_FW_VER}; + uint8_t rsp[128] = {}; + int ret; + + ret = transact(sdi, req, sizeof(req), rsp, sizeof(rsp)); + if (ret == SR_OK) { + rsp[63] = 0; + sr_dbg("fw-version: %s", rsp); + } + + return ret; +} + +static int read_i2c(const struct sr_dev_inst *sdi, uint8_t *data, uint8_t len) +{ + uint8_t req[5]; + uint8_t rsp[1+128]; + int ret; + + if (len < 1 || len > 128 || !data) + return SR_ERR_ARG; + + req[0] = 0x00; + req[1] = COMMAND_READ_I2C; + req[2] = 0xc0; // fixed address + req[3] = len; + req[4] = 0; // len msb? + + ret = transact(sdi, req, sizeof(req), rsp, 1 + len); + if (ret != SR_OK) { + return ret; + } + if (rsp[0] != 0x02) { + sr_dbg("Failed to do I2C read (0x%02x).", rsp[0]); + return SR_ERR; + } + + memcpy(data, rsp+1, len); + return SR_OK; +} + +static int write_i2c(const struct sr_dev_inst *sdi, const uint8_t *data, uint8_t len) +{ + uint8_t req[5 + 128]; + uint8_t rsp[1]; + int ret; + + if (len < 1 || len > 128 || !data) + return SR_ERR_ARG; + + req[0] = 0x00; + req[1] = COMMAND_WRITE_I2C; + req[2] = 0xc0; // fixed address + req[3] = len; + req[4] = 0; // len msb? + memcpy(req + 5, data, len); + + ret = transact(sdi, req, 5 + len, rsp, sizeof(rsp)); + if (ret != SR_OK) { + return ret; + } + if (rsp[0] != 0x02) { + sr_dbg("Failed to do I2C write (0x%02x).", rsp[0]); + return SR_ERR; + } + + return SR_OK; +} + +static int wake_i2c(const struct sr_dev_inst *sdi) +{ + uint8_t req[] = {0x00, COMMAND_WAKE_I2C}; + uint8_t rsp[1] = {}; + uint8_t i2c_rsp[1+1+2] = {}; + int ret; + + ret = transact(sdi, req, sizeof(req), rsp, sizeof(rsp)); + if (ret != SR_OK) { + return ret; + } + if (rsp[0] != 0x00) { + sr_dbg("Failed to do I2C wake trigger (0x%02x).", rsp[0]); + return SR_ERR; + } + + ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp)); + if (ret != SR_OK) { + return ret; + } + if (i2c_rsp[1] != 0x11) { + sr_dbg("Failed to do I2C wake read (0x%02x).", i2c_rsp[0]); + return SR_ERR; + } + + return SR_OK; +} + +static int crypto_random(const struct sr_dev_inst *sdi, uint8_t *data) +{ + uint8_t i2c_req[8] = {0x03, 0x07, 0x1b, 0x00, 0x00, 0x00, 0x24, 0xcd}; + uint8_t i2c_rsp[1+32+2] = {}; + int ret; + + ret = write_i2c(sdi, i2c_req, sizeof(i2c_req)); + if (ret != SR_OK) { + return ret; + } + + g_usleep(100000); // TODO: poll instead + + ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp)); + if (ret != SR_OK) { + return ret; + } + + if (data) { + memcpy(data, i2c_rsp+1, 32); + } + + return SR_OK; +} + +static int crypto_nonce(const struct sr_dev_inst *sdi, uint8_t *data) +{ + uint8_t i2c_req[6+20+2] = {0x03, 0x1b, 0x16, 0x00, 0x00, 0x00}; + uint8_t i2c_rsp[1+32+2] = {}; + int ret; + + // CRC + i2c_req[26] = 0x7d; + i2c_req[27] = 0xe0; + + ret = write_i2c(sdi, i2c_req, sizeof(i2c_req)); + if (ret != SR_OK) { + return ret; + } + + g_usleep(100000); // TODO: poll instead + + ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp)); + if (ret != SR_OK) { + return ret; + } + + if (data) { + memcpy(data, i2c_rsp+1, 32); + } + + return SR_OK; +} + +static int crypto_sign(const struct sr_dev_inst *sdi, uint8_t *data, uint8_t *crc) +{ + uint8_t i2c_req[8] = {0x03, 0x07, 0x41, 0x80, 0x00, 0x00, 0x28, 0x05}; + uint8_t i2c_rsp[1+64+2] = {}; + int ret; + + ret = write_i2c(sdi, i2c_req, sizeof(i2c_req)); + if (ret != SR_OK) { + return ret; + } + + g_usleep(100000); // TODO: poll instead + + ret = read_i2c(sdi, i2c_rsp, sizeof(i2c_rsp)); + if (ret != SR_OK) { + return ret; + } + + memcpy(data, i2c_rsp+1, 64); + memcpy(crc, i2c_rsp+1+64, 2); + + return SR_OK; +} + +static int authenticate(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + uint8_t random[32] = {}; + uint8_t nonce[32] = {}; + uint8_t sig[64] = {}; + uint8_t sig_crc[64] = {}; + uint32_t lfsr; + int i, ret; + + ret = wake_i2c(sdi); + if (ret != SR_OK) + return ret; + + ret = crypto_random(sdi, random); + if (ret != SR_OK) + return ret; + sr_dbg("random: 0x%02x 0x%02x 0x%02x 0x%02x", random[0], random[1], random[2], random[3]); + + ret = crypto_nonce(sdi, nonce); + if (ret != SR_OK) + return ret; + sr_dbg("nonce: 0x%02x 0x%02x 0x%02x 0x%02x", nonce[0], nonce[1], nonce[2], nonce[3]); + + ret = crypto_nonce(sdi, nonce); + if (ret != SR_OK) + return ret; + sr_dbg("nonce: 0x%02x 0x%02x 0x%02x 0x%02x", nonce[0], nonce[1], nonce[2], nonce[3]); + + ret = crypto_sign(sdi, sig, sig_crc); + if (ret != SR_OK) + return ret; + sr_dbg("sig: 0x%02x 0x%02x 0x%02x 0x%02x", sig[0], sig[1], sig[2], sig[3]); + sr_dbg("sig crc: 0x%02x 0x%02x", sig_crc[0], sig_crc[1]); + + lfsr = 0; + for (i = 0; i < 28; i++) + lfsr ^= nonce[i] << (8*(i%4)); + lfsr ^= sig_crc[0] | sig_crc[1] << 8; + + sr_dbg("Authenticate 0x%08x -> 0x%08x", devc->lfsr, lfsr); + devc->lfsr = lfsr; + + return SR_OK; +} + +static int set_led(const struct sr_dev_inst *sdi, uint8_t r, uint8_t g, uint8_t b) +{ + uint8_t regs[][2] = { + {REG_LED_RED, r}, + {REG_LED_GREEN, g}, + {REG_LED_BLUE, b}, + }; + + authenticate(sdi); + + return write_regs(sdi, regs, G_N_ELEMENTS(regs)); +} + +static int configure_channels(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + const struct sr_channel *c; + const GSList *l; + uint16_t mask; + + devc->dig_channel_cnt = 0; + devc->dig_channel_mask = 0; + for (l = sdi->channels; l; l = l->next) { + c = l->data; + if (!c->enabled) + continue; + + mask = 1 << c->index; + devc->dig_channel_masks[devc->dig_channel_cnt++] = mask; + devc->dig_channel_mask |= mask; + + } + sr_dbg("%d channels enabled (0x%04x)", + devc->dig_channel_cnt, + devc->dig_channel_mask); + + return SR_OK; +} + +SR_PRIV int saleae_logicpro_init(const struct sr_dev_inst *sdi) +{ + reseed(sdi); + get_firmware_version(sdi); + /* setting the LED doesn't work yet */ + /* set_led(sdi, 0x00, 0x00, 0xff); */ + + return SR_OK; +} + +SR_PRIV int saleae_logicpro_prepare(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + uint8_t regs_unknown[][2] = { + {0x03, 0x0f}, + {0x04, 0x00}, + {0x05, 0x00}, + }; + uint8_t regs_config[][2] = { + {0x00, 0x00}, + {0x08, 0x00}, /* analog channel mask (LSB) */ + {0x09, 0x00}, /* analog channel mask (MSB) */ + {0x06, 0x01}, /* digital channel mask (LSB) */ + {0x07, 0x00}, /* digital channel mask (MSB) */ + {0x0a, 0x00}, /* analog sample rate? */ + {0x0b, 0x64}, /* digital sample rate? */ + {0x0c, 0x00}, + {0x0d, 0x00}, /* analog mux rate? */ + {0x0e, 0x01}, /* digital mux rate? */ + {0x12, 0x04}, + {0x13, 0x00}, + {0x14, 0xff}, /* pre-divider? */ + }; + uint8_t start_req[] = {0x00, 0x01}; + uint8_t start_rsp[2] = {}; + + configure_channels(sdi); + + /* digital channel mask and muxing */ + regs_config[3][1] = devc->dig_channel_mask; + regs_config[4][1] = devc->dig_channel_mask >> 8; + regs_config[9][1] = devc->dig_channel_cnt; + + /* samplerate */ + switch (devc->dig_samplerate) { + case SR_MHZ(1): + regs_config[6][1] = 0x64; + break; + case SR_MHZ(2): + regs_config[6][1] = 0x32; + break; + case SR_KHZ(2500): + regs_config[6][1] = 0x28; + break; + case SR_MHZ(10): + regs_config[6][1] = 0x0a; + break; + case SR_MHZ(25): + regs_config[6][1] = 0x04; + regs_config[12][1] = 0x80; + break; + case SR_MHZ(50): + regs_config[6][1] = 0x02; + regs_config[12][1] = 0x40; + break; + default: + return SR_ERR_ARG; + } + + authenticate(sdi); + + write_reg(sdi, 0x15, 0x03); + write_regs(sdi, regs_unknown, G_N_ELEMENTS(regs_unknown)); + write_regs(sdi, regs_config, G_N_ELEMENTS(regs_config)); + + transact(sdi, start_req, sizeof(start_req), start_rsp, sizeof(start_rsp)); + + return SR_OK; +} + +SR_PRIV int saleae_logicpro_start(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc = sdi->priv; + + devc->conv_size = 0; + devc->batch_index = 0; + + write_reg(sdi, 0x00, 0x01); + + return SR_OK; +} + +SR_PRIV int saleae_logicpro_stop(const struct sr_dev_inst *sdi) +{ + uint8_t stop_req[] = {0x00, 0x02}; + uint8_t stop_rsp[2] = {}; + + write_reg(sdi, 0x00, 0x00); + transact(sdi, stop_req, sizeof(stop_req), stop_rsp, sizeof(stop_rsp)); + + return SR_OK; +} + +static void saleae_logicpro_send_data(const struct sr_dev_inst *sdi, + void *data, size_t length, size_t unitsize) +{ + const struct sr_datafeed_logic logic = { + .length = length, + .unitsize = unitsize, + .data = data + }; + + const struct sr_datafeed_packet packet = { + .type = SR_DF_LOGIC, + .payload = &logic + }; + + sr_session_send(sdi, &packet); +} + +/* + * One batch from the device consists of 32 samples per active digital channel. + * This stream of batches is packed into USB packets with 16384 bytes each. + */ +static void saleae_logicpro_convert_data(const struct sr_dev_inst *sdi, + const uint32_t *src, size_t srccnt) +{ + struct dev_context *devc = sdi->priv; + uint8_t *dst = devc->conv_buffer; + uint32_t samples; + uint16_t channel_mask; + unsigned int sample_index, batch_index; + uint16_t *dst_batch; + + /* copy partial batch to the beginning */ + memcpy(dst, dst+devc->conv_size, CONV_BATCH_SIZE); + /* reset converted size */ + devc->conv_size = 0; + + batch_index = devc->batch_index; + while (srccnt--) { + samples = *src++; + dst_batch = (uint16_t*)dst; + + /* first index of the batch */ + if (batch_index == 0) + memset(dst, 0, CONV_BATCH_SIZE); + + /* convert one channel */ + channel_mask = devc->dig_channel_masks[batch_index]; + for (sample_index = 0; sample_index <= 31; sample_index++) + if ((samples >> (31-sample_index)) & 1) + dst_batch[sample_index] |= channel_mask; + + /* last index of the batch */ + if (++batch_index == devc->dig_channel_cnt) { + devc->conv_size += CONV_BATCH_SIZE; + batch_index = 0; + dst += CONV_BATCH_SIZE; + } + } + devc->batch_index = batch_index; +} + +SR_PRIV void LIBUSB_CALL saleae_logicpro_receive_data(struct libusb_transfer *transfer) +{ + const struct sr_dev_inst *sdi = transfer->user_data; + struct dev_context *devc = sdi->priv; + int ret; + + switch (transfer->status) { + case LIBUSB_TRANSFER_NO_DEVICE: + sr_dbg("FIXME no device"); + return; + case LIBUSB_TRANSFER_COMPLETED: + case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */ + break; + default: + // FIXME + return; + } + + saleae_logicpro_convert_data(sdi, (uint32_t*)transfer->buffer, 16*1024/4); + saleae_logicpro_send_data(sdi, devc->conv_buffer, devc->conv_size, 2); + + if ((ret = libusb_submit_transfer(transfer)) != LIBUSB_SUCCESS) + sr_dbg("FIXME resubmit failed"); } diff --git a/src/hardware/saleae-logicpro/protocol.h b/src/hardware/saleae-logicpro/protocol.h index 5c49136a..2ac0af10 100644 --- a/src/hardware/saleae-logicpro/protocol.h +++ b/src/hardware/saleae-logicpro/protocol.h @@ -27,18 +27,40 @@ #define LOG_PREFIX "saleae-logicpro" +/* 16 channels * 32 samples */ +#define CONV_BATCH_SIZE (2*32) +/* one packet + one partial conversion: + * worst case is only one active channel converted to 2 bytes per sample, with + * 8*16384 samples per packet + */ +#define CONV_BUFFER_SIZE (2*8*16384 + CONV_BATCH_SIZE) + /** Private, per-device-instance driver context. */ struct dev_context { - /* Model-specific information */ - /* Acquisition settings */ + unsigned int dig_channel_cnt; + uint16_t dig_channel_mask; + uint16_t dig_channel_masks[16]; + uint64_t dig_samplerate; /* Operational state */ + uint32_t lfsr; /* Temporary state across callbacks */ + unsigned int num_transfers; + unsigned int submitted_transfers; + struct libusb_transfer **transfers; + /* Conversion buffer */ + uint8_t *conv_buffer; + unsigned int conv_size; + unsigned int batch_index; }; -SR_PRIV int saleae_logicpro_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int saleae_logicpro_init(const struct sr_dev_inst *sdi); +SR_PRIV int saleae_logicpro_prepare(const struct sr_dev_inst *sdi); +SR_PRIV int saleae_logicpro_start(const struct sr_dev_inst *sdi); +SR_PRIV int saleae_logicpro_stop(const struct sr_dev_inst *sdi); +SR_PRIV void LIBUSB_CALL saleae_logicpro_receive_data(struct libusb_transfer *transfer); #endif