]> sigrok.org Git - libsigrok.git/commitdiff
microchip-pickit2: first driver implementation (acquisition works, triggers don't)
authorGerhard Sittig <redacted>
Tue, 11 Dec 2018 14:14:13 +0000 (15:14 +0100)
committerUwe Hermann <redacted>
Thu, 20 Dec 2018 15:19:42 +0000 (16:19 +0100)
Fill in the scan, open/close, get/set/list, acquisition start/stop logic
such that data acquisition with a PICkit2 works.

Trigger support needs more attention. User specified triggers either
seem to not take effect, or the trigger position is not in the expected
location. It's yet to get determined what's the issue.

This implementation is based on protocol information gathered from the
pk2-la project.

src/hardware/microchip-pickit2/api.c
src/hardware/microchip-pickit2/protocol.c
src/hardware/microchip-pickit2/protocol.h

index abeeedfa9afab0f4f123c79a7b7b4102f68840b6..41e7baa2e909e30f56463cf36a493d5405bdd3b3 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/*
+ * TODO
+ * - Data acquisition works, but triggers either seem to not take effect,
+ *   or the trigger position is not in the expected spot according to the
+ *   user provided acquisition parameters. More research is required. The
+ *   bitmasks for enable/level/edge as well as the magic 16bit values for
+ *   position may need adjustment.
+ * - The trigger position logic assumes that capture ratio specs are in
+ *   the range of 0-6%, which gets mapped to none/10%/50%/90%/+1W/+2W/+3W
+ *   choices. This avoids issues with applications which lack support for
+ *   non-contiguous discrete supported values, and values outside of the
+ *   0-100% range. This is considered acceptable, to avoid the necessity
+ *   to extend common infrastructure to an unusual feature of a single
+ *   device of limited popularity. Just needs to get communicated to users.
+ * - When a formula for the trigger position values in the SETUP packet
+ *   is found, the driver may accept arbitrary values between 0-100%, but
+ *   still could not express the "plus N windows" settings. Though that'd
+ *   be a rather useful feature considering the very short memory depth.
+ * - The current implementation assumes externally provided Vdd, without
+ *   which input levels won't get detected. A future implementation could
+ *   optionally power Vdd from the PICkit2 itself, according to a user
+ *   provided configuration value.
+ * - The current implementation silently accepts sample count limits beyond
+ *   1024, just won't provide more than 1024 samples to the session. A
+ *   future implementation could cap the settings upon reception. Apps
+ *   like Pulseview may not be able to specify 1024, and pass 1000 or
+ *   2000 instead (the latter results in 1024 getting used).
+ * - The manual suggests that users can assign names to devices. The
+ *   current implementation supports conn= specs with USB VID:PID pairs
+ *   or bus/address numbers. A future implementation could scan for user
+ *   assigned names as well (when the opcode to query the name was found).
+ * - The "attach kernel driver" support code probably should move to a
+ *   common location, instead of getting repeated across several drivers.
+ * - Diagnostics may benefit from cleanup.
+ */
+
 #include <config.h>
+#include <libusb.h>
+#include <string.h>
 #include "protocol.h"
 
+#define PICKIT2_VENDOR_NAME    "Microchip"
+#define PICKIT2_PRODUCT_NAME   "PICkit2"
+
+#define PICKIT2_DEFAULT_ADDRESS        "04d8.0033"
+#define PICKIT2_USB_INTERFACE  0
+
 static struct sr_dev_driver microchip_pickit2_driver_info;
 
+static const char *pickit2_channel_names[] = {
+       "pin4", "pin5", "pin6",
+};
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONN | SR_CONF_GET,
+       SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+       SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const int32_t trigger_matches[] = {
+       SR_TRIGGER_ZERO,
+       SR_TRIGGER_ONE,
+       SR_TRIGGER_RISING,
+       SR_TRIGGER_FALLING,
+};
+
+/*
+ * Note that a list of 0, 10, 50, 90, 91, 92, 93, would have been nicer
+ * from a user's perspective, but applications may not support a set of
+ * discrete supported values, and 91+ is as much of a hack to work around
+ * the "0-100%" limitation. So let's map those 0-6 "percent" to the vendor
+ * app's 10/50/90/1W/2W/3W locations.
+ */
+static const uint64_t captureratios[] = {
+       0, 1, 2, 3, 4, 5, 6,
+};
+
+static const uint64_t samplerates[] = {
+       SR_KHZ(5),
+       SR_KHZ(10),
+       SR_KHZ(25),
+       SR_KHZ(50),
+       SR_KHZ(100),
+       SR_KHZ(250),
+       SR_KHZ(500),
+       SR_MHZ(1),
+};
+
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
        struct drv_context *drvc;
-       GSList *devices;
+       const char *conn;
+       GSList *l, *devices, *usb_devices;
+       struct sr_config *cfg;
+       struct sr_usb_dev_inst *usb;
+       struct sr_dev_inst *sdi;
+       struct sr_channel_group *cg;
+       size_t ch_count, ch_idx;
+       struct sr_channel *ch;
+       struct dev_context *devc;
 
-       (void)options;
-
-       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 = PICKIT2_DEFAULT_ADDRESS;
+       for (l = options; l; l = l->next) {
+               cfg = l->data;
+               switch (cfg->key) {
+               case SR_CONF_CONN:
+                       conn = g_variant_get_string(cfg->data, NULL);
+                       break;
+               }
+       }
 
-       return devices;
+       devices = NULL;
+       usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+       if (!usb_devices)
+               return NULL;
+
+       for (l = usb_devices; l; l = l->next) {
+               usb = l->data;
+
+               /* Create the device instance. */
+               sdi = g_malloc0(sizeof(*sdi));
+               devices = g_slist_append(devices, sdi);
+               sdi->status = SR_ST_INACTIVE;
+               sdi->vendor = g_strdup(PICKIT2_VENDOR_NAME);
+               sdi->model = g_strdup(PICKIT2_PRODUCT_NAME);
+               sdi->inst_type = SR_INST_USB;
+               sdi->conn = usb;
+               sdi->connection_id = g_strdup(conn);
+
+               /* Create the logic channels group. */
+               cg = g_malloc0(sizeof(*cg));
+               sdi->channel_groups = g_slist_append(NULL, cg);
+               cg->name = g_strdup("Logic");
+               ch_count = ARRAY_SIZE(pickit2_channel_names);
+               for (ch_idx = 0; ch_idx < ch_count; ch_idx++) {
+                       ch = sr_channel_new(sdi, ch_idx, SR_CHANNEL_LOGIC,
+                               TRUE, pickit2_channel_names[ch_idx]);
+                       cg->channels = g_slist_append(cg->channels, ch);
+               }
+
+               /*
+                * Create the device context. Pre-select the highest
+                * samplerate and the deepest sample count available.
+                */
+               devc = g_malloc0(sizeof(*devc));
+               sdi->priv = devc;
+               devc->samplerates = samplerates;
+               devc->num_samplerates = ARRAY_SIZE(samplerates);
+               devc->curr_samplerate_idx = devc->num_samplerates - 1;
+               devc->captureratios = captureratios;
+               devc->num_captureratios = ARRAY_SIZE(captureratios);
+               devc->curr_captureratio_idx = 0;
+               devc->sw_limits.limit_samples = PICKIT2_SAMPLE_COUNT;
+       }
+
+       return std_scan_complete(di, devices);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
-       (void)sdi;
+       struct sr_usb_dev_inst *usb;
+       struct dev_context *devc;
+       struct sr_dev_driver *di;
+       struct drv_context *drvc;
+       int ret;
+
+       usb = sdi->conn;
+       devc = sdi->priv;
+       di = sdi->driver;
+       drvc = di->context;
 
-       /* TODO: get handle from sdi->conn and open it. */
+       ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
+       if (ret < 0)
+               return SR_ERR;
+
+       if (libusb_kernel_driver_active(usb->devhdl, PICKIT2_USB_INTERFACE) == 1) {
+               ret = libusb_detach_kernel_driver(usb->devhdl, PICKIT2_USB_INTERFACE);
+               if (ret < 0) {
+                       sr_err("Canot detach kernel driver: %s.",
+                               libusb_error_name(ret));
+                       return SR_ERR;
+               }
+               devc->detached_kernel_driver = TRUE;
+       }
+
+       ret = libusb_claim_interface(usb->devhdl, PICKIT2_USB_INTERFACE);
+       if (ret < 0) {
+               sr_err("Cannot claim interface: %s.", libusb_error_name(ret));
+               return SR_ERR;
+       }
 
        return SR_OK;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
-       (void)sdi;
+       struct sr_usb_dev_inst *usb;
+       struct dev_context *devc;
+       int ret;
 
-       /* TODO: get handle from sdi->conn and close it. */
+       usb = sdi->conn;
+       devc = sdi->priv;
+
+       if (!usb->devhdl)
+               return SR_OK;
+
+       ret = libusb_release_interface(usb->devhdl, PICKIT2_USB_INTERFACE);
+       if (ret) {
+               sr_err("Cannot release interface: %s.", libusb_error_name(ret));
+               return SR_ERR;
+       }
+
+       if (devc->detached_kernel_driver) {
+               ret = libusb_attach_kernel_driver(usb->devhdl, PICKIT2_USB_INTERFACE);
+               if (ret) {
+                       sr_err("Cannot attach kernel driver: %s.",
+                               libusb_error_name(ret));
+                       return SR_ERR;
+               }
+               devc->detached_kernel_driver = FALSE;
+       }
+
+       libusb_close(usb->devhdl);
+       sdi->conn = NULL;
 
        return SR_OK;
 }
