summaryrefslogtreecommitdiffstats
path: root/rbutil/mks5lboot/ipoddfu.c
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2016-02-04 23:05:17 +0100
committerCástor Muñoz <cmvidal@gmail.com>2017-04-14 00:03:42 +0200
commit346423c040fe4ac31dae7c1afcb1d853cc80635c (patch)
treebd8dd4c55f083a1162f7019188977213a20dc41c /rbutil/mks5lboot/ipoddfu.c
parenteefc7c73e2495decdc6f242515696fe0e3f85609 (diff)
downloadrockbox-346423c040fe4ac31dae7c1afcb1d853cc80635c.tar.gz
rockbox-346423c040fe4ac31dae7c1afcb1d853cc80635c.zip
mks5lboot v1.0 - dualboot installer for s5l8702 targetsmks5lboot_1.0
A tool to install/uninstall a bootloader into a s5l8702 based device: - iPod Classic 6G - iPod Nano 3G (TODO) See mks5lboot/README for detailed info. Change-Id: I451d2aaff34509ebd356e4660647e5222c5d3409
Diffstat (limited to 'rbutil/mks5lboot/ipoddfu.c')
-rw-r--r--rbutil/mks5lboot/ipoddfu.c875
1 files changed, 875 insertions, 0 deletions
diff --git a/rbutil/mks5lboot/ipoddfu.c b/rbutil/mks5lboot/ipoddfu.c
new file mode 100644
index 0000000000..6d303d6603
--- /dev/null
+++ b/rbutil/mks5lboot/ipoddfu.c
@@ -0,0 +1,875 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 by Cástor Muñoz
+ *
+ * based on:
+ * ipoddfu_c by user890104
+ * xpwn/pwnmetheus2
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef NO_LIBUSBAPI
+#define USE_LIBUSBAPI
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#ifdef WIN32
+#include <windows.h>
+#include <setupapi.h>
+#include <stdbool.h>
+#endif
+
+#ifdef USE_LIBUSBAPI
+#include <libusb-1.0/libusb.h>
+#endif
+
+#include "mks5lboot.h"
+
+
+static void sleep_ms(unsigned int ms)
+{
+ struct timespec req;
+ req.tv_sec = ms / 1000;
+ req.tv_nsec = (ms % 1000) * 1000000;
+ nanosleep(&req, NULL);
+}
+
+/*
+ * CRC32 functions
+ * Based on public domain implementation by Finn Yannick Jacobs.
+ */
+
+/* Written and copyright 1999 by Finn Yannick Jacobs
+ * No rights were reserved to this, so feel free to
+ * manipulate or do with it, what you want or desire :)
+ */
+
+#define CRC32_DEFAULT_SEED 0xffffffff
+
+/* crc32table[] built by crc32_init() */
+static unsigned long crc32table[256];
+
+/* Calculate crc32. Little endian.
+ * Standard seed is 0xffffffff or 0.
+ * Some implementations xor result with 0xffffffff after calculation.
+ */
+static uint32_t crc32(void *data, unsigned int len, uint32_t seed)
+{
+ uint8_t *d = data;
+
+ while (len--)
+ {
+ seed = ((seed >> 8) & 0x00FFFFFF) ^ crc32table [(seed ^ *d++) & 0xFF];
+ }
+
+ return seed;
+}
+
+/* Calculate crc32table */
+static void crc32_init()
+{
+ uint32_t poly = 0xEDB88320L;
+ uint32_t crc;
+ int i, j;
+
+ for (i = 0; i < 256; ++i)
+ {
+ crc = i;
+
+ for (j = 8; j > 0; --j)
+ {
+ crc = (crc >> 1) ^ ((crc & 1) ? poly : 0);
+ }
+
+ crc32table[i] = crc;
+ }
+}
+
+
+/*
+ * DFU
+ */
+
+/* must be pow2 <= wTransferSize (2048) */
+#define DFU_PKT_SZ 2048
+
+#define APPLE_VID 0x05AC
+
+static int KNOWN_PIDS[] =
+{
+ /* DFU */
+ 0x1220, /* Nano 2G */
+ 0x1223, /* Nano 3G and Classic 1G/2G/3G/4G */
+ 0x1224, /* Shuffle 3G */
+ 0x1225, /* Nano 4G */
+ 0x1231, /* Nano 5G */
+ 0x1232, /* Nano 6G */
+ 0x1233, /* Shuffle 4G */
+ 0x1234, /* Nano 7G */
+ /* WTF */
+ 0x1240, /* Nano 2G */
+ 0x1241, /* Classic 1G */
+ 0x1242, /* Nano 3G */
+ 0x1243, /* Nano 4G */
+ 0x1245, /* Classic 2G */
+ 0x1246, /* Nano 5G */
+ 0x1247, /* Classic 3G */
+ 0x1248, /* Nano 6G */
+ 0x1249, /* Nano 7G */
+ 0x124a, /* Nano 7G */
+ 0x1250, /* Classic 4G */
+ 0
+};
+
+struct usbControlSetup {
+ uint8_t bmRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+} __attribute__ ((packed));
+#define USB_CS_SZ (sizeof(struct usbControlSetup))
+
+struct usbStatusData {
+ uint8_t bStatus;
+ uint8_t bwPollTimeout0;
+ uint8_t bwPollTimeout1;
+ uint8_t bwPollTimeout2;
+ uint8_t bState;
+ uint8_t iString;
+} __attribute__ ((packed));
+
+/* DFU 1.1 specs */
+typedef enum DFUState {
+ appIDLE = 0,
+ appDETACH = 1,
+ dfuIDLE = 2,
+ dfuDNLOAD_SYNC = 3,
+ dfuDNBUSY = 4,
+ dfuDNLOAD_IDLE = 5,
+ dfuMANIFEST_SYNC = 6,
+ dfuMANIFEST = 7,
+ dfuMANIFEST_WAIT_RESET = 8,
+ dfuUPLOAD_IDLE = 9,
+ dfuERROR = 10
+} DFUState;
+
+typedef enum DFUStatus {
+ errNONE = 0,
+ errTARGET = 1,
+ errFILE = 2,
+ errWRITE = 3,
+ errERASE = 4,
+ errCHECK_ERASED = 5,
+ errPROG = 6,
+ errVERIFY = 7,
+ errADDRESS = 8,
+ errNOTDONE = 9,
+ errFIRMWARE = 10,
+ errVENDOR = 11,
+ errUSBR = 12,
+ errPOR = 13,
+ errUNKNOWN = 14,
+ errSTALLEDPKT = 15
+} DFUStatus;
+
+typedef enum DFURequest {
+ DFU_DETACH = 0,
+ DFU_DNLOAD = 1,
+ DFU_UPLOAD = 2,
+ DFU_GETSTATUS = 3,
+ DFU_CLRSTATUS = 4,
+ DFU_GETSTATE = 5,
+ DFU_ABORT = 6
+} DFURequest;
+
+struct dfuDev {
+ struct dfuAPI *api;
+ int found_pid;
+ int detached;
+ char descr[256];
+ int res; /* API result: 1->ok, 0->failure */
+ char err[256];
+ /* API private */
+#ifdef WIN32
+ HANDLE fh;
+ HANDLE ph;
+ DWORD ec; /* winapi error code */
+#endif
+#ifdef USE_LIBUSBAPI
+ libusb_context* ctx;
+ libusb_device_handle* devh;
+ int rc; /* libusb return code */
+#endif
+};
+
+struct dfuAPI {
+ char *name;
+ int (*open_fn)(struct dfuDev*, int*);
+ int (*dfureq_fn)(struct dfuDev*, struct usbControlSetup*, void*);
+ int (*reset_fn)(struct dfuDev*);
+ void (*close_fn)(struct dfuDev*);
+};
+
+
+/*
+ * low-level (API specific) functions
+ */
+static int dfu_check_id(int vid, int pid, int *pid_list)
+{
+ int *p;
+ if (vid != APPLE_VID)
+ return 0;
+ for (p = pid_list; *p; p++)
+ if (*p == pid)
+ return 1;
+ return 0;
+}
+
+/* adds extra DFU request error info */
+static void dfu_add_reqerrstr(struct dfuDev *dfuh, struct usbControlSetup *cs)
+{
+ snprintf(dfuh->err + strlen(dfuh->err),
+ sizeof(dfuh->err) - strlen(dfuh->err), " (cs=%02x/%d/%d/%d/%d)",
+ cs->bmRequestType, cs->bRequest, cs->wValue, cs->wIndex, cs->wLength);
+}
+
+#ifdef WIN32
+static int dfu_winapi_chkrc(struct dfuDev *dfuh, char *str, bool success)
+{
+ dfuh->res = (int)success;
+ if (!success) {
+ dfuh->ec = GetLastError();
+ snprintf(dfuh->err, sizeof(dfuh->err), "%s error %ld", str, dfuh->ec);
+ }
+ return dfuh->res;
+}
+
+static int dfu_winapi_request(struct dfuDev *dfuh,
+ struct usbControlSetup* cs, void* data)
+{
+ unsigned char buf[USB_CS_SZ + DFU_PKT_SZ];
+ DWORD rdwr;
+ bool rc;
+
+ memcpy(buf, cs, USB_CS_SZ);
+
+ if (cs->bmRequestType & 0x80)
+ {
+ rc = ReadFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL);
+ memcpy(data, buf+USB_CS_SZ, cs->wLength);
+ dfu_winapi_chkrc(dfuh, "DFU request failed: ReadFile()", rc);
+ }
+ else
+ {
+ memcpy(buf+USB_CS_SZ, data, cs->wLength);
+ rc = WriteFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL);
+ dfu_winapi_chkrc(dfuh, "DFU request failed: WriteFile()", rc);
+ }
+
+ if (!dfuh->res)
+ dfu_add_reqerrstr(dfuh, cs);
+ return dfuh->res;
+}
+
+static int dfu_winapi_reset(struct dfuDev *dfuh)
+{
+ DWORD bytesReturned;
+ bool rc = DeviceIoControl(dfuh->fh, 0x22000c,
+ NULL, 0, NULL, 0, &bytesReturned, NULL);
+ return dfu_winapi_chkrc(dfuh,
+ "Could not reset USB device: DeviceIoControl()", rc);
+}
+
+static void dfu_winapi_close(struct dfuDev *dfuh)
+{
+ if (dfuh->fh != INVALID_HANDLE_VALUE) {
+ CloseHandle(dfuh->fh);
+ dfuh->fh = INVALID_HANDLE_VALUE;
+ }
+ if (dfuh->ph != INVALID_HANDLE_VALUE) {
+ CloseHandle(dfuh->ph);
+ dfuh->ph = INVALID_HANDLE_VALUE;
+ }
+}
+
+static const GUID GUID_AAPLDFU =
+ { 0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
+
+static int dfu_winapi_open(struct dfuDev *dfuh, int *pid_list)
+{
+ const GUID *guid = &GUID_AAPLDFU;
+ HDEVINFO devinfo = NULL;
+ SP_DEVICE_INTERFACE_DETAIL_DATA_A* details = NULL;
+ SP_DEVICE_INTERFACE_DATA iface;
+ char *path = NULL;
+ DWORD i, size;
+ bool rc;
+
+ dfuh->fh =
+ dfuh->ph = INVALID_HANDLE_VALUE;
+ dfuh->found_pid = 0;
+ dfuh->res = 1; /* ok */
+ dfuh->ec = 0;
+
+ /* Get DFU path */
+ devinfo = SetupDiGetClassDevsA(guid, NULL, NULL,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if (!dfu_winapi_chkrc(dfuh, "SetupDiGetClassDevsA()",
+ (devinfo != INVALID_HANDLE_VALUE)))
+ goto error;
+
+ iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+ for (i = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, guid, i, &iface); i++)
+ {
+ int vid, pid;
+
+ SetupDiGetDeviceInterfaceDetailA(devinfo, &iface, NULL, 0, &size, NULL);
+
+ if (details) free(details);
+ details = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(size);
+ details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
+ rc = SetupDiGetDeviceInterfaceDetailA(devinfo, &iface, details, size, NULL, NULL);
+ if (!dfu_winapi_chkrc(dfuh, "SetupDiGetDeviceInterfaceDetailA()", rc))
+ goto error;
+
+ CharUpperA(details->DevicePath);
+ if (sscanf(details->DevicePath, "%*4cUSB#VID_%04x&PID_%04x%*s", &vid, &pid) != 2)
+ continue;
+ if (!dfu_check_id(vid, pid, pid_list))
+ continue;
+
+ if (path) free(path);
+ path = malloc(size - sizeof(DWORD) + 16);
+ memcpy(path, details->DevicePath, size - sizeof(DWORD));
+
+ dfuh->fh = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (!dfu_winapi_chkrc(dfuh, "CreateFileA(fh)", (dfuh->fh != INVALID_HANDLE_VALUE)))
+ goto error;
+
+ strcat(path, "\\PIPE0");
+ dfuh->ph = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (!dfu_winapi_chkrc(dfuh, "CreateFileA(ph)", (dfuh->ph != INVALID_HANDLE_VALUE)))
+ goto error;
+
+ /* ok */
+ snprintf(dfuh->descr, sizeof(dfuh->descr), "%s", details->DevicePath);
+ dfuh->found_pid = pid;
+ goto bye;
+ }
+
+ if (!dfu_winapi_chkrc(dfuh, "SetupDiEnumDeviceInterfaces()",
+ (GetLastError() == ERROR_NO_MORE_ITEMS)))
+ goto error;
+
+ /* no devices found */
+
+bye:
+ if (path) free(path);
+ if (details) free(details);
+ if (devinfo) SetupDiDestroyDeviceInfoList(devinfo);
+ return dfuh->res;
+
+error:
+ dfu_winapi_close(dfuh);
+ goto bye;
+}
+#endif /* WIN32 */
+
+#ifdef USE_LIBUSBAPI
+static int dfu_libusb_chkrc(struct dfuDev *dfuh, char *str)
+{
+ dfuh->res = (dfuh->rc < LIBUSB_SUCCESS) ? 0 : 1;
+ if (dfuh->res == 0)
+ snprintf(dfuh->err, sizeof(dfuh->err),
+ "%s: %s", str, libusb_error_name(dfuh->rc));
+ return dfuh->res;
+}
+
+static int dfu_libusb_request(struct dfuDev *dfuh,
+ struct usbControlSetup *cs, void *data)
+{
+ dfuh->rc = libusb_control_transfer(dfuh->devh, cs->bmRequestType,
+ cs->bRequest, cs->wValue, cs->wIndex, data, cs->wLength, 500);
+ if (!dfu_libusb_chkrc(dfuh, "DFU request failed"))
+ dfu_add_reqerrstr(dfuh, cs);
+ return dfuh->res;
+}
+
+static int dfu_libusb_reset(struct dfuDev *dfuh)
+{
+ dfuh->rc = libusb_reset_device(dfuh->devh);
+ return dfu_libusb_chkrc(dfuh, "Could not reset USB device");
+}
+
+static void dfu_libusb_close(struct dfuDev *dfuh)
+{
+ if (dfuh->devh) {
+ libusb_release_interface(dfuh->devh, 0);
+ if (dfuh->detached)
+ libusb_attach_kernel_driver(dfuh->devh, 0);
+ libusb_close(dfuh->devh);
+ dfuh->devh = NULL;
+ }
+ if (dfuh->ctx) {
+ libusb_exit(dfuh->ctx);
+ dfuh->ctx = NULL;
+ }
+}
+
+static int dfu_libusb_open(struct dfuDev *dfuh, int *pid_list)
+{
+ struct libusb_device_descriptor desc;
+ libusb_device **devs = NULL, *dev;
+ int n_devs, i;
+
+ dfuh->devh = NULL;
+ dfuh->found_pid = 0;
+ dfuh->detached = 0;
+ dfuh->res = 1; /* ok */
+
+ dfuh->rc = libusb_init(&(dfuh->ctx));
+ if (!dfu_libusb_chkrc(dfuh, "Could not init USB library")) {
+ dfuh->ctx = NULL; /* invalidate ctx (if any) */
+ goto error;
+ }
+
+ n_devs =
+ dfuh->rc = libusb_get_device_list(dfuh->ctx, &devs);
+ if (!dfu_libusb_chkrc(dfuh, "Could not get USB device list"))
+ goto error;
+
+ for (i = 0; i < n_devs; ++i)
+ {
+ dev = devs[i];
+
+ /* Note: since libusb-1.0.16 (LIBUSB_API_VERSION >= 0x01000102)
+ this function always succeeds. */
+ if (libusb_get_device_descriptor(dev, &desc) != LIBUSB_SUCCESS)
+ continue; /* Unable to get device descriptor */
+
+ if (!dfu_check_id(desc.idVendor, desc.idProduct, pid_list))
+ continue;
+
+ dfuh->rc = libusb_open(dev, &(dfuh->devh));
+ if (!dfu_libusb_chkrc(dfuh, "Could not open USB device"))
+ goto error;
+
+ dfuh->rc = libusb_set_configuration(dfuh->devh, 1);
+ if (!dfu_libusb_chkrc(dfuh, "Could not set USB configuration"))
+ goto error;
+
+ dfuh->rc = libusb_kernel_driver_active(dfuh->devh, 0);
+ if (dfuh->rc != LIBUSB_ERROR_NOT_SUPPORTED) {
+ if (!dfu_libusb_chkrc(dfuh, "Could not get USB driver status"))
+ goto error;
+ if (dfuh->rc == 1) {
+ dfuh->rc = libusb_detach_kernel_driver(dfuh->devh, 0);
+ if (!dfu_libusb_chkrc(dfuh, "Could not detach USB driver"))
+ goto error;
+ dfuh->detached = 1;
+ }
+ }
+
+ dfuh->rc = libusb_claim_interface(dfuh->devh, 0);
+ if (!dfu_libusb_chkrc(dfuh, "Could not claim USB interface"))
+ goto error;
+
+ /* ok */
+ snprintf(dfuh->descr, sizeof(dfuh->descr),
+ "[%04x:%04x] at bus %d, device %d, USB ver. %04x",
+ desc.idVendor, desc.idProduct, libusb_get_bus_number(dev),
+ libusb_get_device_address(dev), desc.bcdUSB);
+ dfuh->found_pid = desc.idProduct;
+ break;
+ }
+
+bye:
+ if (devs)
+ libusb_free_device_list(devs, 1);
+ if (!dfuh->found_pid)
+ dfu_libusb_close(dfuh);
+ return dfuh->res;
+
+error:
+ goto bye;
+}
+#endif /* USE_LIBUSBAPI */
+
+/* list of suported APIs:
+ * Windows: winapi and libusb (optional)
+ * Linux and OSX: libusb
+ */
+static struct dfuAPI api_list[] =
+{
+#ifdef WIN32
+ { "winapi",
+ dfu_winapi_open,
+ dfu_winapi_request,
+ dfu_winapi_reset,
+ dfu_winapi_close },
+#endif
+#ifdef USE_LIBUSBAPI
+ { "libusb",
+ dfu_libusb_open,
+ dfu_libusb_request,
+ dfu_libusb_reset,
+ dfu_libusb_close },
+#endif
+#ifdef __APPLE__
+ /* TODO: implement API for OS X < 10.6 ??? */
+#endif
+};
+#define DFU_N_APIS (sizeof(api_list)/sizeof(struct dfuAPI))
+
+/*
+ * mid-layer (common) functions
+ */
+static void dfu_set_errstr(struct dfuDev *dfuh, char *str)
+{
+ strncpy(dfuh->err, str, sizeof(dfuh->err));
+}
+
+static int DEBUG_DFUREQ = 0;
+
+static int dfu_request(struct dfuDev *dfuh,
+ struct usbControlSetup *cs, void *data)
+{
+ if (!DEBUG_DFUREQ)
+ return dfuh->api->dfureq_fn(dfuh, cs, data);
+
+ /* DEBUG */
+
+ /* previous state */
+ unsigned char ste = 0;
+ struct usbControlSetup css = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(ste) };
+ if (!dfuh->api->dfureq_fn(dfuh, &css, &ste)) {
+ snprintf(dfuh->err + strlen(dfuh->err), sizeof(dfuh->err) -
+ strlen(dfuh->err), " [DEBUG_DFUREQ ERROR: state=%d]", ste);
+ return 0;
+ }
+
+ int ret = dfuh->api->dfureq_fn(dfuh, cs, data);
+ fprintf(stderr, "[DEBUG]: REQ: ste=%d, cs=%2x/%d/%d/%d/%d -> %s",
+ ste, cs->bmRequestType, cs->bRequest, cs->wValue,
+ cs->wIndex, cs->wLength, ret ? "ok" : "ERROR");
+ if (cs->bRequest == DFU_GETSTATE)
+ fprintf(stderr, " (state=%d)", *((unsigned char*)(data)));
+ if (cs->bRequest == DFU_GETSTATUS) {
+ struct usbStatusData *sd = (struct usbStatusData*)data;
+ fprintf(stderr, " (status=%d, polltmo=%d, state=%d)", sd->bStatus,
+ (sd->bwPollTimeout2 << 16) | (sd->bwPollTimeout1 << 8) |
+ (sd->bwPollTimeout0), sd->bState);
+ }
+ fputc('\n', stderr);
+ fflush(stderr);
+ return ret;
+}
+
+static int dfureq_getstatus(struct dfuDev *dfuh, int *status,
+ int *poll_tmo /*ms*/, int *state)
+{
+ struct usbStatusData sd = { 0, 0, 0, 0, 0, 0 };
+ struct usbControlSetup cs = { 0xA1, DFU_GETSTATUS, 0, 0, sizeof(sd) };
+ int ret = dfu_request(dfuh, &cs, &sd);
+ if (status) *status = sd.bStatus;
+ if (state) *state = sd.bState;
+ if (poll_tmo) *poll_tmo = (sd.bwPollTimeout2 << 16) |
+ (sd.bwPollTimeout1 << 8) | (sd.bwPollTimeout0);
+ return ret;
+}
+
+static int dfureq_getstate(struct dfuDev *dfuh, int *state)
+{
+ if (!state)
+ return 1; /* nothing to do */
+ unsigned char sts = 0;
+ struct usbControlSetup cs = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(sts) };
+ int ret = dfu_request(dfuh, &cs, &sts);
+ *state = sts;
+ return ret;
+}
+
+static int dfureq_dnload(struct dfuDev* dfuh, uint16_t blknum,
+ uint16_t len, unsigned char *data)
+{
+ struct usbControlSetup cs = { 0x21, DFU_DNLOAD, blknum, 0, len };
+ return dfu_request(dfuh, &cs, data);
+}
+
+/* not used */
+#if 0
+static int dfureq_upload(struct dfuDev* dfuh,
+ uint16_t blknum, uint16_t len, unsigned char *data)
+{
+ struct usbControlSetup cs = { 0xA1, DFU_UPLOAD, blknum, 0, len };
+ return dfu_request(dfuh, &cs, data);
+}
+
+static int dfureq_clrstatus(struct dfuDev* dfuh)
+{
+ struct usbControlSetup cs = { 0x21, DFU_CLRSTATUS, 0, 0, 0 };
+ return dfu_request(dfuh, &cs, NULL);
+}
+
+static int dfureq_abort(struct dfuDev* dfuh)
+{
+ struct usbControlSetup cs = { 0x21, DFU_ABORT, 0, 0, 0 };
+ return dfu_request(dfuh, &cs, NULL);
+}
+
+/* not implemented on DFU8702 */
+static int dfureq_detach(struct dfuDev* dfuh, int tmo)
+{
+ struct usbControlSetup cs = { 0x21, DFU_DETACH, tmo, 0, 0 };
+ return dfu_request(dfuh, &cs, NULL);
+}
+#endif
+
+static int dfu_send_packet(struct dfuDev* dfuh, uint16_t blknum,
+ uint16_t len, unsigned char *data, int *status,
+ int *poll_tmo, int *state, int *pre_state)
+{
+ if (!dfureq_dnload(dfuh, blknum, len, data))
+ return 0;
+
+ /* device is in dfuDLSYNC state, waiting for a GETSTATUS request
+ to enter the next state, if she respond with dfuDLBUSY then
+ we must wait to resend the GETSTATUS request */
+
+ if (!dfureq_getstatus(dfuh, status, poll_tmo, state))
+ return 0;
+
+ if (*state == dfuDNBUSY) {
+ if (*poll_tmo)
+ sleep_ms(*poll_tmo);
+ if (!dfureq_getstate(dfuh, pre_state))
+ return 0;
+ if (!dfureq_getstatus(dfuh, status, poll_tmo, state))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int dfu_download_file(struct dfuDev* dfuh,
+ unsigned char *data, unsigned long size)
+{
+ unsigned int blknum, len, remaining;
+ int status, poll_tmo, state;
+
+ if (!dfureq_getstate(dfuh, &state))
+ goto error;
+
+ if (state != dfuIDLE) {
+ dfu_set_errstr(dfuh, "Could not start DFU download: not idle");
+ goto error;
+ }
+
+ blknum = 0;
+ remaining = size;
+ while (remaining)
+ {
+ len = (remaining < DFU_PKT_SZ) ? remaining : DFU_PKT_SZ;
+
+ if (!dfu_send_packet(dfuh, blknum, len, data +
+ blknum*DFU_PKT_SZ, &status, &poll_tmo, &state, NULL))
+ goto error;
+
+ if (state != dfuDNLOAD_IDLE) {
+ dfu_set_errstr(dfuh, "DFU download aborted: unexpected state");
+ goto error;
+ }
+
+ remaining -= len;
+ blknum++;
+ }
+
+ /* send ZLP */
+ int pre_state = 0;
+ if (!dfu_send_packet(dfuh, blknum, 0, NULL,
+ &status, &poll_tmo, &state, &pre_state)) {
+ if (pre_state == dfuMANIFEST_SYNC)
+ goto ok; /* pwnaged .dfu file */
+ goto error;
+ }
+
+ if (state != dfuMANIFEST) {
+ if (status == errFIRMWARE)
+ dfu_set_errstr(dfuh, "DFU download failed: corrupt firmware");
+ else
+ dfu_set_errstr(dfuh, "DFU download failed: unexpected state");
+ goto error;
+ }
+
+ /* wait for manifest stage */
+ if (poll_tmo)
+ sleep_ms(poll_tmo);
+
+ if (!dfureq_getstatus(dfuh, &status, NULL, &state))
+ goto ok; /* 1223 .dfu file */
+
+
+ /* TODO: next code never tested */
+
+ if (state != dfuMANIFEST_WAIT_RESET) {
+ if (status == errVERIFY)
+ dfu_set_errstr(dfuh, "DFU manifest failed: wrong FW verification");
+ else
+ dfu_set_errstr(dfuh, "DFU manifest failed: unexpected state");
+ goto error;
+ }
+
+ if (!dfuh->api->reset_fn(dfuh))
+ goto error;
+
+ok:
+ return 1;
+error:
+ return 0;
+}
+
+static int dfu_open(struct dfuDev *dfuh, int pid)
+{
+ int pid_l[2] = {0};
+ struct dfuAPI *api;
+ unsigned i;
+
+ pid_l[0] = pid;
+
+ for (i = 0; i < DFU_N_APIS; i++)
+ {
+ api = &api_list[i];
+ if (!(api->open_fn(dfuh, pid ? pid_l : KNOWN_PIDS)))
+ return 0; /* error */
+ if (dfuh->found_pid) {
+ dfuh->api = api;
+ printf("[INFO] %s: found %s\n", api->name, dfuh->descr);
+ fflush(stdout);
+ return 1; /* ok */
+ }
+ printf("[INFO] %s: no DFU devices found\n", api->name);
+ fflush(stdout);
+ }
+
+ dfu_set_errstr(dfuh, "DFU device not found");
+ return 0;
+}
+
+static void dfu_destroy(struct dfuDev *dfuh)
+{
+ if (dfuh) {
+ if (dfuh->api)
+ dfuh->api->close_fn(dfuh);
+ free(dfuh);
+ }
+}
+
+static struct dfuDev *dfu_create()
+{
+ return calloc(sizeof(struct dfuDev), 1);
+}
+
+/*
+ * exported functions
+ */
+int ipoddfu_send(int pid, unsigned char *data, int size,
+ char* errstr, int errstrsize)
+{
+ struct dfuDev *dfuh;
+ unsigned char *buf;
+ unsigned int checksum;
+ int ret = 1; /* ok */
+
+ dfuh = dfu_create();
+
+ buf = malloc(size+4);
+ if (!buf) {
+ dfu_set_errstr(dfuh, "Could not allocate memory for DFU buffer");
+ goto error;
+ }
+
+ if (memcmp(data, IM3_IDENT, 4)) {
+ dfu_set_errstr(dfuh, "Bad DFU image data");
+ goto error;
+ }
+
+ /* FIXME: big endian */
+ crc32_init();
+ checksum = crc32(data, size, CRC32_DEFAULT_SEED);
+ memcpy(buf, data, size);
+ memcpy(buf+size, &checksum, 4);
+
+ if (!dfu_open(dfuh, pid))
+ goto error;
+
+ if (!dfu_download_file(dfuh, buf, size+4))
+ goto error;
+
+bye:
+ if (buf) free(buf);
+ dfu_destroy(dfuh);
+ return ret;
+
+error:
+ ret = 0;
+ if (errstr)
+ snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err);
+ goto bye;
+}
+
+/* search for the DFU device and gets its DFUState */
+int ipoddfu_scan(int pid, int *state, int reset,
+ char* errstr, int errstrsize)
+{
+ struct dfuDev *dfuh;
+ int ret = 1; /* ok */
+
+ dfuh = dfu_create();
+
+ if (!dfu_open(dfuh, pid))
+ goto error;
+
+ if (reset)
+ if (!dfuh->api->reset_fn(dfuh))
+ goto error;
+
+ if (!dfureq_getstate(dfuh, state))
+ goto error;
+
+bye:
+ dfu_destroy(dfuh);
+ return ret;
+
+error:
+ ret = 0;
+ if (errstr)
+ snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err);
+ goto bye;
+}
+
+void ipoddfu_debug(int debug)
+{
+ DEBUG_DFUREQ = debug;
+}