From 346423c040fe4ac31dae7c1afcb1d853cc80635c Mon Sep 17 00:00:00 2001 From: Cástor Muñoz Date: Thu, 4 Feb 2016 23:05:17 +0100 Subject: mks5lboot v1.0 - dualboot installer for s5l8702 targets 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 --- rbutil/mks5lboot/ipoddfu.c | 875 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 875 insertions(+) create mode 100644 rbutil/mks5lboot/ipoddfu.c (limited to 'rbutil/mks5lboot/ipoddfu.c') 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 +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#include +#endif + +#ifdef USE_LIBUSBAPI +#include +#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; +} -- cgit v1.2.3