Skip to content

Instantly share code, notes, and snippets.

@kylemanna
Created November 9, 2012 19:48
Show Gist options
  • Save kylemanna/4047794 to your computer and use it in GitHub Desktop.
Save kylemanna/4047794 to your computer and use it in GitHub Desktop.
Cypress PSoC USBFS flash utility
/**
* Loader for the Cypress USBFS boot loader that takes .dld files from the
* Cypress designer.
*
* Ben Goska - TekBots
* Oregon State University - 2008
*
* Kyle Manna - Fuel7, Inc. - 2012
*
* Heavily modified to work with a CY8C24894 using the "Bootloader_Host_PSoC1"
* C#/.NET code included with the PSoC programmer (3.16.0.1322) for Windows.
*
* It appears that the actual USB API is significantly different then what is
* described in "USBFS Bootloader Datasheet BootLdrUSBFS V 1.60"
*
*
*/
#include <stdio.h>
#include <string.h>
#include <usb.h>
#include <assert.h>
#include <linux/errno.h>
#define CYPRESS_BOOTLOADER_VID 0x04b4
#define CYPRESS_BOOTLOADER_PID 0xe006
#define CYPRESS_ENTER_BOOTLOADER 0x38
#define CYPRESS_EXIT_BOOTLOADER 0x3B
#define CYPRESS_WRITE_FLASH 0x39
#define BULK_OUT_ENDPOINT 0x02
#define BULK_IN_ENDPOINT 0x81
/** Default timeout in ms */
#define USB_TIMEOUT 1000
enum Packets {IN, OUT};
struct bootloader_data {
struct usb_dev_handle *handle;
/** Print info about packets if this is set */
void (*notify_packets)(enum Packets, unsigned char *data, size_t len);
};
/** Convert a single character to its binary value
*
* @param c character to convert
* @param result binary representation of c
* @return 0 for success
*/
static inline int from_hex(const char c, unsigned char *result)
{
assert(result);
if ((c > 0x2f) && (c < 0x3a)) {
*result = (int)(c - 0x30);
return EXIT_SUCCESS;
} else if ((c > 0x40) && (c < 0x47)) {
*result = (int)(c - 0x41 + 10);
return EXIT_SUCCESS;
} else if ((c > 0x60) && (c < 0x67)) {
*result = (int)(c - 0x61 + 10);
return EXIT_SUCCESS;
}
// Unable to convert
return -ERANGE;
}
/** Convert a 2 byte hex value to its binary value
*
* @param hex_value string pointer to convert, must be 2+ bytes
* @param result binary representation of hex_value
* @return 0 on success
*/
static inline int int_val(const char *hex_value, unsigned char *result) {
unsigned char i, j;
int ret;
assert(result);
ret = from_hex(hex_value[0], &i);
if (ret)
return ret;
ret = from_hex(hex_value[1], &j);
if (ret)
return ret;
*result = (i << 4) + j;
return EXIT_SUCCESS;
}
/** Calculate checksum of data, assumes length is always 46.
*
* This was copied from BootloaderClasses.cs and modification was avoided when
* possible
*
* @param data pointer to array containing data to checksum
* @return checksum value
*/
static unsigned char CheckSum(const unsigned char *data)
{
int iSum = 0;
int i = 0;
for (i = 0; i < 46; i++)
iSum += data[i];
return (unsigned char)(iSum & 0xff);
}
/** Convert the raw data to USB packets to send on the wire
*
* This was copied from BootloaderClasses.cs and modification was avoided when
* possible
*
* @param data raw binary data after parsed from DLD file
* @param packetLow first packet to send
* @param packetHigh second packet to send
*/
static void GetUSBPackets(const unsigned char *data, unsigned char *packetLow,
unsigned char *packetHigh)
{
int i;
int j = 0;
for (i = 0; i < 12; i++)
{
packetLow[i] = data[i];
packetHigh[i] = data[i];
}
packetLow[12] = 0x00;
packetHigh[12] = 0x01;
for (i = 12; i < 44; i++)
{
packetLow[i + 1] = data[i];
packetHigh[i + 1] = data[44 + j];
j++;
}
packetLow[45] = CheckSum(packetLow);
packetHigh[45] = CheckSum(packetHigh);
}
/** Find a USB device to flash
*
* @param vid USB vendor ID to search for
* @param pid USB product ID to search for
* @return
*/
usb_dev_handle *find_device(int vid, int pid) {
unsigned char located = 0;
struct usb_bus *bus;
struct usb_device *dev;
usb_dev_handle *device_handle = 0;
usb_find_busses();
usb_find_devices();
printf("Looking for 0x%x:0x%x\n", vid, pid);
for (bus = usb_busses; bus; bus = bus->next) {
for (dev = bus->devices; dev; dev = dev->next) {
if ((dev->descriptor.idVendor == vid)
&& (dev->descriptor.idProduct == pid))
{
located++;
device_handle = usb_open(dev);
}
}
}
if (device_handle == 0)
return NULL;
return device_handle;
}
/** Command the primary code to invoke the Cypress bootloader
*
* @return pointer to device if found, otherwise NULL
*/
static usb_dev_handle* start_bootloader() {
char bytes[] = {0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00};
int devices[] = {0x802, 0x1002, 0x1102, 0x1202};
int type = 0x21;
int req = 0x9;
int value = 0x0300;
int index = 0x0;
int ret;
int i;
int timeout;
usb_dev_handle *dev = NULL;
for (i = 0; i < sizeof(devices)/sizeof(devices[0]); i++) {
dev = find_device(0x0dfc, devices[i]);
if (dev != NULL)
break;
}
if (dev == NULL)
return dev;
/* Disconnect the kernel driver so that libusb can use the device */
usb_detach_kernel_driver_np(dev, 0);
if (usb_set_configuration(dev, 1)) {
printf("Error setting configuration\n");
return NULL;
}
if (usb_claim_interface(dev, 0)) {
printf("Error claiming interface\n");
return NULL;
}
printf("Commanding device to start Cypress PSoC bootloader\n");
ret = usb_control_msg(dev, type, req, value, index, bytes, sizeof(bytes),
USB_TIMEOUT);
if (ret < 0) {
printf("Unable to send command\n");
return NULL;
}
/* Wait up to 10 seconds for new device to show up */
printf("Waiting for Cypress PSoC bootloader to enumerate...\n");
timeout = 10;
while ((dev = find_device(CYPRESS_BOOTLOADER_VID, CYPRESS_BOOTLOADER_PID))
== NULL && timeout--) {
sleep(1);
}
printf("Cypress PSoC bootloader found\n");
return dev;
}
/** Print information about packets being sent
*
* @param dir direction of the packet (IN/OUT)
* @param data data read/written
* @param len length of data read/written
*/
static void NotifyPackets(enum Packets dir, unsigned char *data, size_t len)
{
int i;
if (dir == IN)
printf ("\nIN:\n");
else if (dir == OUT)
printf ("\nOUT:\n");
else
assert(0);
for (i = 0 ; i < 64; ++i)
printf("%02hhX ", data[i]);
printf("\n");
}
/** Write USB data, tries to emulate TransportClasses.cs
*
* @param priv private data pointer to bootloader data
* @param data binary data to write to USB
* @param len length of data to write
* @return number of bytes written, negative on error
*/
static int WriteData(struct bootloader_data *priv,
unsigned char *data, size_t len)
{
if (priv->notify_packets != NULL) {
priv->notify_packets(OUT, data, len);
}
return usb_bulk_write(priv->handle, BULK_OUT_ENDPOINT, (char *)data,
len, USB_TIMEOUT);
}
/** Read USB data, tries to emulate TransportClasses.cs
*
* @param priv private data pointer to bootloader data
* @param data binary data to read from USB
* @param len space in data array to read
* @return number of bytes read, negative on error
*/
static ssize_t ReadData(struct bootloader_data *priv,
unsigned char *data, size_t len) {
ssize_t ret;
ret = usb_bulk_read(priv->handle, BULK_IN_ENDPOINT, (char *)data,
len, USB_TIMEOUT);
if (ret < 0) {
printf("USB READ FAILURE with return code %d\n", (int)ret);
assert(ret >= 0);
} else if (priv->notify_packets != NULL) {
priv->notify_packets(IN, data, ret);
}
return ret;
}
/** Star of the show, does it need any introduction?
*
* @param argc argument count
* @param argv argument values
* @return 0 on success
*/
int main(int argc, char **argv) {
struct bootloader_data priv;
char *file_name = "load.dld";
unsigned char data[256];
char line[512];
int timeout = 5;
int ret;
int c;
int file_size;
/* Initialize */
memset(&priv, 0, sizeof(priv));
usb_init();
/*
* Parse command line
*/
while ((c = getopt (argc, argv, "d")) != -1) {
switch (c) {
case 'd':
/* Be noisy, print every packet */
priv.notify_packets = NotifyPackets;
usb_set_debug(1);
break;
}
}
/* Use filename given on the command line */
if (optind)
file_name = argv[optind];
/* Check if the Cypress bootloader is already present due to a bad flash */
priv.handle = find_device(CYPRESS_BOOTLOADER_VID, CYPRESS_BOOTLOADER_PID);
while (priv.handle == NULL && timeout--) {
priv.handle = start_bootloader();
if (priv.handle == NULL)
printf("Failed to start Cypress PSoC bootloader\n");
}
if (priv.handle == NULL) {
printf("Couldn't find the bootloader.\n");
return -EXIT_FAILURE;
}
printf("Bootloader found, loading \"%s\"\n", file_name);
FILE *dld_file = fopen(file_name, "r");
if (!dld_file) {
printf("Couldn't open '%s'\n", file_name);
return -EXIT_FAILURE;
}
/* Get file size so we can determine progress */
fseek(dld_file, 0L, SEEK_END);
file_size = ftell(dld_file);
rewind(dld_file);
/*
* Do USB configuration
*/
if (usb_set_configuration(priv.handle, 1)) {
printf("Error setting configuration\n");
return -EXIT_FAILURE;
}
if (usb_claim_interface(priv.handle, 0)) {
printf("Error claiming interface\n");
return -EXIT_FAILURE;
}
if (usb_set_altinterface(priv.handle, 0)) {
printf("Error setting alternate interface\n");
return -EXIT_FAILURE;
}
while (!feof(dld_file)) {
int i;
size_t len;
char *pch = line;
if (fgets(line, sizeof(line), dld_file) == NULL)
break;
len = strlen(line);
memset(data, 0, sizeof(data));
for (i = 0; i < len; i++) {
/* Quit at NULL or new line characters */
if (*pch == 0x0 || *pch == '\r' || *pch == '\n')
break;
int_val(pch, &data[i]);
pch += 2;
}
assert (data[0] == 0xff);
switch (data[1]) {
case CYPRESS_ENTER_BOOTLOADER:
case CYPRESS_EXIT_BOOTLOADER:
{
ret = WriteData(&priv, data, 64);
if (ret != 64) {
printf("Failed to write USB data to %s bootloader, "
"ret = %d\n",
(data[1] == CYPRESS_ENTER_BOOTLOADER) ?
"enter" : "exit",
ret);
return -EXIT_FAILURE;
}
ret = ReadData(&priv, data, 64);
if (ret != 64) {
printf("Failed to read USB data to %s bootloader, "
"ret = %d\n",
(data[1] == CYPRESS_ENTER_BOOTLOADER) ?
"enter" : "exit",
ret);
return -EXIT_FAILURE;
}
break;
}
case CYPRESS_WRITE_FLASH:
{
unsigned char packetLow[64];
unsigned char packetHigh[64];
static int next_progress = 0;
int progress = (ftell(dld_file) * 100)/file_size;
if (progress >= next_progress) {
printf("%d%%..", progress);
fflush(stdout);
next_progress += 10;
}
/* Initialize the arrays */
memset(packetLow, 0, sizeof(packetLow));
memset(packetHigh, 0, sizeof(packetHigh));
GetUSBPackets(data, &packetLow[0], &packetHigh[0]);
/* Write packetLow */
ret = WriteData(&priv, packetLow, 64);
if (ret != 64) {
printf("Failed to write USB data for packet low, "
"ret = %d\n", ret);
return -EXIT_FAILURE;
}
ret = ReadData(&priv, data, 64);
if (ret != 64) {
printf("Failed to read USB data for packet low, "
"ret = %d\n", ret);
return -EXIT_FAILURE;
}
/* Write packetHigh */
ret = WriteData(&priv, packetHigh, 64);
if (ret != 64) {
printf("Failed to write USB data for packet high, "
"ret = %d\n", ret);
return -EXIT_FAILURE;
}
ret = ReadData(&priv, data, 64);
if (ret != 64) {
printf("Failed to write USB data for packet high, "
"ret = %d\n", ret);
return -EXIT_FAILURE;
}
break;
}
}
}
usb_close(priv.handle);
/* These status codes are mere guesses without documentation */
if (data[0] == 0x2 && data[1] == 0x0) {
printf("\nDevice flashed successfully\n");
return EXIT_SUCCESS;
}
printf("\nAn error occurred while flashing\n");
return -EXIT_FAILURE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment