/* 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 #include #include #include #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; }