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/mpsse_gpio/Makefile b/examples/mpsse_gpio/Makefile new file mode 100644 index 0000000..6b3074b --- /dev/null +++ b/examples/mpsse_gpio/Makefile @@ -0,0 +1,12 @@ +FX2LIBDIR=../../ +BASENAME = ftdi_mpsse +SOURCES=ftdi_mpsse.c mpsse_utils.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/mpsse_gpio/descriptors.c b/examples/mpsse_gpio/descriptors.c new file mode 100644 index 0000000..35fcd56 --- /dev/null +++ b/examples/mpsse_gpio/descriptors.c @@ -0,0 +1,216 @@ +/* +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 aamount +of data which is what we need) +Bus 003 Device 039: ID 0403:6010 +Future Technology Devices International, Ltd FT2232C Dual USB-UART/FIFO IC +vbDevice Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0403 Future Technology Devices International, Ltd + idProduct 0x6010 FT2232C Dual USB-UART/FIFO IC + bcdDevice 7.00 + iManufacturer 1 + iProduct 2 + iSerial 3 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 55 + bNumInterfaces 2 + 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 2 + 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 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0200 1x 512 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 255 Vendor Specific Subclass + bInterfaceProtocol 255 Vendor Specific Protocol + iInterface 2 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + 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 0x04 EP 4 OUT + 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 = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = USB_BCD_V20, + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + .bDeviceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bDeviceProtocol = 0xff, + .bMaxPacketSize0 = 64, + .idVendor = 0x0403, + .idProduct = 0x6010, + .bcdDevice = 0x0007, + .iManufacturer = USB_STRING_INDEX(0), + .iProduct = USB_STRING_INDEX(1), + .iSerialNumber = USB_STRING_INDEX_NONE, + .bNumConfigurations = 1, + }, + .qualifier = { + .bLength = USB_DT_DEVICE_QUALIFIER_SIZE, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD_V20, + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + .bDeviceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bDeviceProtocol = 0xff, + .bMaxPacketSize0 = 64, + .bNumConfigurations = 1, + .bRESERVED = 0, + }, + .highspeed = { + .config = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = sizeof(descriptors.highspeed), + .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 = USB_PROTOCOL_VENDOR_SPEC, + .iInterface = USB_STRING_INDEX(2), + }, + .endpoints = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_ENDPOINT_NUMBER(0x2) | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0, + }, + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_ENDPOINT_NUMBER(0x1) | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0, + }, + }, + }, + .fullspeed = { + .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/mpsse_gpio/descriptors.h b/examples/mpsse_gpio/descriptors.h new file mode 100644 index 0000000..7120bca --- /dev/null +++ b/examples/mpsse_gpio/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/mpsse_gpio/descriptors.strings b/examples/mpsse_gpio/descriptors.strings new file mode 100644 index 0000000..6238452 --- /dev/null +++ b/examples/mpsse_gpio/descriptors.strings @@ -0,0 +1,3 @@ +Hi +There +iFrame diff --git a/examples/mpsse_gpio/ftdi_mpsse.c b/examples/mpsse_gpio/ftdi_mpsse.c new file mode 100644 index 0000000..1ebc2b5 --- /dev/null +++ b/examples/mpsse_gpio/ftdi_mpsse.c @@ -0,0 +1,261 @@ +/** + * 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 +#include +#define DEBUG_MAIN +#ifdef DEBUG_MAIN +#include // NOTE this needs deleted +#else +#define printf(...) +#endif + +#define SYNCDELAY SYNCDELAY4 +void uart_tx(char c); +//For handling SUDAV ISR +volatile __bit got_sud; +volatile __bit got_ep2; + +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(); + ENABLE_EP2(); + ENABLE_TIMER1(); + EP2BCL = 0xff; + EA=1; // global interrupt enable + while(TRUE) + { + //Handles device descriptor requests + if ( got_sud ) + { + handle_setupdata(); + got_sud=FALSE; + } + if ( got_ep2 ) + { + /* Data from the host to the device*/ + printf("Got data\r\n"); + /*Handle the bulk data*/ + mpsse_handle_bulk(); + got_ep2=FALSE; + /* Rearm the EP.*/ + EP2BCL = 0xff; + } + } +} + +BOOL +handle_get_descriptor () +{ + return FALSE; +} + +BOOL +handle_vendorcommand (BYTE cmd) +{ + //printf ("Need to implement vendor command: %02x\n", cmd); + mpsse_handle_control(); + 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 (); +} + +/*Handles the ISR for data on EP 0x02*/ +void ep2_isr() +__interrupt EP2_ISR +{ + got_ep2 = TRUE; + CLEAR_EP2(); +} + +/********************************************************************************* +DELETE THIS +**********************************************************************************/ +unsigned char load_delay; +void uart_tx(char c) +{ + + load_delay = 0x20; + //Done in ASM to improve performance. It takes only 6 + //cycles to move the data out, however a delay has been + //introduced in order to get a baud rate of 115200 + //The mask which is to be written into the pin + OEA |= 0x04; + //An efficient UART bitbang routine in assembly + __asm + //Like #define in C. Can easily be used to change the pin + .equ _TX_PIN, _PA2 + //Disable interrupts + //This is used because timing is critical + //If the FX2 jumps into the ISR temporarily , it may cause transmit + //errors. By clearing EA, we can disable interrupts + clr _EA //(2 cycles) + //Move the data to be sent into the ACC + //The data which is to be shifted out is held in the dpl register + //We move the data into A for easy access to subsequent instructions + mov a , dpl //(2 cyles) + clr c //(1 cycle ) + //We need to send out 8 bits of data + //Load r0 with value 8 + mov r0, #0x08 //(2 cycles) + //Create the start bit + clr _TX_PIN //(2 cycles) + //Precalculated delay since 1 cycle takes 88ns + //At 12Mhz, it should be about 83.33ns + //But it appears to be about 88ns + //These numbers have been verified using an analyzer + mov r1, _load_delay //(2 cycles) + 0006$: + //1 bit is about 8.6us + djnz r1, 0006$ //(3 cycles) + //DJNZ on Rn takes 3 cycles + //NOP takes about 1 cycle + //Add 2 more cycles of delay + //97 cycles + nop //(1 cycle ) + nop //(1 cycle ) + 0001$: + rrc a //(2 cycles) + //The above rotates the accumulator right through the carry + //Move the carry into the port + mov _TX_PIN, c //(2 cycles) + //Now we need to add delay for the next + mov r1, _load_delay //(2 cycles) + //31*3 , 93 cycles of delay + 0004$: + djnz r1, 0004$ //(3 cycles) + nop //(1 cycle ) + //3 more cycles of delay + //97 cycles + djnz r0, 0001$ //(3 cycles) + setb _TX_PIN //(2 cycles) + //This is for stop bit + //We need to delay the stop bit, otherwise we may get errors. + mov r1, _load_delay //(2 cycles) + 0005$: + djnz r1, 0005$ //(3 cycles) + //for DJNZ , Jump for 32*3 , 96 cycles + nop //(1 cycle ) + //97 cycles of delay + setb _EA //(2 cycles) + //Enable back the interrupts + __endasm; +} + +void putchar(char c) +{ + uart_tx(c); +} diff --git a/examples/mpsse_gpio/mpsse_utils.c b/examples/mpsse_gpio/mpsse_utils.c new file mode 100644 index 0000000..af0c09f --- /dev/null +++ b/examples/mpsse_gpio/mpsse_utils.c @@ -0,0 +1,114 @@ +#include +#include +#include "stdio.h" +#include + +#define SYNCDELAY SYNCDELAY4 +__xdata __at(0xE6B8) volatile struct mpsse_control_request control_request; +__xdata __at(0xF000) volatile struct mpsse_read_write read_write; +__xdata enum mpsse_clocking_commands clocking_commands; +__xdata struct mpsse_total_length total_length; + +void mpsse_handle_control() +{ + printf("Control %02x\r\n",SETUPDAT[1]); + switch (SETUPDAT[1]) + { + case SIO_RESET_REQUEST: + { + printf("Resetting %02x\r\n",SETUPDAT[1]); + EP0CS |= 0x80; + } + break; + case SIO_SET_BAUD_RATE: + { + EP0CS |= 0x80; + + } + break; + case SIO_SET_LATENCY_TIMER_REQUEST: + { + EP0CS |= 0x80; + } + break; + case SIO_SET_BITMODE_REQUEST: + { + EP0CS |= 0x80; + } + break; + case SIO_READ_PINS_REQUEST: + { + /* The FT2232 docs and the libftdi just do a read. I am not sure + * which pin to return. Some examples return the CBUS. So , we will + * return port A + */ + OEA = 0x00; + EP0BUF[0] = IOA; + EP0BCL = 0x01; + EP0BCH = 0x00; + EP0CS |= 0x80; + } + break; + case SIO_SET_EVENT_CHAR_REQUEST: + { + EP0CS |= 0x80; + } + break; + default: + break; + } + +} + + +void configure_endpoints() +{ + /* + Interface A is the only supported mode in FX2 currently + We need to configure endpoint 1 as IN, and endpoint 2 + as OUT endpoint. + case INTERFACE_A: + ftdi->interface = 0; + ftdi->index = INTERFACE_A; + ftdi->in_ep = 0x02; + ftdi->out_ep = 0x81; + */ + EP1INCFG = 0xa0; + SYNCDELAY; + /*Out endpoint, double buffered, bulk endpoint*/ + EP2CFG = 0xa2; + SYNCDELAY; + /*Arm the endpoint*/ + EP2BCL = 0xff; + +} + +void mpsse_handle_bulk() +{ + /* Pin mapping docs details how the ports on FX2 are mapped to + those on FT2232H */ + switch(read_write.command) + { + case SET_BITS_LOW: + //Look again and verify that this can actually be done + OEA = read_write.direction; + IOA = read_write.value; + printf("Write direction %02x, value %02x\r\n",read_write.direction,read_write.value); + break; + case SET_BITS_HIGH: + OEB = read_write.direction; + IOB = read_write.value; + printf("Write high bytes\r\n"); + break; + case GET_BITS_LOW: + printf("Read low bytes\r\n"); + break; + case GET_BITS_HIGH: + printf("Read high bytes\r\n"); + break; + default: + break; + } +} + + diff --git a/include/mpsse/mpsse_utils.h b/include/mpsse/mpsse_utils.h new file mode 100644 index 0000000..7060f5d --- /dev/null +++ b/include/mpsse/mpsse_utils.h @@ -0,0 +1,418 @@ +/** \file mpsse_utils.h + * This file is for defining layered structure which can be used + * for handling MPSSE commands. A lot of the #define statements have + * been taken from ftdi.h in libftdi +**/ +#ifndef MPSSE_UTILS_H +#define MPSSE_UTILS_H + +#include +#include +/* Shifting commands IN MPSSE Mode. See section 3.2 +Bit 0 : -ve CLK on write +Bit 1 : bit mode = 1 else byte mode +Bit 2 : -ve CLK on read +Bit 3 : LSB first = 1 else MSB first +Bit 4 : Do write TDI +Bit 5 : Do read TDO +Bit 6 : Do writeTMS +Bit 7 : 0 +*/ +#define MPSSE_WRITE_NEG 0x01 /* Write TDI/DO on negative TCK/SK edge,bit 0 has been set */ +#define MPSSE_BITMODE 0x02 /* Write bits, not bytes, set bit 1 */ +#define MPSSE_READ_NEG 0x04 /* Sample TDO/DI on negative TCK/SK edge , set bit 2*/ +#define MPSSE_LSB 0x08 /* LSB first , set bit 3 */ +#define MPSSE_DO_WRITE 0x10 /* Write TDI/DO , set bit 4*/ +#define MPSSE_DO_READ 0x20 /* Read TDO/DI */ +#define MPSSE_WRITE_TMS 0x40 /* Write TMS/CS */ + +/* FTDI MPSSE commands, these are sent from the host as bulk commands*/ +#define SET_BITS_LOW 0x80 /*These set the ADBUS[7-0] */ +/*BYTE DATA*/ +/*BYTE Direction*/ +#define SET_BITS_HIGH 0x82 /*These set the ACBUS[7-0] */ +/*BYTE DATA*/ +/*BYTE Direction*/ +#define GET_BITS_LOW 0x81 +#define GET_BITS_HIGH 0x83 +/*Used for device teseting, internally connects the 2 pins*/ +#define LOOPBACK_START 0x84 +#define LOOPBACK_END 0x85 +#define TCK_DIVISOR 0x86 +/* H Type specific commands */ +#define DIS_DIV_5 0x8a +#define EN_DIV_5 0x8b +#define EN_3_PHASE 0x8c +#define DIS_3_PHASE 0x8d +#define CLK_BITS 0x8e +#define CLK_BYTES 0x8f +#define CLK_WAIT_HIGH 0x94 +#define CLK_WAIT_LOW 0x95 +#define EN_ADAPTIVE 0x96 +#define DIS_ADAPTIVE 0x97 +#define CLK_BYTES_OR_HIGH 0x9c +#define CLK_BYTES_OR_LOW 0x9d +/*FT232H specific commands */ +#define DRIVE_OPEN_COLLECTOR 0x9e +/* Value Low */ +/* Value HIGH */ /*rate is 12000000/((1+value)*2) */ +#define DIV_VALUE(rate) (rate > 6000000)?0:((6000000/rate -1) > 0xffff)? 0xffff: (6000000/rate -1) +/* Commands in MPSSE and Host Emulation Mode */ +#define SEND_IMMEDIATE 0x87 +#define WAIT_ON_HIGH 0x88 +#define WAIT_ON_LOW 0x89 +/* Commands in Host Emulation Mode */ +#define READ_SHORT 0x90 +/* Address_Low */ +#define READ_EXTENDED 0x91 +/* Address High */ +/* Address Low */ +#define WRITE_SHORT 0x92 +/* Address_Low */ +#define WRITE_EXTENDED 0x93 +/* Address High */ +/* Address Low */ +/* Definitions for flow control */ +#define SIO_RESET 0 /* Reset the port */ +#define SIO_MODEM_CTRL 1 /* Set the modem control register */ +#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */ +#define SIO_SET_BAUD_RATE 3 /* Set baud rate */ +#define SIO_SET_DATA 4 /* Set the data characteristics of the port */ + +/* Requests , these arrive on a control endpoint*/ +#define SIO_RESET_REQUEST SIO_RESET +#define SIO_SET_BAUDRATE_REQUEST SIO_SET_BAUD_RATE +#define SIO_SET_DATA_REQUEST SIO_SET_DATA +#define SIO_SET_FLOW_CTRL_REQUEST SIO_SET_FLOW_CTRL +#define SIO_SET_MODEM_CTRL_REQUEST SIO_MODEM_CTRL +#define SIO_POLL_MODEM_STATUS_REQUEST 0x05 +#define SIO_SET_EVENT_CHAR_REQUEST 0x06 +#define SIO_SET_ERROR_CHAR_REQUEST 0x07 +#define SIO_SET_LATENCY_TIMER_REQUEST 0x09 +#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A +#define SIO_SET_BITMODE_REQUEST 0x0B +#define SIO_READ_PINS_REQUEST 0x0C +#define SIO_READ_EEPROM_REQUEST 0x90 +#define SIO_WRITE_EEPROM_REQUEST 0x91 +#define SIO_ERASE_EEPROM_REQUEST 0x92 +/* Reset commands , control endpoint*/ +#define SIO_RESET_SIO 0 +#define SIO_RESET_PURGE_RX 1 +#define SIO_RESET_PURGE_TX 2 + +BYTE handle_get_configuration(); +BOOL handle_set_configuration(BYTE cfg); +BOOL handle_get_interface(BYTE ifc, BYTE* alt_ifc); +BOOL handle_set_interface(BYTE ifc, BYTE alt_ifc); +BOOL handle_get_descriptor(); + +/* Prints out the control endpoint request.*/ +#define PRINT_REQUEST(str) \ + printf( \ + str " bControl:%d bRequest:%s bLength: %d\n", \ + uvc_ctrl_request.wValue.bControl, \ + uvc_control_request_str(uvc_ctrl_request.bRequest), \ + uvc_ctrl_request.wLength); +/** + * MPSSE control packet. All control packets are sent on endpoint 0. + * This structures handles data from endpoint 0.(Control_Endpoint) +**/ +struct mpsse_control_request { + /* Request Type, Direction, and Recipient */ + BYTE bmRequestType; + /* The actual request.TRM See section 2.3. Decides the operation */ + BYTE bRequest; + /* The values in this struct provide data for operation specified in bRequest */ + struct { + BYTE bZero; + BYTE bControl; + } wValue; + /* The values in this struct define the interface + * \li INTERFACE_ANY + * \li INTERFACE_A + * \li INTERFACE_B(not supported) + * \li INTERFACE_C(not supported) + * \li INTERFACE_D(not supported) + */ + struct { + BYTE bInterface; + BYTE bUnitId; + } wIndex; + /* The number of bytes to follow the SETUPDAT request */ + WORD wLength; +}; + +/** + * MPSSE command length .Control endpoints and bulk endpoints + * have different length messages coming in. The struct below + * copies the length field and is used in all computation. +**/ +struct mpsse_total_length { + /*Low byte of number of bytes recieved*/ + BYTE low_byte; + /* High byte if number of bytes received*/ + BYTE high_byte; +}; + + + +/** + * MPSSE context. + * This structures controls the operation of the MPSSE engine. + * Modelled after ftdi_context. +**/ +struct mpsse_context { + /*Mode of operations */ + enum ftdi_mpsse_mode mode; + /*Interface on which chio should operate */ + enum ftdi_interface interface; + /*Unused variable */ + BYTE baud_rate; + /*Set to 1 if bitbang is enabled*/ + BYTE bitbang_enabled; + /*This pointer is set by the function, before filling the IN buffer*/ + unsigned char *readbuffer; +}; + +/** + * MPSSE read/write commands.See section 3.6 for more information. + * This structures controls the operation of the MPSSE engine. + * Modelled after ftdi_context. +**/ +struct mpsse_read_write { + /*Mode of operations + * 0x80 - Set Data bits low. + * 0x82 - Set Data bits high. + * 0x81 - Read Data bits low. + * 0x83 - Read Data bits high. + */ + BYTE command; + /*The value to write*/ + BYTE value; + /*The direction to set(OEA pins)*/ + BYTE direction; +}; + +/** + * Supported modes by the FT2232H chip. +**/ +enum ftdi_mpsse_mode + { + BITMODE_RESET = 0x00, + BITMODE_BITBANG= 0x01, + BITMODE_MPSSE = 0x02, + BITMODE_SYNCBB = 0x04, + BITMODE_MCU = 0x08, + /* CPU-style fifo mode gets set via EEPROM.*/ + BITMODE_OPTO = 0x10, + BITMODE_CBUS = 0x20, + BITMODE_SYNCFF = 0x40, + BITMODE_FT1284 = 0x80, + }; + +/** + * Supported interfaces. +**/ +enum ftdi_interface + { + INTERFACE_ANY = 0, + INTERFACE_A = 1, //0x81,0x02 endpoints + INTERFACE_B = 2, //0x83,0x04 endpoints + INTERFACE_C = 3, //0x85,0x06 endpoints + INTERFACE_D = 4 //0x87,0x08 endpoints + }; + +enum mpsse_clocking_commands + { + CLOCK_BYTES_OUT_POS_MSB = 0x10, + CLOCK_BYTES_OUT_NEG_MSB = 0x11, + CLOCK_BITS_OUT_POS_MSB = 0x12, + CLOCK_BITS_OUT_NEG_MSB = 0x13, + CLOCK_BYTES_IN_POS_MSB = 0x20, + CLOCK_BYTES_IN_NEG_MSB = 0x24, + CLOCK_BITS_IN_POS_MSB = 0x22, + CLOCK_BITS_IN_NEG_MSB = 0x26, + CLOCK_BYTES_IN_OUT_NORMAL_MSB = 0x31, + CLOCK_BYTES_IN_OUT_INVERTED_MSB = 0x34, + CLOCK_BITS_IN_OUT_NORMAL_MSB = 0x33, + CLOCK_BITS_IN_OUT_INVERTED_MSB = 0x36, + CLOCK_BYTES_OUT_POS_LSB = 0x18, + CLOCK_BYTES_OUT_NEG_LSB = 0x19, + CLOCK_BITS_OUT_POS_LSB = 0x1A, + CLOCK_BITS_OUT_NEG_LSB = 0x1B, + CLOCK_BYTES_IN_POS_LSB = 0x28, + CLOCK_BYTES_IN_NEG_LSB = 0x2C, + CLOCK_BITS_IN_POS_LSB = 0x2A, + CLOCK_BITS_IN_NEG_LSB = 0x2E, + CLOCK_BYTES_IN_OUT_NORMAL_LSB = 0x39, + CLOCK_BYTES_IN_OUT_INVERTED_LSB = 0x3C, + CLOCK_BITS_IN_OUT_NORMAL_LSB = 0x3B, + CLOCK_BITS_IN_OUT_INVERTED_LSB = 0x3E + }; + +/** + * \brief Called when the FTDI_RESET command is issued. +**/ + void mpsse_reset(); + +/** + * \brief Called whenever a SETUP packet is received and the bmRequest is + * 0x40 or 0xc0(vendor commands). +**/ + void mpsse_handle_control(); + +/** + * \brief Called whenever a SETUP packet is received and the bmRequest is + * 0x40 or 0xc0(vendor commands). +**/ + void set_baud_rate(BYTE rate); + +/** + * \brief Sets the latency timer(currently unimplemented). +**/ + void set_latency_timer(BYTE latency); + +/** + * \brief Sets the bitmode to mode. +**/ + void set_bitmode(BYTE mode); + +/** + * \brief Setting the pins occurs via URB bulk request. +**/ + void mpsse_handle_bulk(); + +/** + * \brief Setting the pins occurs via URB bulk request. +**/ + void set_pins_state(BYTE value, BYTE direction); + +/** + * \brief Get the pin state indicated by pin. +**/ + BYTE get_pins_state(BYTE pins); + +/** + * \brief Clear the RX buffer. +**/ +void purge_rx_buffer(); + +/** + * \brief Clear the TX buffer. +**/ +void purge_tx_buffer(); + +/** + * \brief Configures the endpoints according to the FT2232H. +**/ +void configure_endpoints(); + +/** + * \brief Clocks data out depending on length bytes(positive edge). + * \param The data is present in the endpoint buffer, and offset + * indicates the byte position of the length field inside the endpoint buffer + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_obyte_data_pos(unsigned char offset, __bit dir); +/** + * \brief Clocks data out depending on length bytes(negative edge). + * \param offset - The data is present in the endpoint buffer, and offset + * indicates the byte position of the length field inside the endpoint buffer + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_obyte_data_neg(unsigned char offset,__bit dir); + +/** + * \brief Clocks data bits out depending on length field(positive edge). + * \param offset The data is present in the endpoint buffer, and offset + * indicates the byte position of the length field. + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_obits_data_pos(unsigned char offset,__bit dir); + +/** + * \brief Clocks data bits out depending on length field(negative edge). + * \param offset The data is present in the endpoint buffer, and offset + * indicates the byte position of the length field. + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_obits_data_neg(unsigned char offset,__bit dir); + + +/** + * \brief Clocks data in depending on length bytes(positive edge). + * \param offset Location of length field in EP buffer. + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_ibyte_data_pos(unsigned char offset,__bit dir); + +/** + * \brief Clocks data in depending on length bytes(negative edge). + * \param offset Location of length field in EP buffer. + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_ibyte_data_neg(unsigned char offset,__bit dir); + +/** + * \brief Clocks data bits in depending on length field(positive edge). + * \param offset offset Location of length field in EP buffer. + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_ibits_data_pos(unsigned char offset,__bit dir); + +/** + * \brief Clocks data bits out(negative edge) depending on length field(negative edge). + * \param offset Location of length field in EP buffer. + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_ibits_data_neg(unsigned char offset,__bit dir); + +/** + * \brief Clocks data bytes in and out depending on length bytes(positive edge). + * \param offset Location of length field in EP buffer. Normal operation is + * out on -ve,in on +ve. + * \param polarity 0 - normal mode, 1 - polarity inverted + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_iobyte_data(unsigned char offset, __bit polarity,__bit dir); + +/** + * \brief Clocks data bits in and out depending on length bytes(positive edge). + * \param offset Location of length field in EP buffer. Normal operation is + * out on -ve,in on +ve. + * \param polarity 0 - normal mode, 1 - polarity inverted + * \param dir + * \li MSB(0 , MSB clocked out first) + * \li LSB(1 , LSB clocked out first) +**/ +void clock_iobits_data(unsigned char offset, __bit polarity,__bit dir); + +/** + * Allow the struct to be accessed from anyfile that includes this header file. +**/ +extern __xdata __at(0xE6B8) volatile struct mpsse_control_request control_request; +extern __xdata __at(0xF000) volatile struct mpsse_read_write read_write; + +//DELETE: +void putchar(char c); + + +#endif // MPSSE_UTILS_H diff --git a/lib/setupdat.c b/lib/setupdat.c index 961eb27..7c7cce4 100644 --- a/lib/setupdat.c +++ b/lib/setupdat.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2009 Ubixum, Inc. + * 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 @@ -69,66 +69,84 @@ void _handle_get_descriptor(); handshake */ -void handle_setupdata() { + +void handle_setupdata() +{ printf("Handle setupdat: 0x%02x\n", SETUPDAT[1]); + if(SETUPDAT[0] == 0x40 || SETUPDAT[0] == 0xc0) + { + handle_vendorcommand(SETUPDAT[1]); + } + else + { - switch ( SETUPDAT[1] ) { + switch ( SETUPDAT[1] ) + { case GET_STATUS: if (!handle_get_status()) STALLEP0(); break; case CLEAR_FEATURE: - if (!handle_clear_feature()) { + if (!handle_clear_feature()) + { STALLEP0(); } break; case SET_FEATURE: - if (!handle_set_feature()) { + if (!handle_set_feature()) + { STALLEP0(); } break; case GET_DESCRIPTOR: if (!handle_get_descriptor()) - _handle_get_descriptor(); + _handle_get_descriptor(); break; - case GET_CONFIGURATION: + case GET_CONFIGURATION: EP0BUF[0] = handle_get_configuration(); EP0BCH=0; EP0BCL=1; break; case SET_CONFIGURATION: // user callback - if(!handle_set_configuration(SETUPDAT[2])) { + if(!handle_set_configuration(SETUPDAT[2])) + { STALLEP0(); } break; case GET_INTERFACE: + { + BYTE alt_ifc; + if (!handle_get_interface(SETUPDAT[4],&alt_ifc)) { - BYTE alt_ifc; - if (!handle_get_interface(SETUPDAT[4],&alt_ifc)) { - STALLEP0(); - } else { - EP0BUF[0] = alt_ifc; - EP0BCH=0; - EP0BCL=1; - } + STALLEP0(); } - break; + else + { + EP0BUF[0] = alt_ifc; + EP0BCH=0; + EP0BCL=1; + } + } + break; case SET_INTERFACE: // user callback - if (!handle_set_interface(SETUPDAT[4],SETUPDAT[2])) { + if (!handle_set_interface(SETUPDAT[4],SETUPDAT[2])) + { STALLEP0(); } break; default: - if (!handle_vendorcommand(SETUPDAT[1])) { - printf("Unhandled Vendor Command: 0x%02x\n" , SETUPDAT[1]); - STALLEP0(); - } + handle_vendorcommand(SETUPDAT[1]); + //if (!handle_vendorcommand(SETUPDAT[1])) { + // printf("Unhandled Vendor Command: 0x%02x\n" , SETUPDAT[1]); + //STALLEP0(); + // } + } + + // do the handshake + EP0CS |= bmHSNAK; } - - // do the handshake - EP0CS |= bmHSNAK; } __xdata BYTE* ep_addr(BYTE ep) { @@ -156,11 +174,11 @@ volatile BOOL self_powered=FALSE; volatile BOOL remote_wakeup_allowed=FALSE; BOOL handle_get_status() { - + switch ( SETUPDAT[0] ) { // case 0: // sometimes we get a 0 status too - case GS_INTERFACE: + case GS_INTERFACE: EP0BUF[0] = 0; EP0BUF[1] = 0; EP0BCH=0; @@ -256,7 +274,7 @@ BOOL handle_set_feature() { if (!pep) { return FALSE; } - + *pep |= bmEPSTALL; // should now reset data toggles // write ep+dir to TOGCTL @@ -333,7 +351,7 @@ void _handle_get_descriptor() { printf("Config\n"); SUDPTRH = MSB(pDevConfig); SUDPTRL = LSB(pDevConfig); - break; + break; case DSCR_STRING_TYPE: printf("String idx: %d\n", SETUPDAT[2]); { @@ -349,7 +367,7 @@ void _handle_get_descriptor() { //printf("%04x\n", pStr); if (pStr->dsc_type != DSCR_STRING_TYPE) pStr=NULL; } while ( pStr && cur<=idx); - + if (pStr) { //BYTE i; //printf("found str: '"); @@ -362,9 +380,9 @@ void _handle_get_descriptor() { //SUDPTRH = MSB((WORD)&dev_strings); //SUDPTRL = LSB((WORD)&dev_strings); } else {STALLEP0();} - + } - + break; case DSCR_DEVQUAL_TYPE: printf("Device Qualifier\n");