diff options
author | John Rigby <john.rigby@linaro.org> | 2011-11-14 22:36:21 -0700 |
---|---|---|
committer | John Rigby <john.rigby@linaro.org> | 2011-11-14 22:36:21 -0700 |
commit | 27d77171a1b9f0caa60bd8aa310381bacaadf054 (patch) | |
tree | 7719217514886b1cd17882e616f79bbda1bee80f |
Initial commitv0.1
Signed-off-by: John Rigby <john.rigby@linaro.org>
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | usb.h | 66 | ||||
-rw-r--r-- | usb_linux.c | 370 | ||||
-rw-r--r-- | usbboot.c | 228 |
5 files changed, 692 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14e5d15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +local.mk +out diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..86ea1cd --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CFLAGS = -g -O2 -Wall + +prog = usbboot + +all: $(prog) + +install: all + install -d $(DESTDIR)/usr/bin + install $(prog) $(DESTDIR)/usr/bin + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ -MD -MT $@ -MF $(@:%o=%d) + +objs = usbboot.o usb_linux.o + + +$(prog): $(objs) + $(CC) $(CFLAGS) -o $@ $(objs) + +clean: + rm -rf $(prog) $(objs) $(deps) + +deps = $(objs:%o=%d) + +-include $(deps) + @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS 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 THE + * COPYRIGHT OWNER 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. + */ + +#ifndef _USB_H_ +#define _USB_H_ + +typedef struct usb_handle usb_handle; + +typedef struct usb_ifc_info usb_ifc_info; + +struct usb_ifc_info +{ + /* from device descriptor */ + unsigned short dev_vendor; + unsigned short dev_product; + + unsigned char dev_class; + unsigned char dev_subclass; + unsigned char dev_protocol; + + unsigned char ifc_class; + unsigned char ifc_subclass; + unsigned char ifc_protocol; + + unsigned char has_bulk_in; + unsigned char has_bulk_out; + + unsigned char writable; + + char serial_number[256]; +}; + +typedef int (*ifc_match_func)(usb_ifc_info *ifc); + +usb_handle *usb_open(ifc_match_func callback); +int usb_close(usb_handle *h); +int usb_read(usb_handle *h, void *_data, int len); +int usb_write(usb_handle *h, const void *_data, int len); + + +#endif diff --git a/usb_linux.c b/usb_linux.c new file mode 100644 index 0000000..aba9552 --- /dev/null +++ b/usb_linux.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS 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 THE + * COPYRIGHT OWNER 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <ctype.h> + +#include <linux/usbdevice_fs.h> +#include <linux/version.h> +#include <linux/usb/ch9.h> +#include <asm/byteorder.h> + +#include "usb.h" + +#define MAX_RETRIES 5 + +/*#define TRACE_USB*/ +#ifdef TRACE_USB +#define DBG(x...) fprintf(stderr, x) +#else +#define DBG(x...) +#endif + +struct usb_handle { + char fname[64]; + int desc; + unsigned char ep_in; + unsigned char ep_out; +}; + +static inline int badname(const char *name) +{ + while (*name) { + if (!isdigit(*name++)) + return 1; + } + return 0; +} + +static int check(void *_desc, int len, unsigned type, int size) +{ + unsigned char *desc = _desc; + + if (len < size) + return -1; + if (desc[0] < size) + return -1; + if (desc[0] > len) + return -1; + if (desc[1] != type) + return -1; + + return 0; +} + +static int filter_usb_device(int fd, char *ptr, int len, int writable, + ifc_match_func callback, + int *ept_in_id, int *ept_out_id, int *ifc_id) +{ + struct usb_device_descriptor *dev; + struct usb_config_descriptor *cfg; + struct usb_interface_descriptor *ifc; + struct usb_endpoint_descriptor *ept; + struct usb_ifc_info info; + + int in, out; + unsigned i; + unsigned e; + + if (check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE)) + return -1; + dev = (void *)ptr; + len -= dev->bLength; + ptr += dev->bLength; + + if (check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE)) + return -1; + cfg = (void *)ptr; + len -= cfg->bLength; + ptr += cfg->bLength; + + info.dev_vendor = dev->idVendor; + info.dev_product = dev->idProduct; + info.dev_class = dev->bDeviceClass; + info.dev_subclass = dev->bDeviceSubClass; + info.dev_protocol = dev->bDeviceProtocol; + info.writable = writable; + + for (i = 0; i < cfg->bNumInterfaces; i++) { + if (check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE)) + return -1; + ifc = (void *)ptr; + len -= ifc->bLength; + ptr += ifc->bLength; + + in = -1; + out = -1; + info.ifc_class = ifc->bInterfaceClass; + info.ifc_subclass = ifc->bInterfaceSubClass; + info.ifc_protocol = ifc->bInterfaceProtocol; + + for (e = 0; e < ifc->bNumEndpoints; e++) { + if (check + (ptr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE)) + return -1; + ept = (void *)ptr; + len -= ept->bLength; + ptr += ept->bLength; + + if ((ept->bmAttributes & 0x03) != 0x02) + continue; + + if (ept->bEndpointAddress & 0x80) { + in = ept->bEndpointAddress; + } else { + out = ept->bEndpointAddress; + } + } + + info.has_bulk_in = (in != -1); + info.has_bulk_out = (out != -1); + + if (callback(&info) == 0) { + *ept_in_id = in; + *ept_out_id = out; + *ifc_id = ifc->bInterfaceNumber; + return 0; + } + } + + return -1; +} + +static void scanning(char *str) +{ +#ifdef TRACE_USB + DBG("scanning %s\n", str); +#else + fprintf(stdout, "scanning %24s\r", str); +#endif +} + +static usb_handle *find_usb_device(const char *base, ifc_match_func callback) +{ + usb_handle *usb = 0; + char busname[64], devname[64]; + char desc[1024]; + int n, in, out, ifc; + + DIR *busdir, *devdir; + struct dirent *de; + int fd; + int writable; + + busdir = opendir(base); + if (busdir == 0) + return 0; + + while ((de = readdir(busdir)) && (usb == 0)) { + if (badname(de->d_name)) + continue; + + sprintf(busname, "%s/%s", base, de->d_name); + devdir = opendir(busname); + if (devdir == 0) + continue; + + scanning(busname); + while ((de = readdir(devdir)) && (usb == 0)) { + + if (badname(de->d_name)) + continue; + sprintf(devname, "%s/%s", busname, de->d_name); + + scanning(devname); + writable = 1; + if ((fd = open(devname, O_RDWR)) < 0) { + /* + * TODO: Check if we have read-only access, so we + * can give a helpful diagnostic like "adb devices" does. + */ + writable = 0; + if ((fd = open(devname, O_RDONLY)) < 0) { + continue; + } + } + + n = read(fd, desc, sizeof(desc)); + + if (filter_usb_device(fd, desc, n, writable, callback, + &in, &out, &ifc) == 0) { + usb = calloc(1, sizeof(usb_handle)); + strcpy(usb->fname, devname); + usb->ep_in = in; + usb->ep_out = out; + usb->desc = fd; + + n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc); + if (n != 0) { + close(fd); + free(usb); + usb = 0; + continue; + } + } else { + close(fd); + } + } + closedir(devdir); + } + closedir(busdir); +#ifndef TRACE_USB + if (usb) + fprintf(stdout, "\n"); +#endif + + return usb; +} + +int usb_write(usb_handle * h, const void *_data, int len) +{ + unsigned char *data = (unsigned char *)_data; + unsigned count = 0; + struct usbdevfs_bulktransfer bulk; + int n; + + if (h->ep_out == 0) { + return -1; + } + + if (len == 0) { + bulk.ep = h->ep_out; + bulk.len = 0; + bulk.data = data; + bulk.timeout = 0; + + n = ioctl(h->desc, USBDEVFS_BULK, &bulk); + if (n != 0) { + fprintf(stderr, "ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + return 0; + } + + while (len > 0) { + int xfer; + xfer = (len > 4096) ? 4096 : len; + + bulk.ep = h->ep_out; + bulk.len = xfer; + bulk.data = data; + bulk.timeout = 0; + + n = ioctl(h->desc, USBDEVFS_BULK, &bulk); + if (n != xfer) { + DBG("ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + + count += xfer; + len -= xfer; + data += xfer; + } + + return count; +} + +int usb_read(usb_handle * h, void *_data, int len) +{ + unsigned char *data = (unsigned char *)_data; + unsigned count = 0; + struct usbdevfs_bulktransfer bulk; + int n, retry; + + if (h->ep_in == 0) { + return -1; + } + + while (len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + bulk.ep = h->ep_in; + bulk.len = xfer; + bulk.data = data; + bulk.timeout = 0; + retry = 0; + + do { + DBG("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, + h->fname); + n = ioctl(h->desc, USBDEVFS_BULK, &bulk); + DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, + n, h->fname, retry); + + if (n < 0) { + DBG("ERROR: n = %d, errno = %d (%s)\n", n, + errno, strerror(errno)); + if (++retry > MAX_RETRIES) + return -1; + sleep(1); + } + } + while (n < 0); + + count += n; + len -= n; + data += n; + + if (n < xfer) { + break; + } + } + + return count; +} + +int usb_close(usb_handle * h) +{ + int fd; + + fd = h->desc; + h->desc = -1; + if (fd >= 0) { + close(fd); + DBG("[ usb closed %d ]\n", fd); + } + + return 0; +} + +usb_handle *usb_open(ifc_match_func callback) +{ + return find_usb_device("/dev/bus/usb", callback); +} diff --git a/usbboot.c b/usbboot.c new file mode 100644 index 0000000..bafcccf --- /dev/null +++ b/usbboot.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS 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 THE + * COPYRIGHT OWNER 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <stdint.h> +#include <fcntl.h> +#include <string.h> + +#include "usb.h" + +int usb_boot(usb_handle * usb, + void *data1, unsigned sz1, void *data2, unsigned sz2) +{ + if (data1) { + uint32_t msg_boot = 0xF0030002; + uint32_t msg_getid = 0xF0030003; + uint32_t msg_size = sz1; + uint8_t id[81]; + int i; + +#define OFF_CHIP 0x04 +#define OFF_ID 0x0F +#define OFF_MPKH 0x26 + memset(id, 0xee, 81); + fprintf(stderr, "reading ASIC ID\n"); + usb_write(usb, &msg_getid, sizeof(msg_getid)); + usb_read(usb, id, sizeof(id)); + + fprintf(stderr, "CHIP: %02x%02x\n", id[OFF_CHIP + 0], + id[OFF_CHIP + 1]); + fprintf(stderr, "IDEN: "); + for (i = 0; i < 20; i++) + fprintf(stderr, "%02x", id[OFF_ID + i]); + fprintf(stderr, "\nMPKH: "); + for (i = 0; i < 32; i++) + fprintf(stderr, "%02x", id[OFF_MPKH + i]); + fprintf(stderr, "\nCRC0: %02x%02x%02x%02x\n", + id[73], id[74], id[75], id[76]); + fprintf(stderr, "CRC1: %02x%02x%02x%02x\n", + id[77], id[78], id[79], id[80]); + + fprintf(stderr, "sending u-boot-spl to target... %08x\n", + msg_boot); + sleep(1); + usb_write(usb, &msg_boot, sizeof(msg_boot)); + sleep(1); + usb_write(usb, &msg_size, sizeof(msg_size)); + sleep(1); + usb_write(usb, data1, sz1); + } + + if (data2) { + char command[64]; + char response[64]; + long rsz; + + sleep(1); + fprintf(stderr, "sending download command to u-boot-spl\n"); + sprintf(command, "download:%08x", sz2); + usb_write(usb, command, sizeof(command)); + fprintf(stderr, "waiting for \"DATA\" response to download: command\n"); + usb_read(usb, response, sizeof(response)); + if (strncmp("DATA", response, 4)) { + fprintf(stderr, "unexpected response\"%s\"\n", response); + return -1; + } + rsz = strtol(response + 4, 0, 16); + if (rsz != sz2) { + fprintf(stderr, "bad returned size\"%s\"\n", response); + return -1; + } + sleep(1); + fprintf(stderr, "transferring %d bytes of data\n", sz2); + usb_write(usb, data2, sz2); + fprintf(stderr, "write finished, waiting for \"OKAY\"\n"); + usb_read(usb, response, sizeof(response)); + if (strncmp("OKAY", response, 4) == 0) { + fprintf(stderr, "data transfer successful\n"); + } else { + fprintf(stderr, "unexpected response to data xfer\"%s\"\n", response); + return -1; + } + } + + return 0; +} + +int match_omap4_bootloader(usb_ifc_info * ifc) +{ + if (ifc->dev_vendor != 0x0451) + return -1; + if ((ifc->dev_product != 0xd010) && (ifc->dev_product != 0xd00f)) + return -1; + return 0; +} + +/* + * same as above for now + */ +int match_u_boot_spl(usb_ifc_info * ifc) +{ + if (ifc->dev_vendor != 0x0525) + return -1; + if (ifc->dev_product != 0xFFFF) + return -1; + return 0; +} + +void *load_file(const char *file, unsigned *sz) +{ + void *data; + struct stat s; + int fd; + + fd = open(file, O_RDONLY); + if (fd < 0) + return 0; + + if (fstat(fd, &s)) + goto fail; + + data = malloc(s.st_size); + if (!data) + goto fail; + + if (read(fd, data, s.st_size) != s.st_size) { + free(data); + goto fail; + } + + close(fd); + *sz = s.st_size; + return data; + +fail: + close(fd); + return 0; +} + +extern unsigned char aboot_data[]; +extern unsigned aboot_size; + +int main(int argc, char **argv) +{ + void *data[2]; + unsigned sz[2]; + usb_handle *usb; + int i; + int once = 1; + + if (argc < 3) { + fprintf(stderr, "usage: usbboot u-boot-spl.bin u-boot.bin\n"); + return 0; + } + + for (i = 0; i < 2; i++) { + data[i] = load_file(argv[i + 1], sz + i); + if (data[i] == 0) { + fprintf(stderr, "cannot load '%s'\n", argv[i + 1]); + return -1; + } + } + + /* + * First talk to the omap4 mask rom boot loader + */ + for (;;) { + usb = usb_open(match_omap4_bootloader); + if (usb) { + usb_boot(usb, data[0], sz[0], 0, 0); + break; + } + if (once) { + once = 0; + fprintf(stderr, "waiting for OMAP44xx device...\n"); + } + usleep(2500); + } + usb_close(usb); + + /* + * Second talk to u-boot-spl usb boot loader + * this is a new connection since there is no + * hand off between the mask rom usb code and + * u-boot-spl + */ + once = 1; + for (;;) { + usb = usb_open(match_u_boot_spl); + if (usb) + return usb_boot(usb, 0, 0, data[1], sz[1]); + if (once) { + once = 0; + fprintf(stderr, "waiting for U-Boot-spl bootloader...\n"); + } + usleep(2500); + } + + return -1; +} |