diff --git a/examples/download.sh b/examples/download.sh new file mode 100755 index 0000000..9f0d06b --- /dev/null +++ b/examples/download.sh @@ -0,0 +1,15 @@ +#!/bin/bash -e + +DEVS=$(lsusb|grep -E '(2a19|16c0|04b4|1d50|fb9a|1443)' |sed 's/:.*//;s/Bus //;s/Device //;s/ /\//') + +if [ -z "$1" ]; then + echo "$0: usage: $0 " + exit 1; +fi + +for dev in $DEVS;do + echo "Downloading $1 to $dev" + /sbin/fxload -D /dev/bus/usb/$dev -t fx2lp -I $1 +done + +exit 0 diff --git a/examples/loopback_ftdi/Makefile b/examples/loopback_ftdi/Makefile new file mode 100644 index 0000000..500ab7c --- /dev/null +++ b/examples/loopback_ftdi/Makefile @@ -0,0 +1,12 @@ +FX2LIBDIR=../../ +BASENAME = ftdi_main +SOURCES=ftdi_main.c ftdi_conf.c +PID=0x1004 +CODE_SIZE = --code-size 0x3000 +XRAM_LOC = --xram-loc 0x3200 +XRAM_SIZE = --xram-size 0x700 +INT2JT =-Wl"-b INT2JT=0x3100" +include $(FX2LIBDIR)lib/fx2.mk +include $(FX2LIBDIR)lib/fx2-cdesc.mk +fx2_download: + ../download.sh build/$(BASENAME).ihx diff --git a/examples/loopback_ftdi/descriptors.c b/examples/loopback_ftdi/descriptors.c new file mode 100644 index 0000000..139db2b --- /dev/null +++ b/examples/loopback_ftdi/descriptors.c @@ -0,0 +1,272 @@ +/* + * The descriptors below setup an FTDI device with a VID + * of 0x0403, and PID of 0x6010.In this particular case + * endpoint 1 is used to transfer data in and out of the device + * since it can be easily accessed by the CPU and is not used + * for other purposes(Typically used for transferring small amounts + * of data which is what we need) + * lsusb -v will show: + * For an explanation of fields look at + * http://www.keil.com/pack/doc/mw/USB/html/_u_s_b__endpoint__descriptor.html + * vbDevice Descriptor: + * bLength 18 + * bDescriptorType 1 + * bcdUSB 2.00 + * bDeviceClass 255 Vendor Specific Class + * bDeviceSubClass 255 Vendor Specific Subclass + * bDeviceProtocol 255 Vendor Specific Protocol + * bMaxPacketSize0 64 + * idVendor 0x0403 Future Technology Devices International, Ltd + * idProduct 0x6010 FT2232C Dual USB-UART/FIFO IC + * bcdDevice 0.01 + * iManufacturer 1 + * iProduct 2 + * iSerial 0 + * bNumConfigurations 1 + * Configuration Descriptor: + * bLength 9 + * bDescriptorType 2 + * wTotalLength 32 + * bNumInterfaces 1 + * bConfigurationValue 1 + * iConfiguration 0 + * bmAttributes 0x80 + * (Bus Powered) + * MaxPower 100mA + * Interface Descriptor: + * bLength 9 + * bDescriptorType 4 + * bInterfaceNumber 0 + * bAlternateSetting 0 + * bNumEndpoints 2 + * bInterfaceClass 255 Vendor Specific Class + * bInterfaceSubClass 255 Vendor Specific Subclass + * bInterfaceProtocol 255 Vendor Specific Protocol + * iInterface 3 + * Endpoint Descriptor: + * bLength 7 + * bDescriptorType 5 + * bEndpointAddress 0x01 EP 1 OUT + * bmAttributes 2 + * Transfer Type Bulk + * Synch Type None + * Usage Type Data + * wMaxPacketSize 0x0200 1x 512 bytes + * bInterval 0 + * Endpoint Descriptor: + * bLength 7 + * bDescriptorType 5 + * bEndpointAddress 0x81 EP 1 IN + * bmAttributes 2 + * Transfer Type Bulk + * Synch Type None + * Usage Type Data + * wMaxPacketSize 0x0200 1x 512 bytes + * bInterval 0 + */ +#include "descriptors.h" + +__code __at(0x3e00) struct usb_descriptors code_descriptors = +{ + .device = + { + /*Size of Descriptor in Bytes*/ + .bLength = USB_DT_DEVICE_SIZE, + /*Constant Device Descriptor (0x01)*/ + .bDescriptorType = USB_DT_DEVICE, + /*USB Specification Number which device complies too.*/ + .bcdUSB = USB_BCD_V20, + /*Class Code (Assigned by USB Org) + * If equal to Zero, each interface specifies it’s own class code + * If equal to 0xFF, the class code is vendor specified.*/ + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + /*Subclass Code (Assigned by USB Org)*/ + .bDeviceSubClass = USB_SUBCLASS_VENDOR_SPEC, + /*Protocol Code (Assigned by USB Org)*/ + .bDeviceProtocol = 0xff, + /*Maximum Packet Size for Zero Endpoint. Valid Sizes are 8, 16, 32, 64*/ + .bMaxPacketSize0 = 64, + /*Vendor ID*/ + .idVendor = 0x0403, + /*Product ID*/ + .idProduct = 0x6010, + /*Device Release Number*/ + .bcdDevice = 0x0001, + /*Index of Manufacturer String Descriptor*/ + .iManufacturer = USB_STRING_INDEX(0), + /*Index of Product String Descriptor*/ + .iProduct = USB_STRING_INDEX(1), + /*Index of Serial Number String Descriptor*/ + .iSerialNumber = USB_STRING_INDEX_NONE, + /*Number of Possible Configurations*/ + .bNumConfigurations = 1, + }, + .qualifier = + { + /*Size of this descriptor in bytes. */ + .bLength = USB_DT_DEVICE_QUALIFIER_SIZE, + /*Device Qualifier Descriptor Type = 6. */ + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + /*USB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210h). + * This field identifies the release of the USB Specification with which the device + * and its descriptors are compliant. + * At least V2.00 is required to use this descriptor.*/ + .bcdUSB = USB_BCD_V20, + /*Class code (assigned by the USB-IF). + * If this field is set to FFh, the device class is vendor specific.*/ + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + /*Subclass code (assigned by the USB-IF).*/ + .bDeviceSubClass = USB_SUBCLASS_VENDOR_SPEC, + /*Protocol code (assigned by the USB-IF).Set to FFh, + * the device uses a vendor specific protocol on a device basis.*/ + .bDeviceProtocol = 0xff, + /*Maximum packet size for other speed.*/ + .bMaxPacketSize0 = 64, + /*Number of other-speed configurations.*/ + .bNumConfigurations = 1, + /*Reserved for future use, must be zero.*/ + .bRESERVED = 0, + }, + .highspeed = + { + .config = + { + /*Size of this descriptor in bytes.*/ + .bLength = USB_DT_CONFIG_SIZE, + /*Configuration Descriptor Type = 2.*/ + .bDescriptorType = USB_DT_CONFIG, + /*Total length of data returned for this configuration. + * Includes the combined length of all descriptors + *(configuration, interface, endpoint, and class or vendor specific) + * returned for this configuration.*/ + .wTotalLength = sizeof(descriptors.highspeed), + /*Number of interfaces supported by this configuration.*/ + .bNumInterfaces = 1, + /*Value to select this configuration with SetConfiguration().*/ + .bConfigurationValue = 1, + /*Index of string descriptor describing this configuration.*/ + .iConfiguration = 0, + /*Configuration characteristics + * D7: Reserved (must be set to one for historical reasons) + * D6: Self-powered + * D5: Remote Wakeup + * D4...0: Reserved (reset to zero)*/ + .bmAttributes = USB_CONFIG_ATT_ONE, + /*Maximum power consumption of the USB device from the + * bus in this specific configuration when the device is + * fully operational. Expressed in 2mA units (i.e., 50 = 100mA).*/ + .bMaxPower = 0x32, // FIXME: ??? + }, + .interface = + { + /*Size of this descriptor in bytes.*/ + .bLength = USB_DT_INTERFACE_SIZE, + /*Interface Descriptor Type = 4.*/ + .bDescriptorType = USB_DT_INTERFACE, + /*The number of this interface.*/ + .bInterfaceNumber = 0, + /*Value used to select an alternate setting for the + * interface identified in the prior field. + * Allows an interface to change the settings on the fly.*/ + .bAlternateSetting = 0, + /*Number of endpoints used by this interface (excluding endpoint zero).*/ + .bNumEndpoints = 2, + /*Class code (assigned by the USB-IF).*/ + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + /*Subclass code (assigned by the USB-IF).*/ + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + /*Protocol code (assigned by the USB).*/ + .bInterfaceProtocol = USB_PROTOCOL_VENDOR_SPEC, + /*Index of string descriptor describing this interface.*/ + .iInterface = USB_STRING_INDEX(2), + }, + .endpoints = + { + { + /*Size of this descriptor in bytes.*/ + .bLength = USB_DT_ENDPOINT_SIZE, + /*Endpoint Descriptor Type = 5.*/ + .bDescriptorType = USB_DT_ENDPOINT, + /*The address of the endpoint on the USB device described by this descriptor. The address is encoded as follows: + * Bit 3...0: The endpoint number + * Bit 6...4: Reserved, reset to zero + * Bit 7: Direction, ignored for control endpoints. + * 0 = OUT endpoint + * 1 = IN endpoint*/ + .bEndpointAddress = USB_ENDPOINT_NUMBER(0x1) | USB_DIR_OUT, + /*The endpoint attribute when configured through bConfigurationValue.*/ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /*Is the maximum packet size of this endpoint.*/ + .wMaxPacketSize = 512, + /*Interval for polling endpoint for data transfers*/ + .bInterval = 0, + }, + { + /*Size of this descriptor in bytes.*/ + .bLength = USB_DT_ENDPOINT_SIZE, + /*Endpoint Descriptor Type = 5.*/ + .bDescriptorType = USB_DT_ENDPOINT, + /*The address of the endpoint on the USB device described by this descriptor. The address is encoded as follows: + * Bit 3...0: The endpoint number + * Bit 6...4: Reserved, reset to zero + * Bit 7: Direction, ignored for control endpoints. + * 0 = OUT endpoint + * 1 = IN endpoint*/ + .bEndpointAddress = USB_ENDPOINT_NUMBER(0x1) | USB_DIR_IN, + /*The endpoint attribute when configured through bConfigurationValue.*/ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /*Is the maximum packet size of this endpoint.*/ + .wMaxPacketSize = 512, + /*Interval for polling endpoint for data transfers*/ + .bInterval = 0, + }, + }, + }, + .fullspeed = + { + /*Refer above comments for descriptor explanations.*/ + .config = + { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = sizeof(descriptors.fullspeed), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_ONE, + .bMaxPower = 0x32,// FIXME: ??? + }, + .interface = + { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0xff, + .iInterface = 3, + }, + .endpoints = + { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_ENDPOINT_NUMBER(0x2) | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + .bInterval = 0, + }, + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_ENDPOINT_NUMBER(0x6) | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + .bInterval = 0, + }, + }, + }, +#include "build/descriptors_stringtable.inc" +}; diff --git a/examples/loopback_ftdi/descriptors.h b/examples/loopback_ftdi/descriptors.h new file mode 100644 index 0000000..7120bca --- /dev/null +++ b/examples/loopback_ftdi/descriptors.h @@ -0,0 +1,33 @@ +#include + +#include "build/descriptors_stringtable.h" + +#include +#include + +#ifndef DESCRIPTORS_H_ +#define DESCRIPTORS_H_ + +struct usb_section { + struct usb_config_descriptor config; + struct usb_interface_descriptor interface; + struct usb_endpoint_descriptor endpoints[2]; +}; + +struct usb_descriptors { + struct usb_device_descriptor device; + struct usb_qualifier_descriptor qualifier; + struct usb_section highspeed; + struct usb_section fullspeed; + struct usb_descriptors_stringtable stringtable; +}; + +__xdata __at(DSCR_AREA) struct usb_descriptors descriptors; + +__code __at(DSCR_AREA+offsetof(struct usb_descriptors, device)) WORD dev_dscr; +__code __at(DSCR_AREA+offsetof(struct usb_descriptors, qualifier)) WORD dev_qual_dscr; +__code __at(DSCR_AREA+offsetof(struct usb_descriptors, highspeed)) WORD highspd_dscr; +__code __at(DSCR_AREA+offsetof(struct usb_descriptors, fullspeed)) WORD fullspd_dscr; +__code __at(DSCR_AREA+offsetof(struct usb_descriptors, stringtable)) WORD dev_strings; + +#endif // DESCRIPTORS_H_ diff --git a/examples/loopback_ftdi/descriptors.strings b/examples/loopback_ftdi/descriptors.strings new file mode 100644 index 0000000..6238452 --- /dev/null +++ b/examples/loopback_ftdi/descriptors.strings @@ -0,0 +1,3 @@ +Hi +There +iFrame diff --git a/examples/loopback_ftdi/ftdi_conf.c b/examples/loopback_ftdi/ftdi_conf.c new file mode 100644 index 0000000..398ba4e --- /dev/null +++ b/examples/loopback_ftdi/ftdi_conf.c @@ -0,0 +1,102 @@ +#include "ftdi_conf.h" +/* -*- mode: C; c-basic-offset: 3; -*- + * + * convert_serial -- FX2 USB serial port converter + * + * by Brent Baccala July 2009 + * adapted by Roarin + * + * Based on both usb-fx2-local-examples by Wolfgang Wieser (GPLv2) and + * the Cypress FX2 examples. + * + * This is an FX2 program which re-enumerates to mimic a USB-attached + * serial port (an FT8U100AX). Anything transmitted to it by the host + * is echoed back after converting lowercase to uppercase. The + * re-enumerated FX2 appears on a Debian Linux machine as something + * like /dev/ttyUSB0, and can be tested with a serial port program + * like 'minicom'. + * + * An actual FT8U100AX transmits and receives on endpoint 2. Since + * the FX2's endpoint 2 can be configured to either send or receive + * (but not do both), we use endpoint 1. This works with the Linux + * driver, which reads the endpoint addresses from the USB + * descriptors, but might not work with some other driver where the + * endpoint addresses are hardwired in the driver code. + * + * Many features, like setting and querying things like the baudrate + * and line/modem status are unimplemented, and will return USB errors + * when the host attempts to perform these operations. However, the + * program works, and I decided to leave out the additional clutter in + * favor of a simpler program that can be easily modified to suit + * individual needs. + * + * The program ignores USB Suspend interrupts, and probably violates + * the USB standard in this regard, as all USB devices are required to + * support Suspend. The remote wakeup feature is parsed and correctly + * handled in the protocol, but of course does nothing since the + * device never suspends. + */ +unsigned char bytes_waiting_for_xmit = 0; + +void configure_endpoints(void) +{ + + REVCTL=0x03; // See TRM... + SYNCDELAY; + // Endpoint configuration - everything disabled except + // bidirectional transfers on EP1. + EP1OUTCFG=0xa0; + EP1INCFG=0xa0; + EP2CFG=0; + EP4CFG=0; + EP6CFG=0; + EP8CFG=0; + SYNCDELAY; + EP1OUTBC=0xff; // Arm endpoint 1 for OUT (host->device) transfers +} + +// We want to buffer any outgoing data for a short time (40 ms) to see +// if any other data becomes available and it can all be sent +// together. At 12 MHz we consume 83.3 ns/cycle and divide this rate +// by 12 so that our counters increment almost exactly once every us. +// The counter is sixteen bits, so we can specify latencies up to +// about 65 ms. +void process_send_data(void) +{ + // Lead in two bytes in the returned data (modem status and + // line status). + EP1INBUF[0] = FTDI_RS0_CTS | FTDI_RS0_DSR | 1; + EP1INBUF[1] = FTDI_RS_DR; + // Send the packet. + SYNCDELAY; + EP1INBC = bytes_waiting_for_xmit + 2; + bytes_waiting_for_xmit = 0; +} + +void putchar_usb(char c) +{ + __xdata unsigned char *dest=EP1INBUF + bytes_waiting_for_xmit + 2; + // Wait (if needed) for EP1INBUF ready to accept data + while (EP1INCS & 0x02); + *dest = c; + if (++bytes_waiting_for_xmit >= 1) process_send_data(); +} + +void process_recv_data(void) +{ + __xdata const unsigned char *src=EP1OUTBUF; + unsigned int len = EP1OUTBC; + unsigned int i; + // Skip the first byte in the received data (it's a port + // identifier and length). + src++; len--; + for(i=0; i='a' && *src<='z') + { putchar_usb(*src-'a'+'A'); } + else + { putchar_usb(*src); } + } + EP1OUTBC=0xff; // re-arm endpoint 1 for OUT (host->device) transfers + SYNCDELAY; +} diff --git a/examples/loopback_ftdi/ftdi_conf.h b/examples/loopback_ftdi/ftdi_conf.h new file mode 100644 index 0000000..8d0c5c0 --- /dev/null +++ b/examples/loopback_ftdi/ftdi_conf.h @@ -0,0 +1,44 @@ +/** \file ftdi_conf.h + * Definitions for FTDI devices + **/ +#ifndef FTDI_CONF_H +#define FTDI_CONFH +#include "fx2types.h" +#include +#include +// These defines came from the Linux source code: +// drivers/usb/serial/ftdi_sio.h +// That file also does a reasonable job of documenting the protocol, +// particularly the vendor requests I don't implement that do things +// like setting the baud rate. +#define FTDI_RS0_CTS (1 << 4) +#define FTDI_RS0_DSR (1 << 5) +#define FTDI_RS0_RI (1 << 6) +#define FTDI_RS0_RLSD (1 << 7) +#define FTDI_RS_DR 1 +#define FTDI_RS_OE (1<<1) +#define FTDI_RS_PE (1<<2) +#define FTDI_RS_FE (1<<3) +#define FTDI_RS_BI (1<<4) +#define FTDI_RS_THRE (1<<5) +#define FTDI_RS_TEMT (1<<6) +#define FTDI_RS_FIFO (1<<7) +//Used for delay definition +#define SYNCDELAY SYNCDELAY4 +/** + * \brief Configures endpoint 1 +**/ +void configure_endpoints(void); +/** + * \brief Send data present in EP1INBUF out +**/ +void process_send_data(void); +/** + * \brief Inserts data into endpoint buffer +**/ +void putchar_usb(char c); +/** + * \brief Receives data from endpoint 1, and calls putchar_usb +**/ +void process_recv_data(void); +#endif diff --git a/examples/loopback_ftdi/ftdi_main.c b/examples/loopback_ftdi/ftdi_main.c new file mode 100644 index 0000000..3ea1178 --- /dev/null +++ b/examples/loopback_ftdi/ftdi_main.c @@ -0,0 +1,162 @@ +/** + * Copyright (C) 2009 Ubixum, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + **/ +#include +#include +#include +#include +#include +#include +#include "ftdi_conf.h" +#ifdef DEBUG_MAIN +#include // NOTE this needs deleted +#else +#define printf(...) +#endif + + +//For handling SUDAV ISR +volatile __bit got_sud; + +void main() +{ + //Setup data available and other init + got_sud=FALSE; + //Call our custom function to do our UART init + configure_endpoints(); + RENUMERATE(); + SETCPUFREQ(CLK_48M); + //Enable USB auto vectored interrupts + USE_USB_INTS(); + ENABLE_SUDAV(); + ENABLE_SOF(); + ENABLE_HISPEED(); + ENABLE_USBRESET(); + EA=1; // global interrupt enable + while(TRUE) + { + //Handles device descriptor requests + if ( got_sud ) + { + handle_setupdata(); + got_sud=FALSE; + } + // Input data on EP1 + if(!(EP1OUTCS & bmEPBUSY)) + { + process_recv_data(); + } + } +} + +BOOL +handle_get_descriptor () +{ + return FALSE; +} + +BOOL +handle_vendorcommand (BYTE cmd) +{ + printf ("Need to implement vendor command: %02x\n", cmd); + return FALSE; +} + +// this firmware only supports 0,0 +BOOL +handle_get_interface (BYTE ifc, BYTE * alt_ifc) +{ + printf ("Get Interface\n"); + + if (ifc == 0) + { + *alt_ifc = 0; + return TRUE; + } + else + { + return FALSE; + } +} + +BOOL +handle_set_interface (BYTE ifc, BYTE alt_ifc) +{ + printf ("Set interface %d to alt: %d\n", ifc, alt_ifc); + + if (ifc == 0 && alt_ifc == 0) + { + // SEE TRM 2.3.7 + // reset toggles + RESETTOGGLE (0x02); + RESETTOGGLE (0x86); + // restore endpoints to default condition + RESETFIFO (0x02); + EP2BCL = 0x80; + SYNCDELAY; + EP2BCL = 0X80; + SYNCDELAY; + RESETFIFO (0x86); + return TRUE; + } + else + return FALSE; +} + +void +sof_isr () +__interrupt SOF_ISR __using 1 +{ + CLEAR_SOF (); +} + +// get/set configuration +BYTE +handle_get_configuration () +{ + return 1; +} + +BOOL +handle_set_configuration (BYTE cfg) +{ + return cfg == 1 ? TRUE : FALSE; // we only handle cfg 1 +} + +// copied usb jt routines from usbjt.h +void +sudav_isr () +__interrupt SUDAV_ISR +{ + + got_sud = TRUE; + CLEAR_SUDAV (); +} + +void usbreset_isr () +__interrupt USBRESET_ISR +{ + handle_hispeed (FALSE); + CLEAR_USBRESET (); +} + +void hispeed_isr () +__interrupt HISPEED_ISR +{ + handle_hispeed (TRUE); + CLEAR_HISPEED (); +}