629 lines
14 KiB
C
629 lines
14 KiB
C
/* MSPDebug - debugging tool for the eZ430
|
|
* Copyright (C) 2009, 2010 Daniel Beer
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#if !defined(__Windows__) || defined(__MINGW32__)
|
|
#include <usb.h>
|
|
#else
|
|
#include <lusb0_usb.h>
|
|
#endif
|
|
|
|
#include <time.h>
|
|
|
|
#include "ti3410.h"
|
|
#include "util.h"
|
|
#include "usbutil.h"
|
|
#include "output.h"
|
|
#include "ihex.h"
|
|
|
|
/************************************************************************
|
|
* Definitions taken from drivers/usb/serial/ti_usb_3410_5052.h in the
|
|
* Linux kernel (GPLv2+).
|
|
*/
|
|
|
|
/* Configuration ids */
|
|
#define TI_BOOT_CONFIG 1
|
|
#define TI_ACTIVE_CONFIG 2
|
|
|
|
/* Pipe transfer mode and timeout */
|
|
#define TI_PIPE_MODE_CONTINOUS 0x01
|
|
#define TI_PIPE_MODE_MASK 0x03
|
|
#define TI_PIPE_TIMEOUT_MASK 0x7C
|
|
#define TI_PIPE_TIMEOUT_ENABLE 0x80
|
|
|
|
/* Module identifiers */
|
|
#define TI_I2C_PORT 0x01
|
|
#define TI_IEEE1284_PORT 0x02
|
|
#define TI_UART1_PORT 0x03
|
|
#define TI_UART2_PORT 0x04
|
|
#define TI_RAM_PORT 0x05
|
|
|
|
/* Purge modes */
|
|
#define TI_PURGE_OUTPUT 0x00
|
|
#define TI_PURGE_INPUT 0x80
|
|
|
|
/* Commands */
|
|
#define TI_GET_VERSION 0x01
|
|
#define TI_GET_PORT_STATUS 0x02
|
|
#define TI_GET_PORT_DEV_INFO 0x03
|
|
#define TI_GET_CONFIG 0x04
|
|
#define TI_SET_CONFIG 0x05
|
|
#define TI_OPEN_PORT 0x06
|
|
#define TI_CLOSE_PORT 0x07
|
|
#define TI_START_PORT 0x08
|
|
#define TI_STOP_PORT 0x09
|
|
#define TI_TEST_PORT 0x0A
|
|
#define TI_PURGE_PORT 0x0B
|
|
#define TI_RESET_EXT_DEVICE 0x0C
|
|
#define TI_WRITE_DATA 0x80
|
|
#define TI_READ_DATA 0x81
|
|
#define TI_REQ_TYPE_CLASS 0x82
|
|
|
|
/* Bits per character */
|
|
#define TI_UART_5_DATA_BITS 0x00
|
|
#define TI_UART_6_DATA_BITS 0x01
|
|
#define TI_UART_7_DATA_BITS 0x02
|
|
#define TI_UART_8_DATA_BITS 0x03
|
|
|
|
/* Parity */
|
|
#define TI_UART_NO_PARITY 0x00
|
|
#define TI_UART_ODD_PARITY 0x01
|
|
#define TI_UART_EVEN_PARITY 0x02
|
|
#define TI_UART_MARK_PARITY 0x03
|
|
#define TI_UART_SPACE_PARITY 0x04
|
|
|
|
/* Stop bits */
|
|
#define TI_UART_1_STOP_BITS 0x00
|
|
#define TI_UART_1_5_STOP_BITS 0x01
|
|
#define TI_UART_2_STOP_BITS 0x02
|
|
|
|
/* Modem control */
|
|
#define TI_MCR_LOOP 0x04
|
|
#define TI_MCR_DTR 0x10
|
|
#define TI_MCR_RTS 0x20
|
|
|
|
/* Read/Write data */
|
|
#define TI_RW_DATA_ADDR_SFR 0x10
|
|
#define TI_RW_DATA_ADDR_IDATA 0x20
|
|
#define TI_RW_DATA_ADDR_XDATA 0x30
|
|
#define TI_RW_DATA_ADDR_CODE 0x40
|
|
#define TI_RW_DATA_ADDR_GPIO 0x50
|
|
#define TI_RW_DATA_ADDR_I2C 0x60
|
|
#define TI_RW_DATA_ADDR_FLASH 0x70
|
|
#define TI_RW_DATA_ADDR_DSP 0x80
|
|
|
|
#define TI_RW_DATA_UNSPECIFIED 0x00
|
|
#define TI_RW_DATA_BYTE 0x01
|
|
#define TI_RW_DATA_WORD 0x02
|
|
#define TI_RW_DATA_DOUBLE_WORD 0x04
|
|
|
|
#define TI_TRANSFER_TIMEOUT 2
|
|
#define TI_FIRMWARE_BUF_SIZE 16284
|
|
#define TI_DOWNLOAD_MAX_PACKET_SIZE 64
|
|
|
|
/************************************************************************/
|
|
|
|
struct ti3410_transport {
|
|
struct transport base;
|
|
struct usb_dev_handle *hnd;
|
|
};
|
|
|
|
#define USB_FET_VENDOR 0x0451
|
|
#define USB_FET_PRODUCT 0xf430
|
|
|
|
#define USB_FET_INTERFACE 0
|
|
#define USB_FET_IN_EP 0x81
|
|
#define USB_FET_OUT_EP 0x01
|
|
#define USB_FET_INT_EP 0x83
|
|
|
|
#define USB_FDL_INTERFACE 0
|
|
#define USB_FDL_OUT_EP 0x01
|
|
|
|
#define TIMEOUT 1000
|
|
#define READ_TIMEOUT 5000
|
|
|
|
static int open_device(struct ti3410_transport *tr,
|
|
struct usb_device *dev)
|
|
{
|
|
struct usb_dev_handle *hnd;
|
|
|
|
hnd = usb_open(dev);
|
|
if (!hnd) {
|
|
pr_error("ti3410: failed to open USB device");
|
|
return -1;
|
|
}
|
|
|
|
#if defined(__linux__)
|
|
if (usb_detach_kernel_driver_np(hnd, USB_FET_INTERFACE) < 0)
|
|
pr_error("ti3410: warning: can't "
|
|
"detach kernel driver");
|
|
#endif
|
|
|
|
/* This device has two configurations -- we need the one which
|
|
* has two bulk endpoints and a control.
|
|
*/
|
|
if (dev->config->bConfigurationValue == TI_BOOT_CONFIG) {
|
|
printc_dbg("TI3410 device is in boot config, "
|
|
"setting active\n");
|
|
|
|
if (usb_set_configuration(hnd, TI_ACTIVE_CONFIG) < 0) {
|
|
pr_error("ti3410: failed to set active config");
|
|
usb_close(hnd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (usb_claim_interface(hnd, USB_FET_INTERFACE) < 0) {
|
|
pr_error("ti3410: can't claim interface");
|
|
usb_close(hnd);
|
|
return -1;
|
|
}
|
|
|
|
tr->hnd = hnd;
|
|
return 0;
|
|
}
|
|
|
|
static int set_termios(struct ti3410_transport *tr)
|
|
{
|
|
static const uint8_t tios_data[10] = {
|
|
0x00, 0x02, /* 460800 bps */
|
|
0x60, 0x00, /* flags = ENABLE_MS_INTS | AUTO_START_DMA */
|
|
TI_UART_8_DATA_BITS,
|
|
TI_UART_NO_PARITY,
|
|
TI_UART_1_STOP_BITS,
|
|
0x00, /* cXon */
|
|
0x00, /* cXoff */
|
|
0x00 /* UART mode = RS232 */
|
|
};
|
|
|
|
if (usb_control_msg(tr->hnd,
|
|
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
TI_SET_CONFIG,
|
|
0,
|
|
TI_UART1_PORT, (char *)tios_data, sizeof(tios_data), TIMEOUT) < 0) {
|
|
pr_error("ti3410: TI_SET_CONFIG failed");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set_mcr(struct ti3410_transport *tr)
|
|
{
|
|
static const uint8_t wb_data[9] = {
|
|
TI_RW_DATA_ADDR_XDATA,
|
|
TI_RW_DATA_BYTE,
|
|
1, /* byte count */
|
|
0x00, 0x00, 0xff, 0xa4, /* base address */
|
|
TI_MCR_LOOP | TI_MCR_RTS | TI_MCR_DTR, /* mask */
|
|
TI_MCR_RTS | TI_MCR_DTR /* data */
|
|
};
|
|
|
|
if (usb_control_msg(tr->hnd,
|
|
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
TI_WRITE_DATA,
|
|
0,
|
|
TI_RAM_PORT, (char *)wb_data, sizeof(wb_data), TIMEOUT) < 0) {
|
|
pr_error("ti3410: TI_SET_CONFIG failed");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_open_start(struct ti3410_transport *tr)
|
|
{
|
|
if (set_termios(tr) < 0)
|
|
return -1;
|
|
|
|
if (set_mcr(tr) < 0)
|
|
return -1;
|
|
|
|
if (usb_control_msg(tr->hnd,
|
|
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
TI_OPEN_PORT,
|
|
TI_PIPE_MODE_CONTINOUS | TI_PIPE_TIMEOUT_ENABLE |
|
|
(TI_TRANSFER_TIMEOUT << 2),
|
|
TI_UART1_PORT, NULL, 0, TIMEOUT) < 0) {
|
|
pr_error("ti3410: TI_OPEN_PORT failed");
|
|
return -1;
|
|
}
|
|
|
|
if (usb_control_msg(tr->hnd,
|
|
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
TI_START_PORT,
|
|
0,
|
|
TI_UART1_PORT, NULL, 0, TIMEOUT) < 0) {
|
|
pr_error("ti3410: TI_START_PORT failed");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int interrupt_flush(struct ti3410_transport *tr)
|
|
{
|
|
uint8_t buf[2];
|
|
|
|
return usb_interrupt_read(tr->hnd, USB_FET_INT_EP,
|
|
(char *)buf, 2, TIMEOUT);
|
|
}
|
|
|
|
static int setup_port(struct ti3410_transport *tr)
|
|
{
|
|
interrupt_flush(tr);
|
|
|
|
if (do_open_start(tr) < 0)
|
|
return -1;
|
|
|
|
if (usb_control_msg(tr->hnd,
|
|
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
TI_PURGE_PORT,
|
|
TI_PURGE_INPUT,
|
|
TI_UART1_PORT, NULL, 0, TIMEOUT) < 0) {
|
|
pr_error("ti3410: TI_PURGE_PORT (input) failed");
|
|
return -1;
|
|
}
|
|
|
|
interrupt_flush(tr);
|
|
interrupt_flush(tr);
|
|
|
|
if (usb_control_msg(tr->hnd,
|
|
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
TI_PURGE_PORT,
|
|
TI_PURGE_OUTPUT,
|
|
TI_UART1_PORT, NULL, 0, TIMEOUT) < 0) {
|
|
pr_error("ti3410: TI_PURGE_PORT (output) failed");
|
|
return -1;
|
|
}
|
|
|
|
interrupt_flush(tr);
|
|
|
|
if (usb_clear_halt(tr->hnd, USB_FET_IN_EP) < 0 ||
|
|
usb_clear_halt(tr->hnd, USB_FET_OUT_EP) < 0) {
|
|
pr_error("ti3410: failed to clear halt status");
|
|
return -1;
|
|
}
|
|
|
|
if (do_open_start(tr) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void teardown_port(struct ti3410_transport *tr)
|
|
{
|
|
if (usb_control_msg(tr->hnd,
|
|
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
TI_CLOSE_PORT,
|
|
0,
|
|
TI_UART1_PORT, NULL, 0, TIMEOUT) < 0)
|
|
pr_error("ti3410: warning: TI_CLOSE_PORT failed");
|
|
}
|
|
|
|
static int ti3410_send(transport_t tr_base, const uint8_t *data, int len)
|
|
{
|
|
struct ti3410_transport *tr = (struct ti3410_transport *)tr_base;
|
|
int sent;
|
|
|
|
while (len) {
|
|
sent = usb_bulk_write(tr->hnd, USB_FET_OUT_EP,
|
|
(char *)data, len, TIMEOUT);
|
|
if (sent <= 0) {
|
|
pr_error("ti3410: can't send data");
|
|
return -1;
|
|
}
|
|
|
|
data += sent;
|
|
len -= sent;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ti3410_recv(transport_t tr_base, uint8_t *databuf, int max_len)
|
|
{
|
|
struct ti3410_transport *tr = (struct ti3410_transport *)tr_base;
|
|
time_t deadline = time(NULL) + READ_TIMEOUT / 1000;
|
|
int rlen;
|
|
|
|
while (time(NULL) < deadline) {
|
|
rlen = usb_bulk_read(tr->hnd, USB_FET_IN_EP, (char *)databuf,
|
|
max_len, READ_TIMEOUT);
|
|
|
|
if (rlen > 0)
|
|
return rlen;
|
|
|
|
if (rlen < 0) {
|
|
printc_err("ti3410: usb_bulk_read: %s\n",
|
|
usb_strerror());
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
printc_err("ti3410: read timeout\n");
|
|
return -1;
|
|
}
|
|
|
|
static void ti3410_destroy(transport_t tr_base)
|
|
{
|
|
struct ti3410_transport *tr = (struct ti3410_transport *)tr_base;
|
|
|
|
teardown_port(tr);
|
|
free(tr);
|
|
}
|
|
|
|
struct firmware {
|
|
uint8_t buf[TI_FIRMWARE_BUF_SIZE];
|
|
unsigned int size;
|
|
};
|
|
|
|
static FILE *find_firmware(void)
|
|
{
|
|
char path[256];
|
|
const char *env;
|
|
FILE *in;
|
|
|
|
printc_dbg("Searching for firmware for TI3410...\n");
|
|
|
|
env = getenv("MSPDEBUG_TI3410_FW");
|
|
if (env) {
|
|
snprintf(path, sizeof(path), "%s", env);
|
|
printc_dbg(" - checking %s\n", path);
|
|
in = fopen(path, "r");
|
|
if (in)
|
|
return in;
|
|
}
|
|
|
|
snprintf(path, sizeof(path), "%s",
|
|
LIB_DIR "/mspdebug/ti_3410.fw.ihex");
|
|
printc_dbg(" - checking %s\n", path);
|
|
in = fopen(path, "r");
|
|
if (in)
|
|
return in;
|
|
|
|
snprintf(path, sizeof(path), "%s", "ti_3410.fw.ihex");
|
|
printc_dbg(" - checking %s\n", path);
|
|
in = fopen(path, "r");
|
|
if (in)
|
|
return in;
|
|
|
|
printc_err("ti3410: unable to locate firmware\n");
|
|
return NULL;
|
|
}
|
|
|
|
static int do_extract(void *user_data, const struct binfile_chunk *ch)
|
|
{
|
|
struct firmware *f = (struct firmware *)user_data;
|
|
|
|
if (f->size != ch->addr) {
|
|
printc_err("ti3410: firmware gap at 0x%x (ends at 0x%0x)\n",
|
|
f->size, ch->addr);
|
|
return -1;
|
|
}
|
|
|
|
if (f->size + ch->len > sizeof(f->buf)) {
|
|
printc_err("ti3410: maximum firmware size exceeded\n");
|
|
return -1;
|
|
}
|
|
|
|
memcpy(f->buf + f->size, ch->data, ch->len);
|
|
f->size += ch->len;
|
|
return 0;
|
|
}
|
|
|
|
static int load_firmware(struct firmware *f)
|
|
{
|
|
FILE *in = find_firmware();
|
|
|
|
if (!in)
|
|
return -1;
|
|
|
|
if (!ihex_check(in)) {
|
|
printc_err("ti3410: not a valid IHEX file\n");
|
|
fclose(in);
|
|
return -1;
|
|
}
|
|
|
|
memset(f, 0, sizeof(*f));
|
|
if (ihex_extract(in, do_extract, f) < 0) {
|
|
printc_err("ti3410: failed to load firmware\n");
|
|
fclose(in);
|
|
return -1;
|
|
}
|
|
|
|
fclose(in);
|
|
return 0;
|
|
}
|
|
|
|
static void prepare_firmware(struct firmware *f)
|
|
{
|
|
uint8_t cksum = 0;
|
|
uint16_t real_size = f->size - 3;
|
|
int i;
|
|
|
|
for (i = 3; i < f->size; i++)
|
|
cksum += f->buf[i];
|
|
|
|
f->buf[0] = real_size & 0xff;
|
|
f->buf[1] = real_size >> 8;
|
|
f->buf[2] = cksum;
|
|
|
|
printc_dbg("Loaded %d byte firmware image (checksum = 0x%02x)\n",
|
|
f->size, cksum);
|
|
}
|
|
|
|
static int do_download(struct usb_device *dev, const struct firmware *f)
|
|
{
|
|
struct usb_dev_handle *hnd;
|
|
int offset = 0;
|
|
|
|
printc_dbg("Starting download...\n");
|
|
|
|
hnd = usb_open(dev);
|
|
if (!hnd) {
|
|
pr_error("ti3410: failed to open USB device");
|
|
return -1;
|
|
}
|
|
|
|
#if defined(__linux__)
|
|
if (usb_detach_kernel_driver_np(hnd, USB_FDL_INTERFACE) < 0)
|
|
pr_error("ti3410: warning: can't "
|
|
"detach kernel driver");
|
|
#endif
|
|
|
|
if (usb_claim_interface(hnd, USB_FDL_INTERFACE) < 0) {
|
|
pr_error("ti3410: can't claim interface");
|
|
usb_close(hnd);
|
|
return -1;
|
|
}
|
|
|
|
while (offset < f->size) {
|
|
int plen = f->size - offset;
|
|
int r;
|
|
|
|
if (plen > TI_DOWNLOAD_MAX_PACKET_SIZE)
|
|
plen = TI_DOWNLOAD_MAX_PACKET_SIZE;
|
|
|
|
r = usb_bulk_write(hnd, USB_FDL_OUT_EP,
|
|
(char *)f->buf + offset, plen, TIMEOUT);
|
|
if (r < 0) {
|
|
pr_error("ti3410: bulk write failed");
|
|
usb_close(hnd);
|
|
return -1;
|
|
}
|
|
|
|
offset += r;
|
|
}
|
|
|
|
delay_ms(100);
|
|
if (usb_reset(hnd) < 0)
|
|
pr_error("ti3410: warning: reset failed");
|
|
|
|
usb_close(hnd);
|
|
return 0;
|
|
}
|
|
|
|
static int download_firmware(struct usb_device *dev)
|
|
{
|
|
struct firmware frm;
|
|
|
|
if (load_firmware(&frm) < 0)
|
|
return -1;
|
|
|
|
prepare_firmware(&frm);
|
|
|
|
if (do_download(dev, &frm) < 0)
|
|
return -1;
|
|
|
|
printc_dbg("Waiting for TI3410 reset...\n");
|
|
delay_s(2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ti3410_flush(transport_t tr_base)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int ti3410_set_modem(transport_t tr_base, transport_modem_t state)
|
|
{
|
|
printc_err("ti3410: unsupported operation: set_modem\n");
|
|
return -1;
|
|
}
|
|
|
|
static const struct transport_class ti3410_transport = {
|
|
.destroy = ti3410_destroy,
|
|
.send = ti3410_send,
|
|
.recv = ti3410_recv,
|
|
.flush = ti3410_flush,
|
|
.set_modem = ti3410_set_modem
|
|
};
|
|
|
|
transport_t ti3410_open(const char *devpath, const char *requested_serial,
|
|
int has_vid_pid, uint16_t vid, uint16_t pid)
|
|
{
|
|
struct ti3410_transport *tr = malloc(sizeof(*tr));
|
|
struct usb_device *dev;
|
|
|
|
if (!tr) {
|
|
pr_error("ti3410: can't allocate memory");
|
|
return NULL;
|
|
}
|
|
|
|
tr->base.ops = &ti3410_transport;
|
|
|
|
usb_init();
|
|
usb_find_busses();
|
|
usb_find_devices();
|
|
|
|
if (devpath)
|
|
dev = usbutil_find_by_loc(devpath);
|
|
else
|
|
dev = usbutil_find_by_id(has_vid_pid ? vid : USB_FET_VENDOR,
|
|
has_vid_pid ? pid : USB_FET_PRODUCT,
|
|
requested_serial);
|
|
|
|
if (!dev) {
|
|
free(tr);
|
|
return NULL;
|
|
}
|
|
|
|
if (dev->descriptor.bNumConfigurations == 1) {
|
|
if (download_firmware(dev) < 0) {
|
|
printc_err("ti3410: firmware download failed\n");
|
|
free(tr);
|
|
return NULL;
|
|
}
|
|
|
|
usb_find_devices();
|
|
|
|
if (devpath)
|
|
dev = usbutil_find_by_loc(devpath);
|
|
else
|
|
dev = usbutil_find_by_id(USB_FET_VENDOR,
|
|
USB_FET_PRODUCT,
|
|
requested_serial);
|
|
|
|
if (!dev) {
|
|
free(tr);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (open_device(tr, dev) < 0) {
|
|
printc_err("ti3410: failed to open TI3410 device\n");
|
|
free(tr);
|
|
return NULL;
|
|
}
|
|
|
|
if (setup_port(tr) < 0) {
|
|
printc_err("ti3410: failed to set up port\n");
|
|
teardown_port(tr);
|
|
usb_close(tr->hnd);
|
|
free(tr);
|
|
return NULL;
|
|
}
|
|
|
|
return (transport_t)tr;
|
|
}
|