mehfet: add driver and transport

This commit is contained in:
Triss 2021-10-09 23:59:01 +02:00
parent ab76a0ef3c
commit 30af3e9483
10 changed files with 1724 additions and 1 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
*.o
mspdebug
mspdebug.exe
inst/
config.mk

View File

@ -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 \

639
drivers/mehfet.c Normal file
View File

@ -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 <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#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
};

27
drivers/mehfet.h Normal file
View File

@ -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

473
drivers/mehfet_proto.c Normal file
View File

@ -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 <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#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 : "<none>",
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;
}

108
drivers/mehfet_proto.h Normal file
View File

@ -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

View File

@ -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

407
transport/mehfet_xport.c Normal file
View File

@ -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 <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#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;
}

52
transport/mehfet_xport.h Normal file
View File

@ -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

View File

@ -58,6 +58,7 @@
#include "fet3.h"
#include "rom_bsl.h"
#include "chipinfo.h"
#include "mehfet.h"
#ifdef __CYGWIN__
#include <sys/cygwin.h>
@ -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 =