diff --git a/.gitignore b/.gitignore index dede4b8..12b1651 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.o mspdebug mspdebug.exe +inst/ +config.mk diff --git a/Makefile b/Makefile index 6e0cb2d..a5a62bb 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,7 @@ OBJ=\ transport/cp210x.o \ transport/cdc_acm.o \ transport/ftdi.o \ + transport/mehfet_xport.o \ transport/ti3410.o \ transport/comport.o \ $(BSLHID_OBJ) \ @@ -194,6 +195,8 @@ OBJ=\ drivers/jtdev_bus_pirate.o \ drivers/jtdev_gpio.o \ drivers/jtaglib.o \ + drivers/mehfet_proto.o \ + drivers/mehfet.o \ drivers/pif.o \ drivers/loadbsl.o \ drivers/loadbsl_fw.o \ diff --git a/drivers/mehfet.c b/drivers/mehfet.c new file mode 100644 index 0000000..9150511 --- /dev/null +++ b/drivers/mehfet.c @@ -0,0 +1,639 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2021 sys64738@disroot.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "util.h" +#include "output.h" +#include "ctrlc.h" +#include "jtaglib.h" +#include "mehfet_proto.h" +#include "mehfet_xport.h" + +#include "mehfet.h" + + +struct mehfet_device { + struct device base; + struct jtdev jtag; + transport_t trans; + enum mehfet_conn connstat; +}; + + +/*extern void __builtin_trap(void);*/ +#define NO_LL_JTAG_BP 0 +#define no_ll_jtag() \ + do { \ + printc_err("mehfet: %s: low-lewel JTAG function not implemented!\n", __func__); \ + p->failed = 1; \ + if (NO_LL_JTAG_BP) /*__builtin_trap()*/; \ + } while (0) \ + + +#define JTMF_GET_DEV(p) ((struct mehfet_device*)((size_t)(p) - \ + offsetof(struct mehfet_device, jtag))) \ + + +static int jtmf_open(struct jtdev* p, const char* device) +{ + printc_err("mehfet: %s should not get called.\n", __func__); + p->failed = 1; + return -1; +} +static void jtmf_close(struct jtdev* p) +{ + printc_err("mehfet: %s should not get called.\n", __func__); + p->failed = 1; +} +static void jtmf_power_on(struct jtdev* p) +{ + printc_err("mehfet: %s should not get called.\n", __func__); + p->failed = 1; +} +static void jtmf_power_off(struct jtdev* p) +{ + printc_err("mehfet: %s should not get called.\n", __func__); + p->failed = 1; +} +static void jtmf_connect(struct jtdev* p) +{ + printc_err("mehfet: %s should not get called.\n", __func__); + p->failed = 1; +} +static void jtmf_release(struct jtdev* p) +{ + printc_err("mehfet: %s should not get called.\n", __func__); + p->failed = 1; +} + +static void jtmf_tck(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } +static void jtmf_tms(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } +static void jtmf_tdi(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } +static void jtmf_rst(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } +static void jtmf_tst(struct jtdev* p, int _) { (void)_; no_ll_jtag(); } +static int jtmf_tdo_get(struct jtdev* p) { no_ll_jtag(); return 0; } + +static void jtmf_tclk(struct jtdev* p, int out) +{ + struct mehfet_device* d = JTMF_GET_DEV(p); + + int r = mehfet_cmd_tclk_edge(d->trans, out); + if (r < 0) p->failed = 1; +} +static int jtmf_tclk_get(struct jtdev* p) +{ + struct mehfet_device* d = JTMF_GET_DEV(p); + int ret = 1; + + enum mehfet_lines lines = 0; + int r = mehfet_cmd_get_old_lines(d->trans, &lines); + if (r < 0) { + p->failed = 1; + } else { + ret = (lines & mehfet_line_tclk) ? 1 : 0; + } + + return ret; +} +static void jtmf_tclk_strobe(struct jtdev* p, unsigned int count) +{ + struct mehfet_device* d = JTMF_GET_DEV(p); + + int r = mehfet_cmd_tclk_burst(d->trans, count); + if (r < 0) p->failed = 1; +} + +/* MehFET has no leds */ +static void jtmf_led_green(struct jtdev* p, int out) { (void)p; (void)out; } +static void jtmf_led_red (struct jtdev* p, int out) { (void)p; (void)out; } + +static uint8_t jtmf_ir_shift(struct jtdev *p, uint8_t ir) +{ + struct mehfet_device* d = JTMF_GET_DEV(p); + + uint8_t ret = 0; + int r = mehfet_cmd_irshift(d->trans, ir, &ret); + if (r < 0) p->failed = 1; + + return ret; +} +static uint8_t jtmf_dr_shift_8(struct jtdev *p, uint8_t dr) +{ + struct mehfet_device* d = JTMF_GET_DEV(p); + + uint8_t ret = 0; + int r = mehfet_cmd_drshift(d->trans, 8, &dr, &ret); + if (r < 0) p->failed = 1; + + return ret; +} +static uint16_t jtmf_dr_shift_16(struct jtdev *p, uint16_t dr) +{ + struct mehfet_device* d = JTMF_GET_DEV(p); + + uint8_t inbuf[2] = { dr & 0xff, (dr >> 8) & 0xff }; + uint8_t outbuf[2]; + int r = mehfet_cmd_drshift(d->trans, 16, inbuf, outbuf); + if (r < 0) p->failed = 1; + + return outbuf[0] | ((uint16_t)outbuf[1] << 8); +} +static void jtmf_tms_sequence(struct jtdev *p, int bits, unsigned int value) +{ + struct mehfet_device* d = JTMF_GET_DEV(p); + + enum mehfet_lines lines = 0; + int r = mehfet_cmd_get_old_lines(d->trans, &lines); + if (r < 0) { + p->failed = 1; + return; + } + + uint8_t dbuf[4] = { value & 0xff, (value >> 8) & 0xff, + (value >> 16) & 0xff, (value >> 24) & 0xff }; + r = mehfet_cmd_tms_seq(d->trans, lines & mehfet_line_tdi, bits, dbuf); + if (r < 0) p->failed = 1; +} +static void jtmf_init_dap(struct jtdev *p) +{ + struct mehfet_device* dev = JTMF_GET_DEV(p); + + enum mehfet_resettap_status stat = 0; + int r = mehfet_cmd_reset_tap(dev->trans, mehfet_rsttap_do_reset + | mehfet_rsttap_fuse_do, &stat); + + if (r < 0) p->failed = 1; +} + + +static const struct jtdev_func jtdev_func_mehfet = { + .jtdev_open = jtmf_open, + .jtdev_close = jtmf_close, + .jtdev_power_on = jtmf_power_on, + .jtdev_power_off = jtmf_power_off, + .jtdev_connect = jtmf_connect, + .jtdev_release = jtmf_release, + .jtdev_tck = jtmf_tck, + .jtdev_tms = jtmf_tms, + .jtdev_tdi = jtmf_tdi, + .jtdev_rst = jtmf_rst, + .jtdev_tst = jtmf_tst, + .jtdev_tdo_get = jtmf_tdo_get, + .jtdev_tclk = jtmf_tclk, + .jtdev_tclk_get = jtmf_tclk_get, + .jtdev_tclk_strobe = jtmf_tclk_strobe, + .jtdev_led_green = jtmf_led_green, + .jtdev_led_red = jtmf_led_red, + + .jtdev_ir_shift = jtmf_ir_shift, + .jtdev_dr_shift_8 = jtmf_dr_shift_8, + .jtdev_dr_shift_16 = jtmf_dr_shift_16, + .jtdev_tms_sequence= jtmf_tms_sequence, + .jtdev_init_dap = jtmf_init_dap +}; + + +/*---------------------------------------------------------------------------*/ + +// TODO: these five are kinda copied from pif.c, should be deduplicated + +static int read_words(device_t dev_base, const struct chipinfo_memory *m, + address_t addr, address_t len, uint8_t *data) +{ +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: read_words: addr=0x%04x, len=0x%x\n", addr, len); +#endif + + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + struct jtdev *p = &dev->jtag; + + for (unsigned int index = 0; index < len; index += 2) { + unsigned int word = jtag_read_mem(p, 16, addr+index); + data[index ] = word & 0x00ff; + data[index+1] = (word >> 8) & 0x00ff; + } + + return p->failed ? -1 : len; +} + +static int write_ram_word(struct jtdev *p, address_t addr, uint16_t value) +{ + jtag_write_mem(p, 16, addr, value); + + return p->failed ? -1 : 0; +} + +static int write_flash_block(struct jtdev *p, address_t addr, + address_t len, const uint8_t *data) +{ + uint16_t* word = malloc( len / 2 * sizeof(*word) ); + if (!word) { + pr_error("mehfet: failed to allocate memory"); + return -1; + } + + for (unsigned int i = 0; i < len/2; i++) { + word[i]=data[2*i] + (((uint16_t)data[2*i+1]) << 8); + } + jtag_write_flash(p, addr, len/2, word); + + free(word); + + return p->failed ? -1 : 0; +} + +/* Write a word-aligned block to any kind of memory. + * returns the number of bytes written or -1 on failure + */ +static int write_words(device_t dev_base, const struct chipinfo_memory *m, + address_t addr, address_t len, const uint8_t *data) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + struct jtdev *p = &dev->jtag; + int r; + + if (m->type != CHIPINFO_MEMTYPE_FLASH) { +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: write_words: addr=0x%04x, len=0x%x data=0x%04x\n", + addr, len, r16le(data)); + if (len != 2) { + printc_dbg("mehfet: WARN: write_words: len != 2! but 0x%04x\n", len); + __builtin_trap(); + } +#endif + len = 2; + r = write_ram_word(p, addr, r16le(data)); + } else { +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: write_flash_block: addr=0x%04x, len=0x%x\n", addr, len); +#endif + r = write_flash_block(p, addr, len, data); + } + + if (r < 0) { + printc_err("mehfet: write_words at address 0x%x failed\n", addr); + return -1; + } + + return len; +} + +/*---------------------------------------------------------------------------*/ + +static int mehfet_readmem(device_t dev_base, address_t addr, + uint8_t *mem, address_t len) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + dev->jtag.failed = 0; + return readmem(dev_base, addr, mem, len, read_words); +} + +static int mehfet_writemem(device_t dev_base, address_t addr, + const uint8_t *mem, address_t len) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + dev->jtag.failed = 0; + return writemem(dev_base, addr, mem, len, write_words, read_words); +} + +static int mehfet_setregs(device_t dev_base, const address_t *regs) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + + dev->jtag.failed = 0; + +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: set regs\n"); +#endif + for (int i = 0; i < DEVICE_NUM_REGS; i++) { +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: [%d] = 0x%04x\n", i, regs[i]); +#endif + jtag_write_reg(&dev->jtag, i, regs[i]); + } + return dev->jtag.failed ? -1 : 0; +} + +static int mehfet_getregs(device_t dev_base, address_t *regs) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + + dev->jtag.failed = 0; + +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: get regs\n"); +#endif + for (int i = 0; i < DEVICE_NUM_REGS; i++) { + regs[i] = jtag_read_reg(&dev->jtag, i); +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: [%d] = 0x%04x\n", i, regs[i]); +#endif + } + + return dev->jtag.failed ? -1 : 0; +} + +static int mehfet_ctl(device_t dev_base, device_ctl_t type) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + + dev->jtag.failed = 0; + + switch (type) { + case DEVICE_CTL_RESET: + /* perform soft reset */ +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: soft reset (PUC)\n"); +#endif + jtag_execute_puc(&dev->jtag); + break; + + case DEVICE_CTL_RUN: +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: set breakpoints\n"); +#endif + /* transfer changed breakpoints to device */ + if (jtag_refresh_bps("mehfet", &dev->base, &dev->jtag) < 0) return -1; + +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: run @ current PC\n"); +#endif + /* start program execution at current PC */ + jtag_release_device(&dev->jtag, 0xffff); + break; + + case DEVICE_CTL_HALT: +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: halt\n"); +#endif + /* take device under JTAG control */ + jtag_get_device(&dev->jtag); + break; + + case DEVICE_CTL_STEP: +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: single-step\n"); +#endif + /* execute next instruction at current PC */ + jtag_single_step(&dev->jtag); + break; + + default: + printc_err("mehfet: unsupported operation %d\n", type); + return -1; + } + + return dev->jtag.failed ? -1 : 0; +} + +static device_status_t mehfet_poll(device_t dev_base) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + + if (delay_ms(100) < 0 || ctrlc_check()) + return DEVICE_STATUS_INTR; + + int r = jtag_cpu_state(&dev->jtag); + +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: cpu state: %d\n", r); +#endif + + if (r == 1) return DEVICE_STATUS_HALTED; + + return DEVICE_STATUS_RUNNING; +} + +static int mehfet_erase(device_t dev_base, device_erase_type_t type, + address_t addr) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + + dev->jtag.failed = 0; + + switch (type) { + case DEVICE_ERASE_MAIN: + jtag_erase_flash(&dev->jtag, JTAG_ERASE_MAIN, addr); + break; + case DEVICE_ERASE_ALL: + jtag_erase_flash(&dev->jtag, JTAG_ERASE_MASS, addr); + break; + case DEVICE_ERASE_SEGMENT: + jtag_erase_flash(&dev->jtag, JTAG_ERASE_SGMT, addr); + break; + default: return -1; + } + +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: erase flash %d at %04x: %s\n", type, addr, dev->jtag.failed ? "failed" : "succeeded"); +#endif + + return dev->jtag.failed ? -1 : 0; +} + +static int mehfet_getconfigfuses(device_t dev_base) +{ + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + + int r = jtag_get_config_fuses(&dev->jtag); + +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: get_config_fuses: %d\n", r); +#endif + + return r; +} + +/*---------------------------------------------------------------------------*/ + +static int check_dev_ok(struct mehfet_device* dev, const struct device_args* args, + enum mehfet_conn* useconn) { + transport_t t = dev->trans; + struct mehfet_info info; + int r; + + r = mehfet_cmd_info(t, &info); + if (r < 0) return r; + + printc_dbg("mehfet: MehFET %s\n", info.devicename); + free(info.devicename); + + if (info.proto_version < MEHFET_PROTO_VER_MIN_SUPPORTED) { + printc_err("mehfet: device has protocol version %04x, " + "need at least %04x\n", info.proto_version, + MEHFET_PROTO_VER_MIN_SUPPORTED); + return -1; + } + if (info.proto_version > MEHFET_PROTO_VER) { + printc_err("mehfet: device has newer protocol version %04x " + "supporting at most %04x\n", info.proto_version, + MEHFET_PROTO_VER); + return -1; + } + + if (args->flags & DEVICE_FLAG_JTAG) { + if (!(info.caps & (mehfet_cap_jtag_noentry | mehfet_cap_jtag_entryseq))) { + printc_err("mehfet: Cannot do JTAG, device doesn't have the capability\n"); + return -1; + } + + // devices that don't need one will probably work when given an entry + // sequence. no good way of doing a proper autodetect sadly + if (info.caps & mehfet_cap_jtag_entryseq) + *useconn = mehfet_conn_jtag_entryseq; + else + *useconn = mehfet_conn_jtag_noentry; + } else { + if (!(info.caps & mehfet_cap_sbw_entryseq)) { + printc_err("mehfet: Cannot do Spy-Bi-Wire, device doesn't have the capability\n"); + return -1; + } + *useconn = mehfet_conn_sbw_entryseq; + } + + if (args->flags & DEVICE_FLAG_FORCE_RESET) + *useconn |= mehfet_conn_nrstmask; + + mehfet_transport_set_buf_size(t, (int)info.packet_buf_size); + + return 0; +} + +static int init_device(struct mehfet_device* dev) { + printc_dbg("Starting JTAG\n"); + + unsigned int jtagid = jtag_init(&dev->jtag); + if (dev->jtag.failed) return -1; + + printc("JTAG ID: 0x%02x\n", jtagid); + if (jtagid != 0x89 && jtagid != 0x91) { + printc_err("mehfet: unexpected JTAG ID: 0x%02x\n", jtagid); + jtag_release_device(&dev->jtag, 0xfffe); + return -1; + } + + // JTAG fuse check has been performed, so we can now switch to a + // higher-speed physical transport suitable for ~350 kHz TCLK strobes used + // in (and required for) flash programming + int r = mehfet_cmd_set_clkspeed(dev->trans, true); + if (r < 0) { + jtag_release_device(&dev->jtag, 0xfffe); + return -1; + } + + return 0; +} + + +static void mehfet_destroy(device_t dev_base) { + struct mehfet_device* dev = (struct mehfet_device*)dev_base; + + if (!dev) return; + +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: releasing device & disconnecting\n"); +#endif + + if (dev->trans) { + jtag_release_device(&dev->jtag, 0xfffe); // 0xfffe=reset address : POR + + mehfet_cmd_disconnect(dev->trans); + + dev->trans->ops->destroy(dev->trans); + dev->trans = NULL; + } + + free(dev); +} + +static device_t mehfet_open(const struct device_args* args) { + struct mehfet_device* dev; + int r; + + if ((args->flags & DEVICE_FLAG_TTY)) { + printc_err("mehfet: this driver does not support TTY access\n"); + return NULL; + } + + dev = calloc(1, sizeof(*dev)); + if (!dev) { + printc_err("mehfet: malloc: %s\n", last_error()); + return NULL; + } + + dev->base.type = &device_mehfet; + dev->base.max_breakpoints = 2; // TODO + dev->base.need_probe = 1; // TODO + dev->jtag.f = &jtdev_func_mehfet; + + // the MehFET currently doesn't have a designated PID, so we can't really + // default to one. + dev->trans = mehfet_transport_open(args->path, + ((args->flags & DEVICE_FLAG_HAS_VID_PID) ? &args->vid : NULL), + ((args->flags & DEVICE_FLAG_HAS_VID_PID) ? &args->pid : NULL), + args->requested_serial); + if (!dev->trans) goto FAIL; + + enum mehfet_conn useconn; + r = check_dev_ok(dev, args, &useconn); + if (r < 0) goto FAIL; + + r = mehfet_cmd_connect(dev->trans, useconn); + if (r < 0) goto FAIL; + + r = mehfet_cmd_status(dev->trans, &dev->connstat); + if (r < 0) goto FAIL; + + if (dev->connstat != (useconn & mehfet_conn_typemask)) { + printc_err("mehfet: could not create connection to device\n"); + goto FAIL; + } + + r = init_device(dev); + if (r < 0) goto FAIL; + +#ifdef DEBUG_MEHFET_DRIVER + printc_dbg("mehfet: device opened\n"); +#endif + + return (device_t)dev; + +FAIL: + mehfet_destroy((device_t)dev); + return NULL; +} + +const struct device_class device_mehfet = { + .name = "mehfet", + .help = "MehFET USB JTAG/SBW device", + .open = mehfet_open, + .destroy = mehfet_destroy, + .readmem = mehfet_readmem, + .writemem = mehfet_writemem, + .getregs = mehfet_getregs, + .setregs = mehfet_setregs, + .ctl = mehfet_ctl, + .poll = mehfet_poll, + .erase = mehfet_erase, + .getconfigfuses = mehfet_getconfigfuses +}; + diff --git a/drivers/mehfet.h b/drivers/mehfet.h new file mode 100644 index 0000000..b704183 --- /dev/null +++ b/drivers/mehfet.h @@ -0,0 +1,27 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2021, sys64738@disroot.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MEHFET_H_ +#define MEHFET_H_ + +#include "device.h" + +extern const struct device_class device_mehfet; + +#endif + diff --git a/drivers/mehfet_proto.c b/drivers/mehfet_proto.c new file mode 100644 index 0000000..b820e09 --- /dev/null +++ b/drivers/mehfet_proto.c @@ -0,0 +1,473 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2021 sys64738@disroot.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "mehfet_xport.h" +#include "output.h" +#include "util.h" + +#include "mehfet_proto.h" + +// TODO: replace '64' by transport buf size + +int mehfet_cmd_info(transport_t t, struct mehfet_info* info) { + if (!info) return -1; + + uint8_t buf[64]; + int r; + + r = mehfet_send_raw(t, mehfet_info, 0, NULL); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("Info", stat, len, buf); + if (r < 0) return r; + + if (len < 8) { + printc_err("mehfet: Info response too short: %d < 8\n", len); + return -1; + } + + info->caps = buf[0] | ((uint32_t)buf[1] << 8) + | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24); + info->proto_version = buf[4] | ((uint16_t)buf[5] << 8); + info->packet_buf_size = 1u << buf[6]; + // buf[7]: reserved + + if (len > 9) { // len includes the null terminator + char* str = calloc(len - 8, 1); + memcpy(str, &buf[8], len - 8); + str[len - 9] = 0; + + info->devicename = str; + } + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: Info(): '%s' caps=0x%x, protover=0x%04x, pktsize=0x%x\n", + info->devicename ? info->devicename : "", + info->caps, info->proto_version, info->packet_buf_size); +#endif + + return 0; +} +int mehfet_cmd_status(transport_t t, enum mehfet_conn* ret) { + if (!ret) return -1; + + uint8_t buf[64]; + int r; + + r = mehfet_send_raw(t, mehfet_status, 0, NULL); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("Status", stat, len, buf); + if (r < 0) return r; + + if (len != 1) { + printc_err("mehfet: Status response wrong length: %d != 1\n", len); + return -1; + } + + *ret = buf[0]; + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: Status(): %hhu\n", *ret); +#endif + + return 0; +} +int mehfet_cmd_connect(transport_t t, enum mehfet_conn conn) { + uint8_t buf[64]; + int r; + + buf[0] = conn; + r = mehfet_send_raw(t, mehfet_connect, 1, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("Connect", stat, len, buf); + if (r < 0) return r; + + if (len != 0) { + printc_err("mehfet: Connect response wrong length: %d != 0\n", len); + return -1; + } + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: Connect(0x%x)\n", conn); +#endif + + return 0; +} +int mehfet_cmd_disconnect(transport_t t) { + uint8_t buf[64]; + int r; + + r = mehfet_send_raw(t, mehfet_disconnect, 0, NULL); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("Disconnect", stat, len, buf); + if (r < 0) return r; + + if (len != 0) { + printc_err("mehfet: Disconnect response wrong length: %d != 0\n", len); + return -1; + } + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: Disconnect()\n"); +#endif + + return 0; +} +int mehfet_cmd_delay(transport_t t, bool us, bool exact, uint32_t time) { + if (time >= (1u << 30)) return -1; // too large + + uint8_t buf[64]; + int r; + + for (size_t i = 0; i < 4; ++i) buf[i] = (time >> (i*8)) & 0xff; + if (us) buf[3] |= 0x40; + if (exact) buf[3] |= 0x80; + + r = mehfet_send_raw(t, mehfet_delay, 4, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("Delay", stat, len, buf); + if (r < 0) return r; + + if (len != 0) { + printc_err("mehfet: Delay response wrong length: %d != 0\n", len); + return -1; + } + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: Delay(us=%c exact=%c time=%u)\n", + us?'t':'f', exact?'t':'f', time); +#endif + + return 0; +} +int mehfet_cmd_set_clkspeed(transport_t t, bool fast) { + uint8_t buf[64]; + int r; + + buf[0] = fast ? 0xff : 0; + r = mehfet_send_raw(t, mehfet_set_clkspeed, 1, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("SetClkSpeed", stat, len, buf); + if (r < 0) return r; + + if (len != 0) { + printc_err("mehfet: SetClkSpeed response wrong length: %d != 0\n", len); + return -1; + } + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: SetClkSpeed(%s)\n", fast?"fast":"slow"); +#endif + + return 0; +} +int mehfet_cmd_get_old_lines(transport_t t, enum mehfet_lines* lines) { + if (!lines) return -1; + + uint8_t buf[64]; + int r; + + r = mehfet_send_raw(t, mehfet_get_old_lines, 0, NULL); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("GetOldLines", stat, len, buf); + if (r < 0) return r; + + if (len != 1) { + printc_err("mehfet: GetOldLines response wrong length: %d != 1\n", len); + return -1; + } + + *lines = buf[0]; + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: GetOldLines(): 0x%x\n", *lines); +#endif + + return 0; +} + +int mehfet_cmd_tdio_seq(transport_t t, uint32_t nbits, bool tms, const uint8_t* tdi, uint8_t* tdo) { + if (!tdi || !tdo || !nbits) return -1; + + int r; + uint32_t nbytes = (nbits + 7) >> 3; + uint8_t buf[(nbytes + 5) < 64 ? 64 : (nbytes + 5)]; // need min for recv + + for (size_t i = 0; i < 4; ++i) buf[i] = (nbits >> (i*8)) & 0xff; + buf[4] = tms ? 0xff : 0; + memcpy(&buf[5], tdi, nbytes); + + r = mehfet_send_raw(t, mehfet_tdio_seq, nbytes + 5, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; // is a max len + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("TdioSequence", stat, len, buf); + if (r < 0) return r; + + if (len != nbytes) { + printc_err("mehfet: TdioSequence response wrong length: %d != %d\n", + len, nbytes); + return -1; + } + + memcpy(tdo, buf, nbytes); + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: TdioSequence(%u, TMS=%c):\n", nbits, tms?'1':'0'); + debug_hexdump("\tTDI", tdi, nbytes); + debug_hexdump("\tTDO", tdo, nbytes); +#endif + + return 0; +} +int mehfet_cmd_tms_seq(transport_t t, uint32_t nbits, bool tdi, const uint8_t* tms) { + if (!tms || !nbits) return -1; + + int r; + uint32_t nbytes = (nbits + 7) >> 3; + uint8_t buf[(nbytes + 5) < 64 ? 64 : (nbytes + 5)]; // need min for recv + + for (size_t i = 0; i < 4; ++i) buf[i] = (nbits >> (i*8)) & 0xff; + buf[4] = tdi ? 0xff : 0; + memcpy(&buf[5], tms, nbytes); + + r = mehfet_send_raw(t, mehfet_tdio_seq, sizeof buf, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; // is a max len + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("TmsSequence", stat, len, buf); + if (r < 0) return r; + + if (len != 0) { + printc_err("mehfet: TmsSequence response wrong length: %d != 0\n", len); + return -1; + } + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: TmsSequence(%u, TDI=%c):\n", nbits, tdi?'1':'0'); + debug_hexdump("\tTMS", tms, nbytes); +#endif + + return 0; +} +int mehfet_cmd_tclk_edge(transport_t t, bool newtclk) { + uint8_t buf[64]; + int r; + + buf[0] = newtclk ? 0xff : 0; + r = mehfet_send_raw(t, mehfet_tclk_edge, 1, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("TclkEdge", stat, len, buf); + if (r < 0) return r; + + if (len != 0) { + printc_err("mehfet: TclkEdge response wrong length: %d != 0\n", len); + return -1; + } + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: TclkEdge(TCLK=%c)\n", newtclk?'H':'L'); +#endif + + return 0; +} +int mehfet_cmd_tclk_burst(transport_t t, uint32_t ncyc) { + uint8_t buf[64]; + int r; + + for (size_t i = 0; i < 4; ++i) buf[i] = (ncyc >> (i*8)) & 0xff; + r = mehfet_send_raw(t, mehfet_tclk_burst, 4, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("TclkBurst", stat, len, buf); + if (r < 0) return r; + + if (len != 0) { + printc_err("mehfet: TclkBurst response wrong length: %d != 0\n", len); + return -1; + } + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: TclkBurst(ncyc=%u)\n", ncyc); +#endif + + return 0; +} + +int mehfet_cmd_reset_tap(transport_t t, enum mehfet_resettap_flags flags, + enum mehfet_resettap_status* tstat) { + if (!tstat) return -1; + + uint8_t buf[64]; + int r; + + buf[0] = flags; + r = mehfet_send_raw(t, mehfet_reset_tap, 1, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("ResetTAP", stat, len, buf); + if (r < 0) return r; + + if (len != 1) { + printc_err("mehfet: ResetTAP response wrong length: %d != 1\n", len); + return -1; + } + + *tstat = buf[0]; + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: ResetTAP(flags=0x%x) = 0x%x\n", flags, *tstat); +#endif + + return 0; +} + +static uint8_t bitswap_nyb(uint8_t in) { + return ((in >> 3) & 1) | ((in >> 1) & 2) | ((in << 1) & 4) | ((in << 3) & 8); +} +static uint8_t bitswap(uint8_t in) { + return (bitswap_nyb(in&0xf) << 4) | bitswap_nyb(in>>4); +} + +int mehfet_cmd_irshift(transport_t t, uint8_t newir, uint8_t* oldir) { + if (!oldir) return -1; + + // NOTE: jtaglib.c uses bitswapped IR values, while MehFET uses values from + // the SLAU320 PDF, so we need to perform a bitswap here + newir = bitswap(newir); + + uint8_t buf[64]; + int r; + + buf[0] = newir; + r = mehfet_send_raw(t, mehfet_irshift, 1, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("IRshift", stat, len, buf); + if (r < 0) return r; + + if (len != 1) { + printc_err("mehfet: IRshift response wrong length: %d != 1\n", len); + return -1; + } + + *oldir = buf[0]; + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: IRshift(new=0x%02x) = 0x%02x\n", newir, *oldir); +#endif + + return 0; +} +int mehfet_cmd_drshift(transport_t t, uint32_t nbits, const uint8_t* newdr, uint8_t* olddr) { + if (!newdr || !olddr) return -1; + + uint32_t nbytes = (nbits + 7) >> 3; + uint8_t buf[(nbytes + 4) < 64 ? 64 : (nbytes + 4)]; + int r; + + for (size_t i = 0; i < 4; ++i) buf[i] = (nbits >> (i * 8)) & 0xff; + memcpy(&buf[4], newdr, nbytes); + r = mehfet_send_raw(t, mehfet_drshift, nbytes + 4, buf); + if (r < 0) return r; + + uint8_t stat = 0; + int len = sizeof buf; + r = mehfet_recv_raw(t, &stat, &len, buf); + if (r < 0) return r; + r = mehfet_err_on_stat("DRshift", stat, len, buf); + if (r < 0) return r; + + if (len != (int)nbytes) { + printc_err("mehfet: DRshift response wrong length: %d != %u\n", len, nbytes); + return -1; + } + + memcpy(olddr, buf, nbytes); + +#ifdef DEBUG_MEHFET_PROTO_DRIVER + printc_dbg("mehfet: DRshift(nbits=%u):\n", nbits); + debug_hexdump("\tin ", newdr, nbytes); + debug_hexdump("\tout", olddr, nbytes); +#endif + + return 0; +} + diff --git a/drivers/mehfet_proto.h b/drivers/mehfet_proto.h new file mode 100644 index 0000000..11ba195 --- /dev/null +++ b/drivers/mehfet_proto.h @@ -0,0 +1,108 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2021 sys64738@disroot.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MEHFET_PROTO_H_ +#define MEHFET_PROTO_H_ + +#include "transport.h" + +#define MEHFET_PROTO_VER 0x0001 +#define MEHFET_PROTO_VER_MIN_SUPPORTED 0x0001 + +enum mehfet_cmd { + mehfet_info = 0x01, + mehfet_status = 0x02, + mehfet_connect = 0x03, + mehfet_disconnect = 0x04, + mehfet_delay = 0x05, + mehfet_set_clkspeed = 0x06, + mehfet_get_old_lines = 0x07, + mehfet_tdio_seq = 0x08, + mehfet_tms_seq = 0x09, + mehfet_tclk_edge = 0x0a, + mehfet_tclk_burst = 0x0b, + mehfet_reset_tap = 0x0c, + mehfet_irshift = 0x0d, + mehfet_drshift = 0x0e, +}; + +enum mehfet_caps { + mehfet_cap_jtag_noentry = 1<<0, + mehfet_cap_jtag_entryseq = 1<<1, + mehfet_cap_sbw_entryseq = 1<<2, + + mehfet_cap_has_reset_tap = 1<< 8, + mehfet_cap_has_irshift = 1<< 9, + mehfet_cap_has_drshift = 1<<10, + mehfet_cap_has_loop = 1<<11, +}; + +enum mehfet_conn { + mehfet_conn_none = 0, + mehfet_conn_auto = 0, + + mehfet_conn_jtag_noentry = 1, + mehfet_conn_jtag_entryseq = 2, + mehfet_conn_sbw_entryseq = 3, + + mehfet_conn_typemask = 0x7f, + mehfet_conn_nrstmask = 0x80 +}; + +enum mehfet_lines { + mehfet_line_tclk = 1<<0, + mehfet_line_tms = 1<<1, + mehfet_line_tdi = 1<<2 +}; + +enum mehfet_resettap_flags { + mehfet_rsttap_do_reset = 1<<0, // reset TAP to run-test/idle state + mehfet_rsttap_fuse_do = 1<<1, // perform fuse check procedure (TMS pulses) on target + mehfet_rsttap_fuse_read = 1<<2, // check whether the JTAG fuse has been blown +}; +enum mehfet_resettap_status { + mehfet_rsttap_fuse_blown = 0x80 +}; + +struct mehfet_info { + char* devicename; + enum mehfet_caps caps; + uint32_t packet_buf_size; + uint16_t proto_version; +}; + + +int mehfet_cmd_info(transport_t t, struct mehfet_info* info); +int mehfet_cmd_status(transport_t t, enum mehfet_conn* stat); +int mehfet_cmd_connect(transport_t t, enum mehfet_conn conn); +int mehfet_cmd_disconnect(transport_t t); +int mehfet_cmd_delay(transport_t t, bool us, bool exact, uint32_t time); +int mehfet_cmd_set_clkspeed(transport_t t, bool fast); +int mehfet_cmd_get_old_lines(transport_t t, enum mehfet_lines* lines); + +int mehfet_cmd_tdio_seq(transport_t t, uint32_t nbits, bool tms, const uint8_t* tdi, uint8_t* tdo); +int mehfet_cmd_tms_seq(transport_t t, uint32_t nbits, bool tdi, const uint8_t* tms); +int mehfet_cmd_tclk_edge(transport_t t, bool newtclk); +int mehfet_cmd_tclk_burst(transport_t t, uint32_t ncyc); + +int mehfet_cmd_reset_tap(transport_t t, enum mehfet_resettap_flags flags, enum mehfet_resettap_status* stat); +int mehfet_cmd_irshift(transport_t t, uint8_t newir, uint8_t* oldir); +int mehfet_cmd_drshift(transport_t t, uint32_t nbits, const uint8_t* newdr, uint8_t* olddr); + +#endif + diff --git a/mspdebug.man b/mspdebug.man index c75e2bd..5cc0812 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -234,6 +234,13 @@ There are reports of this operation causing an erase of info A in some devices. Use at your own risk. .IP "\fBbus-pirate\fR" Raw JTAG using Bus Pirate devices. +.IP "\fBmehfet\fR" +Connect to a MehFET USB-based debugging protocol-capable device. It can +support both JTAG and Spy-Bi-Wire. For now, only 16-bit CPUs (that is, +non-430X or Xv2) are suppored, as with the \fBpif\fR, \fBgpio\fR, +\fBbus-pirate\fR and \fBgoodfet\fR rivers. + +For documentation on the USB protocol, see the \fBSEE ALSO\fR section. .SH COMMANDS MSPDebug can accept commands either through an interactive prompt, or non-interactively when specified on the command line. The supported @@ -830,6 +837,9 @@ Firmware image for the TI3410 USB interface chip. This file is only required for raw USB access to FET430UIF or eZ430 devices. .SH SEE ALSO \fBnm\fR(1), \fBgdb\fR(1), \fBobjcopy\fR(1) + +\fBmehfet\fR protocol documentation can be found at the following URL: +https://git.lain.faith/sys64738/DragonProbe/wiki/MehFET-USB-protocol .SH BUGS If you find any bugs, you should report them to the author at dlbeer@gmail.com. It would help if you could include a transcript diff --git a/transport/mehfet_xport.c b/transport/mehfet_xport.c new file mode 100644 index 0000000..42f650a --- /dev/null +++ b/transport/mehfet_xport.c @@ -0,0 +1,407 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2021 sys64738@disroot.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "mehfet_xport.h" +#include "util.h" +#include "usbutil.h" +#include "output.h" + +struct mehfet_transport { + struct transport base; + struct usb_dev_handle *handle; + int epin, epout; + int buf_size; +}; + + +#define TIMEOUT_S 30 +#define REQ_TIMEOUT_MS 100 + + +static int open_device(struct mehfet_transport *tr, struct usb_device *dev) +{ +#ifdef __linux__ + int driver; + char drv_name[128]; +#endif + + // first, find the right interface (and associated endpoints) of the USB device + + int config = 0, itf = 0; + bool has = false; + + for (config = 0; config < dev->descriptor.bNumConfigurations; ++config) { + struct usb_config_descriptor* cd = &dev->config[config]; + + for (itf = 0; itf < cd->bNumInterfaces; ++itf) { + struct usb_interface_descriptor* id = &cd->interface[itf].altsetting[0]; + + if (id->bInterfaceClass == USB_CLASS_VENDOR_SPEC + && id->bInterfaceSubClass == '4' + && id->bInterfaceProtocol == '3') { + // here I'd like to check for the "MehFET" substring in the + // interface's iInterface string, but I can't really figure out + // how to do that, so I'll just assume this is enough checking. + + if (id->bNumEndpoints != 2) continue; // this should be 2 + for (size_t i = 0; i < id->bNumEndpoints; ++i) { + struct usb_endpoint_descriptor* ed = &id->endpoint[i]; + + if ((ed->bmAttributes & USB_ENDPOINT_TYPE_MASK) + != USB_ENDPOINT_TYPE_BULK) + break; + + if (ed->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + tr->epin = ed->bEndpointAddress; // input endpoint + else + tr->epout = ed->bEndpointAddress; // output endpoint + } + + if (tr->epin != 0 && tr->epout != 0) { + has = true; + goto break_outer; + } + } + } + } +break_outer: + + if (!has) { + printc_err("mehfet transport: USB device %s has no MehFET interface.\n", + dev->filename); + return -1; + } + + printc_dbg("mehfet transport: trying to open %s\n", dev->filename); + tr->handle = usb_open(dev); + if (!tr->handle) { + printc_err("mehfet transport: can't open device: %s\n", + usb_strerror()); + return -1; + } + +#ifdef __linux__ + driver = usb_get_driver_np(tr->handle, itf, drv_name, sizeof(drv_name)); + if (driver >= 0) { + printc_dbg("Detaching kernel driver \"%s\"\n", drv_name); + if (usb_detach_kernel_driver_np(tr->handle, itf) < 0) + printc_err("warning: mehfet transport: can't detach " + "kernel driver: %s\n", usb_strerror()); + } +#endif + +#ifdef __Windows__ + if (usb_set_configuration(tr->handle, config) < 0) { + printc_err("mehfet transport: can't set configuration: %s\n", + usb_strerror()); + usb_close(tr->handle); + return -1; + } +#endif + + if (usb_claim_interface(tr->handle, itf) < 0) { + printc_err("mehfet transport: can't claim interface: %s\n", + usb_strerror()); + usb_close(tr->handle); + return -1; + } + + return 0; +} + +static void tr_destroy(transport_t tr_base) +{ + struct mehfet_transport *tr = (struct mehfet_transport *)tr_base; + + usb_close(tr->handle); + free(tr); +} + +static int tr_recv(transport_t tr_base, uint8_t *databuf, int max_len) +{ + struct mehfet_transport *tr = (struct mehfet_transport *)tr_base; + time_t deadline = time(NULL) + TIMEOUT_S; + char tmpbuf[tr->buf_size]; + + if (max_len > tr->buf_size) + max_len = tr->buf_size; + + while (time(NULL) < deadline) { + int r = usb_bulk_read(tr->handle, tr->epin, + tmpbuf, max_len, + TIMEOUT_S * 1000); + + if (r <= 0) { + printc_err("mehfet transport: usb_bulk_read: %s\n", + usb_strerror()); + asm volatile("int3"); + return -1; + } + + memcpy(databuf, tmpbuf, r); +#ifdef DEBUG_MEHFET_TRANSPORT + printc_dbg("mehfet transport: tr_recv: flags = %02x %02x\n", + tmpbuf[0], tmpbuf[1]); + debug_hexdump("mehfet transport: tr_recv", databuf, r); +#endif + return r; + } + + printc_err("mehfet transport: timed out while receiving data\n"); + return -1; +} + +static int tr_send(transport_t tr_base, const uint8_t *databuf, int len) +{ + struct mehfet_transport *tr = (struct mehfet_transport *)tr_base; + +#ifdef DEBUG_MEHFET + debug_hexdump("mehfet transport: tr_send", databuf, len); +#endif + while (len) { + int r = usb_bulk_write(tr->handle, tr->epout, + (char *)databuf, len, + TIMEOUT_S * 1000); + + if (r <= 0) { + printc_err("mehfet transport: usb_bulk_write: %s\n", + usb_strerror()); + return -1; + } + + databuf += r; + len -= r; + } + + return 0; +} + +static int tr_flush(transport_t tr_base) +{ + (void)tr_base; + return 0; +} + +static int tr_set_modem(transport_t tr_base, transport_modem_t state) +{ + (void)tr_base; (void)state; + return 0; +} + +static const struct transport_class mehfet_class = { + .destroy = tr_destroy, + .send = tr_send, + .recv = tr_recv, + .flush = tr_flush, + .set_modem = tr_set_modem +}; + +transport_t mehfet_transport_open(const char *devpath, + const uint16_t* vendor, const uint16_t* product, + const char *requested_serial) +{ + struct mehfet_transport *tr = malloc(sizeof(*tr)); + struct usb_device *dev = NULL; + + if (!tr) { + pr_error("mehfet transport: can't allocate memory"); + return NULL; + } + + tr->base.ops = &mehfet_class; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + if (devpath) + dev = usbutil_find_by_loc(devpath); + else if (vendor && product) + dev = usbutil_find_by_id(*vendor, *product, requested_serial); + + if (!dev) { + printc_err("mehfet: no USB device found.%s\n", + vendor ? "" : " (Did you forget to specify a VID:PID?)"); + free(tr); + return NULL; + } + + tr->buf_size = 64; // initial conservative value, will get updated later + + if (open_device(tr, dev) < 0) { + printc_err("mehfet: failed to open device\n"); + free(tr); + return NULL; + } + + return &tr->base; +} + + +void mehfet_transport_set_buf_size(transport_t tr_base, int buf_size) +{ + struct mehfet_transport *tr = (struct mehfet_transport *)tr_base; + + tr->buf_size = buf_size; +} +int mehfet_transport_get_buf_size(transport_t tr_base) +{ + struct mehfet_transport *tr = (struct mehfet_transport *)tr_base; + + return tr->buf_size; +} + + + +int mehfet_send_raw(transport_t xport, uint8_t cmd, int datalen, const void* data) { + if (datalen < 0) return -1; + if (data && !datalen) return -1; + if (!data && datalen) return -1; + + uint8_t buf[1+4+datalen]; + + buf[0] = cmd; + int i, len2; + for (i = 0, len2 = datalen; i < 4 && len2; ++i, len2 >>= 7) { + buf[0] |= 0x80; // command has payload data + + if (i == 3) { + buf[i+1] = len2; + } else { + buf[i+1] = len2 & 0x7f; + if (len2 >> 7) buf[i+1] |= 0x80; + } + } + + if (data && datalen) memcpy(&buf[i+1], data, datalen); + + return xport->ops->send(xport, buf, datalen + 1 + i); +} + +int mehfet_recv_raw(transport_t xport, uint8_t* stat, int* datalen, void* data) { + struct mehfet_transport *tr = (struct mehfet_transport *)xport; + uint8_t rawbuf[tr->buf_size]; + + int nmax = 5, ndata = 0, nfetch; + if (datalen) { + ndata = *datalen; + nmax = ndata + 5; + } + nfetch = nmax; + if (nmax > tr->buf_size) { + nmax = tr->buf_size; + /*printc_err("mehfet transport: bug: asking for more data (%d) than " + "in the device buffer size (%d)\n", nmax, tr->buf_size); + return -1;*/ + } + + int r = xport->ops->recv(xport, rawbuf, nfetch); + if (r < 0) return r; + if (r < 1) { + printc_err("mehfet transport: no status byte received\n"); + return -1; + } + assert(r <= nfetch); + + uint8_t statv = rawbuf[0]; + + uint32_t reallen = 0; + uint8_t lastbyte = statv; + int i; + for (i = 0; i < 4 && (lastbyte & 0x80) /*&& i + 1 < r*/; ++i) { + if (r < i + 2) { + printc_err("mehfet transport: not enough lenght bytes received (%d)\n", r); + return -1; + } + + lastbyte = rawbuf[i + 1]; + + uint8_t mask = (i == 3) ? 0xff : 0x7f; + reallen |= (lastbyte & mask) << (i * 7); + } + + if ((int)reallen > nmax && data) { + printc_err("mehfet transport: too much data returned (%d vs %d)\n", + (int)reallen, nmax); + return -1; + } + + int nrecvdata = r - 1 - i; + assert(nrecvdata >= 0); + + if (nrecvdata && data) memcpy(data, &rawbuf[i+1], nrecvdata); + // now we can use rawbuf for other purposes + + int off = r; + int ntodo = (int)reallen - nrecvdata; + while (ntodo > 0) { // more data bytes following in this logical packet + int thisblock = tr->buf_size; + if (thisblock > ntodo) thisblock = ntodo; + + r = xport->ops->recv(xport, rawbuf, thisblock); + if (r < 0) return r; + assert(r < thisblock); + + memcpy(&((char*)data)[off], rawbuf, thisblock); + ntodo -= r; + off += r; + } + + if (stat) *stat = statv & 0x7f; + if (datalen) *datalen = (int)reallen; + + return (int)reallen; +} + +int mehfet_err_on_stat(const char* pre, uint8_t stat, int datalen, const void* data) { + if (stat == mehfet_ok) return 0; + + const char* d = (const char*)data; + switch (stat) { + case mehfet_badargs: + printc_err("mehfet: %s: %s\n", pre, + datalen ? d : "bad argument sent to command"); + break; + case mehfet_nocaps: + printc_err("mehfet: %s: %s\n", pre, + datalen ? d : "device doesn't have the command capability"); + break; + case mehfet_badstate: + printc_err("mehfet: %s: %s\n", pre, + datalen ? d : "device in wrong state to execute command"); + break; + case mehfet_invalidcmd: + printc_err("mehfet: %s: %s\n", pre, + datalen ? d : "invalid command"); + break; + case mehfet_error: + printc_err("mehfet: %s: %s\n", pre, datalen ? d : "unspecified error"); + break; + default: + printc_err("mehfet: %s: unknown error %hhu\n", pre, stat); + break; + } + + return -1; +} diff --git a/transport/mehfet_xport.h b/transport/mehfet_xport.h new file mode 100644 index 0000000..e04e93f --- /dev/null +++ b/transport/mehfet_xport.h @@ -0,0 +1,52 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2021 sys64738@disroot.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MEHFET_TRANSPORT_H_ +#define MEHFET_TRANSPORT_H_ + +#include "transport.h" + +enum mehfet_status { + mehfet_ok = 0x00, + + mehfet_badargs = 0x7b, + mehfet_nocaps = 0x7c, + mehfet_badstate = 0x7d, + mehfet_invalidcmd = 0x7e, + mehfet_error = 0x7f +}; + + +/* Search the USB bus for the first MehFET device and initialize it. + * If successful, return a transport object. Otherwise, return NULL. + * + * A particular USB device or serial number may be specified. + */ +transport_t mehfet_transport_open(const char *usb_device, + const uint16_t* vendor, const uint16_t* product, + const char *requested_serial); + +int mehfet_transport_get_buf_size(transport_t xport); +void mehfet_transport_set_buf_size(transport_t xport, int buf_size); + +int mehfet_send_raw(transport_t xport, uint8_t cmd, int datalen, const void* data); +int mehfet_recv_raw(transport_t xport, uint8_t* stat, int* datalen, void* data); + +int mehfet_err_on_stat(const char* pre, uint8_t stat, int datalen, const void* data); + +#endif diff --git a/ui/main.c b/ui/main.c index 01c1738..073c848 100644 --- a/ui/main.c +++ b/ui/main.c @@ -58,6 +58,7 @@ #include "fet3.h" #include "rom_bsl.h" #include "chipinfo.h" +#include "mehfet.h" #ifdef __CYGWIN__ #include @@ -92,7 +93,8 @@ static const struct device_class *const driver_table[] = { &device_loadbsl, &device_ezfet, &device_rom_bsl, - &device_bp + &device_bp, + &device_mehfet }; static const char *version_text =