@@ -60,82 +260,185 @@ 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)
 {
-       int ret;
+       struct dev_context *devc;
+       struct sr_usb_dev_inst *usb;
+       uint64_t rate, ratio;
 
-       (void)sdi;
-       (void)data;
+       devc = sdi ? sdi->priv : NULL;
+       (void)devc;
        (void)cg;
 
-       ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_CONN:
+               if (!sdi->conn)
+                       return SR_ERR_ARG;
+               usb = sdi->conn;
+               *data = g_variant_new_printf("%d.%d", usb->bus, usb->address);
+               return SR_OK;
+       case SR_CONF_SAMPLERATE:
+               rate = devc->samplerates[devc->curr_samplerate_idx];
+               *data = g_variant_new_uint64(rate);
+               return SR_OK;
+       case SR_CONF_LIMIT_SAMPLES:
+               return sr_sw_limits_config_get(&devc->sw_limits, key, data);
+       case SR_CONF_CAPTURE_RATIO:
+               ratio = devc->captureratios[devc->curr_captureratio_idx];
+               *data = g_variant_new_uint64(ratio);
+               return SR_OK;
        default:
                return SR_ERR_NA;
        }
-
-       return ret;
 }
 
 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;
+       int idx;
+
+       devc = sdi ? sdi->priv : NULL;
 
