From fbbba9292b07d91916d330b31e7f8644d65ff91a Mon Sep 17 00:00:00 2001 From: Cástor Muñoz Date: Thu, 4 May 2017 10:52:03 +0200 Subject: 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 --- rbutil/mks5lboot/.gitignore | 3 + rbutil/mks5lboot/Makefile | 20 +- rbutil/mks5lboot/README | 49 ++-- rbutil/mks5lboot/ipoddfu.c | 624 ++++++++++++++++++++++++++++---------------- rbutil/mks5lboot/main.c | 41 +-- rbutil/mks5lboot/mkdfu.c | 22 +- 6 files changed, 492 insertions(+), 267 deletions(-) (limited to 'rbutil') diff --git a/rbutil/mks5lboot/.gitignore b/rbutil/mks5lboot/.gitignore index 9078451a7a..9b9b1de6ab 100644 --- a/rbutil/mks5lboot/.gitignore +++ b/rbutil/mks5lboot/.gitignore @@ -1,2 +1,5 @@ buildposix/ +buildmingw/ +builddarwin/ mks5lboot +mks5lboot.exe diff --git a/rbutil/mks5lboot/Makefile b/rbutil/mks5lboot/Makefile index ba118eefd0..72ea521d5f 100644 --- a/rbutil/mks5lboot/Makefile +++ b/rbutil/mks5lboot/Makefile @@ -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 diff --git a/rbutil/mks5lboot/README b/rbutil/mks5lboot/README index c2df299867..c424f7e617 100644 --- a/rbutil/mks5lboot/README +++ b/rbutil/mks5lboot/README @@ -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 , + use "kill -STOP " to suspend the process and "kill -CONT " + 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 +----- + +To build type 'make'. + +Linux needs libusb >= 1.0, use your package manager to install libusb. -Needs libusb > 1.0 installed, tested on: +For Windows, to build with libusb support type 'make USE_LIBUSBAPI=1'. -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 +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 diff --git a/rbutil/mks5lboot/ipoddfu.c b/rbutil/mks5lboot/ipoddfu.c index 6d303d6603..5e2914af4b 100644 --- a/rbutil/mks5lboot/ipoddfu.c +++ b/rbutil/mks5lboot/ipoddfu.c @@ -22,28 +22,32 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef NO_LIBUSBAPI -#define USE_LIBUSBAPI -#endif #include #include #include +#include #include -#include #ifdef WIN32 #include #include -#include #endif - #ifdef USE_LIBUSBAPI #include #endif +#ifdef __APPLE__ +#include +#include +#include +#endif #include "mks5lboot.h" +#ifdef WIN32 +#define sleep_ms(ms) Sleep(ms) +#else +#include 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 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 int dfu_download_file(struct dfuDev* dfuh, +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: diff --git a/rbutil/mks5lboot/main.c b/rbutil/mks5lboot/main.c index 98c85b9bb7..31e16eca82 100644 --- a/rbutil/mks5lboot/main.c +++ b/rbutil/mks5lboot/main.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -35,9 +34,23 @@ #define O_BINARY 0 #endif +#ifdef WIN32 +#include +#define sleep_ms(ms) Sleep(ms) +#else +#include +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"); diff --git a/rbutil/mks5lboot/mkdfu.c b/rbutil/mks5lboot/mkdfu.c index 6ac0daf1ac..bb1929bffd 100644 --- a/rbutil/mks5lboot/mkdfu.c +++ b/rbutil/mks5lboot/mkdfu.c @@ -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); -- cgit