diff options
Diffstat (limited to 'xpp/utils/fpga_load.c')
-rw-r--r-- | xpp/utils/fpga_load.c | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/xpp/utils/fpga_load.c b/xpp/utils/fpga_load.c new file mode 100644 index 0000000..cf20226 --- /dev/null +++ b/xpp/utils/fpga_load.c @@ -0,0 +1,710 @@ +#include <stdio.h> +#include <assert.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <usb.h> +#include "hexfile.h" + +#define XORCOM_INTERNAL + +static const char rcsid[] = "$Id$"; + +#define ERR(fmt, arg...) fprintf(stderr, "%s: ERROR: " fmt, progname, ## arg) +#define INFO(fmt, arg...) fprintf(stderr, "%s: " fmt, progname, ## arg) + +static int verbose = LOG_WARNING; +static char *progname; + +#define MAX_HEX_LINES 2000 +#define PACKET_SIZE 512 +#define EEPROM_SIZE 16 +#define SERIAL_SIZE 8 + +enum fpga_load_packet_types { + STATUS_REPLY = 0x01, + DATA_PACKET = 0x01, +#ifdef XORCOM_INTERNAL + EEPROM_SET = 0x04, +#endif + EEPROM_GET = 0x08, + RENUMERATE = 0x10, + BAD_COMMAND = 0xAA +}; + +struct myeeprom { + uint8_t source; + uint16_t vendor; + uint16_t product; + uint8_t release_major; + uint8_t release_minor; + uint8_t reserved; + uint8_t serial[SERIAL_SIZE]; +} PACKED; + +struct fpga_packet_header { + struct { + uint8_t op; + } PACKED header; + union { + struct { + uint16_t seq; + uint8_t status; + } PACKED status_reply; + struct { + uint16_t seq; + uint8_t reserved; + uint8_t data[ZERO_SIZE]; + } PACKED data_packet; + struct { + struct myeeprom data; + } PACKED eeprom_set; + struct { + struct myeeprom data; + } PACKED eeprom_get; + } d; +} PACKED; + +enum fpga_load_status { + FW_FAIL_RESET = 1, + FW_FAIL_TRANS = 2, + FW_TRANS_OK = 4, + FW_CONFIG_DONE = 8 +}; + +int my_usb_device(struct usb_device *dev, usb_dev_handle *handle); + +const char *load_status2str(enum fpga_load_status s) +{ + switch(s) { + case FW_FAIL_RESET: return "FW_FAIL_RESET"; + case FW_FAIL_TRANS: return "FW_FAIL_TRANS"; + case FW_TRANS_OK: return "FW_TRANS_OK"; + case FW_CONFIG_DONE: return "FW_CONFIG_DONE"; + default: return "UNKNOWN"; + } +} + +int path_of_dev(char buf[], unsigned int buflen, struct usb_device *dev) +{ + return snprintf(buf, buflen, "/proc/bus/usb/%s/%s", dev->bus->dirname, dev->filename); +} + +struct usb_device *dev_of_path(const char *path) +{ + struct usb_bus *bus; + struct usb_device *dev; + char dirname[PATH_MAX]; + char filename[PATH_MAX]; + const char prefix[] = "/proc/bus/usb/"; + const int prefix_len = strlen(prefix); + const char *p; + int bnum; + int dnum; + int ret; + + assert(path != NULL); + if(strncmp(prefix, path, prefix_len) != 0) { + ERR("wrong path: '%s'\n", path); + return NULL; + } + p = path + prefix_len; + ret = sscanf(p, "%d/%d", &bnum, &dnum); + if(ret != 2) { + ERR("wrong path tail: '%s'\n", p); + return NULL; + } + sprintf(dirname, "%03d", bnum); + sprintf(filename, "%03d", dnum); + for (bus = usb_busses; bus; bus = bus->next) { + if(strcmp(bus->dirname, dirname) != 0) + continue; + for (dev = bus->devices; dev; dev = dev->next) { + if(strcmp(dev->filename, filename) == 0) + return dev; + } + } + ERR("no usb device match '%s'\n", path); + return NULL; +} + +int get_usb_string(char *buf, unsigned int len, uint16_t item, usb_dev_handle *handle) +{ + char tmp[BUFSIZ]; + int ret; + + if (!item) + return 0; + ret = usb_get_string_simple(handle, item, tmp, BUFSIZ); + if (ret <= 0) + return ret; + return snprintf(buf, len, "%s", tmp); +} + +/* My device parameters */ +#define MY_INTERFACE 0 +#define MY_CONFIG 1 +#define MY_ENDPOINTS 4 + +#define MY_EP_OUT 0x04 +#define MY_EP_IN 0x88 + +#define TIMEOUT 5000 + +static const int my_endpoints[MY_ENDPOINTS] = { + 0x02, + 0x04, + 0x86, + 0x88 +}; + +void usb_cleanup(usb_dev_handle *handle) +{ + if(usb_release_interface(handle, MY_INTERFACE) != 0) { + ERR("Releasing interface: usb: %s\n", usb_strerror()); + } + if(usb_close(handle) != 0) { + ERR("Closing device: usb: %s\n", usb_strerror()); + } +} + +void print_bcd_ver(const struct myeeprom *eeprom) +{ + /* In this case, print only the version. Also note that this + * is an output, and sent to stdout + */ + printf("%d.%03d\n", eeprom->release_major, eeprom->release_minor); + return; +} + +void dump_eeprom(const struct myeeprom *eeprom) +{ + const uint8_t *data = eeprom->serial; + + INFO("Source: 0x%02X\n", eeprom->source); + INFO("Vendor: 0x%04X\n", eeprom->vendor); + INFO("Product: 0x%04X\n", eeprom->product); + INFO("Release: %d.%03d\n", eeprom->release_major, eeprom->release_minor); + INFO("Data: 0x[%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X]\n", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7]); +} + +void dump_packet(const char *buf, int len) +{ + int i; + + for(i = 0; i < len; i++) + INFO("dump: %2d> 0x%02X\n", i, (uint8_t)buf[i]); +} + +#ifdef XORCOM_INTERNAL +int eeprom_set(struct usb_dev_handle *handle, const struct myeeprom *eeprom) +{ + int ret; + int len; + char buf[PACKET_SIZE]; + struct fpga_packet_header *phead = (struct fpga_packet_header *)buf; + + if(verbose >= LOG_INFO) + INFO("%s Start...\n", __FUNCTION__); + assert(handle != NULL); + phead->header.op = EEPROM_SET; + memcpy(&phead->d.eeprom_set.data, eeprom, EEPROM_SIZE); + len = sizeof(phead->d.eeprom_set) + sizeof(phead->header.op); + if(verbose >= LOG_INFO) { + INFO("%s write %d bytes\n", __FUNCTION__, len); + dump_packet((char *)phead, len); + } + ret = usb_bulk_write(handle, MY_EP_OUT, (char *)phead, len, TIMEOUT); + if(ret < 0) { + ERR("usb: bulk_write failed (%d)\n", ret); + return ret; + } else if(ret != len) { + ERR("usb: bulk_write short write (%d)\n", ret); + return -EFAULT; + } + ret = usb_bulk_read(handle, MY_EP_IN, buf, sizeof(buf), TIMEOUT); + if(ret < 0) { + ERR("usb: bulk_read failed (%d)\n", ret); + return ret; + } else if(ret == 0) + return 0; + phead = (struct fpga_packet_header *)buf; + if(phead->header.op == BAD_COMMAND) { + ERR("BAD_COMMAND\n"); + return -EINVAL; + } else if(phead->header.op != EEPROM_SET) { + ERR("Got unexpected reply op=%d\n", phead->header.op); + return -EINVAL; + } + if(verbose >= LOG_INFO) { + INFO("%s read %d bytes\n", __FUNCTION__, ret); + dump_packet(buf, ret); + } + return 0; +} +#endif + +int eeprom_get(struct usb_dev_handle *handle, struct myeeprom *eeprom) +{ + int ret; + int len; + char buf[PACKET_SIZE]; + struct fpga_packet_header *phead = (struct fpga_packet_header *)buf; + + assert(handle != NULL); + if(verbose >= LOG_INFO) + INFO("%s Start...\n", __FUNCTION__); + phead->header.op = EEPROM_GET; + len = sizeof(phead->header.op); /* warning: sending small packet */ + if(verbose >= LOG_INFO) { + INFO("%s write %d bytes\n", __FUNCTION__, len); + dump_packet(buf, len); + } + ret = usb_bulk_write(handle, MY_EP_OUT, (char *)phead, len, TIMEOUT); + if(ret < 0) { + ERR("usb: bulk_write failed (%d)\n", ret); + return ret; + } else if(ret != len) { + ERR("usb: bulk_write short write (%d)\n", ret); + return -EFAULT; + } + ret = usb_bulk_read(handle, MY_EP_IN, buf, sizeof(buf), TIMEOUT); + if(ret < 0) { + ERR("usb: bulk_read failed (%d)\n", ret); + return ret; + } else if(ret == 0) + return 0; + phead = (struct fpga_packet_header *)buf; + if(phead->header.op == BAD_COMMAND) { + ERR("BAD_COMMAND\n"); + return -EINVAL; + } else if(phead->header.op != EEPROM_GET) { + ERR("Got unexpected reply op=%d\n", phead->header.op); + return -EINVAL; + } + if(verbose >= LOG_INFO) { + INFO("%s read %d bytes\n", __FUNCTION__, ret); + dump_packet(buf, ret); + } + memcpy(eeprom, &phead->d.eeprom_get.data, EEPROM_SIZE); + return 0; +} + +int send_hexline(struct usb_dev_handle *handle, struct hexline *hexline, int seq) +{ + int ret; + int len; + uint8_t *data; + char buf[PACKET_SIZE]; + struct fpga_packet_header *phead = (struct fpga_packet_header *)buf; + enum fpga_load_status status; + + assert(handle != NULL); + assert(hexline != NULL); + len = hexline->d.content.header.ll; /* don't send checksum */ + data = hexline->d.content.tt_data.data; + if(hexline->d.content.header.tt != TT_DATA) { + ERR("Bad record %d type = %d\n", seq, hexline->d.content.header.tt); + return -EINVAL; + } + phead->header.op = DATA_PACKET; + phead->d.data_packet.seq = seq; + phead->d.data_packet.reserved = 0x00; + memcpy(phead->d.data_packet.data, data, len); + len += sizeof(phead); + if(verbose >= LOG_INFO) + INFO("%04d+\r", seq); + ret = usb_bulk_write(handle, MY_EP_OUT, (char *)phead, len, TIMEOUT); + if(ret < 0) { + ERR("usb: bulk_write failed (%d)\n", ret); + return ret; + } else if(ret != len) { + ERR("usb: bulk_write short write (%d)\n", ret); + return -EFAULT; + } + ret = usb_bulk_read(handle, MY_EP_IN, buf, sizeof(buf), TIMEOUT); + if(ret < 0) { + ERR("usb: bulk_read failed (%d)\n", ret); + return ret; + } else if(ret == 0) + return 0; + if(verbose >= LOG_INFO) + INFO("%04d-\r", seq); + phead = (struct fpga_packet_header *)buf; + if(phead->header.op != STATUS_REPLY) { + ERR("Got unexpected reply op=%d\n", phead->header.op); + return -EINVAL; + } + status = (enum fpga_load_status)phead->d.status_reply.status; + switch(status) { + case FW_TRANS_OK: + case FW_CONFIG_DONE: + break; + case FW_FAIL_RESET: + case FW_FAIL_TRANS: + ERR("status reply %s (%d)\n", load_status2str(status), status); + if(verbose >= LOG_INFO) + dump_packet(buf, ret); + return -EPROTO; + default: + ERR("Unknown status reply %d\n", status); + if(verbose >= LOG_INFO) + dump_packet(buf, ret); + return -EPROTO; + } + return 0; +} + +int my_usb_device(struct usb_device *dev, usb_dev_handle *handle) +{ + struct usb_device_descriptor *dev_desc; + struct usb_config_descriptor *config_desc; + struct usb_interface *interface; + struct usb_interface_descriptor *iface_desc; + struct usb_endpoint_descriptor *endpoint; + char iManufacturer[BUFSIZ]; + char iProduct[BUFSIZ]; + int ret; + int i; + + assert(dev != NULL); + dev_desc = &dev->descriptor; + config_desc = dev->config; + interface = config_desc->interface; + iface_desc = interface->altsetting; + if(verbose >= LOG_INFO) + INFO("Vendor:Product=%04X:%04X Class=%d (endpoints=%d)\n", + dev_desc->idVendor, dev_desc->idProduct, dev_desc->bDeviceClass, iface_desc->bNumEndpoints); + if(iface_desc->bInterfaceClass != 0xFF) { + ERR("Wrong Interface class %d\n", iface_desc->bInterfaceClass); + return -EINVAL; + } + if(iface_desc->bInterfaceNumber != MY_INTERFACE) { + ERR("Wrong Interface number %d\n", iface_desc->bInterfaceNumber); + return -EINVAL; + } + if(iface_desc->bNumEndpoints != MY_ENDPOINTS) { + ERR("Wrong number of endpoints: %d\n", iface_desc->bNumEndpoints); + return -EINVAL; + } + endpoint = iface_desc->endpoint; + for(i = 0; i < iface_desc->bNumEndpoints; i++, endpoint++) { + if(endpoint->bEndpointAddress != my_endpoints[i]) { + ERR("Wrong endpoint %d: address = 0x%X\n", i, endpoint->bEndpointAddress); + return -EINVAL; + } + if(endpoint->bEndpointAddress == MY_EP_OUT || endpoint->bEndpointAddress == MY_EP_IN) { + if(endpoint->wMaxPacketSize > PACKET_SIZE) { + ERR("Endpoint #%d wMaxPacketSize too large (%d)\n", i, endpoint->wMaxPacketSize); + return -EINVAL; + } + } + } + if(usb_reset(handle) != 0) { + ERR("Reseting device: usb: %s\n", usb_strerror()); + } + if(usb_set_configuration(handle, MY_CONFIG) != 0) { + ERR("usb: %s\n", usb_strerror()); + return -EINVAL; + } + if(usb_claim_interface(handle, MY_INTERFACE) != 0) { + ERR("usb: %s\n", usb_strerror()); + return -EINVAL; + } + if(usb_resetep(handle, MY_EP_OUT) != 0) { + ERR("usb: %s\n", usb_strerror()); + return -EINVAL; + } + if(usb_resetep(handle, MY_EP_IN) != 0) { + ERR("usb: %s\n", usb_strerror()); + return -EINVAL; + } + ret = get_usb_string(iManufacturer, BUFSIZ, dev_desc->iManufacturer, handle); + ret = get_usb_string(iProduct, BUFSIZ, dev_desc->iProduct, handle); + if(verbose >= LOG_INFO) + INFO("iManufacturer=%s iProduct=%s\n", iManufacturer, iProduct); + return 0; +} + +int renumerate_device(struct usb_dev_handle *handle) +{ + char buf[PACKET_SIZE]; + struct fpga_packet_header *phead = (struct fpga_packet_header *)buf; + int ret; + + assert(handle != NULL); + if(verbose >= LOG_INFO) + INFO("Renumerating\n"); + phead->header.op = RENUMERATE; + ret = usb_bulk_write(handle, MY_EP_OUT, (char *)phead, 1, TIMEOUT); + if(ret < 0) { + ERR("usb: bulk_write failed (%d)\n", ret); + return ret; + } else if(ret != 1) { + ERR("usb: bulk_write short write (%d)\n", ret); + return -EFAULT; + } + return 0; +} + +int fpga_load(struct usb_dev_handle *handle, const struct hexdata *hexdata) +{ + unsigned int i; + int ret; + int finished = 0; + + assert(handle != NULL); + if(verbose >= LOG_INFO) + INFO("Start...\n"); + for(i = 0; i < hexdata->maxlines; i++) { + struct hexline *hexline = hexdata->lines[i]; + + if(!hexline) + break; + if(finished) { + ERR("Extra data after End Of Data Record (line %d)\n", i); + return 0; + } + if(hexline->d.content.header.tt == TT_EOF) { + INFO("End of data\n"); + finished = 1; + continue; + } + if((ret = send_hexline(handle, hexline, i)) != 0) { + perror("Failed sending hexline"); + return 0; + } + } + if(verbose >= LOG_INFO) + INFO("Finished...\n"); + return 1; +} + +#include <getopt.h> + +void usage() +{ + fprintf(stderr, "Usage: %s -D /proc/bus/usb/<bus>/<dev> [options...]\n", progname); + fprintf(stderr, "\tOptions:\n"); + fprintf(stderr, "\t\t[-b <binfile>] # output to <binfile>\n"); + fprintf(stderr, "\t\t[-d] # Get device version from eeprom\n"); + fprintf(stderr, "\t\t[-I <hexfile>] # Input from <hexfile>\n"); + fprintf(stderr, "\t\t[-g] # Get eeprom from device\n"); + fprintf(stderr, "\t\t[-V vendorid] # Set Vendor id on device\n"); + fprintf(stderr, "\t\t[-P productid] # Set Product id on device\n"); + fprintf(stderr, "\t\t[-R release] # Set Release. 2 dot separated decimals\n"); + fprintf(stderr, "\t\t[-S serial] # Set Serial. 8 comma separated numbers\n"); + exit(1); +} + +static void parse_report_func(int level, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + if(level <= verbose) + vfprintf(stderr, msg, ap); + va_end(ap); +} + + +int main(int argc, char *argv[]) +{ + struct usb_device *dev; + usb_dev_handle *handle; + const char *devpath = NULL; + const char *binfile = NULL; + const char *hexfile = NULL; + struct hexdata *hexdata = NULL; + struct myeeprom eeprom_buf; + int opt_read_eeprom = 0; + int opt_print_bcdver_only = 0; +#ifdef XORCOM_INTERNAL + int opt_write_eeprom = 0; + char *vendor = NULL; + char *product = NULL; + char *release = NULL; + char *serial = NULL; + uint8_t serial_buf[SERIAL_SIZE]; + const char options[] = "b:dD:ghI:vV:P:R:S:"; +#else + const char options[] = "b:dD:ghI:v"; +#endif + int ret = 0; + + progname = argv[0]; + assert(sizeof(struct fpga_packet_header) <= PACKET_SIZE); + assert(sizeof(struct myeeprom) == EEPROM_SIZE); + while (1) { + int c; + + c = getopt (argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'D': + devpath = optarg; + break; + case 'b': + binfile = optarg; + break; + case 'd': + opt_print_bcdver_only = 1; + opt_read_eeprom = 1; + break; + case 'g': + opt_read_eeprom = 1; + break; + case 'I': + hexfile = optarg; + break; +#ifdef XORCOM_INTERNAL + case 'V': + vendor = optarg; + break; + case 'P': + product = optarg; + break; + case 'R': + release = optarg; + break; + case 'S': + serial = optarg; + { + int i; + char *p; + unsigned long val; + + p = strtok(serial, ","); + for(i = 0; i < SERIAL_SIZE && p; i++) { + val = strtoul(p, NULL, 0); + if(val > 0xFF) { + ERR("Value #%d for -S option is too large (%lu)\n", i+1, val); + usage(); + } + serial_buf[i] = val; + p = strtok(NULL, ","); + } + if(i < SERIAL_SIZE) { + ERR("got only %d values for -S option. Need %d\n", i, SERIAL_SIZE); + usage(); + } + } + + break; +#endif + case 'v': + verbose++; + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + usage(); + } + } + + if (optind != argc) { + usage(); + } + if(hexfile) { + parse_hexfile_set_reporting(parse_report_func); + hexdata = parse_hexfile(hexfile, MAX_HEX_LINES); + if(!hexdata) { + ERR("Bailing out\n"); + exit(1); + } + if(binfile) { + dump_binary(hexdata, binfile); + return 0; + } + } + if(!devpath) { + ERR("Missing device path\n"); + usage(); + } + if(verbose) + INFO("Startup %s\n", devpath); + + usb_init(); + usb_find_busses(); + usb_find_devices(); + dev = dev_of_path(devpath); + if(!dev) { + ERR("Bailing out\n"); + exit(1); + } + handle = usb_open(dev); + if(!handle) { + ERR("Failed to open usb device '%s/%s': %s\n", dev->bus->dirname, dev->filename, usb_strerror()); + return -ENODEV; + } + if(my_usb_device(dev, handle)) { + ERR("Foreign usb device '%s/%s'\n", dev->bus->dirname, dev->filename); + ret = -ENODEV; + goto dev_err; + } + if(hexdata) { + if(!fpga_load(handle, hexdata)) { + ERR("FPGA loading failed\n"); + ret = -ENODEV; + goto dev_err; + } + ret = renumerate_device(handle); + if(ret < 0) { + ERR("Renumeration failed: errno=%d\n", ret); + goto dev_err; + } + } +#ifdef XORCOM_INTERNAL + if(vendor || product || release || serial) + opt_read_eeprom = opt_write_eeprom = 1; +#endif + if(opt_read_eeprom) { + ret = eeprom_get(handle, &eeprom_buf); + if(ret < 0) { + ERR("Failed reading eeprom: %d\n", ret); + goto dev_err; + } + if (opt_print_bcdver_only) + print_bcd_ver(&eeprom_buf); + else + dump_eeprom(&eeprom_buf); + } +#ifdef XORCOM_INTERNAL + if(opt_write_eeprom) { + // FF: address source is from device. C0: from eeprom + eeprom_buf.source = 0xC0; + if(vendor) + eeprom_buf.vendor = strtoul(vendor, NULL, 0); + if(product) + eeprom_buf.product = strtoul(product, NULL, 0); + if(release) { + int release_major = 0; + int release_minor = 0; + + sscanf(release, "%d.%d", &release_major, &release_minor); + eeprom_buf.release_major = release_major; + eeprom_buf.release_minor = release_minor; + } + if(serial) { + memcpy(eeprom_buf.serial, serial_buf, SERIAL_SIZE); + } + dump_eeprom(&eeprom_buf); + ret = eeprom_set(handle, &eeprom_buf); + if(ret < 0) { + ERR("Failed writing eeprom: %d\n", ret); + goto dev_err; + } + } +#endif + if(verbose) + INFO("Exiting\n"); +dev_err: + usb_cleanup(handle); + return ret; +} |