-       (void)sdi;
-       (void)data;
        (void)cg;
 
-       ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_SAMPLERATE:
+               if (!devc)
+                       return SR_ERR_ARG;
+               idx = std_u64_idx(data, devc->samplerates, devc->num_samplerates);
+               if (idx < 0)
+                       return SR_ERR_ARG;
+               devc->curr_samplerate_idx = idx;
+               return SR_OK;
+       case SR_CONF_CAPTURE_RATIO:
+               if (!devc)
+                       return SR_ERR_ARG;
+               idx = std_u64_idx(data, devc->captureratios, devc->num_captureratios);
+               if (idx >= 0)
+                       devc->curr_captureratio_idx = idx;
+               return SR_OK;
+       case SR_CONF_LIMIT_SAMPLES:
+               return sr_sw_limits_config_set(&devc->sw_limits, key, data);
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
-
-       return ret;
 }
 
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
+       struct dev_context *devc;
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       devc = sdi ? sdi->priv : NULL;
 
-       ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_SAMPLERATE:
+               if (!devc)
+                       return SR_ERR_NA;
+               *data = std_gvar_samplerates(devc->samplerates, devc->num_samplerates);
+               return SR_OK;
+       case SR_CONF_TRIGGER_MATCH:
+               *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
+               return SR_OK;
+       case SR_CONF_CAPTURE_RATIO:
+               *data = std_gvar_array_u64(ARRAY_AND_SIZE(captureratios));
+               return SR_OK;
        default:
                return SR_ERR_NA;
        }
-
-       return ret;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
-       /* TODO: configure hardware, reset acquisition state, set up
-        * callbacks and send header packet. */
+       struct dev_context *devc;
+       struct sr_trigger *trigger;
+       struct sr_trigger_stage *stage;
+       struct sr_trigger_match *match;
+       GSList *l;
+       size_t idx;
+       int ret;
 
