Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loopback FTDI implementation on the FX2 using USB descriptors from the linux-descriptors branch #21

Open
wants to merge 4 commits into
base: linux-descriptors
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/download.sh
Original file line number Diff line number Diff line change
@@ -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 <file>"
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
12 changes: 12 additions & 0 deletions examples/loopback_ftdi/Makefile
Original file line number Diff line number Diff line change
@@ -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
179 changes: 179 additions & 0 deletions examples/loopback_ftdi/descriptors.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
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:
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 = {
.bLength = USB_DT_DEVICE_SIZE,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mixing tab and spaces here which is causing the weird indentation issues.

.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = USB_BCD_V20,
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
.bDeviceSubClass = USB_SUBCLASS_VENDOR_SPEC,
.bDeviceProtocol = 0xff,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include comment for bDeviceProtocol

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually is there a USB_PROTO_VENDOR_SPEC macro or similar?

.bMaxPacketSize0 = 64,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include comments for the fields below.

.idVendor = 0x0403,
.idProduct = 0x6010,
.bcdDevice = 0x0001,
.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(0x1) | 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"
};
33 changes: 33 additions & 0 deletions examples/loopback_ftdi/descriptors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <stddef.h>

#include "build/descriptors_stringtable.h"

#include <linux/ch9.h>
#include <linux/ch9-extra.h>

#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_
3 changes: 3 additions & 0 deletions examples/loopback_ftdi/descriptors.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Hi
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fill these with real appropriate values

There
iFrame
102 changes: 102 additions & 0 deletions examples/loopback_ftdi/ftdi_conf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "ftdi_conf.h"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put the include after the initial comment.

/* -*- mode: C; c-basic-offset: 3; -*-
*
* convert_serial -- FX2 USB serial port converter
*
* by Brent Baccala <cosine@freesoft.org> 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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you still emulating a FT8U100AX?

* 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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you planning on implementing these?

* 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...
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See TRM is not a useful comment. Explain why.

SYNCDELAY;
// Endpoint configuration - everything disabled except
// bidirectional transfers on EP1.
EP1OUTCFG=0xa0;
EP1INCFG=0xa0;
Copy link
Owner

@mithro mithro Jul 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use macros to work out this value.

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)
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this indented differently?

__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<len; i++,src++)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this wrapped differently from everything else?

{
if(*src>='a' && *src<='z')
{ putchar_usb(*src-'a'+'A'); }
else
{ putchar_usb(*src); }
}
EP1OUTBC=0xff; // re-arm endpoint 1 for OUT (host->device) transfers
SYNCDELAY;
}
44 changes: 44 additions & 0 deletions examples/loopback_ftdi/ftdi_conf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** \file ftdi_conf.h
* Definitions for FTDI devices
**/
#ifndef FTDI_CONF_H
#define FTDI_CONFH
#include "fx2types.h"
#include <delay.h>
#include <fx2regs.h>
// 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
Loading