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

@ -1,2 +1,5 @@
buildposix/
buildmingw/
builddarwin/
mks5lboot
mks5lboot.exe

View File

@ -4,6 +4,7 @@
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
CC := gcc
CFLAGS += -Wall -Wextra
OUTPUT = mks5lboot
@ -15,18 +16,25 @@ SOURCES := $(LIBSOURCES) main.c
# dependencies for binary
EXTRADEPS :=
ifeq ($(findstring MINGW,$(shell uname)),MINGW)
CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -)
ifeq ($(findstring WIN32,$(CPPDEFINES)),WIN32)
LDOPTS += -lsetupapi
# optional libusb support on Windows
ifdef DISABLE_LIBUSBAPI
CFLAGS += -DNO_LIBUSBAPI
else
# optional libusb support (needed for WinUSB and libusbK drivers)
ifeq ($(findstring MINGW,$(CPPDEFINES)),MINGW)
ifeq ($(USE_LIBUSBAPI),1)
CFLAGS += -DUSE_LIBUSBAPI
LDOPTS += -Wl,-Bstatic -lusb-1.0
endif
endif
else
# Linux, OS X
ifeq ($(findstring APPLE,$(CPPDEFINES)),APPLE)
LDOPTS += -L/usr/local/lib -framework IOKit -framework CoreFoundation
else # Linux
CFLAGS += -DUSE_LIBUSBAPI
LDOPTS += -lusb-1.0
endif
endif
include ../libtools.make

View File

@ -5,7 +5,7 @@ A tool to install/uninstall a dual bootloader into a s5l8702 based
device:
- iPod Classic 6G
- iPod Nano 3G (TODO)
- iPod Nano 3G (WIP)
Usage
@ -97,32 +97,40 @@ Prerequisites:
[INFO] DFU device state: 2
. When the device is found but there is no driver installed:
[ERR] Could not open USB device: LIBUSB_ERROR_NOT_SUPPORTED
. Then the device is found but driver is not valid (probably a
. When the device is found but driver is not valid (probably a
libusb-win32 driver is installed):
[ERR] Could not set USB configuration: LIBUSB_ERROR_NOT_FOUND
. If there is no valid DFU driver installed, try one of these:
a) Use Zadig (http://zadig.akeo.ie/) to build and install a WinUSB
(libusb.info) or libusbK driver for your device. Note that
libusb-win32 (libusb0) drivers are not valid for mks5lboot.
b) Use Apple Mobile Device USB driver (included with iTunes).
b) Use Apple Mobile Device USB driver (included with iTunes). To
install this driver without iTunes see https://www.freemyipod.org
/wiki/EmCORE_Installation/iPodClassic/InstalliTunesDrivers
Command line install:
- If you are using iTunes on Windows, close iTunes and kill (or pause)
iTunesHelper.exe before entering DFU mode.
- If you are using iTunes on Mac, quit iTunes and kill (or pause) the
iTunesHelper process before entering DFU mode.
You can use "ps x | grep iTunesHelper" to locate the process <PID>,
use "kill -STOP <PID>" to suspend the process and "kill -CONT <PID>"
to resume it once the bootloader is installed.
- Put you device on DFU mode by pressing and holding SELECT+MENU buttons
for about 12 seconds.
You can notice when the device enters DFU mode running the next command
to scan the USB bus every second (press Ctrl-C to abort the scan):
mks5lboot --dfuscan --loop
./mks5lboot --dfuscan --loop
- To install or update a bootloader, build the DFU installer and send it
to the device:
mks5lboot --bl-inst /path/to/bootloader-ipod6g.ipod
./mks5lboot --bl-inst path/to/bootloader-ipod6g.ipod
When the DFU imagen is loaded and executed, the device emits an 'alive'
When the DFU image is loaded and executed, the device emits an 'alive'
tone (2000Hz/100ms). When the bootloader is successfully installed then
a dual tone beep sounds (1000Hz/100ms+2000Hz/150ms) and the device
reboots. If something went bad then 330Hz/500ms tone is emited and the
@ -132,10 +140,15 @@ Command line install:
- To remove a previously installed bootloader, build the DFU uninstaler
and send it to the device:
mks5lboot --bl-uninst ipod6g
./mks5lboot --bl-uninst ipod6g
Notes:
- If USB access is denied, try to run the mks5lboot tool using a privileged
user (i.e. Administrator or root).
- On Windows, use 'mks5lboot' or 'mks5lboot.exe' instead of './mks5lboot'.
If USB access is denied, try to run the mks5lboot tool using a privileged
user (i.e. Administrator or root).
Dual-Boot
@ -192,14 +205,20 @@ To build the DFU single-boot installer and send it to the device:
mks5lboot --bl-inst --single /path/to/bootloader-ipod6g.ipod
Compilation
-----------
Build
-----
Needs libusb > 1.0 installed, tested on:
To build type 'make'.
Linux: gcc-4.9.2 + libusb-1.0.19
Windows XP: mingw32-gcc-4.8.1 + libusbx-1.0.15
OS X 10.11: clang-7.3.0 + libusb-1.0.20
Linux needs libusb >= 1.0, use your package manager to install libusb.
For Windows, to build with libusb support type 'make USE_LIBUSBAPI=1'.
Tested on:
Linux: gcc-4.9.2 + libusb-1.0.19
Windows XP: mingw32-gcc-4.8.1 + libusbx-1.0.15
OS X 10.11: clang-7.3.0 + libusb-1.0.20
MXE: i686-w64-mingw32.static-gcc 5.4.0 + libusb-1.0.21
Hacking

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:

View File