-       (void)sdi;
+       devc = sdi->priv;
+
+       /*
+        * Query triggers, translate the more complex caller spec to
+        * "flat" internal variables, to simplify the construction of
+        * the SETUP packet elsewhere. This driver supports a single
+        * stage, with match conditions for one or multiple channels.
+        */
+       memset(&devc->triggers, 0, sizeof(devc->triggers));
+       trigger = sr_session_trigger_get(sdi->session);
+       if (trigger) {
+               if (g_slist_length(trigger->stages) > 1)
+                       return SR_ERR_NA;
+               stage = g_slist_nth_data(trigger->stages, 0);
+               if (!stage)
+                       return SR_ERR_ARG;
+               for (l = stage->matches; l; l = l->next) {
+                       match = l->data;
+                       if (!match->match)
+                               continue;
+                       if (!match->channel->enabled)
+                               continue;
+                       idx = match->channel->index;
+                       devc->triggers[idx] = match->match;
+               }
+               sr_dbg("acq start: trigger specs: %x/%x/%x",
+                       devc->triggers[0], devc->triggers[1],
+                       devc->triggers[2]);
+       }
+       devc->trigpos = trigger ? devc->curr_captureratio_idx : 0;
+
+       /* Have the SETUP packet sent, then poll for the status. */
+       devc->state = STATE_CONF;
+       ret = microchip_pickit2_setup_trigger(sdi);
+       if (ret) {
+               devc->state = STATE_IDLE;
+               return ret;
+       }
+       devc->state = STATE_WAIT;
+
+       std_session_send_df_header(sdi);
+       sr_session_source_add(sdi->session, -1, 0, 20,
+               microchip_pickit2_receive_data, (void *)sdi);
 
        return SR_OK;
 }
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       /* TODO: stop acquisition. */
+       struct dev_context *devc;
+
+       devc = sdi->priv;
+       if (devc->state < STATE_CONF)
+               return SR_OK;
+
+       /*
+        * Keep up the acquisition until either data becomes available
+        * (according to the previously configured trigger condition),
+        * or until the user cancels the acquisition by pressing the
+        * device's button. This is a firmware limitation which the
+        * vendor software "suffers from" as well.
+        */
+       if (devc->state == STATE_WAIT) {
+               sr_err("Cannot terminate by software, need either data trigger or cancel button.");
+               return SR_OK;
+       }
 
