X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fmicrochip-pickit2%2Fprotocol.c;h=4ee1e92bd263e4e2e955dae6621fc78cf120bbf7;hb=fe4bb7749271f6514359290f9606d3c2cf07b06a;hp=b25f49f5988edb9252920ccf3b4a4a8b0b3b2890;hpb=a5c0259c4ab9d3f7807e8d748551ae209ae69e4e;p=libsigrok.git diff --git a/src/hardware/microchip-pickit2/protocol.c b/src/hardware/microchip-pickit2/protocol.c index b25f49f5..4ee1e92b 100644 --- a/src/hardware/microchip-pickit2/protocol.c +++ b/src/hardware/microchip-pickit2/protocol.c @@ -18,24 +18,420 @@ */ #include +#include #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; }