@ -24,7 +24,6 @@
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -35,9 +34,23 @@
#define O_BINARY 0
#endif
#ifdef WIN32
#include <windows.h>
#define sleep_ms(ms) Sleep(ms)
#else
#include <time.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);
}
#endif
#define DEFAULT_LOOP_PERIOD 1 /* seconds */
#define ERROR(format, ...) \
#define _ERR(format, ...) \
do { \
snprintf(errstr, errstrsize, "[ERR] "format, __VA_ARGS__); \
goto error; \
@ -48,10 +61,10 @@ static int write_file(char *outfile, unsigned char* buf,
{
int fd = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666);
if (fd < 0)
ERROR("Could not open %s for writing", outfile);
_ERR("Could not open %s for writing", outfile);
if (write(fd, buf, bufsize) != bufsize)
ERROR("Could not write file %s", outfile);
_ERR("Could not write file %s", outfile);
return 1;
@ -68,19 +81,19 @@ static unsigned char *read_file(char *infile, int *bufsize,
fd = open(infile, O_RDONLY|O_BINARY);
if (fd < 0)
ERROR("Could not open %s for reading", infile);
_ERR("Could not open %s for reading", infile);
if (fstat(fd, &s) < 0)
ERROR("Checking size of input file %s", infile);
_ERR("Checking size of input file %s", infile);
*bufsize = s.st_size;
buf = malloc(*bufsize);
if (buf == NULL)
ERROR("Could not allocate memory for %s", infile);
_ERR("Could not allocate memory for %s", infile);
if (read(fd, buf, *bufsize) != *bufsize)
ERROR("Could not read file %s", infile);
_ERR("Could not read file %s", infile);
return buf;
@ -88,14 +101,6 @@ error:
return NULL;
}
static void sleep_ms(unsigned int ms)
{
struct timespec req;
req.tv_sec = ms / 1000;
req.tv_nsec = (ms % 1000) * 1000000;
nanosleep(&req, NULL);
}
static void usage(void)
{
fprintf(stderr,
@ -169,7 +174,11 @@ int main(int argc, char* argv[])
int dfusize;
fprintf(stderr,
#if defined(WIN32) && defined(USE_LIBUSBAPI)
"mks5lboot Version " VERSION " (libusb)\n"
#else
"mks5lboot Version " VERSION "\n"
#endif
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
"\n");

View File

@ -117,7 +117,7 @@ static void put_uint32le(unsigned char* p, uint32_t x)
p[3] = (x >> 24) & 0xff;
}
#define ERROR(format, ...) \
#define _ERR(format, ...) \
do { \
snprintf(errstr, errstrsize, "[ERR] "format, __VA_ARGS__); \
goto error; \
@ -135,16 +135,16 @@ static unsigned char *load_file(char *filename, int *bufsize,
fd = open(filename, O_RDONLY|O_BINARY);
if (fd < 0)
ERROR("Could not open %s for reading", filename);
_ERR("Could not open %s for reading", filename);
if (fstat(fd, &s) < 0)
ERROR("Checking filesize of input file %s", filename);
_ERR("Checking filesize of input file %s", filename);
*bufsize = s.st_size;
if (is_rbbl) {
/* Read Rockbox header */
if (read(fd, header, sizeof(header)) != sizeof(header))
ERROR("Could not read file %s", filename);
_ERR("Could not read file %s", filename);
*bufsize -= sizeof(header);
for (i = 0; i < NUM_MODELS; i++)
@ -152,7 +152,7 @@ static unsigned char *load_file(char *filename, int *bufsize,
break;
if (i == NUM_MODELS)
ERROR("Model name \"%4.4s\" unknown. "
_ERR("Model name \"%4.4s\" unknown. "
"Is this really a rockbox bootloader?", header + 4);
*model = &ipod_identity[i];
@ -160,10 +160,10 @@ static unsigned char *load_file(char *filename, int *bufsize,
buf = malloc(*bufsize);
if (buf == NULL)
ERROR("Could not allocate memory for %s", filename);
_ERR("Could not allocate memory for %s", filename);
if (read(fd, buf, *bufsize) != *bufsize)
ERROR("Could not read file %s", filename);
_ERR("Could not read file %s", filename);
if (is_rbbl) {
/* Check checksum */
@ -173,7 +173,7 @@ static unsigned char *load_file(char *filename, int *bufsize,
sum += buf[i];
}
if (sum != get_uint32be(header))
ERROR("Checksum mismatch in %s", filename);
_ERR("Checksum mismatch in %s", filename);
}
close(fd);
@ -223,7 +223,7 @@ unsigned char *mkdfu(int dfu_type, char *dfu_arg, int* dfu_size,
}
}
if (!model)
ERROR("Platform name \"%s\" unknown", dfu_arg);
_ERR("Platform name \"%s\" unknown", dfu_arg);
*dfu_size = BIN_OFFSET + model->dualboot_uninstall_size;
dfu_desc = "BL uninstaller";
@ -255,11 +255,11 @@ unsigned char *mkdfu(int dfu_type, char *dfu_arg, int* dfu_size,
}
if (*dfu_size > DFU_MAXSIZE)
ERROR("DFU image (%d bytes) too big", *dfu_size);
_ERR("DFU image (%d bytes) too big", *dfu_size);
dfu_buf = calloc(*dfu_size, 1);
if (!dfu_buf)
ERROR("Could not allocate %d bytes for DFU image", *dfu_size);
_ERR("Could not allocate %d bytes for DFU image", *dfu_size);
cert_off = get_uint32le(s5l8702hdr.u.enc34.cert_off);
cert_sz = get_uint32le(s5l8702hdr.u.enc34.cert_sz);