Created
November 9, 2012 19:48
-
-
Save kylemanna/4047794 to your computer and use it in GitHub Desktop.
Cypress PSoC USBFS flash utility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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