#include #include #include #include #include #include #include #include #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 void usage() { fprintf(stderr, "Usage: %s -D /proc/bus/usb// [options...]\n", progname); fprintf(stderr, "\tOptions:\n"); fprintf(stderr, "\t\t[-b ] # output to \n"); fprintf(stderr, "\t\t[-d] # Get device version from eeprom\n"); fprintf(stderr, "\t\t[-I ] # Input from \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; }