| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2007 Dave Chapman |
| * |
| * USB code based on ifp-line - http://ifp-driver.sourceforge.net |
| * |
| * ifp-line is (C) Pavel Kriz, Jun Yamishiro and Joe Roback and |
| * licensed under the GPL (v2) |
| * |
| * |
| * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <inttypes.h> |
| #include <usb.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| #define MAX_FIRMWARESIZE (10*1024*1024) /* Arbitrary limit (for safety) */ |
| |
| /* For win32 compatibility: */ |
| #ifndef O_BINARY |
| #define O_BINARY 0 |
| #endif |
| |
| struct device_t |
| { |
| char* name; |
| char* label; |
| uint16_t productid; |
| uint32_t loadaddr; |
| uint32_t sdcfg; |
| }; |
| |
| static struct device_t devices[] = |
| { |
| {"c100", "Sansa C100 series", 0xb021, 0x20000000, 0x42e97010 }, |
| {"m200", "Sansa M200 series", 0xb021, 0x20000000, 0x42e97010 }, |
| {"cowond2", "Cowon D2", 0xb011, 0x20000000, 0xa2e92010 }, |
| {"cowonj3", "Cowon J3", 0xb057, 0x20000000, 0xaaac7800 }, |
| {"cowons9", "Cowon S9", 0xb057, 0x20000000, 0xa1102800 }, |
| {"iaudio6", "iAudio 6", 0xb021, 0x20000000, 0x62e97010 }, |
| {"iaudio7", "iAudio 7", 0xb021, 0x20000000, 0x62e97010 }, |
| {"logikdax", "Logik DAX 1GB DAB/MP3 player", 0xb021, 0x20000000, 0x52e97410 }, |
| {"x20", "iRiver X20", 0xb051, 0x20000000, 0x02e92010 }, |
| {"ypp2", "Samsung YP-P2", 0xb011, 0x20000000, 0x22e92010 }, |
| {"ypk3", "Samsung YP-K3", 0xb021, 0x20000000, 0x62e92018 }, |
| }; |
| |
| #define NUM_DEVICES ((sizeof(devices) / sizeof(struct device_t))) |
| |
| int find_device(char* devname) |
| { |
| unsigned int i = 0; |
| |
| while ((i < NUM_DEVICES) && (strcmp(devices[i].name,devname))) |
| i++; |
| |
| if (i==NUM_DEVICES) |
| return -1; |
| else |
| return i; |
| } |
| |
| void print_devices(void) |
| { |
| unsigned int i; |
| |
| printf("Valid devices are:\n"); |
| for (i=0; i<NUM_DEVICES; i++) |
| { |
| printf(" %10s - %s\n",devices[i].name,devices[i].label); |
| } |
| } |
| |
| /* USB IDs for USB Boot Mode */ |
| #define TCC_VENDORID 0x140e |
| |
| #define TCC_BULK_TO 1 |
| #define TOUT 5000 |
| #define PACKET_SIZE 64 /* Number of bytes to send in one write */ |
| |
| #ifndef MAX |
| #define MAX(a,b) (((a)>(b))?(a):(b)) |
| #endif |
| |
| static void put_int32le(uint32_t x, char* p) |
| { |
| p[0] = x & 0xff; |
| p[1] = (x >> 8) & 0xff; |
| p[2] = (x >> 16) & 0xff; |
| p[3] = (x >> 24) & 0xff; |
| } |
| |
| int upload_app(usb_dev_handle* dh, int device, char* p, int len) |
| { |
| char buf[PACKET_SIZE]; |
| int err; |
| int i; |
| |
| /* Send the header - Destination address, length and SDCFG value */ |
| memset(buf, 0, PACKET_SIZE); |
| |
| put_int32le(0xf0000000, buf); /* Unknown - always the same */ |
| put_int32le(len / PACKET_SIZE, buf + 4); |
| put_int32le(devices[device].loadaddr, buf + 8); |
| put_int32le(devices[device].sdcfg, buf + 12); |
| |
| err = usb_bulk_write(dh, TCC_BULK_TO, buf, PACKET_SIZE, TOUT); |
| |
| if (err < 0) |
| { |
| fprintf(stderr,"[ERR] Error writing header\n"); |
| fprintf(stderr,"[ERR] Bulk write error (%d, %s)\n", err, strerror(-err)); |
| return -1; |
| } |
| |
| /* Now send the data, PACKET_SIZE bytes at a time. */ |
| |
| for (i=0 ; i < (len / PACKET_SIZE) ; i++) |
| { |
| err = usb_bulk_write(dh, TCC_BULK_TO, p, PACKET_SIZE, TOUT); |
| |
| if (err < 0) |
| { |
| fprintf(stderr,"[ERR] Error writing data\n"); |
| fprintf(stderr,"[ERR] Bulk write error (%d, %s)\n", err, strerror(-err)); |
| return -1; |
| } |
| |
| p += PACKET_SIZE; |
| } |
| |
| return 0; |
| } |
| |
| |
| /* The main function */ |
| |
| int do_patching(int device, char* buf, int len) |
| { |
| struct usb_bus *busses; |
| struct usb_bus *bus; |
| struct usb_device *tmp_dev; |
| struct usb_device *dev = NULL; |
| usb_dev_handle *dh; |
| int err; |
| |
| fprintf(stderr,"[INFO] Searching for TCC device...\n"); |
| |
| usb_init(); |
| if(usb_find_busses() < 0) { |
| fprintf(stderr, "[ERR] Could not find any USB busses.\n"); |
| return -1; |
| } |
| |
| if (usb_find_devices() < 0) { |
| fprintf(stderr, "[ERR] USB devices not found(nor hubs!).\n"); |
| return -1; |
| } |
| |
| /* C calling convention, it's not nice to use global stuff */ |
| busses = usb_get_busses(); |
| |
| for (bus = busses; bus; bus = bus->next) { |
| for (tmp_dev = bus->devices; tmp_dev; tmp_dev = tmp_dev->next) { |
| //printf("Found Vendor %04x Product %04x\n",tmp_dev->descriptor.idVendor, tmp_dev->descriptor.idProduct); |
| if (tmp_dev->descriptor.idVendor == TCC_VENDORID && |
| tmp_dev->descriptor.idProduct == devices[device].productid) { |
| |
| dev = tmp_dev; |
| goto found; |
| |
| } |
| } |
| } |
| |
| if (dev == NULL) { |
| fprintf(stderr, "[ERR] TCC device not found.\n"); |
| fprintf(stderr, "[ERR] Ensure your TCC device is in USB boot mode and run tcctool again.\n"); |
| return -1; |
| } |
| |
| found: |
| if ( (dh = usb_open(dev)) == NULL) { |
| fprintf(stderr,"[ERR] Unable to open TCC device.\n"); |
| return -1; |
| } |
| |
| err = usb_set_configuration(dh, 1); |
| |
| if (err < 0) { |
| fprintf(stderr, "[ERR] usb_set_configuration failed (%d)\n", err); |
| usb_close(dh); |
| return -1; |
| } |
| |
| /* "must be called" written in the libusb documentation */ |
| err = usb_claim_interface(dh, dev->config->interface->altsetting->bInterfaceNumber); |
| if (err < 0) { |
| fprintf(stderr, "[ERR] Unable to claim interface (%d)\n", err); |
| usb_close(dh); |
| return -1; |
| } |
| |
| fprintf(stderr,"[INFO] Found TCC device, uploading application.\n"); |
| |
| /* Now we can transfer the application to the device. */ |
| |
| if ( (err = upload_app(dh, device, buf, len)) < 0) |
| { |
| fprintf(stderr,"[ERR] Upload of application failed.\n"); |
| } |
| else |
| { |
| fprintf(stderr,"[INFO] Patching application uploaded successfully!\n"); |
| } |
| |
| /* release claimed interface */ |
| usb_release_interface(dh, dev->config->interface->altsetting->bInterfaceNumber); |
| usb_close(dh); |
| |
| return err < 0 ? -1: 0; |
| } |
| |
| off_t filesize(int fd) { |
| struct stat buf; |
| |
| if (fstat(fd,&buf) < 0) { |
| perror("[ERR] Checking filesize of input file"); |
| return -1; |
| } else { |
| return(buf.st_size); |
| } |
| } |
| |
| void print_usage(void) |
| { |
| printf("Usage: tcctool -d devicename firmware.bin\n"); |
| } |
| |
| int main(int argc, char* argv[]) |
| { |
| char* buf; |
| int n,len,padded_len; |
| int fd; |
| int device; |
| |
| printf("tcctool " VERSION " - (C) 2007-2010 Dave Chapman\n"); |
| printf("This is free software; see the source for copying conditions. There is NO\n"); |
| printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); |
| |
| if (argc != 4) |
| { |
| print_usage(); |
| print_devices(); |
| return 1; |
| } |
| |
| if (strcmp(argv[1],"-d")) |
| { |
| print_usage(); |
| print_devices(); |
| return 2; |
| } |
| |
| device = find_device(argv[2]); |
| |
| if (device < 0) |
| { |
| printf("[ERR] Unknown device \"%s\"\n",argv[2]); |
| print_devices(); |
| return 3; |
| } |
| |
| printf("[INFO] Using device \"%s\"\n",devices[device].label); |
| fd = open(argv[3], O_RDONLY|O_BINARY); |
| if (fd < 0) |
| { |
| printf("[ERR] Could not open %s\n", argv[3]); |
| return 4; |
| } |
| |
| len = filesize(fd); |
| |
| if (len > MAX_FIRMWARESIZE) |
| { |
| printf("[ERR] Firmware file too big\n"); |
| close(fd); |
| return 5; |
| } |
| |
| /* Round len up to multiple of PACKET_SIZE */ |
| padded_len = (len + PACKET_SIZE) & ~(PACKET_SIZE-1); |
| |
| buf = malloc(padded_len); |
| if (buf == NULL) |
| { |
| printf("[ERR] Could not allocate memory.\n"); |
| close(fd); |
| return 6; |
| } |
| |
| n = read(fd, buf, len); |
| if (n != len) |
| { |
| printf("[ERR] Short read.\n"); |
| close(fd); |
| return 7; |
| } |
| close(fd); |
| |
| if (do_patching(device, buf, padded_len)) |
| { |
| return 8; |
| } |
| |
| return 0; |
| } |