diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2014-04-07 22:25:06 +0200 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2014-04-07 22:25:06 +0200 |
commit | 399acc038750943c1a0ee7178a18714334871472 (patch) | |
tree | 343079cb893fe9b27d66455d0459536764b0865a /utils/e200tool | |
parent | 68370b6eea8b5bed54e82a07086ad7c81cc966dd (diff) |
Add new tool to upload code on e200/c200/view in manufacturer mode
This is actually the trivial part of e200tool from MrH: it simply writes the
code on the bulk endpoint. Code was mostly copied from imxtools/sbloader.
Change-Id: I6c208840d23553aaf3bd8b9374e6b0337e54f3b0
Diffstat (limited to 'utils/e200tool')
-rw-r--r-- | utils/e200tool/Makefile | 21 | ||||
-rw-r--r-- | utils/e200tool/e200load.c | 310 |
2 files changed, 331 insertions, 0 deletions
diff --git a/utils/e200tool/Makefile b/utils/e200tool/Makefile new file mode 100644 index 0000000000..63f82b9cdd --- /dev/null +++ b/utils/e200tool/Makefile @@ -0,0 +1,21 @@ +DEFINES= +CC=gcc +LD=gcc +CFLAGS=-O2 -g -std=c99 -W -Wall `pkg-config --cflags libusb-1.0` $(DEFINES) +LDFLAGS=`pkg-config --libs libusb-1.0` +BINS=e200load + +all: $(BINS) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +e200load: e200load.o + $(LD) -o $@ $^ $(LDFLAGS) + +clean: + rm -fr *.o + +veryclean: + rm -rf $(BINS) + diff --git a/utils/e200tool/e200load.c b/utils/e200tool/e200load.c new file mode 100644 index 0000000000..8e5005b1f8 --- /dev/null +++ b/utils/e200tool/e200load.c @@ -0,0 +1,310 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2014 Amaury Pouly + * + * 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 <stdlib.h> +#include <string.h> +#include <libusb.h> +#include <stdint.h> +#include <stdbool.h> +#include <getopt.h> + +bool g_debug = false; + +struct dev_info_t +{ + uint16_t vendor_id; + uint16_t product_id; + unsigned xfer_size; +}; + +struct dev_info_t g_dev_info[] = +{ + {0x0781, 0x0720, 64}, /* Sandisk E200/C200/View */ + {0x0b70, 0x0003, 64}, /* Portal Player */ +}; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +static void put32le(uint8_t *buf, uint32_t i) +{ + *buf++ = i & 0xff; + *buf++ = (i >> 8) & 0xff; + *buf++ = (i >> 16) & 0xff; + *buf++ = (i >> 24) & 0xff; +} + +static int send_recovery(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size) +{ + // there should be no kernel driver attached but in doubt... + libusb_detach_kernel_driver(dev, 0); + libusb_claim_interface(dev, 0); + + uint8_t size_buffer[4]; + put32le(size_buffer, size); + int xfered; + int ret = libusb_bulk_transfer(dev, 1, size_buffer, sizeof(size_buffer), &xfered, 1000); + if(ret < 0 || xfered != sizeof(size_buffer)) + { + printf("transfer error at init step: %d", ret); + return 1; + } + + int sent = 0; + while(sent < size) + { + int xfered; + int len = MIN(size - sent, xfer_size); + int ret = libusb_bulk_transfer(dev, 1, data + sent, len, &xfered, 1000); + if(ret < 0 || xfered != len) + { + printf("transfer error at send offset %d: %d\n", sent, ret); + return 1; + } + sent += xfered; + } + return 0; +} + +static void usage(void) +{ + printf("sbloader [options] file\n"); + printf("options:\n"); + printf(" -h/-?/--help Display this help\n"); + printf(" -d/--debug Enable debug output\n"); + printf(" -x <size> Force transfer size\n"); + printf(" -u <vid>:<pid> Force USB PID and VID\n"); + printf(" -b <bus>:<dev> Force USB bus and device\n"); + printf("The following devices are known to this tool:\n"); + for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) + { + printf(" %04x:%04x (%d bytes/xfer)\n", g_dev_info[i].vendor_id, + g_dev_info[i].product_id, g_dev_info[i].xfer_size); + } + printf("You can select a particular device by USB PID and VID.\n"); + printf("In case this is ambiguous, use bus and device number.\n"); + printf("Protocol is infered if possible and unspecified.\n"); + printf("Transfer size is infered if possible.\n"); + exit(1); +} + +static bool dev_match(libusb_device *dev, struct dev_info_t *arg_di, + int usb_bus, int usb_dev, int *db_idx) +{ + // match bus/dev + if(usb_bus != -1) + return libusb_get_bus_number(dev) == usb_bus && libusb_get_device_address(dev) == usb_dev; + // get device descriptor + struct libusb_device_descriptor desc; + if(libusb_get_device_descriptor(dev, &desc)) + return false; + // match command line vid/pid if specified + if(arg_di->vendor_id != 0) + return desc.idVendor == arg_di->vendor_id && desc.idProduct == arg_di->product_id; + // match known vid/pid + for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) + if(desc.idVendor == g_dev_info[i].vendor_id && desc.idProduct == g_dev_info[i].product_id) + { + if(db_idx) + *db_idx = i; + return true; + } + return false; +} + +static void print_match(libusb_device *dev) +{ + struct libusb_device_descriptor desc; + if(libusb_get_device_descriptor(dev, &desc)) + printf("????:????"); + else + printf("%04x:%04x", desc.idVendor, desc.idProduct); + printf(" @ %d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev)); +} + +int main(int argc, char **argv) +{ + if(argc <= 1) + usage(); + struct dev_info_t di = {.vendor_id = 0, .product_id = 0, .xfer_size = 64}; + int usb_bus = -1; + int usb_dev = -1; + int force_xfer_size = 0; + /* parse command line */ + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"debug", no_argument, 0, 'd'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?dx:u:b:p:", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'd': + g_debug = true; + break; + case '?': + usage(); + break; + case 'x': + { + char *end; + force_xfer_size = strtoul(optarg, &end, 0); + if(*end) + { + printf("Invalid transfer size!\n"); + exit(2); + } + break; + } + case 'u': + { + char *end; + di.vendor_id = strtoul(optarg, &end, 16); + if(*end != ':') + { + printf("Invalid USB PID!\n"); + exit(3); + } + di.product_id = strtoul(end + 1, &end, 16); + if(*end) + { + printf("Invalid USB VID!\n"); + exit(4); + } + break; + } + case 'b': + { + char *end; + usb_bus = strtol(optarg, &end, 0); + if(*end != ':') + { + printf("Invalid USB bus!\n"); + exit(5); + } + usb_dev = strtol(end, &end, 0); + if(*end) + { + printf("Invalid USB device!\n"); + exit(6); + } + break; + } + default: + printf("Internal error: unknown option '%c'\n", c); + abort(); + } + } + + if(optind + 1 != argc) + usage(); + const char *filename = argv[optind]; + /* lookup device */ + libusb_init(NULL); + libusb_set_debug(NULL, 3); + libusb_device **list; + ssize_t list_size = libusb_get_device_list(NULL, &list); + libusb_device_handle *dev = NULL; + int db_idx = -1; + + { + libusb_device *mdev = NULL; + int nr_matches = 0; + for(int i = 0; i < list_size; i++) + { + // match bus/dev if specified + if(dev_match(list[i], &di, usb_bus, usb_dev, &db_idx)) + { + mdev = list[i]; + nr_matches++; + } + } + if(nr_matches == 0) + { + printf("No device found\n"); + exit(8); + } + if(nr_matches > 1) + { + printf("Several devices match the specified parameters:\n"); + for(int i = 0; i < list_size; i++) + { + // match bus/dev if specified + if(dev_match(list[i], &di, usb_bus, usb_dev, NULL)) + { + printf(" "); + print_match(list[i]); + } + } + } + printf("Device: "); + print_match(mdev); + libusb_open(mdev, &dev); + } + if(dev == NULL) + { + printf("Cannot open device\n"); + return 1; + } + /* get protocol */ + int xfer_size = di.xfer_size; + if(db_idx >= 0) + xfer_size = g_dev_info[db_idx].xfer_size; + if(force_xfer_size > 0) + xfer_size = force_xfer_size; + /* open file */ + FILE *f = fopen(filename, "r"); + if(f == NULL) + { + perror("Cannot open file"); + return 1; + } + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + printf("Transfer size: %d\n", xfer_size); + uint8_t *file_buf = malloc(size); + if(fread(file_buf, size, 1, f) != 1) + { + perror("read error"); + fclose(f); + return 1; + } + fclose(f); + /* send file */ + return send_recovery(dev, xfer_size, file_buf, size); +} + + |