mks5lboot: updates

- fix Makefile to allow cross compilation
- Windows: use Sleep() instead of nanosleep()
- Windows: libusb now is optional
- OS X: use IOKit instead of libusb
- small rework on the DFU API

Change-Id: Ia4b07012c098ad608594e15f6effe9c9d2164b9b
This commit is contained in:
Cástor Muñoz 2017-05-04 10:52:03 +02:00
parent cf168d4636
commit fbbba9292b
6 changed files with 492 additions and 267 deletions

View file

@ -22,28 +22,32 @@
* KIND, either express or implied.
*
****************************************************************************/
#ifndef NO_LIBUSBAPI
#define USE_LIBUSBAPI
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.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
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#endif
#include "mks5lboot.h"
#ifdef WIN32
#define sleep_ms(ms) Sleep(ms)
#else
#include <time.h>
static void sleep_ms(unsigned int ms)
{
struct timespec req;
@ -51,36 +55,36 @@ static void sleep_ms(unsigned int ms)
req.tv_nsec = (ms % 1000) * 1000000;
nanosleep(&req, NULL);
}
#endif
static void put_uint32le(unsigned char* p, uint32_t x)
{
p[0] = x & 0xff;
p[1] = (x >> 8) & 0xff;
p[2] = (x >> 16) & 0xff;
p[3] = (x >> 24) & 0xff;
}
/*
* CRC32 functions
* Based on public domain implementation by Finn Yannick Jacobs.
*/
/* Written and copyright 1999 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];
static uint32_t 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)
/* Calculate crc32 */
static uint32_t crc32(void *data, unsigned int len, uint32_t previousCrc32)
{
uint8_t *d = data;
uint32_t crc = ~previousCrc32;
unsigned char *d = (unsigned char*) data;
while (len--)
{
seed = ((seed >> 8) & 0x00FFFFFF) ^ crc32table [(seed ^ *d++) & 0xFF];
}
return seed;
crc = (crc >> 8) ^ crc32table[(crc & 0xFF) ^ *d++];
return ~crc;
}
/* Calculate crc32table */
@ -89,55 +93,49 @@ 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)
{
for (j = 0; j < 8; ++j)
crc = (crc >> 1) ^ ((crc & 1) ? poly : 0);
}
crc32table[i] = crc;
}
}
/*
* DFU
*/
/* must be pow2 <= wTransferSize (2048) */
#define DFU_PKT_SZ 2048
/* USB */
#define APPLE_VID 0x05AC
static int KNOWN_PIDS[] =
struct pid_info {
int pid;
int mode; /* 0->DFU, 1->WTF */
char *desc;
};
struct pid_info 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 */
{ 0x1220, 0, "Nano 2G" },
{ 0x1223, 0, "Nano 3G / Classic" },
{ 0x1224, 0, "Shuffle 3G" },
{ 0x1225, 0, "Nano 4G" },
{ 0x1231, 0, "Nano 5G" },
{ 0x1232, 0, "Nano 6G" },
{ 0x1233, 0, "Shuffle 4G" },
{ 0x1234, 0, "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
{ 0x1240, 1, "Nano 2G" },
{ 0x1241, 1, "Classic 1G" },
{ 0x1242, 1, "Nano 3G" },
{ 0x1243, 1, "Nano 4G" },
{ 0x1245, 1, "Classic 2G" },
{ 0x1246, 1, "Nano 5G" },
{ 0x1247, 1, "Classic 3G" },
{ 0x1248, 1, "Nano 6G" },
{ 0x1249, 1, "Nano 7G" },
{ 0x124a, 1, "Nano 7G" },
{ 0x1250, 1, "Classic 4G" },
};
#define N_KNOWN_PIDS (sizeof(known_pids)/sizeof(struct pid_info))
struct usbControlSetup {
uint8_t bmRequestType;
@ -157,8 +155,14 @@ struct usbStatusData {
uint8_t iString;
} __attribute__ ((packed));
/*
* DFU API
*/
#define DFU_PKT_SZ 2048 /* must be pow2 <= wTransferSize (2048) */
/* DFU 1.1 specs */
typedef enum DFUState {
typedef enum {
appIDLE = 0,
appDETACH = 1,
dfuIDLE = 2,
@ -172,7 +176,7 @@ typedef enum DFUState {
dfuERROR = 10
} DFUState;
typedef enum DFUStatus {
typedef enum {
errNONE = 0,
errTARGET = 1,
errFILE = 2,
@ -191,7 +195,7 @@ typedef enum DFUStatus {
errSTALLEDPKT = 15
} DFUStatus;
typedef enum DFURequest {
typedef enum {
DFU_DETACH = 0,
DFU_DNLOAD = 1,
DFU_UPLOAD = 2,
@ -201,12 +205,17 @@ typedef enum DFURequest {
DFU_ABORT = 6
} DFURequest;
typedef enum {
DFUAPIFail = 0,
DFUAPISuccess,
} dfuAPIResult;
struct dfuDev {
struct dfuAPI *api;
int found_pid;
int detached;
char descr[256];
int res; /* API result: 1->ok, 0->failure */
dfuAPIResult res;
char err[256];
/* API private */
#ifdef WIN32
@ -219,21 +228,25 @@ struct dfuDev {
libusb_device_handle* devh;
int rc; /* libusb return code */
#endif
#ifdef __APPLE__
IOUSBDeviceInterface** dev;
kern_return_t kr;
#endif
};
struct dfuAPI {
char *name;
int (*open_fn)(struct dfuDev*, int*);
int (*dfureq_fn)(struct dfuDev*, struct usbControlSetup*, void*);
int (*reset_fn)(struct dfuDev*);
dfuAPIResult (*open_fn)(struct dfuDev*, int*);
dfuAPIResult (*dfureq_fn)(struct dfuDev*, struct usbControlSetup*, void*);
dfuAPIResult (*reset_fn)(struct dfuDev*);
void (*close_fn)(struct dfuDev*);
};
/*
* low-level (API specific) functions
* DFU API low-level (specific) functions
*/
static int dfu_check_id(int vid, int pid, int *pid_list)
static bool dfu_check_id(int vid, int pid, int *pid_list)
{
int *p;
if (vid != APPLE_VID)
@ -253,17 +266,17 @@ static void dfu_add_reqerrstr(struct dfuDev *dfuh, struct usbControlSetup *cs)
}
#ifdef WIN32
static int dfu_winapi_chkrc(struct dfuDev *dfuh, char *str, bool success)
static bool dfu_winapi_chkrc(struct dfuDev *dfuh, char *str, bool success)
{
dfuh->res = (int)success;
dfuh->res = (success) ? DFUAPISuccess : DFUAPIFail;
if (!success) {
dfuh->ec = GetLastError();
snprintf(dfuh->err, sizeof(dfuh->err), "%s error %ld", str, dfuh->ec);
}
return dfuh->res;
return success;
}
static int dfu_winapi_request(struct dfuDev *dfuh,
static dfuAPIResult dfu_winapi_request(struct dfuDev *dfuh,
struct usbControlSetup* cs, void* data)
{
unsigned char buf[USB_CS_SZ + DFU_PKT_SZ];
@ -284,19 +297,20 @@ static int dfu_winapi_request(struct dfuDev *dfuh,
rc = WriteFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL);
dfu_winapi_chkrc(dfuh, "DFU request failed: WriteFile()", rc);
}
if (!dfuh->res)
if (!rc)
dfu_add_reqerrstr(dfuh, cs);
return dfuh->res;
}
static int dfu_winapi_reset(struct dfuDev *dfuh)
static dfuAPIResult 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,
bool rc = DeviceIoControl(dfuh->fh,
0x22000c, NULL, 0, NULL, 0, &bytesReturned, NULL);
dfu_winapi_chkrc(dfuh,
"Could not reset USB device: DeviceIoControl()", rc);
return dfuh->res;
}
static void dfu_winapi_close(struct dfuDev *dfuh)
@ -314,7 +328,7 @@ static void dfu_winapi_close(struct dfuDev *dfuh)
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)
static dfuAPIResult dfu_winapi_open(struct dfuDev *dfuh, int *pid_list)
{
const GUID *guid = &GUID_AAPLDFU;
HDEVINFO devinfo = NULL;
@ -327,7 +341,7 @@ static int dfu_winapi_open(struct dfuDev *dfuh, int *pid_list)
dfuh->fh =
dfuh->ph = INVALID_HANDLE_VALUE;
dfuh->found_pid = 0;
dfuh->res = 1; /* ok */
dfuh->res = DFUAPISuccess;
dfuh->ec = 0;
/* Get DFU path */
@ -398,16 +412,16 @@ error:
#endif /* WIN32 */
#ifdef USE_LIBUSBAPI
static int dfu_libusb_chkrc(struct dfuDev *dfuh, char *str)
static bool dfu_libusb_chkrc(struct dfuDev *dfuh, char *str)
{
dfuh->res = (dfuh->rc < LIBUSB_SUCCESS) ? 0 : 1;
if (dfuh->res == 0)
dfuh->res = (dfuh->rc < LIBUSB_SUCCESS) ? DFUAPIFail : DFUAPISuccess;
if (dfuh->res == DFUAPIFail)
snprintf(dfuh->err, sizeof(dfuh->err),
"%s: %s", str, libusb_error_name(dfuh->rc));
return dfuh->res;
return (dfuh->res == DFUAPISuccess);
}
static int dfu_libusb_request(struct dfuDev *dfuh,
static dfuAPIResult dfu_libusb_request(struct dfuDev *dfuh,
struct usbControlSetup *cs, void *data)
{
dfuh->rc = libusb_control_transfer(dfuh->devh, cs->bmRequestType,
@ -417,10 +431,11 @@ static int dfu_libusb_request(struct dfuDev *dfuh,
return dfuh->res;
}
static int dfu_libusb_reset(struct dfuDev *dfuh)
static dfuAPIResult dfu_libusb_reset(struct dfuDev *dfuh)
{
dfuh->rc = libusb_reset_device(dfuh->devh);
return dfu_libusb_chkrc(dfuh, "Could not reset USB device");
dfu_libusb_chkrc(dfuh, "Could not reset USB device");
return dfuh->res;
}
static void dfu_libusb_close(struct dfuDev *dfuh)
@ -438,7 +453,7 @@ static void dfu_libusb_close(struct dfuDev *dfuh)
}
}
static int dfu_libusb_open(struct dfuDev *dfuh, int *pid_list)
static dfuAPIResult dfu_libusb_open(struct dfuDev *dfuh, int *pid_list)
{
struct libusb_device_descriptor desc;
libusb_device **devs = NULL, *dev;
@ -447,7 +462,7 @@ static int dfu_libusb_open(struct dfuDev *dfuh, int *pid_list)
dfuh->devh = NULL;
dfuh->found_pid = 0;
dfuh->detached = 0;
dfuh->res = 1; /* ok */
dfuh->res = DFUAPISuccess;
dfuh->rc = libusb_init(&(dfuh->ctx));
if (!dfu_libusb_chkrc(dfuh, "Could not init USB library")) {
@ -517,10 +532,138 @@ error:
}
#endif /* USE_LIBUSBAPI */
/* list of suported APIs:
* Windows: winapi and libusb (optional)
* Linux and OSX: libusb
*/
#ifdef __APPLE__
static bool dfu_iokit_chkrc(struct dfuDev *dfuh, char *str)
{
dfuh->res = (dfuh->kr == kIOReturnSuccess) ? DFUAPISuccess : DFUAPIFail;
if (dfuh->res == DFUAPIFail)
snprintf(dfuh->err, sizeof(dfuh->err),
"%s: error %08x", str, dfuh->kr);
return (dfuh->res == DFUAPISuccess);
}
static dfuAPIResult dfu_iokit_request(struct dfuDev *dfuh,
struct usbControlSetup *cs, void *data)
{
IOUSBDevRequest req;
req.bmRequestType = cs->bmRequestType;
req.bRequest = cs->bRequest;
req.wValue = cs->wValue;
req.wIndex = cs->wIndex;
req.wLength = cs->wLength;
req.pData = data;
dfuh->kr = (*(dfuh->dev))->DeviceRequest(dfuh->dev, &req);
if (!dfu_iokit_chkrc(dfuh, "DFU request failed"))
dfu_add_reqerrstr(dfuh, cs);
return dfuh->res;
}
static dfuAPIResult dfu_iokit_reset(struct dfuDev *dfuh)
{
dfuh->kr = (*(dfuh->dev))->ResetDevice(dfuh->dev);
#if 0
/* On 10.11+ ResetDevice() returns no error but does not perform
* any reset, just a kernel log message.
* USBDeviceReEnumerate() could be used as a workaround.
*/
dfuh->kr = (*(dfuh->dev))->USBDeviceReEnumerate(dfuh->dev, 0);
#endif
dfu_iokit_chkrc(dfuh, "Could not reset USB device");
return dfuh->res;
}
static void dfu_iokit_close(struct dfuDev *dfuh)
{
if (dfuh->dev) {
(*(dfuh->dev))->USBDeviceClose(dfuh->dev);
(*(dfuh->dev))->Release(dfuh->dev);
dfuh->dev = NULL;
}
}
static dfuAPIResult dfu_iokit_open(struct dfuDev *dfuh, int *pid_list)
{
kern_return_t kr;
CFMutableDictionaryRef usb_matching_dict = 0;
io_object_t usbDevice;
io_iterator_t usb_iterator = IO_OBJECT_NULL;
IOCFPlugInInterface **plugInInterface = NULL;
IOUSBDeviceInterface **dev = NULL;
HRESULT result;
SInt32 score;
UInt16 vendor;
UInt16 product;
UInt16 release;
dfuh->dev = NULL;
dfuh->found_pid = 0;
dfuh->res = DFUAPISuccess;
usb_matching_dict = IOServiceMatching(kIOUSBDeviceClassName);
dfuh->kr = IOServiceGetMatchingServices(
kIOMasterPortDefault, usb_matching_dict, &usb_iterator);
if (!dfu_iokit_chkrc(dfuh, "Could not get matching services"))
goto error;
while ((usbDevice = IOIteratorNext(usb_iterator)))
{
/* Create an intermediate plug-in */
kr = IOCreatePlugInInterfaceForService(usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&plugInInterface,
&score);
IOObjectRelease(usbDevice);
if ((kIOReturnSuccess != kr) || !plugInInterface)
continue; /* Unable to create a plugin */
/* Now create the device interface */
result = (*plugInInterface)->QueryInterface(plugInInterface,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
(LPVOID*)&dev);
(*plugInInterface)->Release(plugInInterface);
if (result || !dev)
continue; /* Couldn't create a device interface */
kr = (*dev)->GetDeviceVendor(dev, &vendor);
kr = (*dev)->GetDeviceProduct(dev, &product);
kr = (*dev)->GetDeviceReleaseNumber(dev, &release);
if (!dfu_check_id(vendor, product, pid_list)) {
(*dev)->Release(dev);
continue;
}
/* Device found, open it */
dfuh->kr = (*dev)->USBDeviceOpen(dev);
if (!dfu_iokit_chkrc(dfuh, "Could not open USB device")) {
(*dev)->Release(dev);
goto error;
}
/* ok */
dfuh->found_pid = product;
dfuh->dev = dev;
snprintf(dfuh->descr, sizeof(dfuh->descr),
"[%04x:%04x] release: %d", vendor, product, release);
break;
}
bye:
if (usb_iterator != IO_OBJECT_NULL)
IOObjectRelease(usb_iterator);
return dfuh->res;
error:
goto bye;
}
#endif /* __APPLE__ */
/* list of suported APIs */
static struct dfuAPI api_list[] =
{
#ifdef WIN32
@ -538,22 +681,22 @@ static struct dfuAPI api_list[] =
dfu_libusb_close },
#endif
#ifdef __APPLE__
/* TODO: implement API for OS X < 10.6 ??? */
{ "IOKit",
dfu_iokit_open,
dfu_iokit_request,
dfu_iokit_reset,
dfu_iokit_close },
#endif
};
#define DFU_N_APIS (sizeof(api_list)/sizeof(struct dfuAPI))
#define N_DFU_APIS (sizeof(api_list)/sizeof(struct dfuAPI))
/*
* mid-layer (common) functions
* DFU API 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,
static dfuAPIResult dfuapi_request(struct dfuDev *dfuh,
struct usbControlSetup *cs, void *data)
{
if (!DEBUG_DFUREQ)
@ -564,16 +707,17 @@ static int dfu_request(struct dfuDev *dfuh,
/* previous state */
unsigned char ste = 0;
struct usbControlSetup css = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(ste) };
if (!dfuh->api->dfureq_fn(dfuh, &css, &ste)) {
if (dfuh->api->dfureq_fn(dfuh, &css, &ste) != DFUAPISuccess) {
snprintf(dfuh->err + strlen(dfuh->err), sizeof(dfuh->err) -
strlen(dfuh->err), " [DEBUG_DFUREQ ERROR: state=%d]", ste);
return 0;
goto error;
}
int ret = dfuh->api->dfureq_fn(dfuh, cs, data);
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");
cs->wIndex, cs->wLength,
(dfuh->res == DFUAPISuccess) ? "ok" : "ERROR");
if (cs->bRequest == DFU_GETSTATE)
fprintf(stderr, " (state=%d)", *((unsigned char*)(data)));
if (cs->bRequest == DFU_GETSTATUS) {
@ -584,106 +728,189 @@ static int dfu_request(struct dfuDev *dfuh,
}
fputc('\n', stderr);
fflush(stderr);
return ret;
bye:
return dfuh->res;
error:
goto bye;
}
static int dfureq_getstatus(struct dfuDev *dfuh, int *status,
int *poll_tmo /*ms*/, int *state)
static dfuAPIResult dfuapi_req_getstatus(struct dfuDev *dfuh,
DFUStatus *status, int *poll_tmo /*ms*/,
DFUState *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);
dfuapi_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;
return dfuh->res;
}
static int dfureq_getstate(struct dfuDev *dfuh, int *state)
static dfuAPIResult dfuapi_req_getstate(struct dfuDev *dfuh, DFUState *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;
dfuapi_request(dfuh, &cs, &sts);
if (state) *state = sts;
return dfuh->res;
}
static int dfureq_dnload(struct dfuDev* dfuh, uint16_t blknum,
static dfuAPIResult dfuapi_req_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);
return dfuapi_request(dfuh, &cs, data);
}
/* not used */
#if 0
static int dfureq_upload(struct dfuDev* dfuh,
static dfuAPIResult dfuapi_req_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);
return dfuapi_request(dfuh, &cs, data);
}
static int dfureq_clrstatus(struct dfuDev* dfuh)
static dfuAPIResult dfuapi_req_clrstatus(struct dfuDev* dfuh)
{
struct usbControlSetup cs = { 0x21, DFU_CLRSTATUS, 0, 0, 0 };
return dfu_request(dfuh, &cs, NULL);
return dfuapi_request(dfuh, &cs, NULL);
}
static int dfureq_abort(struct dfuDev* dfuh)
static dfuAPIResult dfuapi_req_abort(struct dfuDev* dfuh)
{
struct usbControlSetup cs = { 0x21, DFU_ABORT, 0, 0, 0 };
return dfu_request(dfuh, &cs, NULL);
return dfuapi_request(dfuh, &cs, NULL);
}
/* not implemented on DFU8702 */
static int dfureq_detach(struct dfuDev* dfuh, int tmo)
static dfuAPIResult dfuapi_req_detach(struct dfuDev* dfuh, int tmo)
{
struct usbControlSetup cs = { 0x21, DFU_DETACH, tmo, 0, 0 };
return dfu_request(dfuh, &cs, NULL);
return dfuapi_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)
static dfuAPIResult dfuapi_reset(struct dfuDev *dfuh)
{
if (!dfureq_dnload(dfuh, blknum, len, data))
return 0;
return dfuh->api->reset_fn(dfuh);
}
static dfuAPIResult dfuapi_send_packet(struct dfuDev* dfuh, uint16_t blknum,
uint16_t len, unsigned char *data, DFUStatus *status,
int *poll_tmo, DFUState *state, DFUState *pre_state)
{
if (dfuapi_req_dnload(dfuh, blknum, len, data) != DFUAPISuccess)
goto error;
/* 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 */
* 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 (dfuapi_req_getstatus(dfuh, status, poll_tmo, state) != DFUAPISuccess)
goto error;
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;
if (pre_state)
if (dfuapi_req_getstate(dfuh, pre_state) != DFUAPISuccess)
goto error;
if (dfuapi_req_getstatus(dfuh, status, poll_tmo, state) != DFUAPISuccess)
goto error;
}
return 1;
bye:
return dfuh->res;
error:
goto bye;
}
static int dfu_download_file(struct dfuDev* dfuh,
static void dfuapi_set_err(struct dfuDev *dfuh, char *str)
{
dfuh->res = DFUAPIFail;
strncpy(dfuh->err, str, sizeof(dfuh->err));
}
static dfuAPIResult dfuapi_open(struct dfuDev *dfuh, int pid)
{
int pid_l[N_KNOWN_PIDS+1] = { 0 };
struct dfuAPI *api;
unsigned i, p;
/* fill pid list */
if (pid)
pid_l[0] = pid;
else
for (p = 0; p < N_KNOWN_PIDS; p++)
pid_l[p] = known_pids[p].pid;
for (i = 0; i < N_DFU_APIS; i++)
{
api = &api_list[i];
if (api->open_fn(dfuh, pid_l) != DFUAPISuccess)
goto error;
if (dfuh->found_pid) {
/* ok */
dfuh->api = api;
printf("[INFO] %s: found %s\n", api->name, dfuh->descr);
for (p = 0; p < N_KNOWN_PIDS; p++) {
if (known_pids[p].pid == dfuh->found_pid) {
printf("[INFO] iPod %s, mode: %s\n", known_pids[p].desc,
known_pids[p].mode ? "WTF" : "DFU");
break;
}
}
fflush(stdout);
goto bye;
}
printf("[INFO] %s: no DFU devices found\n", api->name);
fflush(stdout);
}
/* error */
dfuapi_set_err(dfuh, "DFU device not found");
bye:
return dfuh->res;
error:
goto bye;
}
static void dfuapi_destroy(struct dfuDev *dfuh)
{
if (dfuh) {
if (dfuh->api)
dfuh->api->close_fn(dfuh);
free(dfuh);
}
}
static struct dfuDev *dfuapi_create(void)
{
return calloc(sizeof(struct dfuDev), 1);
}
/*
* app level functions
*/
static int ipoddfu_download_file(struct dfuDev* dfuh,
unsigned char *data, unsigned long size)
{
unsigned int blknum, len, remaining;
int status, poll_tmo, state;
int poll_tmo;
DFUStatus status;
DFUState state;
if (!dfureq_getstate(dfuh, &state))
if (dfuapi_req_getstate(dfuh, &state) != DFUAPISuccess)
goto error;
if (state != dfuIDLE) {
dfu_set_errstr(dfuh, "Could not start DFU download: not idle");
dfuapi_set_err(dfuh, "Could not start DFU download: not idle");
goto error;
}
@ -693,12 +920,12 @@ static int dfu_download_file(struct dfuDev* dfuh,
{
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))
if (dfuapi_send_packet(dfuh, blknum, len, data + blknum*DFU_PKT_SZ,
&status, &poll_tmo, &state, NULL) != DFUAPISuccess)
goto error;
if (state != dfuDNLOAD_IDLE) {
dfu_set_errstr(dfuh, "DFU download aborted: unexpected state");
dfuapi_set_err(dfuh, "DFU download aborted: unexpected state");
goto error;
}
@ -707,9 +934,9 @@ static int dfu_download_file(struct dfuDev* dfuh,
}
/* send ZLP */
int pre_state = 0;
if (!dfu_send_packet(dfuh, blknum, 0, NULL,
&status, &poll_tmo, &state, &pre_state)) {
DFUState pre_state = appIDLE; /* dummy state */
if (dfuapi_send_packet(dfuh, blknum, 0, NULL,
&status, &poll_tmo, &state, &pre_state) != DFUAPISuccess) {
if (pre_state == dfuMANIFEST_SYNC)
goto ok; /* pwnaged .dfu file */
goto error;
@ -717,9 +944,9 @@ static int dfu_download_file(struct dfuDev* dfuh,
if (state != dfuMANIFEST) {
if (status == errFIRMWARE)
dfu_set_errstr(dfuh, "DFU download failed: corrupt firmware");
dfuapi_set_err(dfuh, "DFU download failed: corrupt firmware");
else
dfu_set_errstr(dfuh, "DFU download failed: unexpected state");
dfuapi_set_err(dfuh, "DFU download failed: unexpected state");
goto error;
}
@ -727,21 +954,20 @@ static int dfu_download_file(struct dfuDev* dfuh,
if (poll_tmo)
sleep_ms(poll_tmo);
if (!dfureq_getstatus(dfuh, &status, NULL, &state))
if (dfuapi_req_getstatus(dfuh, &status, NULL, &state) != DFUAPISuccess)
goto ok; /* 1223 .dfu file */
/* TODO: next code never tested */
/* XXX: next code never tested */
if (state != dfuMANIFEST_WAIT_RESET) {
if (status == errVERIFY)
dfu_set_errstr(dfuh, "DFU manifest failed: wrong FW verification");
dfuapi_set_err(dfuh, "DFU manifest failed: wrong FW verification");
else
dfu_set_errstr(dfuh, "DFU manifest failed: unexpected state");
dfuapi_set_err(dfuh, "DFU manifest failed: unexpected state");
goto error;
}
if (!dfuh->api->reset_fn(dfuh))
if (dfuapi_reset(dfuh) != DFUAPISuccess)
goto error;
ok:
@ -750,86 +976,42 @@ 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
*/
/* 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;
uint32_t checksum;
int ret = 1; /* ok */
dfuh = dfu_create();
dfuh = dfuapi_create();
buf = malloc(size+4);
if (!buf) {
dfu_set_errstr(dfuh, "Could not allocate memory for DFU buffer");
dfuapi_set_err(dfuh, "Could not allocate memory for DFU buffer");
goto error;
}
if (memcmp(data, IM3_IDENT, 4)) {
dfu_set_errstr(dfuh, "Bad DFU image data");
dfuapi_set_err(dfuh, "Bad DFU image data");
goto error;
}
/* FIXME: big endian */
crc32_init();
checksum = crc32(data, size, CRC32_DEFAULT_SEED);
checksum = crc32(data, size, 0);
memcpy(buf, data, size);
memcpy(buf+size, &checksum, 4);
put_uint32le(buf+size, ~checksum);
if (!dfu_open(dfuh, pid))
if (dfuapi_open(dfuh, pid) != DFUAPISuccess)
goto error;
if (!dfu_download_file(dfuh, buf, size+4))
if (!ipoddfu_download_file(dfuh, buf, size+4))
goto error;
bye:
if (buf) free(buf);
dfu_destroy(dfuh);
dfuapi_destroy(dfuh);
return ret;
error:
@ -846,20 +1028,24 @@ int ipoddfu_scan(int pid, int *state, int reset,
struct dfuDev *dfuh;
int ret = 1; /* ok */
dfuh = dfu_create();
dfuh = dfuapi_create();
if (!dfu_open(dfuh, pid))
if (dfuapi_open(dfuh, pid) != DFUAPISuccess)
goto error;
if (reset)
if (!dfuh->api->reset_fn(dfuh))
if (dfuapi_reset(dfuh) != DFUAPISuccess)
goto error;
if (!dfureq_getstate(dfuh, state))
goto error;
if (state) {
DFUState sts;
if (dfuapi_req_getstate(dfuh, &sts) != DFUAPISuccess)
goto error;
*state = (int)sts;
}
bye:
dfu_destroy(dfuh);
dfuapi_destroy(dfuh);
return ret;
error: