/* * tt-loader - A system loader through USB derived from OMAP Flash Loader * * Copyright (C) 2008 Guillaume Bougard * Copyright (C) 2005 Luis Recuerda * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include #include #include #include #include /* * Here Vendor/Product detected for the target device */ #define OMAP_VENDOR 0x0451 #define OMAP_PRODUCT 0x3f01 #define IN_EP 0x81 #define OUT_EP 0x02 /* * Length of memory dump done by 2nd.bin */ #define MEM_READ_SIZE 32 struct mem_file { void *content ; int size ; }; #define MAX_SIZE 65536 static char buffer[MAX_SIZE + 128]; static int buffsize = 0; static struct mem_file cmdfile = { NULL, 0 }; static double btime ; static double ticks ; #define log1(X) fprintf(stderr, "%9.3f: "X,((double)times(NULL)-btime)/ticks) #define log2(X,Y) fprintf(stderr, "%9.3f: "X,((double)times(NULL)-btime)/ticks,Y) #define log3(X,Y,Z) fprintf(stderr, "%9.3f: "X,((double)times(NULL)-btime)/ticks,Y,Z) #if __BYTE_ORDER == __LITTLE_ENDIAN # define cpu_to_le32(x) (x) # define le32_to_cpu(x) (x) #else # define cpu_to_le32(x) bswap_32 (x) # define le32_to_cpu(x) bswap_32 (x) #endif static inline unsigned do_div (unsigned v, unsigned d) { v+= --d; return v & ~d; } static int stringcopy( char * dest, const char *src, int max ) { char *tmp = dest, *s = (char *) src; int count = 0 ; while (count++ < max) if (!(*tmp++ = *s++ )) break ; return count; } static int send_cmd (usb_dev_handle *handle, const char req, const char *src) { int res, len = 5 ; char buffer[64]; buffer[0]= 'T'; buffer[1]= 'I'; buffer[2]= 'S'; buffer[3]= req ; buffer[4]= '\0' ; if (src!=NULL) len += stringcopy (&buffer[4], src, 60); buffer[63]= '\0' ; // truncate anyway res= usb_bulk_write (handle, OUT_EP, buffer, len, 1000); if (res < len) { fprintf (stderr, "Error in usb_bulk_write during send_char: %d/4\n", res); return 0; } return 1; } static int send_binsize (usb_dev_handle *handle, int sz) { int res; char buffer[8]; buffer[0]= 'T'; buffer[1]= 'I'; buffer[2]= 'S'; buffer[3]= 's'; *(u_int32_t *) &buffer[4]= cpu_to_le32 ((sz>4096)?4096:sz); res= usb_bulk_write (handle, OUT_EP, buffer, 8, 1000); if (res < 8) { fprintf (stderr, "Error in usb_bulk_write during send_binsize: %d/8\n", res); return 0; } return 1; } static int send_address (usb_dev_handle *handle, int addr) { int res; char buffer[8]; buffer[0]= 'T'; buffer[1]= 'I'; buffer[2]= 'S'; buffer[3]= 'a'; *(u_int32_t *) &buffer[4]= cpu_to_le32 (addr); res= usb_bulk_write (handle, OUT_EP, buffer, 8, 1000); if (res < 8) { fprintf (stderr, "Error in usb_bulk_write: %d/8\n", res); return 0; } return 1; } static struct mem_file readfile( const char *filename ) { struct mem_file toread = { NULL, 0 }; int fd= open (filename, O_RDONLY); if (fd < 0) { fprintf (stderr, "Error opening %s file\n", filename); return toread; } toread.size = lseek (fd, 0, SEEK_END); if (toread.size < 0 || lseek (fd, 0, SEEK_SET) < 0) { fprintf (stderr, "Error with lseek other %s file\n", filename); close (fd); return toread; } toread.content= malloc (do_div (toread.size, 4)); if (toread.content == NULL) { fprintf (stderr, "Out of memory requesting %d bytes\n", toread.size); close (fd); return toread; } if ((toread.size= read (fd, toread.content, toread.size)) < 0) { fprintf (stderr, "Error reading %s file\n", filename); close (fd); return toread; } close (fd); return toread ; } static int process (usb_dev_handle *handle) { int err = -1 ; log1("OMAP found, trying to configure it\n"); for (;;) { err= usb_set_configuration (handle, 1); if (err == 0) break; if (err == -ENODEV) { log1("OMAP error, retrying\n"); return 1; } sleep (1); } err = usb_claim_interface (handle, 0); if ( err < 0) log2("Error in usb_claim_interface (%d)\n",err); else { char inbuff[256]; int insize= usb_bulk_read (handle, IN_EP, inbuff, sizeof (inbuff), 5000); if (insize < 48) log2("Error in usb_bulk_read: %d\n", insize); else { log1("OMAP 1st boot contacted, sending 2nd boot\n"); memcpy (&buffer[20], inbuff, 44); memcpy (&buffer[80], inbuff, 48); char *p = buffer; int size= buffsize; while (size > 0) { int outsize= usb_bulk_write (handle, OUT_EP, p, 64, 5000); if (outsize < 64) { log2("Error in usb_bulk_write: %d/64\n", outsize); break; } p+= 64; size-= 64; } if (size <= 0) { log2("Sent %d bytes to OMAP\n",buffsize); usb_bulk_write (handle, OUT_EP, buffer, 0, 1000); p= NULL; size= 0; int res=1, sz= 0, loop= 1, cmd=0 ; int vendor, device, info ; char *cmdp = cmdfile.content ; struct mem_file current = { NULL, 0 }; err = 1 ; // Set error by default while (loop != 0) { if (loop<0) loop++ ; // Avoid infinite loop if (res>0) res= usb_bulk_read (handle, IN_EP, inbuff, sizeof (inbuff), 5000); if (res < 0) { log2("Error in usb_bulk_read: %d\n",res); break; } if (res >= 4) { if (inbuff[0] == 'T' && inbuff[1] == 'I' && inbuff[2] == 'S') { switch (inbuff[3]) { case 'f': // Target claims a file log3("OMAP File asked: %s (%d bytes)\n", &inbuff[4], size); if (!send_binsize (handle, size)) loop = 0 ; // Just quit on error break; case 'o': // Update upload file buffer offset sz= le32_to_cpu (*(u_int32_t *) &inbuff[4]); p+= sz; size-= sz; //log3("OMAP seek %d to 0x%08lX\n", sz, (long)p-(long)current.content); // DEBUG if (size > 0 && !send_binsize (handle, size)) loop = 0 ; // Just quit on error if (size==0) res = 0 ; break; case 'n': // Upload a requested size chunk from the file buffer sz= le32_to_cpu (*(u_int32_t *) &inbuff[4]); //log2("OMAP read %d\n", sz); // DEBUG res= usb_bulk_write (handle, OUT_EP, p, sz, 5000); if (res < sz) { log3("Error in usb_bulk_write: %d/%d\n", res, sz); loop = 0 ; // Just quit on error } break; case 'I': info = le32_to_cpu (*(u_int32_t *) &inbuff[4]); log2("OMAP integer info: %d\n", info); break; case 'A': info = le32_to_cpu (*(u_int32_t *) &inbuff[4]); log2("OMAP address info: 0x%08X\n", info); break; case 'i': info = le32_to_cpu (*(u_int32_t *) &inbuff[4]); log2("OMAP Info returned: 0x%08X\n", info); res = 0 ; // Target is waiting a new command break; case 'v': vendor= le32_to_cpu (*(u_int32_t *) &inbuff[4]); log2("Flash Vendor found: 0x%02X\n", vendor); break; case 'd': device= le32_to_cpu (*(u_int32_t *) &inbuff[4]); log2("Flash Device found: 0x%02X\n", device); break; case 'm': log2("OMAP Message: %s\n", &inbuff[4]); break; case 'M': loop=3 ; log2("0x%08X | ",info); while (++loop<36) fprintf (stderr,"%02hhX", inbuff[loop]); loop=3 ; fprintf (stderr, " |"); while (++loop<36) if ((int)inbuff[loop]>=0x20 && (int)inbuff[loop]<0x7f) fprintf (stderr,"%c", inbuff[loop]); else fprintf (stderr,"."); fprintf (stderr,"|\n"); res = 0 ; // Target is waiting a new command info += 32 ; break; case 'r': log2("OMAP is ready: %s\n", &inbuff[4]); res = 0 ; // Target is waiting a new command break; case 'b': // Boot command has been received info = le32_to_cpu (*(u_int32_t *) &inbuff[4]); log2("OMAP is booting at address: 0x%08X\n", info); res = 0 ; // Target is waiting a new command break; default: log2("Unknown packet type '%c'\n", inbuff[3]); break; } } else { // Fix the string just in case inbuff[res] = '\0' ; log2("Got: %s\n", inbuff); } } else { // Manage commands if (cmd + 1 >= cmdfile.size) { if (loop>0) { log1("Command file read finished\n"); loop = 0 ; } } else { // res < 4 //log2("Next command is '%c'\n", cmdp[cmd]); // DEBUG switch (cmdp[cmd]) { case 'f': // Put a file to be uploaded in a memory buffer cmd += 2 ; if (cmd >= cmdfile.size || cmdp[cmd] == '\0') { log1("Bad 'f' format in command file\n"); break ; } // Free previously used memory buffer if (current.content != NULL) free((void *)current.content); current = readfile((const char *)&cmdp[cmd]); if (current.content == NULL) { log2("Can't read file '%s'\n",&cmdp[cmd]); loop = 0 ; // Just quit on error } p = current.content ; size = current.size ; // Say to OMAP target it can reclaim the file if (send_cmd (handle,'f',(const char *)&cmdp[cmd])) log1("File loaded and ready for upload\n"); else { log1("Can't say we are ready for upload\n"); // Just quit on error loop = 0 ; } break; case 'a': // Get an address and send it to target cmd += 2 ; if (sscanf((const char *)&cmdp[cmd], "%i", &info) != 1) { log2("Can't read address '%s'\n",&cmdp[cmd]); loop = 0 ; // Just quit on error } if (send_address (handle, info)) log2("Sending address 0x%02X\n", info); else { log2("Can't send address 0x%02X\n", info); loop = 0 ; // Just quit on error } break; case 'M': // Ask memory dump if (!(send_cmd (handle,'M',NULL))) { log1("Can't ask to read memory\n"); loop = 0 ; // Just quit on error } break; case 'c': // Make a call to the last given address if (send_cmd (handle,'c',NULL)) log1("Asking function call\n"); else { log1("Can't ask to call a function\n"); loop = 0 ; // Just quit on error } break; case 'b': // Boot the target by just branching to the last given address if (send_cmd (handle,'b',NULL)) log1("Asking boot\n"); else { log1("Can't ask to boot\n"); loop = 0 ; // Just quit on error } break; case 'e': // End of commands log1("Commands read and sent\n"); // Quit now witout error set loop = err = 0 ; cmd = cmdfile.size ; res = 1 ; break ; case '#': case '/': // skip comments res -- ; break ; default: log2("Unknown command type '%c'\n",cmdp[cmd]); break; } // kind of readline while (cmdp[++cmd] != '\0' && cmd < cmdfile.size); // point to next line while (cmdp[++cmd] == '\0' && cmd < cmdfile.size); res ++ ; } } } } } } if (err>=0) { err = usb_release_interface (handle, 0); if (err < 0) log2("Error in usb_release_interface (%d)\n",err); } return err ; } enum { ARG_PROGNAME, ARG_2NDFILE, ARG_CMDFILE, NUM_ARGS }; int main (int argc, char *argv[]) { int size; btime = (double) times(NULL); ticks = (double) sysconf(_SC_CLK_TCK) / 1000 ; if (argc < NUM_ARGS) { log2("Usage: %s <2nd_boot_file> \n", argv[ARG_PROGNAME]); return 1; } memset (buffer, 0, 128); u_int32_t *p= (u_int32_t *) buffer; p[0x00]= cpu_to_le32 (0xf0030002); int fd= open (argv[ARG_2NDFILE], O_RDONLY); if (fd < 0) { log1("open 2nd boot file\n"); return 1; } else { size= read (fd, &buffer[128], MAX_SIZE); if (size < 0) { log1("read 2nd boot file\n"); close (fd); return 1; } close (fd); if (size < 1024) { log2("2ndfile is too small! (%d bytes)\n", size); return 1; } } size= do_div (size, 4); p[0x21]= cpu_to_le32 (size - 0x40); p[0x01]= cpu_to_le32 (size); p[0x02]= cpu_to_le32 (size); p[0x03]= cpu_to_le32 (size); p[0x10]= cpu_to_le32 (size); p[0x11]= cpu_to_le32 (size); p[0x12]= cpu_to_le32 (size); buffsize= 128 + do_div (size, 4); cmdfile = readfile(argv[ARG_CMDFILE]); if (cmdfile.content == NULL) { log1("open cmdfile\n"); return 1; } else { int i = 0 ; char *cmdbuffer = (char *)cmdfile.content ; do { if (cmdbuffer[i] == '\n' || cmdbuffer[i] == '\r' || cmdbuffer[i] == ';') cmdbuffer[i] = '\0' ; } while ( i++ < cmdfile.size ); } usb_init (); usb_find_busses (); log1("Try to found OMAP730-USB connection\n"); int n= usb_find_devices (); if (n) { log1("Searching OMAP730-USB connection...\n"); struct usb_bus *bus; for (bus= usb_get_busses (); bus; bus= bus->next) { struct usb_device *dev; for (dev= bus->devices; dev; dev= dev->next) { usb_dev_handle *handle; if (dev->descriptor.idVendor == OMAP_VENDOR && dev->descriptor.idProduct == OMAP_PRODUCT && (handle= usb_open (dev)) != NULL) { log1("Found OMAP730-USB connection\n"); int res= process (handle); usb_close (handle); if (res == 0) { log1("OMAP730-USB connection processed\n"); return 0 ; } else log1("OMAP730-USB processing failed\n"); } } } } else { log1("No USB bus found\n"); } // Free memory if (cmdfile.content!=NULL) free((void *)cmdfile.content); log1("No target connection found\n"); return 1 ; }