commit bed7488a9c172bc2c77abdd69d25375cb6990b9f Author: sys64738 Date: Thu Jan 20 05:20:10 2022 +0100 stuff diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffd48bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.bin +*.bak +*.rom +fx3tool +*.img +*.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5abf79e --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ + +default: all + +all: fx3tool + +fx3tool: cyusb.h download_fx3.cpp libcyusb.cpp + $(CXX) -o "$@" $^ -lusb-1.0 + +clean: + @$(RM) -v fx3tool + +.PHONY: all default clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f23bab --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# fx3tool + +Download and upload code and query the status of a Cypress FX3 in USB +bootloader mode. Built on top of Cypress' own code, so license is LGPL v2.1 + +## Planned + +* Converter from ELF to FX3-IMG and back. diff --git a/cyusb.h b/cyusb.h new file mode 100644 index 0000000..2207322 --- /dev/null +++ b/cyusb.h @@ -0,0 +1,455 @@ +#ifndef __CYUSB_H +#define __CYUSB_H + +/*********************************************************************************\ + * This is the main header file for the cyusb suite for Linux/Mac, called cyusb.h * + * * + * Author : V. Radhakrishnan ( rk@atr-labs.com ) * + * License : LGPL Ver 2.1 * + * Copyright : Cypress Semiconductors Inc. / ATR-LABS * + * Date written : March 12, 2012 * + * Modification Notes : * + * 1. Cypress Semiconductor, January 23, 2013 * + * Added function documentation. * + * Added new constant to specify number of device ID entries. * + * * + \********************************************************************************/ + +#include + +typedef struct libusb_device cyusb_device; +typedef struct libusb_device_handle cyusb_handle; + +/* This is the maximum number of 'devices of interest' we are willing to store as default. */ +/* These are the maximum number of devices we will communicate with simultaneously */ +#define MAXDEVICES 10 + +/* This is the maximum number of VID/PID pairs that this library will consider. This limits + the number of valid VID/PID entries in the configuration file. + */ +#define MAX_ID_PAIRS 100 + +/* This is the maximum length for the description string for a device in the configuration + file. If the actual string in the file is longer, only the first MAX_STR_LEN characters + will be considered. + */ +#define MAX_STR_LEN 30 + +struct cydev { + cyusb_device *dev; /* as above ... */ + cyusb_handle *handle; /* as above ... */ + unsigned short vid; /* Vendor ID */ + unsigned short pid; /* Product ID */ + unsigned char is_open; /* When device is opened, val = 1 */ + unsigned char busnum; /* The bus number of this device */ + unsigned char devaddr; /* The device address*/ + unsigned char filler; /* Padding to make struct = 16 bytes */ +}; + +/* Function prototypes */ + +/******************************************************************************************* + Prototype : int cyusb_error(int err); + Description : Print out a verbose message corresponding to an error code, to the stderr + stream. + Parameters : + int err : Error code + Return Value : none + *******************************************************************************************/ +extern void cyusb_error(int err); + +/******************************************************************************************* + Prototype : int cyusb_open(void); + Description : This initializes the underlying libusb library, populates the cydev[] + array, and returns the number of devices of interest detected. A + 'device of interest' is a device which appears in the /etc/cyusb.conf file. + Parameters : None + Return Value : Returns an integer, equal to number of devices of interest detected. + *******************************************************************************************/ +extern int cyusb_open(void); + +/******************************************************************************************* + Prototype : int cyusb_open(unsigned short vid, unsigned short pid); + Description : This is an overloaded function that populates the cydev[] array with + just one device that matches the provided vendor ID and Product ID. + This function is only useful if you know in advance that there is only + one device with the given VID and PID attached to the host system. + Parameters : + unsigned short vid : Vendor ID + unsigned short pid : Product ID + Return Value : Returns 1 if a device of interest exists, else returns 0. + *******************************************************************************************/ +extern int cyusb_open(unsigned short vid, unsigned short pid); + +/******************************************************************************************* + Prototype : cyusb_handle * cyusb_gethandle(int index); + Description : This function returns a libusb_device_handle given an index from the cydev[] array. + Parameters : + int index : Equal to the index in the cydev[] array that gets populated + during the cyusb_open() call described above. + Return Value : Returns the pointer to a struct of type cyusb_handle, also called as + libusb_device_handle. + *******************************************************************************************/ +extern cyusb_handle * cyusb_gethandle(int index); + +/******************************************************************************************* + Prototype : unsigned short cyusb_getvendor(cyusb_handle *); + Description : This function returns a 16-bit value corresponding to the vendor ID given + a device's handle. + Parameters : + cyusb_handle *handle : Pointer to a struct of type cyusb_handle. + Return Value : Returns the 16-bit unique vendor ID of the given device. + *******************************************************************************************/ +extern unsigned short cyusb_getvendor(cyusb_handle *); + +/******************************************************************************************* + Prototype : unsigned short cyusb_getproduct(cyusb_handle *); + Description : This function returns a 16-bit value corresponding to the device ID given + a device's handle. + Parameters : + cyusb_handle *handle : Pointer to a struct of type cyusb_handle. + Return Value : Returns the 16-bit product ID of the given device. + *******************************************************************************************/ +extern unsigned short cyusb_getproduct(cyusb_handle *); + +/******************************************************************************************* + Prototype : void cyusb_close(void); + Description : This function closes the libusb library and releases memory allocated to cydev[]. + Parameters : none. + Return Value : none. + *******************************************************************************************/ +extern void cyusb_close(void); + +/******************************************************************************************* + Prototype : int cyusb_get_busnumber(cyusb_handle * handle); + Description : This function returns the Bus Number pertaining to a given device handle. + Parameters : + cyusb_handle *handle : The libusb device handle + Return Value : An integer value corresponding to the Bus Number on which the device resides. + This is also the same value present in the cydev[] array. + *******************************************************************************************/ +extern int cyusb_get_busnumber(cyusb_handle *); + +/******************************************************************************************* + Prototype : int cyusb_get_devaddr(cyusb_handle * handle); + Description : This function returns the device address pertaining to a given device handle + Parameters : + cyusb_handle *handle : The libusb device handle + Return Value : An integer value corresponding to the device address (between 1 and 127). + This is also the same value present in the cydev[] array. + *******************************************************************************************/ +extern int cyusb_get_devaddr(cyusb_handle *); + +/******************************************************************************************* + Prototype : int cyusb_get_max_packet_size(cyusb_handle * handle,unsigned char endpoint); + Description : This function returns the max packet size that an endpoint can handle, without + taking into account high-bandwidth capabiity. It is therefore only useful + for Bulk, not Isochronous endpoints. + Parameters : + cyusb_handle *handle : The libusb device handle + unsigned char endpoint : The endpoint number + Return Value : Max packet size in bytes for the endpoint. + *******************************************************************************************/ +extern int cyusb_get_max_packet_size(cyusb_handle *, unsigned char endpoint); + +/******************************************************************************************* + Prototype : int cyusb_get_max_iso_packet_size(cyusb_handle * handle,unsigned char endpoint); + Description : This function returns the max packet size that an isochronous endpoint can + handle, after considering multiple transactions per microframe if present. + Parameters : + cyusb_handle *handle : The libusb device handle + unsigned char endpoint : The endpoint number + Return Value : Maximum amount of data that an isochronous endpoint can transfer per + microframe. + *******************************************************************************************/ +extern int cyusb_get_max_iso_packet_size(cyusb_handle *, unsigned char endpoint); + +/******************************************************************************************* + Prototype : int cyusb_get_configuration(cyusb_handle * handle,int *config); + Description : This function determines the bConfiguration value of the active configuration. + Parameters : + cyusb_handle *handle : The libusb device handle + int * config : Address of an integer variable that will store the + currently active configuration number. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR + *******************************************************************************************/ +extern int cyusb_get_configuration(cyusb_handle *, int *config); + +/******************************************************************************************* + Prototype : int cyusb_set_configuration(cyusb_handle * handle,int config); + Description : This function sets the device's active configuration (standard request). + Parameters : + cyusb_handle *handle : The libusb device handle + int config : Configuration number required to be made active. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR + *******************************************************************************************/ +extern int cyusb_set_configuration(cyusb_handle *, int config); + +/******************************************************************************************* + Prototype : int cyusb_claim_interface(cyusb_handle * handle,int interface); + Description : This function claims an interface for a given device handle. + You must claim an interface before performing I/O operations on the device. + Parameters : + cyusb_handle *handle : The libusb device handle + int interface : The bInterfaceNumber of the interface you wish to claim. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR + *******************************************************************************************/ +extern int cyusb_claim_interface(cyusb_handle *, int interface); + +/********************************************************************************************* + Prototype : int cyusb_release_interface(cyusb_handle * handle,int interface); + Description : This function releases an interface previously claimed for a given device handle. + You must release all claimed interfaces before closing a device handle. + This is a blocking funcion, where a standard SET_INTERFACE control request is + sent to the device, resetting interface state to the first alternate setting. + Parameters : + cyusb_handle *handle : The libusb device handle + int interface : The bInterfaceNumber of the interface you wish to release + Return Value : 0 on success, or an appropriate LIBUSB_ERROR + *********************************************************************************************/ +extern int cyusb_release_interface(cyusb_handle *, int interface); + +/******************************************************************************************* + Prototype : int cyusb_set_interface_alt_setting(cyusb_handle * handle, + int interface,int altsetting); + Description : This function activates an alternate setting for an interface. + The interface itself must have been previously claimed using + cyusb_claim_interface. This is a blocking funcion, where a standard + control request is sent to the device. + Parameters : + cyusb_handle *handle : The libusb device handle + int interface : The bInterfaceNumber of the interface you wish to set, + int altsetting : The bAlternateSetting number to activate + Return Value : 0 on success, or an appropriate LIBUSB_ERROR + *****************************************************************************************/ +extern int cyusb_set_interface_alt_setting(cyusb_handle *, int interface, int altsetting); + +/******************************************************************************************* + Prototype : int cyusb_clear_halt(cyusb_handle * handle, unsigned char endpoint); + Description : This function clears a halt condition on an endpoint. + Endpoints with a halt condition are unable to send/receive data unless + the condition is specifically cleared by the Host. + This is a blocking funcion. + Parameters : + cyusb_handle *handle : The libusb device handle + unsigned char endpoint : The endpoint for which the clear request is sent. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR + *****************************************************************************************/ +extern int cyusb_clear_halt(cyusb_handle *, unsigned char endpoint); + +/****************************************************************************************** + Prototype : int cyusb_reset_device(cyusb_handle * handle); + Description : This function performs a USB port reset to the device. + This is a blocking funcion. + Parameters : + cyusb_handle *handle : The libusb device handle + Return Value : 0 on success, or an appropriate LIBUSB_ERROR + *****************************************************************************************/ +extern int cyusb_reset_device(cyusb_handle *); + +/****************************************************************************************** + Prototype : int cyusb_kernel_driver_active(cyusb_handle * handle, int interface); + Description : This function returns whether a kernel driver has already claimed an + interface. If a kernel driver is active and has claimed an interface, + cyusb cannot perform I/O operations on that interface unless the interface + is first released. + Parameters : + cyusb_handle *handle : The libusb device handle + int interface : The interface which you are testing. + Return Value : 0 if no kernel driver is active, 1 if a kernel driver is active; + LIBUSB error code in case of error. + *****************************************************************************************/ +extern int cyusb_kernel_driver_active(cyusb_handle *, int interface); + +/****************************************************************************************** + Prototype : int cyusb_detach_kernel_driver(cyusb_handle * handle, int interface); + Description : This function detaches a kernel mode driver in order for cyusb to claim + the interface. If a kernel driver is active and has claimed an interface, + cyusb cannot perform I/O operations on that interface unless the interface + is first released. + Parameters : + cyusb_handle *handle : The libusb device handle + int interface : The interface which you want to be detached. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + *****************************************************************************************/ +extern int cyusb_detach_kernel_driver(cyusb_handle *, int interface); + +/****************************************************************************************** + Prototype : int cyusb_attach_kernel_driver(cyusb_handle * handle, int interface); + Description : This function reattaches a kernel mode driver which was previously detached. + Parameters : + cyusb_handle *handle : The libusb device handle + int interface : The interface which you want to be reattached. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + *****************************************************************************************/ +extern int cyusb_attach_kernel_driver(cyusb_handle *, int interface); + +/****************************************************************************************** + Prototype : int cyusb_get_device_descriptot(cyusb_handle * handle, + struct libusb_device_descriptor *); + Description : This function returns the usb device descriptor for the given device. + Parameters : + cyusb_handle *handle : The libusb device handle + struct libusb_device_descriptor *desc : Address of a device_desc structure + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + *****************************************************************************************/ +extern int cyusb_get_device_descriptor(cyusb_handle *, struct libusb_device_descriptor *desc); + +/****************************************************************************************** + Prototype : int cyusb_get_active_config_descriptor(cyusb_handle * handle, + struct libusb_config_descriptor **); + Description : This function returns the usb configuration descriptor for the given device. + The descriptor structure must be freed with cyusb_free_config_descriptor() + explained below. + Parameters : + cyusb_handle *handle : The libusb device handle + struct libusb_configuration_descriptor **desc : Address of a config_descriptor + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + ******************************************************************************************/ +extern int cyusb_get_active_config_descriptor(cyusb_handle *, struct libusb_config_descriptor **config); + +/***************************************************************************************** + Prototype : int cyusb_get_config_descriptor(cyusb_handle * handle, unsigned char index, + struct libusb_config_descriptor **); + Description : This function returns the usb configuration descriptor with the specified + index for the given device. The descriptor structure must be freed using + the cyusb_free_config_descriptor() call later. + Parameters : + cyusb_handle *handle : The libusb device handle + unsigned char index : Index of configuration you wish to retrieve. + struct libusb_configuration_descriptor **desc : Address of a config_descriptor + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + *****************************************************************************************/ +extern int cyusb_get_config_descriptor(cyusb_handle *, unsigned char index, struct libusb_config_descriptor **config); + +/***************************************************************************************** + Prototype : void cyusb_free_config_descriptor(struct libusb_config_descriptor *); + Description : Frees the configuration descriptor obtained earlier. + Parameters : + struct libusb_config_descriptor *config : The config descriptor you wish to free. + Return Value : NIL. + *****************************************************************************************/ +extern void cyusb_free_config_descriptor(struct libusb_config_descriptor *config); + +/***************************************************************************************** + Prototype : void cyusb_control_transfer(cyusb_handle *h, unsigned char bmRequestType, + unsigned char bRequest, unsigned short wValue, unsigned short wIndex, + unsigned char *data, unsigned short wLength, unsigned int timeout); + Description : Performs a USB Control Transfer. Please note that this is a generic transfer + function which can be used for READ (IN) and WRITE (OUT) data transfers, as + well as transfers with no data. The direction bit (MSB) in the bmRequestType + parameter should be set in the case of READ requests, and cleared in the case + of WRITE requests. + Parameters : + cyusb_handle *h : Device handle + unsigned char bmRequestType : The request type field for the setup packet + unsigned char bRequest : The request field of the setup packet + unsigned short wValue : The value field of the setup packet + unsigned short wIndex : The index field of the setup packet + unsigned char *data : Data Buffer ( for input or output ) + unsigned short wLength : The length field of the setup packet. + unsigned int timeout : Timeout in milliseconds. 0 means no Timeout. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + ****************************************************************************************/ +extern int cyusb_control_transfer(cyusb_handle *h, unsigned char bmRequestType, unsigned char bRequest, + unsigned short wValue, unsigned short wIndex, unsigned char *data, unsigned short wLength, + unsigned int timeout); + +/***************************************************************************************** + Prototype : void cyusb_control_read(cyusb_handle *h, unsigned char bmRequestType, + unsigned char bRequest, unsigned short wValue, unsigned short wIndex, + unsigned char *data, unsigned short wLength, unsigned int timeout); + Description : Performs a USB control transfer including a READ (IN) data phase. Please + note that it is not advisable to use this function with wLength=0, because + most USB hosts/devices do not handle this case properly. + Parameters : + cyusb_handle *h : Device handle + unsigned char bmRequestType : The request type field for the setup packet + unsigned char bRequest : The request field of the setup packet + unsigned short wValue : The value field of the setup packet + unsigned short wIndex : The index field of the setup packet + unsigned char *data : Data Buffer ( for input or output ) + unsigned short wLength : The length field of the setup packet. + unsigned int timeout : Timeout in milliseconds. 0 means no Timeout. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + ****************************************************************************************/ +extern int cyusb_control_read(cyusb_handle *h, unsigned char bmRequestType, unsigned char bRequest, + unsigned short wValue, unsigned short wIndex, unsigned char *data, unsigned short wLength, + unsigned int timeout); + +/***************************************************************************************** + Prototype : void cyusb_control_write(cyusb_handle *h, unsigned char bmRequestType, + unsigned char bRequest, unsigned short wValue, unsigned short wIndex, + unsigned char *data, unsigned short wLength, unsigned int timeout); + Description : Performs a USB control transfer including a WRITE (OUT) data phase. + Parameters : + cyusb_handle *h : Device handle + unsigned char bmRequestType : The request type field for the setup packet + unsigned char bRequest : The request field of the setup packet + unsigned short wValue : The value field of the setup packet + unsigned short wIndex : The index field of the setup packet + unsigned char *data : Data Buffer ( for input or output ) + unsigned short wLength : The length field of the setup packet. + unsigned int timeout : Timeout in milliseconds. 0 means no Timeout. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + ****************************************************************************************/ +extern int cyusb_control_write(cyusb_handle *h, unsigned char bmRequestType, unsigned char bRequest, + unsigned short wValue, unsigned short wIndex, unsigned char *data, unsigned short wLength, + unsigned int timeout); + +/**************************************************************************************** + Prototype : void cyusb_bulk_transfer(cyusb_handle *h, unsigned char endpoint, + unsigned char *data, int length, int *transferred, int timeout); + Description : Performs a USB Bulk Transfer. + Parameters : + cyusb_handle *h : Device handle + unsigned char endpoint : Address of endpoint to comunicate with + unsigned char *data : Data Buffer ( for input or output ) + unsigned short wLength : The length field of the data buffer for read or write + int * transferred : Output location of bytes actually transferred + unsigned int timeout : Timeout in milliseconds. 0 means no Timeout. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + ****************************************************************************************/ +extern int cyusb_bulk_transfer(cyusb_handle *h, unsigned char endpoint, unsigned char *data, + int length, int *transferred, int timeout); + +/**************************************************************************************** + Prototype : void cyusb_interrupt_transfer(cyusb_handle *h, unsigned char endpoint, + unsigned char *data, int length, int *transferred, int timeout); + Description : Performs a USB Interrupt Transfer. + Parameters : + cyusb_handle *h : Device handle + unsigned char endpoint : Address of endpoint to comunicate with + unsigned char *data : Data Buffer ( for input or output ) + unsigned short wLength : The length field of the data buffer for read or write + int * transferred : Output location of bytes actually transferred + unsigned int timeout : Timeout in milliseconds. 0 means no Timeout. + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + ****************************************************************************************/ +extern int cyusb_interrupt_transfer(cyusb_handle *h, unsigned char endpoint, unsigned char *data, + int length, int *transferred, unsigned int timeout); + +/**************************************************************************************** + Prototype : void cyusb_download_fx2(cyusb_handle *h, char *filename, + unsigned char vendor_command); + Description : Performs firmware download on FX2. + Parameters : + cyusb_handle *h : Device handle + char * filename : Path where the firmware file is stored + unsigned char vendor_command : Vendor command that needs to be passed during download + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + ****************************************************************************************/ +extern int cyusb_download_fx2(cyusb_handle *h, char *filename, unsigned char vendor_command); + + +/**************************************************************************************** + Prototype : void cyusb_download_fx3(cyusb_handle *h, char *filename); + Description : Performs firmware download on FX3. + Parameters : + cyusb_handle *h : Device handle + char *filename : Path where the firmware file is stored + Return Value : 0 on success, or an appropriate LIBUSB_ERROR. + ***************************************************************************************/ +extern int cyusb_download_fx3(cyusb_handle *h, char *filename); + +#endif /* __CYUSB_H */ diff --git a/download_fx3.cpp b/download_fx3.cpp new file mode 100644 index 0000000..afc2afb --- /dev/null +++ b/download_fx3.cpp @@ -0,0 +1,850 @@ +/* + * Filename : download_fx3.cpp + * Description : Downloads FX3 firmware to RAM, I2C EEPROM or SPI Flash. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "cyusb.h" + +#define FLASHPROG_VID (0x04b4) // USB VID for the FX3 flash programmer. + +#define MAX_FWIMG_SIZE (512 * 1024) // Maximum size of the firmware binary. +#define MAX_WRITE_SIZE (2 * 1024) // Max. size of data that can be written through one vendor command. + +#define I2C_PAGE_SIZE (64) // Page size for I2C EEPROM. +#define I2C_SLAVE_SIZE (64 * 1024) // Max. size of data that can fit on one EEPROM address. + +#define SPI_PAGE_SIZE (256) // Page size for SPI flash memory. +#define SPI_SECTOR_SIZE (64 * 1024) // Sector size for SPI flash memory. + +#define VENDORCMD_TIMEOUT (5000) // Timeout (in milliseconds) for each vendor command. +#define GETHANDLE_TIMEOUT (5) // Timeout (in seconds) for getting a FX3 flash programmer handle. + +/* Utility macros. */ +#define ROUND_UP(n,v) ((((n) + ((v) - 1)) / (v)) * (v)) // Round n upto a multiple of v. +#define GET_LSW(v) ((unsigned short)((v) & 0xFFFF)) // Get Least Significant Word part of an integer. +#define GET_MSW(v) ((unsigned short)((v) >> 16)) // Get Most Significant Word part of an integer. + +/* Enumeration representing the FX3 firmware target. */ +typedef enum { + FW_TARGET_NONE = 0, // Invalid value + FW_TARGET_RAM, // Program to device RAM + FW_TARGET_I2C, // Program to I2C EEPROM + FW_TARGET_SPI // Program to SPI Flash +} fx3_fw_target; + +typedef enum { + FW_ACTION_NONE = 0, + FW_ACTION_STATUS, + FW_ACTION_LOAD, + FW_ACTION_READ, + FW_ACTION_ERASE +} fx3_ldr_action; + +/* Array representing physical size of EEPROM corresponding to each size encoding. */ +const int i2c_eeprom_size[] = +{ + 1024, // bImageCtl[2:0] = 'b000 + 2048, // bImageCtl[2:0] = 'b001 + 4096, // bImageCtl[2:0] = 'b010 + 8192, // bImageCtl[2:0] = 'b011 + 16384, // bImageCtl[2:0] = 'b100 + 32768, // bImageCtl[2:0] = 'b101 + 65536, // bImageCtl[2:0] = 'b110 + 131072 // bImageCtl[2:0] = 'b111 +}; + +static int +fx3_ram_read ( + cyusb_handle *h, + unsigned char *buf, + unsigned int ramAddress, + int len) +{ + int r; + int index = 0; + int size; + + while (len > 0) { + size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len; + r = cyusb_control_transfer (h, 0xC0, 0xA0, GET_LSW(ramAddress), GET_MSW(ramAddress), + &buf[index], size, VENDORCMD_TIMEOUT); + if (r != size) { + fprintf (stderr, "Error: Vendor read from FX3 RAM failed\n"); + return -1; + } + + ramAddress += size; + index += size; + len -= size; + } + + return 0; +} + +static int +fx3_ram_write ( + cyusb_handle *h, + unsigned char *buf, + unsigned int ramAddress, + int len) +{ + int r; + int index = 0; + int size; + + while (len > 0) { + size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len; + r = cyusb_control_transfer (h, 0x40, 0xA0, GET_LSW(ramAddress), GET_MSW(ramAddress), + &buf[index], size, VENDORCMD_TIMEOUT); + if (r != size) { + fprintf (stderr, "Error: Vendor write to FX3 RAM failed\n"); + return -1; + } + + ramAddress += size; + index += size; + len -= size; + } + + return 0; +} + +/* Read the firmware image from the file into a buffer. */ +static int +read_firmware_image ( + const char *filename, + unsigned char *buf, + int *romsize, + int *filesize) +{ + int fd; + int nbr; + struct stat filestat; + + if (stat (filename, &filestat) != 0) { + fprintf (stderr, "Error: Failed to stat file %s\n", filename); + return -1; + } + + // Verify that the file size does not exceed our limits. + *filesize = filestat.st_size; + if (*filesize > MAX_FWIMG_SIZE) { + fprintf (stderr, "Error: File size exceeds maximum firmware image size\n"); + return -2; + } + + fd = open (filename, O_RDONLY); + if (fd < 0) { + fprintf (stderr, "Error: File not found\n"); + return -3; + } + nbr = read (fd, buf, 2); /* Read first 2 bytes, must be equal to 'CY' */ + if (strncmp ((char *)buf,"CY",2)) { + fprintf (stderr, "Error: Image does not have 'CY' at start.\n"); + return -4; + } + nbr = read (fd, buf, 1); /* Read 1 byte. bImageCTL */ + if (buf[0] & 0x01) { + fprintf (stderr, "Error: Image does not contain executable code\n"); + return -5; + } + if (romsize != 0) + *romsize = i2c_eeprom_size[(buf[0] >> 1) & 0x07]; + + nbr = read (fd, buf, 1); /* Read 1 byte. bImageType */ + if (!(buf[0] == 0xB0)) { + fprintf (stderr, "Error: Not a normal FW binary with checksum\n"); + return -6; + } + + // Read the complete firmware binary into a local buffer. + lseek (fd, 0, SEEK_SET); + nbr = read (fd, buf, *filesize); + + close (fd); + return 0; +} + +static int +fx3_usbboot_download ( + cyusb_handle *h, + const char *filename) +{ + unsigned char *fwBuf; + unsigned int *data_p; + unsigned int i, checksum; + unsigned int address, length; + int r, index, filesize; + + fwBuf = (unsigned char *)calloc (1, MAX_FWIMG_SIZE); + if (fwBuf == 0) { + fprintf (stderr, "Error: Failed to allocate buffer to store firmware binary\n"); + return -1; + } + + // Read the firmware image into the local RAM buffer. + r = read_firmware_image (filename, fwBuf, NULL, &filesize); + if (r != 0) { + fprintf (stderr, "Error: Failed to read firmware file %s\n", filename); + free (fwBuf); + return -2; + } + + // Run through each section of code, and use vendor commands to download them to RAM. + index = 4; + checksum = 0; + while (index < filesize) { + data_p = (unsigned int *)(fwBuf + index); + length = data_p[0]; + address = data_p[1]; + if (length != 0) { + for (i = 0; i < length; i++) + checksum += data_p[2 + i]; + r = fx3_ram_write (h, fwBuf + index + 8, address, length * 4); + if (r != 0) { + fprintf (stderr, "Error: Failed to download data to FX3 RAM\n"); + free (fwBuf); + return -3; + } + } else { + if (checksum != data_p[2]) { + fprintf (stderr, "Error: Checksum error in firmware binary\n"); + free (fwBuf); + return -4; + } + + r = cyusb_control_transfer (h, 0x40, 0xA0, GET_LSW(address), GET_MSW(address), NULL, + 0, VENDORCMD_TIMEOUT); + if (r != 0) + printf ("Info: Ignored error in control transfer: %d\n", r); + break; + } + + index += (8 + length * 4); + } + + free (fwBuf); + return 0; +} + +/* Check if the current device handle corresponds to the FX3 flash programmer. */ +static int check_fx3_flashprog (cyusb_handle *handle) +{ + int r; + char local[8]; + + r = cyusb_control_transfer (handle, 0xC0, 0xB0, 0, 0, (unsigned char *)local, 8, VENDORCMD_TIMEOUT); + if ((r != 8) || (strncasecmp (local, "FX3PROG", 7) != 0)) { + printf ("Info: Current device is not the FX3 flash programmer\n"); + return -1; + } + + printf ("Info: Found FX3 flash programmer\n"); + return 0; +} + +/* Get the handle to the FX3 flash programmer device, if found. */ +static int +get_fx3_prog_handle ( + cyusb_handle **h) +{ + char *progfile_p, *tmp; + cyusb_handle *handle; + int i, j, r; + struct stat filestat; + + handle = *h; + r = check_fx3_flashprog (handle); + if (r == 0) + return 0; + + printf ("Info: Trying to download flash programmer to RAM\n"); + + tmp = getenv ("CYUSB_ROOT"); + if (tmp != NULL) { + i = strlen (tmp); + progfile_p = (char *)malloc (i + 32); + strcpy (progfile_p, tmp); + strcat (progfile_p, "/fx3_images/cyfxflashprog.img"); + } + else { + progfile_p = (char *)malloc (32); + strcpy (progfile_p, "fx3_images/cyfxflashprog.img"); + } + + r = stat (progfile_p, &filestat); + if (r != 0) { + fprintf (stderr, "Error: Failed to find cyfxflashprog.img file\n"); + return -1; + } + + r = fx3_usbboot_download (handle, progfile_p); + free (progfile_p); + if (r != 0) { + fprintf (stderr, "Error: Failed to download flash prog utility\n"); + return -1; + } + + cyusb_close (); + *h = NULL; + + // Now wait for the flash programmer to enumerate, and get a handle to it. + for (j = 0; j < GETHANDLE_TIMEOUT; j++) { + sleep (1); + r = cyusb_open (); + if (r > 0) { + for (i = 0; i < r; i++) { + handle = cyusb_gethandle (i); + if (cyusb_getvendor (handle) == FLASHPROG_VID) { + r = check_fx3_flashprog (handle); + if (r == 0) { + printf ("Info: Got handle to FX3 flash programmer\n"); + *h = handle; + return 0; + } + } + } + printf("cyusb close\n"); + cyusb_close (); + printf("cyusb closeD!\n"); + } + } + + fprintf (stderr, "Error: Failed to get handle to flash programmer\n"); + return -2; +} + +static int fx3_init_progimg(cyusb_handle* h) { + int r = 0; + + r = get_fx3_prog_handle(&h); + if (r != 0) { + fprintf (stderr, "Error: FX3 flash programmer not found\n"); + return -1; + } + + return 0; +} + +static int +fx3_i2c_write ( + cyusb_handle *h, + unsigned char *buf, + int devAddr, + int start, + int len) +{ + int r = 0; + int index = start; + unsigned short address = 0; + int size; + + while (len > 0) { + size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len; + r = cyusb_control_transfer (h, 0x40, 0xBA, devAddr, address, &buf[index], size, VENDORCMD_TIMEOUT); + if (r != size) { + fprintf (stderr, "Error: I2C write failed\n"); + return -1; + } + + address += size ; + index += size; + len -= size; + } + + return 0; +} + +/* Function to read I2C data and compare against the expected value. */ +static int +fx3_i2c_read ( + cyusb_handle *h, + uint8_t* dst, + int devAddr, + int len) +{ + int r = 0; + int index = 0; + unsigned short address = 0; + int size; + + while (len > 0) { + size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len; + r = cyusb_control_transfer (h, 0xC0, 0xBB, devAddr, address, dst, size, VENDORCMD_TIMEOUT); + if (r != size) { + fprintf (stderr, "Error: I2C read failed\n"); + return -1; + } + + address += size; + index += size; + len -= size; + dst += size; + } + + return 0; +} + +/* Function to read I2C data and compare against the expected value. */ +static int +fx3_i2c_read_verify ( + cyusb_handle *h, + unsigned char *expData, + int devAddr, + int len) +{ + int r = 0; + int index = 0; + unsigned short address = 0; + int size; + unsigned char tmpBuf[MAX_WRITE_SIZE]; + + while (len > 0) { + size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len; + r = cyusb_control_transfer (h, 0xC0, 0xBB, devAddr, address, tmpBuf, size, VENDORCMD_TIMEOUT); + if (r != size) { + fprintf (stderr, "Error: I2C read failed\n"); + return -1; + } + + if (memcmp (expData + index, tmpBuf, size) != 0) { + fprintf (stderr, "Error: Failed to read expected data from I2C EEPROM\n"); + return -2; + } + + address += size ; + index += size; + len -= size; + } + + return 0; +} + +int +fx3_i2cboot_download ( + cyusb_handle *h, + const char *filename) +{ + int romsize, size; + int address = 0, offset = 0; + int r, filesize; + unsigned char *fwBuf = 0; + + // Check if we have a handle to the FX3 flash programmer. + r = get_fx3_prog_handle (&h); + if (r != 0) { + fprintf (stderr, "Error: FX3 flash programmer not found\n"); + return -1; + } + + // Allocate memory for holding the firmware binary. + fwBuf = (unsigned char *)calloc (1, MAX_FWIMG_SIZE); + + if (read_firmware_image (filename, fwBuf, &romsize, &filesize)) { + fprintf (stderr, "Error: File %s does not contain valid FX3 firmware image\n", filename); + free (fwBuf); + return -2; + } + + printf ("Info: Writing firmware image to I2C EEPROM\n"); + + filesize = ROUND_UP(filesize, I2C_PAGE_SIZE); + while (filesize != 0) { + + size = (filesize <= romsize) ? filesize : romsize; + if (size > I2C_SLAVE_SIZE) { + r = fx3_i2c_write (h, fwBuf, address, offset, I2C_SLAVE_SIZE); + if (r == 0) { + r = fx3_i2c_read_verify (h, fwBuf + offset, address, I2C_SLAVE_SIZE); + if (r != 0) { + fprintf (stderr, "Error: Read-verify from I2C EEPROM failed\n"); + free (fwBuf); + return -3; + } + + r = fx3_i2c_write (h, fwBuf, address + 4, offset + I2C_SLAVE_SIZE, size - I2C_SLAVE_SIZE); + if (r == 0) { + r = fx3_i2c_read_verify (h, fwBuf + offset + I2C_SLAVE_SIZE, + address + 4, size - I2C_SLAVE_SIZE); + if (r != 0) { + fprintf (stderr, "Error: Read-verify from I2C EEPROM failed\n"); + free (fwBuf); + return -3; + } + } + } + } else { + r = fx3_i2c_write (h, fwBuf, address, offset, size); + if (r == 0) { + r = fx3_i2c_read_verify (h, fwBuf + offset, address, size); + if (r != 0) { + fprintf (stderr, "Error: Read-verify from I2C EEPROM failed\n"); + free (fwBuf); + return -3; + } + } + } + + if (r != 0) { + fprintf (stderr, "Error: Write to I2C EEPROM failed\n"); + free (fwBuf); + return -4; + } + + /* Move to the next slave address. */ + offset += size; + filesize -= size; + address++; + } + + free (fwBuf); + printf ("Info: I2C programming completed\n"); + return 0; +} + +static int +fx3_spi_read ( + cyusb_handle *h, + unsigned char *buf, + int offset, + int len) +{ + int r = 0; + int index = 0; + int size; + unsigned short page_address = offset / SPI_PAGE_SIZE; + + while (len > 0) { + size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len; + r = cyusb_control_transfer (h, 0xC0, 0xC2, 0, page_address, &buf[index], size, VENDORCMD_TIMEOUT); // TODO: C3? C1? + if (r != size) { + fprintf (stderr, "Error: Read from SPI flash failed\n"); + return -1; + } + index += size; + len -= size; + page_address += (size / SPI_PAGE_SIZE); + } + + return 0; +} + +static int +fx3_spi_write ( + cyusb_handle *h, + unsigned char *buf, + int len) +{ + int r = 0; + int index = 0; + int size; + unsigned short page_address = 0; + + while (len > 0) { + size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len; + r = cyusb_control_transfer (h, 0x40, 0xC2, 0, page_address, &buf[index], size, VENDORCMD_TIMEOUT); + if (r != size) { + fprintf (stderr, "Error: Write to SPI flash failed\n"); + return -1; + } + index += size; + len -= size; + page_address += (size / SPI_PAGE_SIZE); + } + + return 0; +} + +static int +fx3_spi_erase_sector ( + cyusb_handle *h, + unsigned short nsector) +{ + unsigned char stat; + int timeout = 10; + int r; + + r = cyusb_control_transfer (h, 0x40, 0xC4, 1, nsector, NULL, 0, VENDORCMD_TIMEOUT); + if (r != 0) { + fprintf (stderr, "Error: SPI sector erase failed\n"); + return -1; + } + + // Wait for the SPI flash to become ready again. + do { + r = cyusb_control_transfer (h, 0xC0, 0xC4, 0, 0, &stat, 1, VENDORCMD_TIMEOUT); + if (r != 1) { + fprintf (stderr, "Error: SPI status read failed\n"); + return -2; + } + sleep (1); + timeout--; + } while ((stat != 0) && (timeout > 0)); + + if (stat != 0) { + fprintf (stderr, "Error: Timed out on SPI status read\n"); + return -3; + } + + printf ("Info: Erased sector %d of SPI flash\n", nsector); + return 0; +} + +int +fx3_spiboot_download ( + cyusb_handle *h, + const char *filename) +{ + unsigned char *fwBuf; + int r, i, filesize; + + // Check if we have a handle to the FX3 flash programmer. + r = get_fx3_prog_handle (&h); + if (r != 0) { + fprintf (stderr, "Error: FX3 flash programmer not found\n"); + return -1; + } + + // Allocate memory for holding the firmware binary. + fwBuf = (unsigned char *)calloc (1, MAX_FWIMG_SIZE); + if (fwBuf == 0) { + fprintf (stderr, "Error: Failed to allocate buffer to store firmware binary\n"); + return -2; + } + + if (read_firmware_image (filename, fwBuf, NULL, &filesize)) { + fprintf (stderr, "Error: File %s does not contain valid FX3 firmware image\n", filename); + free (fwBuf); + return -3; + } + + filesize = ROUND_UP(filesize, SPI_PAGE_SIZE); + + // Erase as many SPI sectors as are required to hold the firmware binary. + for (i = 0; i < ((filesize + SPI_SECTOR_SIZE - 1) / SPI_SECTOR_SIZE); i++) { + r = fx3_spi_erase_sector (h, i); + if (r != 0) { + fprintf (stderr, "Error: Failed to erase SPI flash\n"); + free (fwBuf); + return -4; + } + } + + r = fx3_spi_write (h, fwBuf, filesize); + if (r != 0) { + fprintf (stderr, "Error: SPI write failed\n"); + } else { + printf ("Info: SPI flash programming completed\n"); + } + + free (fwBuf); + return r; +} + +void +print_usage_info ( + const char *arg0) +{ + printf ("%s: FX3 firmware programmer\n\n", arg0); + printf ("Usage:\n"); + printf ("\t%s -h: Print this help message\n\n", arg0); + printf ("\t%s-a status: display device status\n", arg0); + printf ("\t%s-a load -t -i : Program firmware binary from to \n", arg0); + printf ("\t\t\twhere is one of:\n"); + printf ("\t\t\t\t\"RAM\": Program to FX3 RAM\n"); + printf ("\t\t\t\t\"I2C\": Program to I2C EEPROM\n"); + printf ("\t\t\t\t\"SPI\": Program to SPI FLASH\n"); + printf ("\t%s-a read -t -i -o -l : read data from into at the specified offset and length (in bytes)\n", arg0); + printf ("\t%s-a erase -t -o : erase sector # from \n", arg0); + printf ("\n\n"); +} + +int main ( + int argc, + char **argv) +{ + char *filename = NULL; + char *tgt_str = NULL; + fx3_fw_target tgt = FW_TARGET_NONE; + fx3_ldr_action act = FW_ACTION_NONE; + long offset = -1, length = -1; + + /* Parse command line arguments. */ + for (int i = 1; i < argc; i++) { + if ((strcasecmp (argv[i], "-h") == 0) || (strcasecmp (argv[i], "--help") == 0)) { + print_usage_info (argv[0]); + return 0; + } else { + if ((strcasecmp (argv[i], "-t") == 0) || (strcasecmp (argv[i], "--target") == 0)) { + if (argc > (i + 1)) { + tgt_str = argv[i + 1]; + if (strcasecmp (argv[i + 1], "ram") == 0) + tgt = FW_TARGET_RAM; + if (strcasecmp (argv[i + 1], "i2c") == 0) + tgt = FW_TARGET_I2C; + if (strcasecmp (argv[i + 1], "spi") == 0) + tgt = FW_TARGET_SPI; + if (tgt == FW_TARGET_NONE) { + fprintf (stderr, "Error: Unknown target %s\n", argv[i + 1]); + print_usage_info (argv[0]); + return 1; + } + } + i++; + } else if (!strcmp(argv[i], "-a")) { + if (argc > i + 1) { + const char* acts = argv[i+1]; + if (!strcasecmp(acts, "status")) act = FW_ACTION_STATUS; + if (!strcasecmp(acts, "load")) act = FW_ACTION_LOAD; + if (!strcasecmp(acts, "read")) act = FW_ACTION_READ; + if (!strcasecmp(acts, "erase")) act = FW_ACTION_ERASE; + if (act == FW_ACTION_NONE) { + fprintf(stderr, "Error: unknown action '%s'\n", acts); + print_usage_info(argv[0]); + return 1; + } + } + ++i; + } else if ((strcmp (argv[i], "-i") == 0) || (strcmp (argv[i], "--image") == 0)) { + if (argc > (i + 1)) + filename = argv[i + 1]; + i++; + } else if (!strcmp(argv[i], "-o")) { + if (argc > i + 1) + if (sscanf(argv[i+1], "%li", &offset) != 1) + offset = -1; + ++i; + } else if (!strcmp(argv[i], "-l")) { + if (argc > i + 1) + if (sscanf(argv[i+1], "%li", &length) != 1) + length = -1; + ++i; + } else { + fprintf (stderr, "Error: Unknown parameter %s\n", argv[i]); + print_usage_info (argv[0]); + return 1; + } + } + } + int r = cyusb_open (); + if (r < 0) { + fprintf (stderr, "Error opening library\n"); + return 2; + } + else if (r == 0) { + fprintf (stderr, "Error: No FX3 device found\n"); + return 2; + } + else if (r > 1) { + fprintf (stderr, "Error: More than one Cypress device found\n"); + return 1; + } + + cyusb_handle *h = cyusb_gethandle (0); + + switch (act) { + case FW_ACTION_STATUS: + r = check_fx3_flashprog(h); + break; + case FW_ACTION_LOAD: + if ((filename == NULL) || (tgt == FW_TARGET_NONE)) { + fprintf (stderr, "Error: Firmware binary or target not specified\n"); + print_usage_info (argv[0]); + return 1; + } + + switch (tgt) { + case FW_TARGET_RAM: + r = fx3_usbboot_download (h, filename); + break; + case FW_TARGET_I2C: + r = fx3_i2cboot_download (h, filename); + break; + case FW_TARGET_SPI: + r = fx3_spiboot_download (h, filename); + break; + default: break; + } + break; + case FW_ACTION_READ: { + if ((filename == NULL) || (tgt == FW_TARGET_NONE)) { + fprintf (stderr, "Error: Firmware binary or target not specified\n"); + print_usage_info (argv[0]); + return 1; + } + + if (length <= 0 || offset == -1) { + fprintf(stderr, "Error: need an offset and length (both in bytes) specified.\n"); + return 1; + } + uint8_t *databuf = (uint8_t*)calloc(1, length); + + switch (tgt) { + case FW_TARGET_RAM: + r = fx3_ram_read(h, databuf, offset, length); + break; + case FW_TARGET_I2C: + r = fx3_init_progimg(h); + printf("progimg done\n"); + if (r == 0) r = fx3_i2c_read(h, databuf, offset, length); + printf("read done\n"); + break; + case FW_TARGET_SPI: + r = fx3_init_progimg(h); + if (r == 0) r = fx3_spi_read(h, databuf, offset, length); + break; + default: + r = -1; + break; + } + + if (r == 0) { + FILE* rf = fopen(filename, "wb"); + fwrite(databuf, 1, length, rf); + fclose(rf); + } + free(databuf); + } break; + case FW_ACTION_ERASE: + if ((tgt == FW_TARGET_NONE)) { + fprintf (stderr, "Error: Target not specified\n"); + print_usage_info (argv[0]); + return 1; + } + + if (offset <= 0) { + fprintf(stderr, "Error: need an offset (in sectors) specified.\n"); + return 1; + } + + switch (tgt) { + case FW_TARGET_SPI: + r = fx3_init_progimg(h); + if (r == 0) r = fx3_spi_erase_sector(h, offset); + break; + default: + printf("Info: target does not support erasing\n"); + break; + } + default: break; + } + + if (r != 0) { + fprintf (stderr, "Error: FX3 firmware programming failed\n"); + return 3; + } else { + printf ("FX3 firmware programming to %s completed\n", tgt_str); + } + + return 0; +} diff --git a/libcyusb.cpp b/libcyusb.cpp new file mode 100644 index 0000000..76593d9 --- /dev/null +++ b/libcyusb.cpp @@ -0,0 +1,918 @@ +/*******************************************************************************\ + * Program Name : libcyusb.cpp * + * Author : V. Radhakrishnan ( rk@atr-labs.com ) * + * License : LGPL Ver 2.1 * + * Copyright : Cypress Semiconductors Inc. / ATR-LABS * + * Date written : March 12, 2012 * + * Modification Notes : * + * * + * This program is the main library for all cyusb applications to use. * + * This is a thin wrapper around libusb * + \*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include "cyusb.h" + +/* Maximum length of a string read from the Configuration file (/etc/cyusb.conf) for the library. */ +#define MAX_CFG_LINE_LENGTH (120) + +/* Maximum length for a filename. */ +#define MAX_FILEPATH_LENGTH (256) + +/* Maximum size of EZ-USB FX3 firmware binary. Limited by amount of RAM available. */ +#define FX3_MAX_FW_SIZE (524288) + +static struct cydev cydev[MAXDEVICES]; /* List of devices of interest that are connected. */ +static int nid; /* Number of Interesting Devices. */ +static libusb_device **list; /* libusb device list used by the cyusb library. */ + +/* + struct VPD + Used to store information about the devices of interest listed in /etc/cyusb.conf + */ +struct VPD { + unsigned short vid; /* USB Vendor ID. */ + unsigned short pid; /* USB Product ID. */ + char desc[MAX_STR_LEN]; /* Device description. */ +}; + +static struct VPD vpd[MAX_ID_PAIRS]; /* Known device database. */ +static int maxdevices; /* Number of devices in the vpd database. */ +static unsigned int checksum = 0; /* Checksum calculated on the Cypress firmware binary. */ + +/* The following variables are used by the cyusb_linux application. */ + char pidfile[MAX_FILEPATH_LENGTH]; /* Full path to the PID file specified in /etc/cyusb.conf */ + char logfile[MAX_FILEPATH_LENGTH]; /* Full path to the LOG file specified in /etc/cyusb.conf */ + int logfd; /* File descriptor for the LOG file. */ + int pidfd; /* File descriptor for the PID file. */ + +/* isempty: + Check if the first L characters of the string buf are white-space characters. + */ +static bool +isempty ( + char *buf, + int L) +{ + bool flag = true; + int i; + + for (i = 0; i < L; ++i ) { + if ( (buf[i] != ' ') && ( buf[i] != '\t' ) ) { + flag = false; + break; + } + } + + return flag; +} + +/* parse_configfile: + Parse the /etc/cyusb.conf file and get the list of USB devices of interest. + */ +static void +parse_configfile ( + void) +{ + FILE *inp; + char buf[MAX_CFG_LINE_LENGTH]; + char *cp1, *cp2, *cp3; + int i; + + inp = fopen("/etc/cyusb.conf", "r"); + if (inp == NULL) + return; + + memset(buf,'\0',MAX_CFG_LINE_LENGTH); + while ( fgets(buf,MAX_CFG_LINE_LENGTH,inp) ) { + if ( buf[0] == '#' ) /* Any line starting with a # is a comment */ + continue; + if ( buf[0] == '\n' ) + continue; + if ( isempty(buf,strlen(buf)) ) /* Any blank line is also ignored */ + continue; + + cp1 = strtok(buf," =\t\n"); + if ( !strcmp(cp1,"LogFile") ) { + cp2 = strtok(NULL," \t\n"); + strcpy(logfile,cp2); + } + else if ( !strcmp(cp1,"PIDFile") ) { + cp2 = strtok(NULL," \t\n"); + strcpy(pidfile,cp2); + } + else if ( !strcmp(cp1,"") ) { + while ( fgets(buf,MAX_CFG_LINE_LENGTH,inp) ) { + if ( buf[0] == '#' ) /* Any line starting with a # is a comment */ + continue; + if ( buf[0] == '\n' ) + continue; + if ( isempty(buf,strlen(buf)) ) /* Any blank line is also ignored */ + continue; + if ( maxdevices == (MAX_ID_PAIRS - 1) ) + continue; + cp1 = strtok(buf," \t\n"); + if ( !strcmp(cp1,"") ) + break; + cp2 = strtok(NULL, " \t"); + cp3 = strtok(NULL, " \t\n"); + + vpd[maxdevices].vid = strtol(cp1,NULL,16); + vpd[maxdevices].pid = strtol(cp2,NULL,16); + strncpy(vpd[maxdevices].desc,cp3,MAX_STR_LEN); + vpd[maxdevices].desc[MAX_STR_LEN - 1] = '\0'; /* Make sure of NULL-termination. */ + + ++maxdevices; + } + } + else { + printf("Error in config file /etc/cyusb.conf: %s \n",buf); + exit(1); + } + } + + fclose(inp); +} + +/* device_is_of_interest: + Check whether the current USB device is among the devices of interest. + */ +static int +device_is_of_interest ( + cyusb_device *d) +{ + int i; + int found = 0; + struct libusb_device_descriptor desc; + int vid; + int pid; + + libusb_get_device_descriptor(d, &desc); + vid = desc.idProduct; + pid = desc.idProduct; + + for ( i = 0; i < maxdevices; ++i ) { + if ( (vpd[i].vid == desc.idVendor) && (vpd[i].pid == desc.idProduct) ) { + found = 1; + break; + } + } + return found; +} + +/* cyusb_getvendor: + Get the Vendor ID for the current USB device. + */ +unsigned short +cyusb_getvendor ( + cyusb_handle *h) +{ + struct libusb_device_descriptor d; + cyusb_get_device_descriptor(h, &d); + return d.idVendor; +} + +/* cyusb_getproduct: + Get the Product ID for the current USB device. +*/ +unsigned short +cyusb_getproduct ( + cyusb_handle *h) +{ + struct libusb_device_descriptor d; + cyusb_get_device_descriptor(h, &d); + return d.idProduct; +} + +/* renumerate: + Get handles to and store information about all USB devices of interest. + */ +static int +renumerate ( + void) +{ + cyusb_device *dev = NULL; + cyusb_handle *handle = NULL; + int numdev; + int found = 0; + int i; + int r; + + numdev = libusb_get_device_list(NULL, &list); + if ( numdev < 0 ) { + printf("Library: Error in enumerating devices...\n"); + return -ENODEV; + } + + nid = 0; + for ( i = 0; i < numdev; ++i ) { + cyusb_device *tdev = list[i]; + if ( device_is_of_interest(tdev) ) { + cydev[nid].dev = tdev; + r = libusb_open(tdev, &cydev[nid].handle); + if ( r ) { + printf("Error in opening device\n"); + return -EACCES; + } + else + handle = cydev[nid].handle; + + cydev[nid].vid = cyusb_getvendor(handle); + cydev[nid].pid = cyusb_getproduct(handle); + cydev[nid].is_open = 1; + cydev[nid].busnum = cyusb_get_busnumber(handle); + cydev[nid].devaddr = cyusb_get_devaddr(handle); + ++nid; + } + } + + return nid; +} + +/* cyusb_open: + Opens handles to all USB devices of interest, and returns their count. + */ +int cyusb_open ( + void) +{ + int fd1; + int r; + + fd1 = open("/etc/cyusb.conf", O_RDONLY); + if ( fd1 < 0 ) { + printf("/etc/cyusb.conf file not found. Exiting\n"); + return -ENOENT; + } + else { + close(fd1); + parse_configfile(); /* Parse the file and store information inside exported data structures */ + } + + r = libusb_init(NULL); + if (r) { + printf("Error in initializing libusb library...\n"); + return -EACCES; + } + + /* Get list of USB devices of interest. */ + r = renumerate(); + return r; +} + +/* cyusb_open: + Open a handle to the USB device with specified vid/pid. + */ +int cyusb_open ( + unsigned short vid, + unsigned short pid) +{ + int r; + cyusb_handle *h = NULL; + + r = libusb_init(NULL); + if (r) { + printf("Error in initializing libusb library...\n"); + return -EACCES; + } + + h = libusb_open_device_with_vid_pid(NULL, vid, pid); + if ( !h ) { + printf("Device not found\n"); + return -ENODEV; + } + + cydev[0].dev = libusb_get_device(h); + cydev[0].handle = h; + cydev[0].vid = cyusb_getvendor(h); + cydev[0].pid = cyusb_getproduct(h); + cydev[0].is_open = 1; + cydev[0].busnum = cyusb_get_busnumber(h); + cydev[0].devaddr = cyusb_get_devaddr(h); + nid = 1; + + return 1; +} + +/* cyusb_error: + Print verbose information about the error returned by the cyusb API. These are essentially descriptions of + status values defined as part of the libusb library. + */ +void +cyusb_error ( + int err) +{ + switch (err) + { + case -1: + fprintf(stderr, "Input/output error\n"); + break; + case -2: + fprintf(stderr, "Invalid parameter\n"); + break; + case -3: + fprintf(stderr, "Access denied (insufficient permissions)\n"); + break; + case -4: + fprintf(stderr, "No such device. Disconnected...?\n"); + break; + case -5: + fprintf(stderr, "Entity not found\n"); + break; + case -6: + fprintf(stderr, "Resource busy\n"); + break; + case -7: + fprintf(stderr, "Operation timed out\n"); + break; + case -8: + fprintf(stderr, "Overflow\n"); + break; + case -9: + fprintf(stderr, "Pipe error\n"); + break; + case -10: + fprintf(stderr, "System call interrupted, ( due to signal ? )\n"); + break; + case -11: + fprintf(stderr, "Insufficient memory\n"); + break; + case -12: + fprintf(stderr, "Operation not supported/implemented\n"); + break; + default: + fprintf(stderr, "Unknown internal error\n"); + break; + } +} + +/* cyusb_gethandle: + Get a handle to the USB device with specified index. + */ +cyusb_handle * +cyusb_gethandle ( + int index) +{ + return cydev[index].handle; +} + +/* cyusb_close: + Close all device handles and de-initialize the libusb library. + */ +void +cyusb_close ( + void) +{ + int i; + + for ( i = 0; i < nid; ++i ) { + libusb_close(cydev[i].handle); + } + + libusb_free_device_list(list, 1); + libusb_exit(NULL); +} + +/* cyusb_get_busnumber: + Get USB bus number on which the specified device is connected. + */ +int +cyusb_get_busnumber ( + cyusb_handle *h) +{ + cyusb_device *tdev = libusb_get_device(h); + return libusb_get_bus_number( tdev ); +} + +/* cyusb_get_devaddr: + Get USB device address assigned to the specified device. + */ +int +cyusb_get_devaddr ( + cyusb_handle *h) +{ + cyusb_device *tdev = libusb_get_device(h); + return libusb_get_device_address( tdev ); +} + +/* cyusb_get_max_packet_size: + Get the max packet size for the specified USB endpoint. + */ +int +cyusb_get_max_packet_size ( + cyusb_handle *h, + unsigned char endpoint) +{ + cyusb_device *tdev = libusb_get_device(h); + return ( libusb_get_max_packet_size(tdev, endpoint) ); +} + +/* cyusb_get_max_iso_packet_size: + Calculate the maximum packet size which a specific isochronous endpoint is capable of sending or + receiving in the duration of 1 microframe. + */ +int +cyusb_get_max_iso_packet_size ( + cyusb_handle *h, + unsigned char endpoint) +{ + cyusb_device *tdev = libusb_get_device(h); + return ( libusb_get_max_iso_packet_size(tdev, endpoint) ); +} + +/* cyusb_get_configuration: + Get the id of the active configuration on the specified USB device. + */ +int +cyusb_get_configuration ( + cyusb_handle *h, + int *config) +{ + return ( libusb_get_configuration(h, config) ); +} + +/* cyusb_set_configuration: + Set the active configuration for the specified USB device. + */ +int +cyusb_set_configuration ( + cyusb_handle *h, + int config) +{ + return ( libusb_set_configuration(h, config) ); +} + +/* cyusb_claim_interface: + Claim the specified USB device interface, so that cyusb/libusb APIs can be used for transfers on the + corresponding endpoints. + */ +int +cyusb_claim_interface ( + cyusb_handle *h, + int interface) +{ + return ( libusb_claim_interface(h, interface) ); +} + +/* cyusb_release_interface: + Release an interface previously claimed using cyusb_claim_interface. + */ +int +cyusb_release_interface ( + cyusb_handle *h, + int interface) +{ + return ( libusb_release_interface(h, interface) ); +} + +/* cyusb_set_interface_alt_setting: + Select the alternate setting for the specified USB device and interface. + */ +int +cyusb_set_interface_alt_setting ( + cyusb_handle *h, + int interface, + int altsetting) +{ + return ( libusb_set_interface_alt_setting(h, interface, altsetting) ); +} + +/* cyusb_clear_halt: + Clear the STALL or HALT condition on the specified endpoint. + */ +int +cyusb_clear_halt ( + cyusb_handle *h, + unsigned char endpoint) +{ + return ( libusb_clear_halt(h, endpoint) ); +} + +/* cyusb_reset_device: + Perform a USB port reset to re-initialize the specified USB device. + */ +int +cyusb_reset_device ( + cyusb_handle *h) +{ + return ( libusb_reset_device(h) ); +} + +/* cyusb_kernel_driver_active: + Check whether a Kernel driver is currently bound to the specified USB device and interface. The cyusb/libusb + APIs cannot be used on an interface while a kernel driver is active. + */ +int +cyusb_kernel_driver_active ( + cyusb_handle *h, + int interface) +{ + return ( libusb_kernel_driver_active(h, interface) ); +} + +/* cyusb_detach_kernel_driver: + Detach the kernel driver from the specified USB device and interface, so that the APIs can be used. + */ +int +cyusb_detach_kernel_driver ( + cyusb_handle *h, + int interface) +{ + return ( libusb_detach_kernel_driver(h, interface) ); +} + +/* cyusb_attach_kernel_driver: + Re-attach the kernel driver from the specified USB device and interface. + */ +int +cyusb_attach_kernel_driver ( + cyusb_handle *h, + int interface) +{ + return ( libusb_attach_kernel_driver(h, interface) ); +} + +/* cyusb_get_device_descriptor: + Get the device descriptor for the specified USB device. + */ +int +cyusb_get_device_descriptor ( + cyusb_handle *h, + struct libusb_device_descriptor *desc) +{ + cyusb_device *tdev = libusb_get_device(h); + return ( libusb_get_device_descriptor(tdev, desc ) ); +} + +/* cyusb_get_active_config_descriptor: + Get the active configuration descriptor for the specified USB device. + */ +int +cyusb_get_active_config_descriptor ( + cyusb_handle *h, + struct libusb_config_descriptor **config) +{ + cyusb_device *tdev = libusb_get_device(h); + return ( libusb_get_active_config_descriptor(tdev, config) ); +} + +/* cyusb_get_config_descriptor: + Get the configuration descriptor at index config_index, for the specified USB device. + */ +int +cyusb_get_config_descriptor ( + cyusb_handle *h, + unsigned char config_index, + struct libusb_config_descriptor **config) +{ + cyusb_device *tdev = libusb_get_device(h); + return ( libusb_get_config_descriptor(tdev, config_index, config) ); +} + +/* cyusb_get_config_descriptor_by_value: + Get the configuration descriptor with value bConfigurationValue for the specified USB device. + */ +int +cyusb_get_config_descriptor_by_value ( + cyusb_handle *h, + unsigned char bConfigurationValue, + struct usb_config_descriptor **config) +{ + cyusb_device *tdev = libusb_get_device(h); + return ( libusb_get_config_descriptor_by_value(tdev, bConfigurationValue, + (struct libusb_config_descriptor **)config) ); +} + +/* cyusb_free_config_descriptor: + Free the configuration descriptor structure after it has been used. + */ +void +cyusb_free_config_descriptor ( + struct libusb_config_descriptor *config) +{ + libusb_free_config_descriptor( (libusb_config_descriptor *)config ); +} + +/* cyusb_get_string_descriptor_ascii: + Get the specified USB string descriptor in the format of an ASCII string. + */ +int +cyusb_get_string_descriptor_ascii ( + cyusb_handle *h, + unsigned char index, + unsigned char *data, + int length) +{ + cyusb_device *tdev = libusb_get_device(h); + return ( libusb_get_string_descriptor_ascii(h, index, data, length) ); +} + +/* cyusb_get_descriptor: + Get a USB descriptor given the type and index. + */ +int +cyusb_get_descriptor ( + cyusb_handle *h, + unsigned char desc_type, + unsigned char desc_index, + unsigned char *data, + int len) +{ + return ( libusb_get_descriptor(h, desc_type, desc_index, data, len) ); +} + +/* cyusb_get_string_descriptor: + Get the USB string descriptor with the specified index. + */ +int +cyusb_get_string_descriptor ( + cyusb_handle *h, + unsigned char desc_index, + unsigned short langid, + unsigned char *data, + int len) +{ + return ( libusb_get_string_descriptor(h, desc_index, langid, data, len) ); +} + +/* cyusb_control_transfer: + Perform the desired USB control transfer (can be read or write based on the bmRequestType value). + */ +int +cyusb_control_transfer ( + cyusb_handle *h, + unsigned char bmRequestType, + unsigned char bRequest, + unsigned short wValue, + unsigned short wIndex, + unsigned char *data, + unsigned short wLength, + unsigned int timeout) +{ + return ( libusb_control_transfer(h, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout) ); +} + +/* cyusb_control_read: + Perform a read transfer on the USB control endpoint. + */ +int +cyusb_control_read ( + cyusb_handle *h, + unsigned char bmRequestType, + unsigned char bRequest, + unsigned short wValue, + unsigned short wIndex, + unsigned char *data, + unsigned short wLength, + unsigned int timeout) +{ + /* Set the direction bit to indicate a read transfer. */ + return ( libusb_control_transfer(h, bmRequestType | 0x80, bRequest, wValue, wIndex, data, wLength, timeout) ); +} + +/* cyusb_control_write: + Perform a write transfer on the USB control endpoint. This can be a zero byte (no data) transfer as well. + */ +int +cyusb_control_write ( + cyusb_handle *h, + unsigned char bmRequestType, + unsigned char bRequest, + unsigned short wValue, + unsigned short wIndex, + unsigned char *data, + unsigned short wLength, + unsigned int timeout) +{ + /* Clear the direction bit to indicate a write transfer. */ + return ( libusb_control_transfer(h, bmRequestType & 0x7F, bRequest, wValue, wIndex, data, wLength, timeout) ); +} + +/* cyusb_bulk_transfer: + Perform a data transfer on a USB Bulk endpoint. + */ +int +cyusb_bulk_transfer ( + cyusb_handle *h, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + int timeout) +{ + return ( libusb_bulk_transfer(h, endpoint, data, length, transferred, timeout) ); +} + +/* cyusb_interrupt_transfer: + Perform a data transfer on a USB interrupt endpoint. + */ +int +cyusb_interrupt_transfer ( + cyusb_handle *h, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + unsigned int timeout) +{ + return ( libusb_interrupt_transfer(h, endpoint, data, length, transferred, timeout) ); +} + +/* cyusb_download_fx2: + Download firmware to the Cypress FX2/FX2LP device using USB vendor commands. + */ +int +cyusb_download_fx2 ( + cyusb_handle *h, + char *filename, + unsigned char vendor_command) +{ + FILE *fp = NULL; + char buf[256]; + char tbuf1[3]; + char tbuf2[5]; + char tbuf3[3]; + unsigned char reset = 0; + int r; + int count = 0; + unsigned char num_bytes = 0; + unsigned short address = 0; + unsigned char *dbuf = NULL; + int i; + + fp = fopen(filename, "r" ); + tbuf1[2] ='\0'; + tbuf2[4] = '\0'; + tbuf3[2] = '\0'; + + /* Place the FX2/FX2LP CPU in reset, so that the vendor commands can be handled by the device. */ + reset = 1; + r = cyusb_control_transfer(h, 0x40, 0xA0, 0xE600, 0x00, &reset, 0x01, 1000); + if ( !r ) { + printf("Error in control_transfer\n"); + return r; + } + sleep(1); + + count = 0; + + while ( fgets(buf, 256, fp) != NULL ) { + if ( buf[8] == '1' ) + break; + strncpy(tbuf1,buf+1,2); + num_bytes = strtoul(tbuf1,NULL,16); + strncpy(tbuf2,buf+3,4); + address = strtoul(tbuf2,NULL,16); + dbuf = (unsigned char *)malloc(num_bytes); + for ( i = 0; i < num_bytes; ++i ) { + strncpy(tbuf3,&buf[9+i*2],2); + dbuf[i] = strtoul(tbuf3,NULL,16); + } + + r = cyusb_control_transfer(h, 0x40, vendor_command, address, 0x00, dbuf, num_bytes, 1000); + if ( !r ) { + printf("Error in control_transfer\n"); + free(dbuf); + return r; + } + count += num_bytes; + free(dbuf); + } + + printf("Total bytes downloaded = %d\n", count); + sleep(1); + + /* Bring the CPU out of reset to run the newly loaded firmware. */ + reset = 0; + r = cyusb_control_transfer(h, 0x40, 0xA0, 0xE600, 0x00, &reset, 0x01, 1000); + fclose(fp); + return 0; +} + +/* control_transfer: + Internal function that issues the vendor command that incrementally loads firmware segments to the + Cypress FX3 device RAM. + */ +static void +control_transfer ( + cyusb_handle *h, + unsigned int address, + unsigned char *dbuf, + int len) +{ + int j; + int r; + int b; + unsigned int *pint; + int index; + + int balance = len; + pint = (unsigned int *)dbuf; + + index = 0; + while ( balance > 0 ) { + if ( balance > 4096 ) + b = 4096; + else b = balance; + r = cyusb_control_transfer (h, 0x40, 0xA0, ( address & 0x0000ffff ), address >> 16, + &dbuf[index], b, 1000); + if ( r != b ) { + printf("Error in control_transfer\n"); + } + address += b ; + balance -= b; + index += b; + } + + /* Update the firmware checksum as the download is being performed. */ + for ( j = 0; j < len/4; ++j ) + checksum += pint[j]; +} + +/* cyusb_download_fx3: + Download a firmware binary the Cypress FX3 device RAM. + */ +int +cyusb_download_fx3 ( + cyusb_handle *h, + const char *filename) +{ + int fd; + unsigned char buf[FX3_MAX_FW_SIZE]; + int nbr; + int dlen; + int count; + unsigned int *pdbuf = NULL; + unsigned int address; + unsigned int *pint; + unsigned int program_entry; + int r; + + fd = open(filename, O_RDONLY); + if ( fd < 0 ) { + printf("File not found\n"); + return -ENOENT; + } + else + printf("File successfully opened\n"); + + count = 0; + checksum = 0; + nbr = read(fd, buf, 2); /* Read first 2 bytes, must be equal to 'CY' */ + if ( strncmp((char *)buf,"CY",2) ) { + printf("Image does not have 'CY' at start. aborting\n"); + return -EINVAL; + } + nbr = read(fd, buf, 1); /* Read 1 byte. bImageCTL */ + if ( buf[0] & 0x01 ) { + printf("Image does not contain executable code\n"); + return -EINVAL; + } + nbr = read(fd, buf, 1); /* Read 1 byte. bImageType */ + if ( !(buf[0] == 0xB0) ) { + printf("Not a normal FW binary with checksum\n"); + return -EINVAL; + } + + while (1) { + nbr = read(fd, buf, 4); /* Read Length of section 1,2,3, ... */ + pdbuf = (unsigned int *)buf; + dlen = *pdbuf; + nbr = read(fd,buf,4); /* Read Address of section 1,2,3,... */ + pint = (unsigned int *)buf; + address = *pint; + if ( dlen != 0 ) { + nbr = read(fd, buf, dlen*4); /* Read data bytes */ + control_transfer(h, address, buf, dlen*4); + } + else { + program_entry = address; + break; + } + } + + nbr = read(fd, buf, 4); /* Read checksum */ + pdbuf = (unsigned int *)buf; + if ( *pdbuf != checksum ) { + printf("Error in checksum\n"); + return -EINVAL; + } + + sleep(1); + r = cyusb_control_transfer(h, 0x40, 0xA0, (program_entry & 0x0000ffff ) , program_entry >> 16, NULL, 0, 1000); + if ( r ) { + printf("Ignored error in control_transfer: %d\n", r); + } + + close(fd); + return 0; +} + +/*[]*/ +