396 lines
10 KiB
C
396 lines
10 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 <usb.h>
|
||
|
|
||
|
#include "ti3410.h"
|
||
|
#include "util.h"
|
||
|
#include "usbutil.h"
|
||
|
#include "output.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
|
||
|
|
||
|
/************************************************************************/
|
||
|
|
||
|
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 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;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
int rlen;
|
||
|
|
||
|
rlen = usb_bulk_read(tr->hnd, USB_FET_IN_EP, (char *)databuf,
|
||
|
max_len, READ_TIMEOUT);
|
||
|
|
||
|
if (rlen < 0) {
|
||
|
pr_error("ti3410: can't receive data");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return rlen;
|
||
|
}
|
||
|
|
||
|
static void ti3410_destroy(transport_t tr_base)
|
||
|
{
|
||
|
struct ti3410_transport *tr = (struct ti3410_transport *)tr_base;
|
||
|
|
||
|
teardown_port(tr);
|
||
|
free(tr);
|
||
|
}
|
||
|
|
||
|
transport_t ti3410_open(const char *devpath, const char *requested_serial)
|
||
|
{
|
||
|
struct ti3410_transport *tr = malloc(sizeof(*tr));
|
||
|
struct usb_device *dev;
|
||
|
|
||
|
if (!tr) {
|
||
|
pr_error("ti3410: can't allocate memory");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
tr->base.destroy = ti3410_destroy;
|
||
|
tr->base.send = ti3410_send;
|
||
|
tr->base.recv = ti3410_recv;
|
||
|
|
||
|
usb_init();
|
||
|
usb_find_busses();
|
||
|
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");
|
||
|
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;
|
||
|
}
|