346423c040
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
875 lines
23 KiB
C
875 lines
23 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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;
|
|
}
|