/* * Written by Oron Peled * Copyright (C) 2006-2008, Xorcom * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #include #include #include "hexfile.h" static const char rcsid[] = "$Id$"; #define ERR(fmt, arg...) do { \ if(verbose >= LOG_ERR) \ fprintf(stderr, "%s: ERROR (%d): " fmt, \ progname, __LINE__, ## arg); \ } while(0); #define INFO(fmt, arg...) do { \ if(verbose >= LOG_INFO) \ fprintf(stderr, "%s: " fmt, \ progname, ## arg); \ } while(0); #define DBG(fmt, arg...) do { \ if(verbose >= LOG_DEBUG) \ fprintf(stderr, "%s: DBG: " fmt, \ progname, ## arg); \ } while(0); static int verbose = LOG_WARNING; static char *progname; static int disconnected = 0; #define MAX_HEX_LINES 10000 #define PACKET_SIZE 512 #define EEPROM_SIZE 16 #define LABEL_SIZE 8 #define TIMEOUT 5000 /* My device parameters */ #define MY_EP_OUT 0x04 #define MY_EP_IN 0x88 #define FPGA_EP_OUT 0x02 #define FPGA_EP_IN 0x86 /* USB firmware types */ #define USB_11xx 0 #define USB_FIRMWARE_II 1 #define TYPE_ENTRY(t,ni,n,ne,out,in,...) \ [t] = { \ .type_code = (t), \ .num_interfaces = (ni), \ .my_interface_num = (n), \ .num_endpoints = (ne), \ .my_ep_in = (in), \ .my_ep_out = (out), \ .name = #t, \ .endpoints = { __VA_ARGS__ }, \ } static const struct astribank_type { int type_code; int num_interfaces; int my_interface_num; int num_endpoints; int my_ep_out; int my_ep_in; char *name; int endpoints[4]; /* for matching */ } astribank_types[] = { TYPE_ENTRY(USB_11xx, 1, 0, 4, MY_EP_OUT, MY_EP_IN, FPGA_EP_OUT, MY_EP_OUT, FPGA_EP_IN, MY_EP_IN), TYPE_ENTRY(USB_FIRMWARE_II, 2, 1, 2, MY_EP_OUT, MY_EP_IN, MY_EP_OUT, MY_EP_IN), }; #undef TYPE_ENTRY enum fpga_load_packet_types { PT_STATUS_REPLY = 0x01, PT_DATA_PACKET = 0x01, #ifdef XORCOM_INTERNAL PT_EEPROM_SET = 0x04, #endif PT_EEPROM_GET = 0x08, PT_RENUMERATE = 0x10, PT_RESET = 0x20, PT_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 label[LABEL_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 }; struct my_usb_device { struct usb_device *dev; usb_dev_handle *handle; int my_interface_num; int my_ep_out; int my_ep_in; char iManufacturer[BUFSIZ]; char iProduct[BUFSIZ]; char iSerialNumber[BUFSIZ]; char iInterface[BUFSIZ]; int is_usb2; struct myeeprom eeprom; const struct astribank_type *abtype; }; 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"; } } /* return 1 if: * - str has a number * - It is larger than 0 * - It equals num */ int num_matches(int num, const char* str) { int str_val = atoi(str); if (str_val <= 0) return 0; return (str_val == num); } 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 *p; int bnum; int dnum; int ret; assert(path != NULL); if(access(path, F_OK) < 0) { perror(path); return NULL; } /* Find last '/' */ if((p = (const char *)memrchr(path, '/', strlen(path))) == NULL) { ERR("Missing a '/' in %s\n", path); return NULL; } /* Get the device number */ ret = sscanf(p + 1, "%d", &dnum); if(ret != 1) { ERR("Path tail is not a device number: '%s'\n", p); return NULL; } /* Search for a '/' before that */ p = (const char *)memrchr(path, '/', p - path); if(p == NULL) p = path; /* Relative path */ else p++; /* skip '/' */ /* Get the bus number */ ret = sscanf(p, "%d", &bnum); if(ret != 1) { ERR("Path tail is not a bus number: '%s'\n", p); return NULL; } sprintf(dirname, "%03d", bnum); sprintf(filename, "%03d", dnum); for (bus = usb_busses; bus; bus = bus->next) { if (! num_matches(bnum, bus->dirname)) //if(strcmp(bus->dirname, dirname) != 0) continue; for (dev = bus->devices; dev; dev = dev->next) { //if(strcmp(dev->filename, filename) == 0) if (num_matches(dnum, dev->filename)) 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); } void my_usb_device_cleanup(struct my_usb_device *mydev) { assert(mydev != NULL); if(!mydev->handle) { return; /* Nothing to do */ } if(!disconnected) { if(usb_release_interface(mydev->handle, mydev->abtype->my_interface_num) != 0) { ERR("Releasing interface: usb: %s\n", usb_strerror()); } } if(usb_close(mydev->handle) != 0) { ERR("Closing device: usb: %s\n", usb_strerror()); } disconnected = 1; mydev->handle = NULL; } static void show_device_info(const struct my_usb_device *mydev) { const struct myeeprom *eeprom; uint8_t data[LABEL_SIZE + 1]; assert(mydev != NULL); eeprom = &mydev->eeprom; memset(data, 0, LABEL_SIZE + 1); memcpy(data, eeprom->label, LABEL_SIZE); printf("USB Firmware Type: [%s]\n", mydev->abtype->name); printf("USB iManufacturer: [%s]\n", mydev->iManufacturer); printf("USB iProduct: [%s]\n", mydev->iProduct); printf("USB iSerialNumber: [%s]\n", mydev->iSerialNumber); printf("EEPROM Source: 0x%02X\n", eeprom->source); printf("EEPROM Vendor: 0x%04X\n", eeprom->vendor); printf("EEPROM Product: 0x%04X\n", eeprom->product); printf("EEPROM Release: %d.%03d\n", eeprom->release_major, eeprom->release_minor); printf("EEPROM Label: HEX(%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X) [%s]\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data); } void dump_packet(const char *msg, const char *buf, int len) { int i; for(i = 0; i < len; i++) INFO("%s: %2d> 0x%02X\n", msg, i, (uint8_t)buf[i]); } int send_usb(const char *msg, struct my_usb_device *mydev, struct fpga_packet_header *phead, int len, int timeout) { char *p = (char *)phead; int ret; if(verbose >= LOG_DEBUG) dump_packet(msg, p, len); if(mydev->my_ep_out & USB_ENDPOINT_IN) { ERR("send_usb called with an input endpoint 0x%x\n", mydev->my_ep_out); return -EINVAL; } ret = usb_bulk_write(mydev->handle, mydev->my_ep_out, p, len, timeout); if(ret < 0) { /* * If the device was gone, it may be the * result of renumeration. Ignore it. */ if(ret != -ENODEV) { ERR("bulk_write to endpoint 0x%x failed: %s\n", mydev->my_ep_out, usb_strerror()); dump_packet("send_usb[ERR]", p, len); } else { disconnected = 1; my_usb_device_cleanup(mydev); } return ret; } else if(ret != len) { ERR("bulk_write to endpoint 0x%x short write: %s\n", mydev->my_ep_out, usb_strerror()); dump_packet("send_usb[ERR]", p, len); return -EFAULT; } return ret; } int recv_usb(const char *msg, struct my_usb_device *mydev, char *buf, size_t len, int timeout) { int ret; if(mydev->my_ep_in & USB_ENDPOINT_OUT) { ERR("recv_usb called with an output endpoint 0x%x\n", mydev->my_ep_in); return -EINVAL; } ret = usb_bulk_read(mydev->handle, mydev->my_ep_in, buf, len, timeout); if(ret < 0) { ERR("bulk_read from endpoint 0x%x failed: %s\n", mydev->my_ep_in, usb_strerror()); return ret; } if(verbose >= LOG_DEBUG) dump_packet(msg, buf, ret); return ret; } int flush_read(struct my_usb_device *mydev) { char tmpbuf[BUFSIZ]; int ret; memset(tmpbuf, 0, BUFSIZ); ret = recv_usb("flush_read", mydev, tmpbuf, sizeof(tmpbuf), TIMEOUT); if(ret < 0 && ret != -ETIMEDOUT) { ERR("ret=%d\n", ret); return ret; } else if(ret > 0) { DBG("Got %d bytes:\n", ret); dump_packet(__FUNCTION__, tmpbuf, ret); } return 0; } #ifdef XORCOM_INTERNAL int eeprom_set(struct my_usb_device *mydev, const struct myeeprom *eeprom) { int ret; int len; char buf[PACKET_SIZE]; struct fpga_packet_header *phead = (struct fpga_packet_header *)buf; DBG("%s Start...\n", __FUNCTION__); assert(mydev != NULL); phead->header.op = PT_EEPROM_SET; memcpy(&phead->d.eeprom_set.data, eeprom, EEPROM_SIZE); len = sizeof(phead->d.eeprom_set) + sizeof(phead->header.op); ret = send_usb("eeprom_set[W]", mydev, phead, len, TIMEOUT); if(ret < 0) return ret; ret = recv_usb("eeprom_set[R]", mydev, buf, sizeof(buf), TIMEOUT); if(ret <= 0) return ret; phead = (struct fpga_packet_header *)buf; if(phead->header.op == PT_BAD_COMMAND) { ERR("Firmware rejected PT_EEPROM_SET command\n"); return -EINVAL; } else if(phead->header.op != PT_EEPROM_SET) { ERR("Got unexpected reply op=%d\n", phead->header.op); return -EINVAL; } return 0; } #endif int eeprom_get(struct my_usb_device *mydev) { int ret; int len; char buf[PACKET_SIZE]; struct fpga_packet_header *phead = (struct fpga_packet_header *)buf; struct myeeprom *eeprom; assert(mydev != NULL); eeprom = &mydev->eeprom; DBG("%s Start...\n", __FUNCTION__); phead->header.op = PT_EEPROM_GET; len = sizeof(phead->header.op); /* warning: sending small packet */ ret = send_usb("eeprom_get[W]", mydev, phead, len, TIMEOUT); if(ret < 0) return ret; ret = recv_usb("eeprom_get[R]", mydev, buf, sizeof(buf), TIMEOUT); if(ret <= 0) return ret; phead = (struct fpga_packet_header *)buf; if(phead->header.op == PT_BAD_COMMAND) { ERR("PT_BAD_COMMAND\n"); return -EINVAL; } else if(phead->header.op != PT_EEPROM_GET) { ERR("Got unexpected reply op=%d\n", phead->header.op); return -EINVAL; } memcpy(eeprom, &phead->d.eeprom_get.data, EEPROM_SIZE); return 0; } int send_hexline(struct my_usb_device *mydev, 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(mydev != NULL); assert(hexline != NULL); if(hexline->d.content.header.tt != TT_DATA) { DBG("Non data record %d type = %d\n", seq, hexline->d.content.header.tt); return 0; } len = hexline->d.content.header.ll; /* don't send checksum */ data = hexline->d.content.tt_data.data; phead->header.op = PT_DATA_PACKET; phead->d.data_packet.seq = seq; phead->d.data_packet.reserved = 0x00; memcpy(phead->d.data_packet.data, data, len); len += sizeof(hexline->d.content.header); DBG("%04d+\r", seq); ret = send_usb("hexline[W]", mydev, phead, len, TIMEOUT); if(ret < 0) return ret; ret = recv_usb("hexline[R]", mydev, buf, sizeof(buf), TIMEOUT); if(ret <= 0) return ret; DBG("%04d-\r", seq); phead = (struct fpga_packet_header *)buf; if(phead->header.op != PT_STATUS_REPLY) { ERR("Got unexpected reply op=%d\n", phead->header.op); dump_packet("hexline[ERR]", buf, ret); 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); dump_packet("hexline[ERR]", buf, ret); return -EPROTO; default: ERR("Unknown status reply %d\n", status); dump_packet("hexline[ERR]", buf, ret); return -EPROTO; } return 0; } //. returns > 0 - ok, the number of lines sent //. returns < 0 - error number int send_splited_hexline(struct my_usb_device *mydev, struct hexline *hexline, int seq, uint8_t maxwidth) { struct hexline *extraline; int linessent = 0; int allocsize; int extra_offset = 0; unsigned int this_line = 0; uint8_t bytesleft = 0; assert(mydev != NULL); if(!hexline) { ERR("Bad record %d type = %d\n", seq, hexline->d.content.header.tt); return -EINVAL; } bytesleft = hexline->d.content.header.ll; // split the line into several lines while (bytesleft > 0) { int status; this_line = (bytesleft >= maxwidth) ? maxwidth : bytesleft; allocsize = sizeof(struct hexline) + this_line + 1; // generate the new line if((extraline = (struct hexline *)malloc(allocsize)) == NULL) { ERR("Not enough memory for spliting the lines\n" ); return -EINVAL; } memset(extraline, 0, allocsize); extraline->d.content.header.ll = this_line; extraline->d.content.header.offset = hexline->d.content.header.offset + extra_offset; extraline->d.content.header.tt = hexline->d.content.header.tt; memcpy( extraline->d.content.tt_data.data, hexline->d.content.tt_data.data+extra_offset, this_line); status = send_hexline(mydev, extraline, seq+linessent ); // cleanups free(extraline); extra_offset += this_line; bytesleft -= this_line; if (status) return status; linessent++; } return linessent; } int match_usb_device_identity(const struct usb_config_descriptor *config_desc, const struct astribank_type *ab) { struct usb_interface *interface; struct usb_interface_descriptor *iface_desc; if(config_desc->bNumInterfaces <= ab->my_interface_num) return 0; interface = &config_desc->interface[ab->my_interface_num]; iface_desc = interface->altsetting; return iface_desc->bInterfaceClass == 0xFF && iface_desc->bInterfaceNumber == ab->my_interface_num && iface_desc->bNumEndpoints == ab->num_endpoints; } const struct astribank_type *my_usb_device_identify(const char devpath[], struct my_usb_device *mydev) { struct usb_device_descriptor *dev_desc; struct usb_config_descriptor *config_desc; int i; assert(mydev != NULL); usb_init(); usb_find_busses(); usb_find_devices(); mydev->dev = dev_of_path(devpath); if(!mydev->dev) { ERR("Bailing out\n"); return 0; } dev_desc = &mydev->dev->descriptor; config_desc = mydev->dev->config; for(i = 0; i < sizeof(astribank_types)/sizeof(astribank_types[0]); i++) { if(match_usb_device_identity(config_desc, &astribank_types[i])) { DBG("Identified[%d]: interfaces=%d endpoints=%d: \"%s\"\n", i, astribank_types[i].num_interfaces, astribank_types[i].num_endpoints, astribank_types[i].name); return &astribank_types[i]; } } return NULL; } int my_usb_device_init(const char devpath[], struct my_usb_device *mydev, const struct astribank_type *abtype) { 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; int ret; int i; assert(mydev != NULL); usb_init(); usb_find_busses(); usb_find_devices(); mydev->dev = dev_of_path(devpath); if(!mydev->dev) { ERR("Bailing out\n"); return 0; } mydev->handle = usb_open(mydev->dev); if(!mydev->handle) { ERR("Failed to open usb device '%s/%s': %s\n", mydev->dev->bus->dirname, mydev->dev->filename, usb_strerror()); return 0; } if(usb_claim_interface(mydev->handle, abtype->my_interface_num) != 0) { ERR("usb_claim_interface: %s\n", usb_strerror()); return 0; } dev_desc = &mydev->dev->descriptor; config_desc = mydev->dev->config; if (!config_desc) { ERR("usb interface without a configuration\n"); return 0; } interface = &config_desc->interface[abtype->my_interface_num]; iface_desc = interface->altsetting; endpoint = iface_desc->endpoint; mydev->is_usb2 = (endpoint->wMaxPacketSize == 512); for(i = 0; i < iface_desc->bNumEndpoints; i++, endpoint++) { if(endpoint->bEndpointAddress != abtype->endpoints[i]) { ERR("Wrong endpoint 0x%X (at index %d)\n", endpoint->bEndpointAddress, i); return 0; } 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 0; } } } mydev->abtype = abtype; mydev->my_ep_in = abtype->my_ep_in; mydev->my_ep_out = abtype->my_ep_out; ret = get_usb_string(mydev->iManufacturer, BUFSIZ, dev_desc->iManufacturer, mydev->handle); ret = get_usb_string(mydev->iProduct, BUFSIZ, dev_desc->iProduct, mydev->handle); ret = get_usb_string(mydev->iSerialNumber, BUFSIZ, dev_desc->iSerialNumber, mydev->handle); ret = get_usb_string(mydev->iInterface, BUFSIZ, iface_desc->iInterface, mydev->handle); INFO("ID=%04X:%04X Manufacturer=[%s] Product=[%s] SerialNumber=[%s] Interface=[%s]\n", dev_desc->idVendor, dev_desc->idProduct, mydev->iManufacturer, mydev->iProduct, mydev->iSerialNumber, mydev->iInterface); if(usb_clear_halt(mydev->handle, mydev->my_ep_out) != 0) { ERR("Clearing output endpoint: %s\n", usb_strerror()); return 0; } if(usb_clear_halt(mydev->handle, mydev->my_ep_in) != 0) { ERR("Clearing input endpoint: %s\n", usb_strerror()); return 0; } if(flush_read(mydev) < 0) { ERR("flush_read failed\n"); return 0; } return 1; } int renumerate_device(struct my_usb_device *mydev, enum fpga_load_packet_types pt) { char buf[PACKET_SIZE]; struct fpga_packet_header *phead = (struct fpga_packet_header *)buf; int ret; assert(mydev != NULL); DBG("Renumerating with 0x%X\n", pt); phead->header.op = pt; ret = send_usb("renumerate[W]", mydev, phead, 1, TIMEOUT); if(ret < 0 && ret != -ENODEV) return ret; #if 0 /* * FIXME: we count on our USB firmware to reset the device... should we? */ ret = usb_reset(mydev->handle); if(ret < 0) { ERR("usb_reset: %s\n", usb_strerror()); return -ENODEV; } #endif return 0; } /* * Returns: true on success, false on failure */ int fpga_load(struct my_usb_device *mydev, const struct hexdata *hexdata) { unsigned int i; unsigned int j = 0; int ret; int finished = 0; const char *v = hexdata->version_info; v = (v[0]) ? v : "Unknown"; assert(mydev != NULL); INFO("FPGA_LOAD (version %s)\n", v); /* * i - is the line number * j - is the sequence number, on USB 2, i=j, but on * USB 1 send_splited_hexline may increase the sequence * number, as it needs */ 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) { DBG("End of data\n"); finished = 1; continue; } if(mydev->is_usb2) { if((ret = send_hexline(mydev, hexline, i)) != 0) { perror("Failed sending hexline"); return 0; } } else { if((ret = send_splited_hexline(mydev, hexline, j, 60)) < 0) { perror("Failed sending hexline (splitting did not help)"); return 0; } j += ret; } } DBG("Finished...\n"); return 1; } #include void usage() { fprintf(stderr, "Usage: %s -D {/proc/bus/usb|/dev/bus/usb}// [options...]\n", progname); fprintf(stderr, "\tOptions:\n"); fprintf(stderr, "\t\t[-r] # Reset the device\n"); fprintf(stderr, "\t\t[-b ] # Output to \n"); fprintf(stderr, "\t\t[-I ] # Input from \n"); fprintf(stderr, "\t\t[-H ] # Output to ('-' is stdout)\n"); fprintf(stderr, "\t\t[-i] # Show hexfile information\n"); fprintf(stderr, "\t\t[-g] # Get eeprom from device\n"); fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); #ifdef XORCOM_INTERNAL fprintf(stderr, "\t\t[-C srC byte] # Set Address sourCe (default: C0)\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[-L label] # Set label.\n"); #endif 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); } #ifdef XORCOM_INTERNAL static void eeprom_fill(struct myeeprom *myeeprom, const char vendor[], const char product[], const char release[], const char label[], const char source[]) { // FF: address source is from device. C0: from eeprom if (source) myeeprom->source = strtoul(source, NULL, 0); else myeeprom->source = 0xC0; if(vendor) myeeprom->vendor = strtoul(vendor, NULL, 0); if(product) myeeprom->product = strtoul(product, NULL, 0); if(release) { int release_major = 0; int release_minor = 0; sscanf(release, "%d.%d", &release_major, &release_minor); myeeprom->release_major = release_major; myeeprom->release_minor = release_minor; } if(label) { /* padding */ memset(myeeprom->label, 0, LABEL_SIZE); memcpy(myeeprom->label, label, strlen(label)); } } #endif int main(int argc, char *argv[]) { const struct astribank_type *abtype; struct my_usb_device mydev; const char *devpath = NULL; const char *binfile = NULL; const char *inhexfile = NULL; const char *outhexfile = NULL; struct hexdata *hexdata = NULL; int opt_reset = 0; int opt_info = 0; int opt_read_eeprom = 0; int opt_output_width = 0; int output_is_set = 0; #ifdef XORCOM_INTERNAL int opt_write_eeprom = 0; char *vendor = NULL; char *source = NULL; char *product = NULL; char *release = NULL; char *label = NULL; const char options[] = "rib:D:ghH:I:vw:C:V:P:R:S:"; #else const char options[] = "rib:D:ghH:I:vw:"; #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; if(output_is_set++) { ERR("Cannot set -D. Another output option is already selected\n"); return 1; } break; case 'r': opt_reset = 1; break; case 'i': opt_info = 1; break; case 'b': binfile = optarg; if(output_is_set++) { ERR("Cannot set -b. Another output option is already selected\n"); return 1; } break; case 'g': opt_read_eeprom = 1; break; case 'H': outhexfile = optarg; if(output_is_set++) { ERR("Cannot set -H. Another output option is already selected\n"); return 1; } break; case 'I': inhexfile = optarg; break; #ifdef XORCOM_INTERNAL case 'V': vendor = optarg; break; case 'C': source = optarg; break; case 'P': product = optarg; break; case 'R': release = optarg; break; case 'S': label = optarg; { const char GOOD_CHARS[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "-_."; int len = strlen(label); int goodlen = strspn(label, GOOD_CHARS); if(len > LABEL_SIZE) { ERR("Label too long (%d > %d)\n", len, LABEL_SIZE); usage(); } if(goodlen != len) { ERR("Bad character in label number (pos=%d)\n", goodlen); usage(); } } break; #endif case 'w': opt_output_width = strtoul(optarg, NULL, 0); break; case 'v': verbose++; break; case 'h': default: ERR("Unknown option '%c'\n", c); usage(); } } if (optind != argc) { usage(); } if(inhexfile) { #ifdef XORCOM_INTERNAL if(vendor || product || release || label || source ) { ERR("The -I option is exclusive of -[VPRSC]\n"); return 1; } #endif parse_hexfile_set_reporting(parse_report_func); hexdata = parse_hexfile(inhexfile, MAX_HEX_LINES); if(!hexdata) { ERR("Bailing out\n"); exit(1); } if(opt_info) { printf("%s: Version=%s Checksum=%d\n", inhexfile, hexdata->version_info, bsd_checksum(hexdata)); } if(binfile) { dump_binary(hexdata, binfile); return 0; } if(outhexfile) { if(opt_output_width) dump_hexfile2(hexdata, outhexfile, opt_output_width); else dump_hexfile(hexdata, outhexfile); return 0; } } #ifdef XORCOM_INTERNAL else if(vendor || product || release || label || source ) { if(outhexfile) { FILE *fp; if(strcmp(outhexfile, "-") == 0) fp = stdout; else if((fp = fopen(outhexfile, "w")) == NULL) { perror(outhexfile); return 1; } memset(&mydev.eeprom, 0, sizeof(struct myeeprom)); eeprom_fill(&mydev.eeprom, vendor, product, release, label, source); gen_hexline((uint8_t *)&mydev.eeprom, 0, sizeof(mydev.eeprom), fp); gen_hexline(NULL, 0, 0, fp); /* EOF */ return 0; } } #endif if(!devpath) { ERR("Missing device path\n"); usage(); } DBG("Startup %s\n", devpath); if((abtype = my_usb_device_identify(devpath, &mydev)) == NULL) { ERR("Bad device. Does not match our types.\n"); usage(); } INFO("FIRMWARE: %s (type=%d)\n", abtype->name, abtype->type_code); if(!my_usb_device_init(devpath, &mydev, abtype)) { ERR("Failed to initialize USB device '%s'\n", devpath); ret = -ENODEV; goto dev_err; } ret = eeprom_get(&mydev); if(ret < 0) { ERR("Failed reading eeprom\n"); goto dev_err; } #ifdef XORCOM_INTERNAL if(vendor || product || release || label || source ) { eeprom_fill(&mydev.eeprom, vendor, product, release, label, source); opt_write_eeprom = 1; opt_read_eeprom = 1; } #endif if(opt_read_eeprom) { show_device_info(&mydev); } if(hexdata) { if (!mydev.is_usb2) INFO("Warning: working on a low end USB1 backend\n"); if(!fpga_load(&mydev, hexdata)) { ERR("FPGA loading failed\n"); ret = -ENODEV; goto dev_err; } ret = renumerate_device(&mydev, PT_RENUMERATE); if(ret < 0) { ERR("Renumeration failed: errno=%d\n", ret); goto dev_err; } } #ifdef XORCOM_INTERNAL else if(opt_write_eeprom) { if(abtype->type_code == USB_FIRMWARE_II) { ERR("No EEPROM burning command in %s. Use fxload for that\n", abtype->name); goto dev_err; } ret = eeprom_set(&mydev, &mydev.eeprom); if(ret < 0) { ERR("Failed writing eeprom: %s\n", strerror(-ret)); goto dev_err; } printf("------- RESULTS -------\n"); show_device_info(&mydev); } #endif if(opt_reset) { DBG("Reseting to default\n"); ret = renumerate_device(&mydev, PT_RESET); if(ret < 0) { ERR("Renumeration to default failed: errno=%d\n", ret); goto dev_err; } } DBG("Exiting\n"); dev_err: my_usb_device_cleanup(&mydev); return ret; }