-       (void)sdi;
+       if (devc->state > STATE_CONF) {
+               std_session_send_df_end(sdi);
+       }
+       sr_session_source_remove(sdi->session, -1);
+       devc->state = STATE_IDLE;
 
        return SR_OK;
 }
 
 static struct sr_dev_driver microchip_pickit2_driver_info = {
        .name = "microchip-pickit2",
-       .longname = "Microchip PICkit2",
+       .longname = PICKIT2_VENDOR_NAME " " PICKIT2_PRODUCT_NAME,
        .api_version = 1,
        .init = std_init,
        .cleanup = std_cleanup,
index b25f49f5988edb9252920ccf3b4a4a8b0b3b2890..17cc7586ac4baa80255467674ce6c3b443013a0d 100644 (file)
  */
 
 #include <config.h>
+#include <string.h>
 #include "protocol.h"
 
+#define PICKIT2_PACKET_LENGTH  64
+#define PICKIT2_USB_ENDPOINT   1
+#define PICKIT2_USB_TIMEOUT    250
+
+#define PICKIT2_CMD_CHKSTAT    0xa2
+#define PICKIT2_CMD_CHKVOLT    0xa3
+#define PICKIT2_CMD_READ       0xac
+#define PICKIT2_CMD_PADCHAR    0xad
+#define PICKIT2_CMD_SETUP      0xb8
+#define PICKIT2_CMD_SETPOS     0xb9
+
+#define PICKIT2_SEL_BANK0      0x06
+#define PICKIT2_SEL_BANK1      0x07
+
+struct pickit2_cmd {
+       size_t length;
+       uint8_t raw[PICKIT2_PACKET_LENGTH];
+};
+
+static void pickit2_cmd_clear(struct pickit2_cmd *cmd)
+{
+
+       if (!cmd)
+               return;
+       memset(&cmd->raw[0], PICKIT2_CMD_PADCHAR, PICKIT2_PACKET_LENGTH);
+       cmd->length = 0;
+}
+
+static void pickit2_cmd_append(struct pickit2_cmd *cmd, uint8_t b)
+{
+
+       if (!cmd)
+               return;
+       if (cmd->length == PICKIT2_PACKET_LENGTH)
+               return;
+       cmd->raw[cmd->length++] = b;
+}
+
+static int pickit2_usb_send(const struct sr_dev_inst *sdi, struct pickit2_cmd *cmd)
+{
+       struct sr_usb_dev_inst *usb;
+       int ret, sent;
+       GString *text;
+
+       if (!cmd)
+               return SR_OK;
+       usb = sdi->conn;
+       if (!usb)
+               return SR_ERR_ARG;
+
+       text = sr_hexdump_new(&cmd->raw[0], cmd->length);
+       sr_dbg("usb sent: %s", text->str);
+       sr_hexdump_free(text);
+
+       ret = libusb_interrupt_transfer(usb->devhdl,
+               LIBUSB_ENDPOINT_OUT | PICKIT2_USB_ENDPOINT,
+               &cmd->raw[0], PICKIT2_PACKET_LENGTH,
+               &sent, PICKIT2_USB_TIMEOUT);
+       if (ret < 0) {
+               sr_err("USB transmit error: %s.", libusb_error_name(ret));
+               return SR_ERR_IO;
+       }
+       if (sent != PICKIT2_PACKET_LENGTH) {
+               sr_err("USB short send: %d/%d bytes.",
+                       sent, PICKIT2_PACKET_LENGTH);
+               return SR_ERR_IO;
+       }
+
+       return SR_OK;
+}
+
+static int pickit2_usb_recv(const struct sr_dev_inst *sdi, struct pickit2_cmd *cmd)
+{
+       struct sr_usb_dev_inst *usb;
+       int ret, rcvd;
+       GString *text;
+
+       if (!cmd)
+               return SR_ERR_ARG;
+       usb = sdi->conn;
+       if (!usb)
+               return SR_ERR_ARG;
+
+       ret = libusb_interrupt_transfer(usb->devhdl,
+               LIBUSB_ENDPOINT_IN | PICKIT2_USB_ENDPOINT,
+               &cmd->raw[0], PICKIT2_PACKET_LENGTH,
+               &rcvd, PICKIT2_USB_TIMEOUT);
+       if (ret < 0) {
+               if (ret == LIBUSB_ERROR_TIMEOUT)
+                       sr_dbg("USB receive error: %s.", libusb_error_name(ret));
+               else
+                       sr_err("USB receive error: %s.", libusb_error_name(ret));
+               return SR_ERR_IO;
+       }
+
+       text = sr_hexdump_new(&cmd->raw[0], rcvd);
+       sr_dbg("usb recv: %s", text->str);
+       sr_hexdump_free(text);
+
+       cmd->length = rcvd;
+       if (rcvd != PICKIT2_PACKET_LENGTH) {
+               sr_err("USB short recv: %d/%d bytes.",
+                       rcvd, PICKIT2_PACKET_LENGTH);
+               return SR_ERR_IO;
+       }
+
+       return SR_OK;
+}
+
+/* Send a request, (optionally) keep reading until response became available. */
+static int pickit2_usb_send_recv(const struct sr_dev_inst *sdi,
+       struct pickit2_cmd *send_cmd, struct pickit2_cmd *recv_cmd, int do_wait)
+{
+       int ret;
+
+       /* Send the command when one got specified. Ignore errors. */
+       if (send_cmd)
+               (void)pickit2_usb_send(sdi, send_cmd);
+
+       /*
+        * Try receiving data, always ignore errors. When requested by
+        * the caller then keep receiving until response data became
+        * available.
+        */
+       if (!recv_cmd)
+               return SR_OK;
+       do {
+               ret = pickit2_usb_recv(sdi, recv_cmd);
+               if (ret == SR_OK)
+                       return SR_OK;
+               if (!do_wait)
+                       return ret;
+       } while (1);
+       /* UNREACH */
+}
+
+SR_PRIV int microchip_pickit2_setup_trigger(const struct sr_dev_inst *sdi)
+{
+       static const uint8_t trigger_channel_masks[PICKIT2_CHANNEL_COUNT] = {
+               /* Bit positions for channels in trigger registers. */
+               0x04, 0x08, 0x10,
+       };
+       static const uint16_t captureratio_magics[] = {
+               /* TODO
+                * How to exactly calculate these magic 16bit values?
+                * They seem to neither match a percentage value nor a
+                * sample count (assuming 1 window holds 1K samples).
+                * As long as the formula is unknown, we are stuck with
+                * looking up magic values from a table of few pre-sets.
+                */
+               0x0000,                 /* unspecified ratio value */
+               0x03cc, 0x000a, 0x0248, /* 10%/50%/90% in the first window */
+               0x07b4, 0x0b9c, 0x0f84, /* 10% "plus 1/2/3 window widths" */
+       };
+
+       struct dev_context *devc;
+       uint8_t trig_en, trig_lvl, trig_edge, trig_rep, trig_div;
+       uint16_t trig_pos;
+       uint64_t rate;
+       size_t trig_pos_idx, ch_idx;
+       uint8_t ch_mask, ch_cond;
+       struct pickit2_cmd cmd;
+
+       devc = sdi->priv;
+
+       /* Translate user specs to internal setup values. */
+       trig_en = trig_lvl = trig_edge = 0;
+       for (ch_idx = 0; ch_idx < PICKIT2_CHANNEL_COUNT; ch_idx++) {
+               if (!devc->triggers[ch_idx])
+                       continue;
+               ch_mask = trigger_channel_masks[ch_idx];
+               ch_cond = devc->triggers[ch_idx];
+               trig_en |= ch_mask;
+               switch (ch_cond) {
+               case SR_TRIGGER_ONE:
+               case SR_TRIGGER_RISING:
+                       trig_lvl |= ch_mask;
+                       break;
+               }
+               switch (ch_cond) {
+               case SR_TRIGGER_FALLING:
+               case SR_TRIGGER_RISING:
+                       trig_edge |= ch_mask;
+                       break;
+               }
+       }
+       trig_rep = 1;
+       trig_rep = MIN(trig_rep, 255);
+       trig_rep = MAX(trig_rep, 1);
+       if (!trig_en)
+               trig_rep = 0;
+       rate = devc->samplerates[devc->curr_samplerate_idx];
+       rate = SR_MHZ(1) / rate - 1;
+       trig_div = rate & 0xff;
+       trig_pos_idx = devc->trigpos;
+       if (trig_pos_idx >= ARRAY_SIZE(captureratio_magics))
+               trig_pos_idx = 0;
+       trig_pos = captureratio_magics[trig_pos_idx];
+
+       /* Construct the SETUP packet. */
+       pickit2_cmd_clear(&cmd);
+       pickit2_cmd_append(&cmd, PICKIT2_CMD_SETUP);
+       pickit2_cmd_append(&cmd, 0x01);
+       pickit2_cmd_append(&cmd, trig_en);
+       pickit2_cmd_append(&cmd, trig_lvl);
+       pickit2_cmd_append(&cmd, trig_edge);
+       pickit2_cmd_append(&cmd, trig_rep);
+       pickit2_cmd_append(&cmd, trig_pos % 256);
+       pickit2_cmd_append(&cmd, trig_pos / 256);
+       pickit2_cmd_append(&cmd, trig_div);
+
+       /*
+        * Transmit the SETUP packet. Only send it out, poll for the
+        * response later. When a trigger is involved, the response may
+        * take considerable amounts of time to arrive. We want apps
+        * to remain responsive during that period of time.
+        */
+       (void)pickit2_usb_send_recv(sdi, &cmd, NULL, FALSE);
+
+       return SR_OK;
+}
+
+/* Read specified bank data at given offset into caller provided buffer. */
+static int pickit2_retrieve_bank(struct sr_dev_inst *sdi,
+       size_t bank_idx, size_t offset, uint8_t **buf, size_t *len)
+{
+       struct pickit2_cmd send_cmd, recv_cmd;
+       int ret;
+       size_t copy_iter, copy_len;
+
+       /* Construct and send the SETPOS packet. No response expected. */
+       pickit2_cmd_clear(&send_cmd);
+       pickit2_cmd_append(&send_cmd, PICKIT2_CMD_SETPOS);
+       pickit2_cmd_append(&send_cmd, offset & 0xff);
+       pickit2_cmd_append(&send_cmd, PICKIT2_SEL_BANK0 + bank_idx);
+       ret = pickit2_usb_send_recv(sdi, &send_cmd, NULL, FALSE);
+       if (ret != SR_OK)
+               return ret;
+       sr_dbg("read bank: pos set");
+
+       /* Run two READ cycles, get 2x 64 bytes => 128 bytes raw data. */
+       pickit2_cmd_clear(&send_cmd);
+       pickit2_cmd_append(&send_cmd, PICKIT2_CMD_READ);
+       copy_iter = 2;
+       while (copy_iter-- > 0) {
+               ret = pickit2_usb_send_recv(sdi, &send_cmd, &recv_cmd, TRUE);
+               if (ret != SR_OK)
+                       return ret;
+               copy_len = MIN(PICKIT2_PACKET_LENGTH, *len);
+               memcpy(*buf, &recv_cmd.raw[0], copy_len);
+               *buf += copy_len;
+               *len -= copy_len;
+       }
+
+       return SR_OK;
+}
+
+/* Read all of the (banked, raw) sample data after acquisition completed. */
+static int pickit2_retrieve_sample_data(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       uint8_t *rdpos;
+       size_t rdlen;
+       int ret;
+
+       devc = sdi->priv;
+       rdpos = &devc->samples_raw[0];
+       rdlen = sizeof(devc->samples_raw);
+
+       ret = pickit2_retrieve_bank(sdi, 0, 0x00, &rdpos, &rdlen);
+       if (ret)
+               return ret;
+       ret = pickit2_retrieve_bank(sdi, 0, 0x80, &rdpos, &rdlen);
+       if (ret)
+               return ret;
+       ret = pickit2_retrieve_bank(sdi, 1, 0x00, &rdpos, &rdlen);
+       if (ret)
+               return ret;
+       ret = pickit2_retrieve_bank(sdi, 1, 0x80, &rdpos, &rdlen);
+       if (ret)
+               return ret;
+
+       return SR_OK;
+}
+
+/* Send converted sample data to the sigrok session. */
+static int pickit2_submit_logic_data(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct {
+               uint8_t raw_mask, conv_mask;
+       } ch_map[PICKIT2_CHANNEL_COUNT] = {
+               { 0x04, 0x01, },
+               { 0x08, 0x02, },
+               { 0x01, 0x04, },
+       };
+       uint8_t *raw_buf, raw_byte, *conv_buf;
+       size_t raw_len, conv_len;
+       uint64_t limit;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_logic logic;
+
+       devc = sdi->priv;
+
+       /*
+        * TODO Manipulate (or create) the above channel mapping table.
+        * Remove disabled channels, create dense output format.
+        * Could:
+        * - Loop over the index, check the corresponding channel's
+        *   state, clear out the conv_mask part and shift down all
+        *   subsequent conv_mask parts.
+        */
+
+       /*
+        * Convert raw dump (two samples per byte, at odd positions) to
+        * internal sigrok format (one sample per byte, at increasing
+        * offsets which start at 0).
+        */
+#define handle_nibble(n) do { \
+       uint8_t conv_byte; \
+       size_t ch_idx; \
+       conv_byte = 0x00; \
+       for (ch_idx = 0; ch_idx < PICKIT2_CHANNEL_COUNT; ch_idx++) { \
+               if ((n) & ch_map[ch_idx].raw_mask) \
+                       conv_byte |= ch_map[ch_idx].conv_mask; \
+       } \
+       *conv_buf++ = conv_byte; \
+       conv_len++; \
+} while (0)
+
+       raw_len = sizeof(devc->samples_raw);
+       raw_buf = &devc->samples_raw[raw_len];
+       conv_buf = &devc->samples_conv[0];
+       conv_len = 0;
+       while (raw_len-- > 0) {
+               raw_byte = *(--raw_buf);
+               handle_nibble((raw_byte >> 0) & 0x0f);
+               handle_nibble((raw_byte >> 4) & 0x0f);
+       }
+
+       /* Submit a logic packet to the sigrok session. */
+       packet.type = SR_DF_LOGIC;
+       packet.payload = &logic;
+       logic.unitsize = sizeof(uint8_t);
+       logic.data = &devc->samples_conv[0];
+       logic.length = conv_len;
+       limit = devc->sw_limits.limit_samples;
+       if (limit && limit < logic.length)
+               logic.length = limit;
+       sr_session_send(sdi, &packet);
+
+       return SR_OK;
+}
+
+static gboolean pickit2_status_is_cancel(uint16_t status)
+{
+       /* "Button press" and "transfer timeout" translate to "cancelled". */
+       static const uint16_t status_cancel_mask = 0x4004;
+
+       sr_dbg("recv: status 0x%04x", status);
+       if ((status & status_cancel_mask) == status_cancel_mask)
+               return TRUE;
+       return FALSE;
+}
+
+/* Periodically invoked poll routine, checking for incoming receive data. */
 SR_PRIV int microchip_pickit2_receive_data(int fd, int revents, void *cb_data)
 {
-       const struct sr_dev_inst *sdi;
+       struct sr_dev_inst *sdi;
        struct dev_context *devc;
+       struct pickit2_cmd cmd;
+       int ret;
+       uint16_t status;
 
        (void)fd;
+       (void)revents;
 
-       if (!(sdi = cb_data))
+       sdi = cb_data;
+       if (!sdi)
                return TRUE;
-
-       if (!(devc = sdi->priv))
+       devc = sdi->priv;
+       if (!devc)
                return TRUE;
 
-       if (revents == G_IO_IN) {
-               /* TODO */
+       /* Waiting for the trigger condition? */
+       if (devc->state == STATE_WAIT) {
+               /* Keep waiting until status becomes available. */
+               ret = pickit2_usb_send_recv(sdi, NULL, &cmd, FALSE);
+               if (ret != SR_OK)
+                       return TRUE;
+               /* Check status flags for cancel requests. */
+               devc->state = STATE_DATA;
+               status = RL16(&cmd.raw[0]);
+               if (pickit2_status_is_cancel(status)) {
+                       sr_info("User cancelled acquisition.");
+                       sr_dev_acquisition_stop(sdi);
+                       return TRUE;
+               }
+               sr_dbg("recv: Data has become available.");
+               /* FALLTHROUGH */
        }
 
+       /*
+        * Retrieve acquired sample data (blocking, acquisition has
+        * completed and samples are few), and stop acquisition (have
+        * the poll routine unregistered).
+        */
+       ret = pickit2_retrieve_sample_data(sdi);
+       if (ret != SR_OK)
+               return ret;
+       ret = pickit2_submit_logic_data(sdi);
+       if (ret != SR_OK)
+               return ret;
+       sr_dev_acquisition_stop(sdi);
        return TRUE;
 }
index d531b52c49c624253c60aa0566f02ef43d649433..fcb72c5fe4c5266cfeafc9e7358a6ba4284f59ab 100644 (file)
 #ifndef LIBSIGROK_HARDWARE_MICROCHIP_PICKIT2_PROTOCOL_H
 #define LIBSIGROK_HARDWARE_MICROCHIP_PICKIT2_PROTOCOL_H
 
-#include <stdint.h>
 #include <glib.h>
+#include <stdint.h>
+#include <stdlib.h>
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "microchip-pickit2"
 
+#define PICKIT2_CHANNEL_COUNT  3
+#define PICKIT2_SAMPLE_COUNT   1024
+#define PICKIT2_SAMPLE_RAWLEN  (4 * 128)
+
+enum pickit_state {
+       STATE_IDLE,
+       STATE_CONF,
+       STATE_WAIT,
+       STATE_DATA,
+};
+
 struct dev_context {
+       enum pickit_state state;
+       const uint64_t *samplerates;
+       size_t num_samplerates;
+       size_t curr_samplerate_idx;
+       const uint64_t *captureratios;
+       size_t num_captureratios;
+       size_t curr_captureratio_idx;
+       struct sr_sw_limits sw_limits;
+       gboolean detached_kernel_driver;
+       int32_t triggers[PICKIT2_CHANNEL_COUNT];        /**!< see \ref SR_TRIGGER_ZERO et al */
+       size_t trigpos;
+       uint8_t samples_raw[PICKIT2_SAMPLE_RAWLEN];
+       uint8_t samples_conv[PICKIT2_SAMPLE_COUNT];
 };
 
+SR_PRIV int microchip_pickit2_setup_trigger(const struct sr_dev_inst *sdi);
 SR_PRIV int microchip_pickit2_receive_data(int fd, int revents, void *cb_data);
 
 #endif