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:
parent
cf168d4636
commit
fbbba9292b
|
@ -1,2 +1,5 @@
|
|||
buildposix/
|
||||
buildmingw/
|
||||
builddarwin/
|
||||
mks5lboot
|
||||
mks5lboot.exe
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue