/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2003-2007 Marcel Holtmann * Copyright 2007 Bjørn Mork * * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dfu.h" #if __BYTE_ORDER == __LITTLE_ENDIAN #define cpu_to_le16(d) (d) #define cpu_to_le32(d) (d) #define le16_to_cpu(d) (d) #define le32_to_cpu(d) (d) #elif __BYTE_ORDER == __BIG_ENDIAN #define cpu_to_le16(d) bswap_16(d) #define cpu_to_le32(d) bswap_32(d) #define le16_to_cpu(d) bswap_16(d) #define le32_to_cpu(d) bswap_32(d) #else #error "Unknown byte order" #endif #ifdef NEED_USB_GET_BUSSES static inline struct usb_bus *usb_get_busses(void) { return usb_busses; } #endif #ifndef USB_CLASS_APPLICATION #define USB_CLASS_APPLICATION 0xfe #endif #ifndef VERSION #define VERSION "0.01" #endif static struct usb_interface_descriptor *get_interface(struct usb_device *dev) { int c, i, a; for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { struct usb_config_descriptor *config = &dev->config[c]; for (i = 0; i < config->bNumInterfaces; i++) { struct usb_interface *interface = &config->interface[i]; for (a = 0; a < interface->num_altsetting; a++) { struct usb_interface_descriptor *desc = &interface->altsetting[a]; if (desc->bInterfaceClass != USB_CLASS_APPLICATION) continue; if (desc->bInterfaceSubClass != DFU_INTF_SUBCLASS) continue; return desc; } } } return NULL; } static void print_device(struct usb_device *dev) { struct usb_interface_descriptor *desc = get_interface(dev); printf("Bus %s Device %s: ID %04x:%04x Interface %d%s\n", dev->bus->dirname, dev->filename, dev->descriptor.idVendor, dev->descriptor.idProduct, desc->bInterfaceNumber, desc->bInterfaceProtocol == DFU_PROTOCOL_DFUMODE ? " (DFU mode)" : ""); /* NOTE: 1668:2441 Actiontec Electronics, Inc. [hex] uses bInterfaceProtocol = DFU_PROTOCOL_NONE in both runtime and dfu mode: Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 2 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 254 Application Specific Interface bInterfaceSubClass 1 Device Firmware Update bInterfaceProtocol 0 iInterface 0 This is not complying with version 1.1 of the DFU spec, but I guess we must handle it */ } static void print_status(struct usb_dev_handle *udev, struct dfu_status status) { unsigned long timeout = (status.bwPollTimeout[2]<<16) | (status.bwPollTimeout[1]<<8) | status.bwPollTimeout[0]; char msg[255] = ""; if (status.iString) usb_get_string_simple(udev, status.iString, msg, sizeof(msg)); printf("dfu_status = { bStatus = %s, bwPollTimeout = %lu, bState = %s, iString = %d %s }\n", dfu_status_to_string(status.bStatus), timeout, dfu_state_to_string(status.bState), status.iString, msg); } static void print_dfu_descriptor(struct usb_dfu_descriptor dfu_dsc) { printf(" DFU descriptor\n" " bLength%18d\n" " bDescriptorType 0x%02x\n" " bmAttributes 0x%02x %s\n" " wDetachTimeout%11d ms\n" " wTransferSize 0x%04x 1x %d bytes\n", dfu_dsc.bLength, dfu_dsc.bDescriptorType, dfu_dsc.bmAttributes, dfu_attributes_to_string(dfu_dsc.bmAttributes), dfu_dsc.wDetachTimeout, dfu_dsc.wTransferSize, dfu_dsc.wTransferSize); } static void dfu_sleep(struct dfu_status status) { unsigned long timeout; timeout = (status.bwPollTimeout[2] << 16) | (status.bwPollTimeout[1] << 8) | status.bwPollTimeout[0]; usleep(timeout * 1000); } static struct usb_dev_handle *open_device(char *device, struct dfu_suffix *suffix, struct usb_dfu_descriptor *dfu_dsc) { struct usb_bus *bus; struct usb_device *dev, *dfu_dev[10]; struct usb_dev_handle *udev; struct dfu_status status; struct usb_interface_descriptor *intf_dsc; char str[8]; int i, intf, sel, num = 0, try = 5, bus_id = -1, dev_id = -1; printf("Scanning USB busses ... "); fflush(stdout); usb_find_busses(); usb_find_devices(); for (bus = usb_get_busses(); bus; bus = bus->next) { if (bus_id > 0) { snprintf(str, sizeof(str) - 1, "%03i", bus_id); if (strcmp(str, bus->dirname)) continue; } for (dev = bus->devices; dev; dev = dev->next) { if (bus_id > 0 && dev_id > 0) { snprintf(str, sizeof(str) - 1, "%03i", dev_id); if (strcmp(str, dev->filename)) continue; } if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) continue; if (num > 9 || get_interface(dev) == NULL) continue; dfu_dev[num++] = dev; } } if (num < 1) { printf("\rCan't find any DFU devices\n"); return NULL; } printf("\rAvailable devices with DFU support:\n\n"); for (i = 0; i < num; i++) { printf("\t%2d) ", i + 1); print_device(dfu_dev[i]); } printf("\n"); do { printf("\rSelect device (abort with 0): "); fflush(stdout); memset(str, 0, sizeof(str)); if (!fgets(str, sizeof(str) - 1, stdin)) continue; sel = atoi(str); } while (!isdigit(str[0]) || sel < 0 || sel > num ); if (sel < 1) return NULL; sel--; intf_dsc = get_interface(dfu_dev[sel]); intf = intf_dsc->bInterfaceNumber; printf("\n"); udev = usb_open(dfu_dev[sel]); if (!udev) { printf("Can't open device: %s (%d)\n", strerror(errno), errno); return NULL; } if (usb_claim_interface(udev, intf) < 0) { printf("Can't claim interface: %s (%d)\n", strerror(errno), errno); usb_close(udev); return NULL; } if (intf_dsc->bInterfaceProtocol == DFU_PROTOCOL_RUNTIME) { /* support for DFU_GET_STATUS in state DFU_STATE_APP_IDLE is optional - failing is no error */ if (dfu_get_status(udev, intf, &status) >= 0) print_status(udev, status); else /* assume APP_IDLE if DFU_GET_STATUS is unavailable */ status.bState = DFU_STATE_APP_IDLE; /* no need to try any other commands in this mode, since they are unsupported anyway... */ } else { /* DFU_PROTOCOL_DFUMODE or DFU_PROTOCOL_NONE - both may indicate DFU mode */ /* could the device be in DFU_STATE_APP_IDLE with DFU_PROTOCOL_NONE and not support DFU_GET_STATUS? - if so, then this will fail I'll leave it for now, since the only device I have using DFU_PROTOCOL_NONE does support DFU_GET_STATUS in all states */ if (dfu_get_status(udev, intf, &status) < 0) { printf("Can't get status: %s (%d)\n", strerror(errno), errno); goto error; } /* it's perfectly legal to be stuck in DFU_PROTOCOL_DFUMODE with DFU_STATE_ERROR if a previous firmware upgrade failed */ if (status.bState == DFU_STATE_ERROR) { if (dfu_clear_status(udev, intf) < 0) { printf("Can't clear status: %s (%d)\n", strerror(errno), errno); goto error; } /* this should not be necessary, since DFU_CLR_STATUS always will bring us into DFU IDLE if (dfu_abort(udev, intf) < 0) { printf("Can't abort previous action: %s (%d)\n", strerror(errno), errno); goto error; } */ if (dfu_get_status(udev, intf, &status) < 0) { printf("Can't get status: %s (%d)\n", strerror(errno), errno); goto error; } print_status(udev, status); } /* we might be stuck in any DFU MODE state - try DFU_ABORT */ if (status.bState > DFU_STATE_DFU_IDLE) { if (dfu_abort(udev, intf) < 0) { printf("Can't abort previous action: %s (%d)\n", strerror(errno), errno); goto error; } */ if (dfu_get_status(udev, intf, &status) < 0) { printf("Can't get status: %s (%d)\n", strerror(errno), errno); goto error; } print_status(udev, status); } } /* at this point, the device should either be in DFU mode and state DFU_STATE_DFU_IDLE or in runtime mode and state DFU_STATE_APP_IDLE - anything else is an unknown error */ if (dfu_get_dfu_descriptor(udev, intf, dfu_dsc) < 0) { printf("Can't get DFU descriptor: %s (%d)\n", strerror(errno), errno); goto error; } print_dfu_descriptor(*dfu_dsc); if (status.bState == DFU_STATE_DFU_IDLE) { /* already in DFU mode - return udev */ if (suffix) { suffix->idVendor = cpu_to_le16(0x0000); suffix->idProduct = cpu_to_le16(0x0000); suffix->bcdDevice = cpu_to_le16(0x0000); } return udev; } if (status.bState != DFU_STATE_APP_IDLE) { /* unknown and unhandled error - bug out */ printf("Device is not idle, can't detach it (state %s)\n", dfu_state_to_string(status.bState)); goto error; } printf("Switching device into DFU mode ... "); fflush(stdout); if (suffix) { suffix->idVendor = cpu_to_le16(dfu_dev[sel]->descriptor.idVendor); suffix->idProduct = cpu_to_le16(dfu_dev[sel]->descriptor.idProduct); suffix->bcdDevice = cpu_to_le16(dfu_dev[sel]->descriptor.bcdDevice); } /* my first attempt on implementing a DFU firmware made the DFU_DETACH request fail but the actual command to succeed, due to resetting too fast - is that legal? We'll assume it's not, and fail if DFU_DETACH fails */ if (dfu_detach(udev, intf, dfu_dsc->wDetachTimeout) < 0) { printf("\rCan't detach device: %s (%d)\n", strerror(errno), errno); goto error; } /* will the device initiate a detach-attach sequence? if so, then it's not allowed for us to do a USB reset */ if (dfu_dsc->bmAttributes & DFU_WILL_DETACH) { usb_release_interface(udev, intf); usb_close(udev); usleep(dfu_dsc->wDetachTimeout * 1000); /* assume this indicates } else { /* verify that State == DFU_STATE_APP_DETACH and do a USB reset */ if (dfu_get_status(udev, intf, &status) < 0) { printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); goto error; } if (status.bState != DFU_STATE_APP_DETACH) { printf("\rDevice is not in detach mode, try again\n"); goto error; } usb_release_interface(udev, intf); usb_reset(udev); usb_close(udev); } bus = dfu_dev[sel]->bus; /* device shouldn't change bus... */ num = 0; while (num != 1 && try-- > 0) { sleep(1); usb_find_devices(); for (dev = bus->devices; dev; dev = dev->next) { if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) continue; /* is this correct? - I've sort of assumed that changing VID is allowed too... if (suffix && dev->descriptor.idVendor != le16_to_cpu(suffix->idVendor)) continue; */ intf_dsc = get_interface(dev); if (intf_dsc == NULL) continue; if (num > 9 || intf_dsc->bInterfaceNumber != 0) continue; dfu_dev[num++] = dev; } } if (num != 1) { printf("\rCan't identify device with DFU mode\n"); goto error; } printf("\r"); intf = 0; udev = usb_open(dfu_dev[0]); if (!udev) { printf("Can't open device: %s (%d)\n", strerror(errno), errno); return NULL; } if (usb_claim_interface(udev, intf) < 0) { printf("Can't claim interface: %s (%d)\n", strerror(errno), errno); usb_close(udev); return NULL; } if (dfu_get_status(udev, intf, &status) < 0) { printf("Can't get status: %s (%d)\n", strerror(errno), errno); goto error; } if (status.bState != DFU_STATE_DFU_IDLE) { printf("Device is not in DFU mode, can't use it\n"); goto error; } print_status(udev, status); return udev; error: usb_release_interface(udev, intf); usb_close(udev); return NULL; } static void usage(void); static void cmd_verify(char *device, int argc, char **argv) { struct stat st; struct dfu_suffix *suffix; uint32_t crc; uint16_t bcd; char str[16]; unsigned char *buf; unsigned long size; char *filename; int i, fd, len; if (argc < 2) { usage(); exit(1); } filename = argv[1]; if (stat(filename, &st) < 0) { perror("Can't access firmware"); exit(1); } size = st.st_size; if (!(buf = malloc(size))) { perror("Unable to allocate file buffer"); exit(1); } if ((fd = open(filename, O_RDONLY)) < 0) { perror("Can't open firmware"); free(buf); exit(1); } if (read(fd, buf, size) < size) { perror("Can't load firmware"); free(buf); close(fd); exit(1); } printf("Filename\t%s\n", basename(filename)); printf("Filesize\t%ld\n", size); crc = crc32_init(); for (i = 0; i < size - 4; i++) crc = crc32_byte(crc, buf[i]); printf("Checksum\t%08x\n", crc); printf("\n"); len = buf[size - 5]; printf("DFU suffix\t"); for (i = 0; i < len; i++) { printf("%02x ", buf[size - len + i]); } printf("\n\n"); suffix = (struct dfu_suffix *) (buf + size - DFU_SUFFIX_SIZE); printf("idVendor\t%04x\n", le16_to_cpu(suffix->idVendor)); printf("idProduct\t%04x\n", le16_to_cpu(suffix->idProduct)); printf("bcdDevice\t%x\n", le16_to_cpu(suffix->bcdDevice)); printf("\n"); bcd = le16_to_cpu(suffix->bcdDFU); printf("bcdDFU\t\t%x.%x\n", bcd >> 8, bcd & 0xff); printf("ucDfuSignature\t%c%c%c\n", suffix->ucDfuSignature[2], suffix->ucDfuSignature[1], suffix->ucDfuSignature[0]); printf("bLength\t\t%d\n", suffix->bLength); printf("dwCRC\t\t%08x\n", le32_to_cpu(suffix->dwCRC)); printf("\n"); memset(str, 0, sizeof(str)); memcpy(str, buf, 8); if (!strcmp(str, "CSR-dfu1") || !strcmp(str, "CSR-dfu2")) { crc = crc32_init(); for (i = 0; i < size - DFU_SUFFIX_SIZE; i++) crc = crc32_byte(crc, buf[i]); printf("Firmware type\t%s\n", str); printf("Firmware check\t%s checksum\n", crc == 0 ? "valid" : "corrupt"); printf("\n"); } free(buf); close(fd); } static void cmd_modify(char *device, int argc, char **argv) { } static void cmd_upgrade(char *device, int argc, char **argv) { struct usb_dev_handle *udev; struct dfu_status status; struct dfu_suffix suffix; struct usb_dfu_descriptor dfu_dsc; struct stat st; char *buf; unsigned long filesize, count, timeout = 0; char *filename; uint32_t crc, dwCRC; int fd, i, block, len, size, sent = 0, try = 10; if (argc < 2) { usage(); exit(1); } filename = argv[1]; if (stat(filename, &st) < 0) { perror("Can't access firmware"); exit(1); } filesize = st.st_size; if (!(buf = malloc(filesize))) { perror("Unable to allocate file buffer"); exit(1); } if ((fd = open(filename, O_RDONLY)) < 0) { perror("Can't open firmware"); free(buf); exit(1); } if (read(fd, buf, filesize) < filesize) { perror("Can't load firmware"); free(buf); close(fd); exit(1); } memcpy(&suffix, buf + filesize - DFU_SUFFIX_SIZE, sizeof(suffix)); dwCRC = le32_to_cpu(suffix.dwCRC); printf("Filename\t%s\n", basename(filename)); printf("Filesize\t%ld\n", filesize); crc = crc32_init(); for (i = 0; i < filesize - 4; i++) crc = crc32_byte(crc, buf[i]); printf("Checksum\t%08x (%s)\n", crc, crc == dwCRC ? "valid" : "corrupt"); if (crc != dwCRC) { free(buf); close(fd); exit(1); } printf("\n"); udev = open_device(device, &suffix, &dfu_dsc); if (!udev) exit(1); printf("\r" " " " " " " " " " "); printf("\rFirmware download ... "); fflush(stdout); count = filesize - DFU_SUFFIX_SIZE; block = 0; while (count) { size = (count > dfu_dsc.wTransferSize) ? dfu_dsc.wTransferSize : count; if (dfu_get_status(udev, 0, &status) < 0) { if (try-- > 0) { sleep(1); continue; } printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); goto done; } if (status.bStatus != DFU_OK) { if (try-- > 0) { dfu_clear_status(udev, 0); printf("here: "); print_status(udev, status); sleep(1); continue; } printf("\rFirmware download ... aborting "); print_status(udev, status); goto done; } print_status(udev, status); dfu_sleep(status); printf("size=%d,dfu_dsc.wTransferSize=%d\n", size, dfu_dsc.wTransferSize); len = dfu_download(udev, 0, block, buf + sent, size); if (len < 0) { if (try-- > 0) { printf("here2\n"); sleep(1); continue; } printf("\rCan't upload next block: %s (%d)\n", strerror(errno), errno); goto done; } printf("\rFirmware download ... %d bytes ", block * dfu_dsc.wTransferSize + len); fflush(stdout); sent += len; count -= len; block++; } printf("\r" " " " " " " " " " "); printf("\rFinishing firmware download ... "); fflush(stdout); sleep(1); if (dfu_get_status(udev, 0, &status) < 0) { printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); goto done; } print_status(udev, status); dfu_sleep(status); sleep(2); len = dfu_download(udev, 0, block, NULL, 0); /* send 0 byte block to finish download */ if (len < 0) { printf("\rCan't send final block: %s (%d)\n", strerror(errno), errno); goto done; } /* should now be in MANIFEST-SYNC state. send GET_STATUS to enter MANIFEST state: */ if (dfu_get_status(udev, 0, &status) < 0) { printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); goto done; } print_status(udev, status); dfu_sleep(status); /* DFU spec 1.1: Following a successful reprogramming, the device enters one of two states: dfuMANIFEST-SYNC or dfuMANIFEST-WAIT-RESET, depending on whether or not it is still capable of communicating via USB. The host is aware of which state the device will enter by virtue of the bmAttributes bit bitManifestationTolerant. If the device enters dfuMANIFEST-SYNC (bitMainfestationTolerant = 1), then the host issues the DFU_GETSTATUS request, and the device enters the dfuIDLE state. At that point, the host can perform another download, solicit an upload, or issue a USB reset to return the device to application run-time mode. If, however, the device enters the dfuMANIFEST-WAIT-RESET state (bitManifestationTolerant = 0), then if bitWillDetach = 1 the device generates a detach-attach sequence on the bus, otherwise (bitWillDetach = 0) the host must issue a USB reset to the device. After the bus reset the device will evaluate the firmware status and enter the appropriate mode. */ if (dfu_dsc.bmAttributes & DFU_MANIFEST_TOL) { dfu_get_status(udev, 0, &status); print_status(udev, status); /* state should be MANIFEST-SYNC */ dfu_get_status(udev, 0, &status); print_status(udev, status); /* state should be DFU-IDLE */ } /* else: state is MANIFEST-WAIT-RESET - further communication is not allowed! */ printf("\r" " " " " " " " " " "); printf("\rWaiting for device ... "); fflush(stdout); sleep(10); printf("\n"); done: free(buf); close(fd); usb_release_interface(udev, 0); if (!(dfu_dsc.bmAttributes & DFU_WILL_DETACH)) usb_reset(udev); /* don't reset if device already detached */ usb_close(udev); } static void cmd_archive(char *device, int argc, char **argv) { struct usb_dev_handle *udev; struct dfu_status status; struct dfu_suffix suffix; struct usb_dfu_descriptor dfu_dsc; char buf[2048]; unsigned long timeout = 0; char *filename; uint32_t crc; int fd, i, n, len, try = 8; if (argc < 2) { usage(); exit(1); } filename = argv[1]; udev = open_device(device, &suffix, &dfu_dsc); if (!udev) exit(1); fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { printf("Can't open firmware file: %s (%d)\n", strerror(errno), errno); goto done; } printf("\r" " " " " " " " " " "); printf("\rFirmware upload ... "); fflush(stdout); crc = crc32_init(); n = 0; while (1) { if (dfu_get_status(udev, 0, &status) < 0) { if (try-- > 0) { sleep(1); continue; } printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); goto done; } if (status.bStatus != DFU_OK) { if (try-- > 0) { dfu_clear_status(udev, 0); sleep(1); continue; } printf("\rFirmware upload ... aborting "); print_status(udev, status); goto done; } if (status.bState != DFU_STATE_DFU_IDLE && status.bState != DFU_STATE_UPLOAD_IDLE) { sleep(1); continue; } dfu_sleep(status); len = dfu_upload(udev, 0, n, buf, dfu_dsc.wTransferSize); if (len < 0) { if (try-- > 0) { sleep(1); continue; } printf("\rCan't upload next block: %s (%d)\n", strerror(errno), errno); goto done; } printf("\rFirmware upload ... %d bytes ", n * dfu_dsc.wTransferSize + len); fflush(stdout); for (i = 0; i < len; i++) crc = crc32_byte(crc, buf[i]); if (len > 0) { if (write(fd, buf, len) < 0) { printf("\rCan't write next block: %s (%d)\n", strerror(errno), errno); goto done; } } n++; if (len != dfu_dsc.wTransferSize) /* i.e. upload complete */ break; } printf("\n"); suffix.bcdDFU = cpu_to_le16(0x0100); suffix.ucDfuSignature[0] = 'U'; suffix.ucDfuSignature[1] = 'F'; suffix.ucDfuSignature[2] = 'D'; suffix.bLength = DFU_SUFFIX_SIZE; memcpy(buf, &suffix, DFU_SUFFIX_SIZE); for (i = 0; i < DFU_SUFFIX_SIZE - 4; i++) crc = crc32_byte(crc, buf[i]); suffix.dwCRC = cpu_to_le32(crc); if (write(fd, &suffix, DFU_SUFFIX_SIZE) < 0) printf("Can't write suffix block: %s (%d)\n", strerror(errno), errno); done: close(fd); usb_release_interface(udev, 0); usb_reset(udev); usb_close(udev); } static void cmd_suffix(char *device, int argc, char **argv) { struct usb_dev_handle *udev; struct dfu_suffix suffix; struct usb_dfu_descriptor dfu_dsc; char buf[2048]; char *filename; uint32_t crc; int fd, i, len; if (argc < 2) { usage(); exit(1); } filename = argv[1]; udev = open_device(device, &suffix, &dfu_dsc); if (!udev) exit(1); fd = open(filename, O_RDWR | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { printf("Can't open firmware file: %s (%d)\n", strerror(errno), errno); goto done; } crc = crc32_init(); while (1) { len = read(fd, buf, 2048); if (len == 0) break; for (i = 0; i < len; i++) crc = crc32_byte(crc, buf[i]); } suffix.bcdDFU = cpu_to_le16(0x0100); suffix.ucDfuSignature[0] = 'U'; suffix.ucDfuSignature[1] = 'F'; suffix.ucDfuSignature[2] = 'D'; suffix.bLength = DFU_SUFFIX_SIZE; memcpy(buf, &suffix, DFU_SUFFIX_SIZE); for (i = 0; i < DFU_SUFFIX_SIZE - 4; i++) crc = crc32_byte(crc, buf[i]); suffix.dwCRC = cpu_to_le32(crc); if (write(fd, &suffix, DFU_SUFFIX_SIZE) < 0) printf("Can't write suffix block: %s (%d)\n", strerror(errno), errno); done: close(fd); } struct { char *cmd; char *alt; void (*func)(char *device, int argc, char **argv); char *opt; char *doc; } command[] = { { "verify", "check", cmd_verify, "", "Check firmware file" }, { "modify", "change", cmd_modify, "", "Change firmware attributes" }, { "upgrade", "download", cmd_upgrade, "", "Download a new firmware" }, { "archive", "upload", cmd_archive, "", "Upload the current firmware" }, { "suffix", "change", cmd_suffix, "", "Add suffix to firmware file" }, { NULL, NULL, NULL, 0, 0 } }; static void usage(void) { int i; printf("dfutool - Device Firmware Upgrade utility ver %s\n\n", VERSION); printf("Usage:\n" "\tdfutool [options] \n" "\n"); printf("Options:\n" "\t-d, --device USB device\n" "\t-h, --help Display help\n" "\n"); printf("Commands:\n"); for (i = 0; command[i].cmd; i++) printf("\t%-8s %-10s\t%s\n", command[i].cmd, command[i].opt ? command[i].opt : " ", command[i].doc); printf("\n"); } static struct option main_options[] = { { "help", 0, 0, 'h' }, { "device", 1, 0, 'd' }, { 0, 0, 0, 0 } }; int main(int argc, char *argv[]) { char *device = NULL; int i, opt; while ((opt = getopt_long(argc, argv, "+d:h", main_options, NULL)) != -1) { switch(opt) { case 'd': device = strdup(optarg); break; case 'h': usage(); exit(0); default: exit(0); } } argc -= optind; argv += optind; optind = 0; if (argc < 1) { usage(); exit(1); } usb_init(); for (i = 0; command[i].cmd; i++) { if (strcmp(command[i].cmd, argv[0]) && strcmp(command[i].alt, argv[0])) continue; command[i].func(device, argc, argv); exit(0); } usage(); exit(1); }