/* * Copyright (C) 1999 Dirk-Willem van Gulik, dirkx@webweaving.org * WebWeaving Consultancy, All rights reserved. * * Original version for FreeBSD; Changes for Linux by Brad Hards, * brad.hards@dao.defence.gov.au. See '#ifdef BSD'. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY WEBWEAVING AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL WEBWEAVING OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * * This downloader should work with all with all USB-EZ Anchorchips which * have on board ram; i.e. the AN21y1, AN21y5 and AN21y6QC series. Where y * is the log2 of the internal memory size (i.e. aAN2146QC has 2^64=16kByte * of ram.) * * It has however only been tested; and written specifically for the * ActiveWire Inc. USB board. See * http://www.activewireinc.com * * Anchorchips has excelent documentation on line at * http://www.anchorchips.com */ #ifndef lint static const char rcsid[] = "$Id: ezdownload.c,v 1.4 2004/12/23 22:27:14 mcampos Exp $"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #ifdef BSD #include /* BSD (native) */ #else #include "/usr/src/linux/drivers/usb/usb.h" /* assume linux... */ #endif struct intels { int addr, len; unsigned char * data; struct intels * nxt; } intels; static int verbose = 0; #define SHARENV "USB_FIRMWARE_PATH" #define SHAREDIRS "/usr/share/usb/firmware:/usr/local/share/usb/firmware" #define WHITESPACE " \t_-\a" #define LFCR "\r\n" #define COMMENT "#;/*" /* Maximum number of bytes to upload in one packet */ /* Although USB does not limit you here, the Anchor docs * quote 64 as a limit, and Mato@activewireinc.com suggested * to use 16. * Another advantage of keeping it to 16 is that we do not * get bitten by the bug '\r' * Where len, type and crc are two char hex, addr * is a 4 char hex value and len data is 2*len * hex chars. */ int read_head(FILE * fh, struct intels * * doutp ) { unsigned char crc; unsigned char buff[256*2 + 9 + 2 + 1]; /* read buffer */ unsigned char tmp[5]; /* temp buf for conversions */ unsigned char data[256]; char *last; /* last character processed */ int len, type, c, i; int line = 1; /* current line number */ int comment = 0; /* are we in a comment? */ int addr; // quat_t :-) /* macro for error printing */ #define BAIL(x) \ do { \ fprintf(stderr, "%s at line %d\n", x, line); \ return 0; \ } while(0) /* XXX silly way of doing it. Should add a cyclic * buffer of course. And you could use fscanf if * you are not worried about broken lines. But * unfortunately we are. */ while(!feof(fh)) { /* * read in the first 9 bytes: * : -> 1+2+4+2 = 9 */ for(i = 0; i < 9 && (c = fgetc(fh)) != EOF; /* noop */) { if (index(LFCR, c)) { line++; comment = 0; continue; } else if (index(WHITESPACE, c)) { continue; } else if (index(COMMENT, c)) { comment = 1; } if (comment) continue; buff[i] = c; i++; } if (ferror(fh)) { BAIL(strerror(errno)); } else if (feof(fh)) { if (i) BAIL("HDR Truncated"); else BAIL("EOF before a type 1 line"); } /* the colon at the begginning of the line */ if (buff[0] != ':') BAIL("Colon expected"); /* the len field */ if (!isxdigit(buff[1]) || !isxdigit(buff[2])) BAIL("Len malformed"); memcpy(tmp, buff+1, 2); tmp[2]='\0'; len = strtol(tmp, &last, 16); if (last[0] != '\0') /* up to end of string? */ BAIL("Could not convert len"); if (len < 0 || len > 255) BAIL("Bad len (>255)"); /* the addr field */ if (!isxdigit(buff[3]) || !isxdigit(buff[4]) || !isxdigit(buff[5]) || !isxdigit(buff[6]) ) BAIL("Addr malformed"); memcpy(tmp, buff+3, 4); tmp[4]='\0'; addr = strtol(tmp, &last, 16); if (last[0]) /* up to end of string? */ BAIL("Could not convert addr"); /* the type field */ if (!isxdigit(buff[7]) || !isxdigit(buff[8])) BAIL("Type malformed"); memcpy(tmp, buff+7, 2); tmp[2]='\0'; type = strtol(tmp, &last, 16); if (last[0] != '\0') /* up to end of string? */ BAIL("Could not convert type"); if (type < 0 || type > 1) BAIL("Unkown type (not one of 00, 01)"); /* read 2 * len pairs and a CRC.. */ for(i = 0; i < 2*(len)+2 && (c = fgetc(fh)) != EOF;) { if (index(LFCR, c)) { line++; continue; } else if (index(WHITESPACE, c)) { continue; } if (!isxdigit(c)) BAIL("Malformed hex data"); buff[9+i] = c; i++; } if (ferror(fh)) { BAIL(strerror(errno)); } else if (i != 2*(len)+2) { BAIL("data or CRC block Truncated"); } for(i = 0; i < len+1+4; i++) { memcpy(tmp, buff+1+i*2, 2); tmp[2]='\0'; data[i] = strtol(tmp, &last, 16); if (last[0] != '\0') /* up to end of string? */ BAIL("Could not convert data or crc pair"); } for(crc = 0, i = 0; i < 4+len;i++) crc += data[i]; crc = ~crc + 1; if (crc != data[len+4]) BAIL("Checksum mismatch"); if (len && type == 0) { *doutp = malloc(sizeof(intels)); (*doutp)->addr = addr; (*doutp)->len = len; (*doutp)->data = malloc(len); memcpy((*doutp)->data, data+4, len); doutp = &( (*doutp)->nxt ); } else if (type == 1) { break; } else { BAIL("Unkown intel type"); } } return 1; } /* Transfer len bytes of buf, store them on the device at address at. * * If the payload is too big, it will automatically break it into * MAX_PAYLOAD chunks. */ int shunt(int fd, unsigned int at, void *buf, unsigned int len) { int i = 0; #ifdef BSD struct usb_ctl_request ur; #else struct usb_proc_ctrltransfer ur; #endif int err = 0; #ifdef BSD ur.ucr_request.bmRequestType= 0x40; /* see TRM v1.6 page 64 */ ur.ucr_request.bRequest = 0xA0; /* Anchor Download */ USETW(ur.ucr_request.wValue, at); /* Starting Address */ USETW(ur.ucr_request.wIndex, 0); /* unused */ USETW(ur.ucr_request.wLength, len); /* payload len in bytes */ ur.ucr_flags = 0; ur.ucr_actlen = 0; #else ur.requesttype= 0x40; /* see TRM v1.6 page 64 */ ur.request = 0xA0; /* Anchor Download */ ur.value = at; /* Starting Address */ ur.index = 0; /* unused */ ur.length = len; /* payload len in bytes */ #endif if (verbose >= 2) { fprintf(stderr, "Shunt called: START=%#x, BUF=%p, LEN=%d.\n", at, buf, len); } while (i < len) { int bytes_left = len - i; int pktlen = bytes_left < MAX_PAYLOAD ? bytes_left : MAX_PAYLOAD; unsigned int start_addr = at + i; if (verbose >= 2) { fprintf(stderr, "Downloading %d bytes @ %#x.\n", pktlen, start_addr); } ur.ucr_data = buf + i; #ifdef BSD USETW(ur.ucr_request.wValue, start_addr); /* Starting Address */ USETW(ur.ucr_request.wLength, pktlen); /* payload len in bytes */ #else ur.value = start_addr; /* Starting Address */ ur.length = pktlen; /* payload len in bytes */ #endif err = ioctl(fd, USB_DO_REQUEST, &ur); if (err) return err; i += pktlen; } return(0); } char * progname; void usage() { fprintf(stderr, "Syntax: %s [-r] [-v] [-f hexfile] device\n", progname); exit(1); } int main(int argc, char **argv) { char *devicename; int fd; int count = 0; /* numer of lines processed */ int force = 0; int renum = 0; struct intels *hx, *d; char tmp[MAXPATHLEN],*hexfile = NULL; char *dirs=SHAREDIRS; FILE *f; unsigned char b[2]; struct usb_device_info udi; struct usb_alt_interface ai; int ch; progname = argv[0]; /* handle the arguments */ while((ch = getopt(argc, argv, "xrvf:")) != -1) switch (ch) { case 'v': verbose++; break; case 'r': renum = 1; break; case 'f': hexfile = optarg; break; case 'x': force = 1; break; case '?': default: usage(); }; argc -= optind; argv += optind; if (argc != 1) usage(); devicename = argv[argc-1]; /* open the device, check what vendor/product it is */ fd = open(devicename, O_WRONLY, 0); if ( fd == -1) { fprintf(stderr, "Open usb ctrl endpoint '%s' failed: %s\n", devicename, strerror(errno)); return(1); } else { if (verbose) printf("Device %s opened for config\n", devicename); } /* as we -really- do not want people to accidentially send the * wrong firmware to a device; do some sanity checks on the * vendor and device ID's. I.e. like: * ugen0: vendor 0x0854 product 0x0100, rev 1.00/0.00, addr 2 */ if (ioctl(fd, USB_GET_DEVICEINFO, &udi)) { perror("Could not get information on this device"); return (1); }; #define SURE(x) (((x!=NULL) && (*x !='\0')) ? x : "") if (verbose) #ifdef BSD printf("Device information: vendor %04x (%s) product %04x (%s) rev 0x%04x addr %x\n", udi.udi_vendorNo, SURE(udi.udi_vendor), udi.udi_productNo, SURE(udi.udi_product), udi.udi_releaseNo, udi.udi_addr); #else printf("Device information: vendor %04x (%s) product %04x (%s) rev 0x%04x addr %x\n", udi.vendorNo, SURE(udi.vendor), udi.productNo, SURE(udi.product), udi.releaseNo, udi.addr); #endif /* standard setup for AnchorChips ISAIK */ #ifdef BSD ai.uai_config_index = 0; ai.uai_interface_index = 0; ai.uai_alt_no = 0; #else ai.config_index = 0; ai.interface_index = 0; ai.alt_no = 0; #endif if ( ioctl(fd, USB_SET_ALTINTERFACE, &ai) == -1 ) { fprintf(stderr, "ioctl 1 returned: %s\n", strerror(errno)); return(1); } if (verbose) printf("Fixed Configuration, Interface and Alternative settings to 0,0,0\n"); if (!hexfile) { /* construct a hex file, and check the various locations */ struct stat sb; char * p, *dir; if ((p=getenv(SHARENV)) == NULL) p = dirs; /* make a copy, as to not royally screw up ones * environment :-) */ if ((p=strdup(p)) == NULL) { perror(""); return(1); } for(;(dir=strsep(&p," \t:;"))!=NULL;) { /* XXX is the revision # really hardware specific ? Is it a string * or a BCD number ? */ #ifdef BSD snprintf(tmp,sizeof(tmp),"%s/%04x.%04x.%04x.hex", dir,udi.udi_vendorNo,udi.udi_productNo,udi.udi_releaseNo); #else snprintf(tmp,sizeof(tmp),"%s/%04x.%04x.%04x.hex", dir,udi.vendorNo,udi.productNo,udi.releaseNo); #endif /* we could just do a loop through the directory * and slack compare ? */ if (stat(tmp,&sb) == 0) { hexfile = tmp; break; }; if (verbose) printf("No %s\n",tmp); }; if (!hexfile) { #ifdef BSD fprintf(stderr,"No hexfile with firmware for " "%04x.%04x.%04x available.\n", udi.udi_vendorNo,udi.udi_productNo,udi.udi_releaseNo); #else fprintf(stderr,"No hexfile with firmware for " "%04x.%04x.%04x available.\n", udi.vendorNo,udi.productNo,udi.releaseNo); #endif if (!force) return(1); }; } else { /* take the leafname of the file and check the various ID's. */ char * l = rindex(hexfile,'/'); if (l==NULL) l=hexfile; else l++; #ifdef BSD snprintf(tmp,sizeof(tmp),"%04x.%04x.%04x", udi.udi_vendorNo,udi.udi_productNo,udi.udi_releaseNo); #else snprintf(tmp,sizeof(tmp),"%04x.%04x.%04x", udi.vendorNo,udi.productNo,udi.releaseNo); #endif if (strncmp(l,tmp,strlen(tmp))) { fprintf(stderr,"The firmware specified does not match the device: %s\n",tmp); if (!force) return(1); }; } /* Read the hex file. */ if (!(f=fopen(hexfile, "r"))) { fprintf(stderr, "Open hexfile '%s' failed: %s\n", hexfile, strerror(errno)); return(1); } hx = NULL; if (read_head(f, &hx)) { if (verbose) printf("Read hex file\n"); } else { fprintf(stderr, "Aborted\n"); return(1); } fclose(f); b[0] = 1; if (shunt(fd, CPUCS, (void *) b, 1)) { fprintf(stderr, "Could not send command to bring device in reset: %s", strerror(errno)); return(1); } else { if (verbose) printf("Command to bring 8051 in reset sent.\n"); } usleep(250000); for( d = hx; d; d = d->nxt) { #if 0 if ((d->len == 1) && (plen == 64) && (d->nxt == NULL)) fprintf(stderr,"Warning - Some chips cannot cope with 64*n+1 downloads\n" "See the erratum on the anchorchip.com site\n"); #endif if (shunt(fd, d->addr, (void *) (d->data), d->len)) { perror("Download failed"); return(1); } if (verbose) { printf("."); fflush(stdout); count++; /* number of blocks xferred */ } } if (verbose) printf("all %d packets downloaded\n", count); usleep(250000); b[0] = 0; if (shunt(fd, CPUCS, (void *) b, 1)) { fprintf(stderr, "Warning: Could not send command to get the " "device out of reset: %s\n", strerror(errno)); } else if (verbose) printf("Command to bring 8051 out of reset sent.\n"); if (renum) { usleep(250000); if (shunt(fd, USBSC, (void *) b, 1)) { fprintf(stderr, "Warning: Could not send command to " "renumerate the device: %s\n", strerror(errno)); } else if (verbose) printf("Command to renumerate sent.\n"); }; if (verbose) printf("It might take seconds before the entire cycle\n" "is completed and the usb(4) bus has noticed\n" "the renummeration.\n"); return 0; }