From c876d3bbefe0dc00c27ca0c12d29da5874946962 Mon Sep 17 00:00:00 2001 From: Dominik Riebeling Date: Wed, 15 Dec 2021 21:04:28 +0100 Subject: rbutil: Merge rbutil with utils folder. rbutil uses several components from the utils folder, and can be considered part of utils too. Having it in a separate folder is an arbitrary split that doesn't help anymore these days, so merge them. This also allows other utils to easily use libtools.make without the need to navigate to a different folder. Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21 --- utils/mks5lboot/ipoddfu.c | 1061 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1061 insertions(+) create mode 100644 utils/mks5lboot/ipoddfu.c (limited to 'utils/mks5lboot/ipoddfu.c') diff --git a/utils/mks5lboot/ipoddfu.c b/utils/mks5lboot/ipoddfu.c new file mode 100644 index 0000000000..5e2914af4b --- /dev/null +++ b/utils/mks5lboot/ipoddfu.c @@ -0,0 +1,1061 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2015 by Cástor Muñoz + * + * based on: + * ipoddfu_c by user890104 + * xpwn/pwnmetheus2 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#ifdef WIN32 +#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; + req.tv_sec = ms / 1000; + 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 + * No rights were reserved to this, so feel free to + * manipulate or do with it, what you want or desire :) + */ + +/* crc32table[] built by crc32_init() */ +static uint32_t crc32table[256]; + +/* Calculate crc32 */ +static uint32_t crc32(void *data, unsigned int len, uint32_t previousCrc32) +{ + uint32_t crc = ~previousCrc32; + unsigned char *d = (unsigned char*) data; + while (len--) + crc = (crc >> 8) ^ crc32table[(crc & 0xFF) ^ *d++]; + return ~crc; +} + +/* Calculate crc32table */ +static void crc32_init() +{ + uint32_t poly = 0xEDB88320L; + uint32_t crc; + int i, j; + for (i = 0; i < 256; ++i) + { + crc = i; + for (j = 0; j < 8; ++j) + crc = (crc >> 1) ^ ((crc & 1) ? poly : 0); + crc32table[i] = crc; + } +} + +/* USB */ +#define APPLE_VID 0x05AC + +struct pid_info { + int pid; + int mode; /* 0->DFU, 1->WTF */ + char *desc; +}; + +struct pid_info known_pids[] = +{ + /* DFU */ + { 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, 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; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__ ((packed)); +#define USB_CS_SZ (sizeof(struct usbControlSetup)) + +struct usbStatusData { + uint8_t bStatus; + uint8_t bwPollTimeout0; + uint8_t bwPollTimeout1; + uint8_t bwPollTimeout2; + uint8_t bState; + uint8_t iString; +} __attribute__ ((packed)); + + +/* + * DFU API + */ +#define DFU_PKT_SZ 2048 /* must be pow2 <= wTransferSize (2048) */ + +/* DFU 1.1 specs */ +typedef enum { + appIDLE = 0, + appDETACH = 1, + dfuIDLE = 2, + dfuDNLOAD_SYNC = 3, + dfuDNBUSY = 4, + dfuDNLOAD_IDLE = 5, + dfuMANIFEST_SYNC = 6, + dfuMANIFEST = 7, + dfuMANIFEST_WAIT_RESET = 8, + dfuUPLOAD_IDLE = 9, + dfuERROR = 10 +} DFUState; + +typedef enum { + errNONE = 0, + errTARGET = 1, + errFILE = 2, + errWRITE = 3, + errERASE = 4, + errCHECK_ERASED = 5, + errPROG = 6, + errVERIFY = 7, + errADDRESS = 8, + errNOTDONE = 9, + errFIRMWARE = 10, + errVENDOR = 11, + errUSBR = 12, + errPOR = 13, + errUNKNOWN = 14, + errSTALLEDPKT = 15 +} DFUStatus; + +typedef enum { + DFU_DETACH = 0, + DFU_DNLOAD = 1, + DFU_UPLOAD = 2, + DFU_GETSTATUS = 3, + DFU_CLRSTATUS = 4, + DFU_GETSTATE = 5, + DFU_ABORT = 6 +} DFURequest; + +typedef enum { + DFUAPIFail = 0, + DFUAPISuccess, +} dfuAPIResult; + +struct dfuDev { + struct dfuAPI *api; + int found_pid; + int detached; + char descr[256]; + dfuAPIResult res; + char err[256]; + /* API private */ +#ifdef WIN32 + HANDLE fh; + HANDLE ph; + DWORD ec; /* winapi error code */ +#endif +#ifdef USE_LIBUSBAPI + libusb_context* ctx; + libusb_device_handle* devh; + int rc; /* libusb return code */ +#endif +#ifdef __APPLE__ + IOUSBDeviceInterface** dev; + kern_return_t kr; +#endif +}; + +struct dfuAPI { + char *name; + dfuAPIResult (*open_fn)(struct dfuDev*, int*); + dfuAPIResult (*dfureq_fn)(struct dfuDev*, struct usbControlSetup*, void*); + dfuAPIResult (*reset_fn)(struct dfuDev*); + void (*close_fn)(struct dfuDev*); +}; + + +/* + * DFU API low-level (specific) functions + */ +static bool dfu_check_id(int vid, int pid, int *pid_list) +{ + int *p; + if (vid != APPLE_VID) + return 0; + for (p = pid_list; *p; p++) + if (*p == pid) + return 1; + return 0; +} + +/* adds extra DFU request error info */ +static void dfu_add_reqerrstr(struct dfuDev *dfuh, struct usbControlSetup *cs) +{ + snprintf(dfuh->err + strlen(dfuh->err), + sizeof(dfuh->err) - strlen(dfuh->err), " (cs=%02x/%d/%d/%d/%d)", + cs->bmRequestType, cs->bRequest, cs->wValue, cs->wIndex, cs->wLength); +} + +#ifdef WIN32 +static bool dfu_winapi_chkrc(struct dfuDev *dfuh, char *str, bool success) +{ + dfuh->res = (success) ? DFUAPISuccess : DFUAPIFail; + if (!success) { + dfuh->ec = GetLastError(); + snprintf(dfuh->err, sizeof(dfuh->err), "%s error %ld", str, dfuh->ec); + } + return success; +} + +static dfuAPIResult dfu_winapi_request(struct dfuDev *dfuh, + struct usbControlSetup* cs, void* data) +{ + unsigned char buf[USB_CS_SZ + DFU_PKT_SZ]; + DWORD rdwr; + bool rc; + + memcpy(buf, cs, USB_CS_SZ); + + if (cs->bmRequestType & 0x80) + { + rc = ReadFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL); + memcpy(data, buf+USB_CS_SZ, cs->wLength); + dfu_winapi_chkrc(dfuh, "DFU request failed: ReadFile()", rc); + } + else + { + memcpy(buf+USB_CS_SZ, data, cs->wLength); + rc = WriteFile(dfuh->ph, buf, USB_CS_SZ + cs->wLength, &rdwr, NULL); + dfu_winapi_chkrc(dfuh, "DFU request failed: WriteFile()", rc); + } + if (!rc) + dfu_add_reqerrstr(dfuh, cs); + + return dfuh->res; +} + +static dfuAPIResult dfu_winapi_reset(struct dfuDev *dfuh) +{ + DWORD bytesReturned; + 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) +{ + if (dfuh->fh != INVALID_HANDLE_VALUE) { + CloseHandle(dfuh->fh); + dfuh->fh = INVALID_HANDLE_VALUE; + } + if (dfuh->ph != INVALID_HANDLE_VALUE) { + CloseHandle(dfuh->ph); + dfuh->ph = INVALID_HANDLE_VALUE; + } +} + +static const GUID GUID_AAPLDFU = + { 0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}}; + +static dfuAPIResult dfu_winapi_open(struct dfuDev *dfuh, int *pid_list) +{ + const GUID *guid = &GUID_AAPLDFU; + HDEVINFO devinfo = NULL; + SP_DEVICE_INTERFACE_DETAIL_DATA_A* details = NULL; + SP_DEVICE_INTERFACE_DATA iface; + char *path = NULL; + DWORD i, size; + bool rc; + + dfuh->fh = + dfuh->ph = INVALID_HANDLE_VALUE; + dfuh->found_pid = 0; + dfuh->res = DFUAPISuccess; + dfuh->ec = 0; + + /* Get DFU path */ + devinfo = SetupDiGetClassDevsA(guid, NULL, NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (!dfu_winapi_chkrc(dfuh, "SetupDiGetClassDevsA()", + (devinfo != INVALID_HANDLE_VALUE))) + goto error; + + iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + for (i = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, guid, i, &iface); i++) + { + int vid, pid; + + SetupDiGetDeviceInterfaceDetailA(devinfo, &iface, NULL, 0, &size, NULL); + + if (details) free(details); + details = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(size); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + rc = SetupDiGetDeviceInterfaceDetailA(devinfo, &iface, details, size, NULL, NULL); + if (!dfu_winapi_chkrc(dfuh, "SetupDiGetDeviceInterfaceDetailA()", rc)) + goto error; + + CharUpperA(details->DevicePath); + if (sscanf(details->DevicePath, "%*4cUSB#VID_%04x&PID_%04x%*s", &vid, &pid) != 2) + continue; + if (!dfu_check_id(vid, pid, pid_list)) + continue; + + if (path) free(path); + path = malloc(size - sizeof(DWORD) + 16); + memcpy(path, details->DevicePath, size - sizeof(DWORD)); + + dfuh->fh = CreateFileA(path, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (!dfu_winapi_chkrc(dfuh, "CreateFileA(fh)", (dfuh->fh != INVALID_HANDLE_VALUE))) + goto error; + + strcat(path, "\\PIPE0"); + dfuh->ph = CreateFileA(path, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (!dfu_winapi_chkrc(dfuh, "CreateFileA(ph)", (dfuh->ph != INVALID_HANDLE_VALUE))) + goto error; + + /* ok */ + snprintf(dfuh->descr, sizeof(dfuh->descr), "%s", details->DevicePath); + dfuh->found_pid = pid; + goto bye; + } + + if (!dfu_winapi_chkrc(dfuh, "SetupDiEnumDeviceInterfaces()", + (GetLastError() == ERROR_NO_MORE_ITEMS))) + goto error; + + /* no devices found */ + +bye: + if (path) free(path); + if (details) free(details); + if (devinfo) SetupDiDestroyDeviceInfoList(devinfo); + return dfuh->res; + +error: + dfu_winapi_close(dfuh); + goto bye; +} +#endif /* WIN32 */ + +#ifdef USE_LIBUSBAPI +static bool dfu_libusb_chkrc(struct dfuDev *dfuh, char *str) +{ + 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 == DFUAPISuccess); +} + +static dfuAPIResult dfu_libusb_request(struct dfuDev *dfuh, + struct usbControlSetup *cs, void *data) +{ + dfuh->rc = libusb_control_transfer(dfuh->devh, cs->bmRequestType, + cs->bRequest, cs->wValue, cs->wIndex, data, cs->wLength, 500); + if (!dfu_libusb_chkrc(dfuh, "DFU request failed")) + dfu_add_reqerrstr(dfuh, cs); + return dfuh->res; +} + +static dfuAPIResult dfu_libusb_reset(struct dfuDev *dfuh) +{ + dfuh->rc = libusb_reset_device(dfuh->devh); + dfu_libusb_chkrc(dfuh, "Could not reset USB device"); + return dfuh->res; +} + +static void dfu_libusb_close(struct dfuDev *dfuh) +{ + if (dfuh->devh) { + libusb_release_interface(dfuh->devh, 0); + if (dfuh->detached) + libusb_attach_kernel_driver(dfuh->devh, 0); + libusb_close(dfuh->devh); + dfuh->devh = NULL; + } + if (dfuh->ctx) { + libusb_exit(dfuh->ctx); + dfuh->ctx = NULL; + } +} + +static dfuAPIResult dfu_libusb_open(struct dfuDev *dfuh, int *pid_list) +{ + struct libusb_device_descriptor desc; + libusb_device **devs = NULL, *dev; + int n_devs, i; + + dfuh->devh = NULL; + dfuh->found_pid = 0; + dfuh->detached = 0; + dfuh->res = DFUAPISuccess; + + dfuh->rc = libusb_init(&(dfuh->ctx)); + if (!dfu_libusb_chkrc(dfuh, "Could not init USB library")) { + dfuh->ctx = NULL; /* invalidate ctx (if any) */ + goto error; + } + + n_devs = + dfuh->rc = libusb_get_device_list(dfuh->ctx, &devs); + if (!dfu_libusb_chkrc(dfuh, "Could not get USB device list")) + goto error; + + for (i = 0; i < n_devs; ++i) + { + dev = devs[i]; + + /* Note: since libusb-1.0.16 (LIBUSB_API_VERSION >= 0x01000102) + this function always succeeds. */ + if (libusb_get_device_descriptor(dev, &desc) != LIBUSB_SUCCESS) + continue; /* Unable to get device descriptor */ + + if (!dfu_check_id(desc.idVendor, desc.idProduct, pid_list)) + continue; + + dfuh->rc = libusb_open(dev, &(dfuh->devh)); + if (!dfu_libusb_chkrc(dfuh, "Could not open USB device")) + goto error; + + dfuh->rc = libusb_set_configuration(dfuh->devh, 1); + if (!dfu_libusb_chkrc(dfuh, "Could not set USB configuration")) + goto error; + + dfuh->rc = libusb_kernel_driver_active(dfuh->devh, 0); + if (dfuh->rc != LIBUSB_ERROR_NOT_SUPPORTED) { + if (!dfu_libusb_chkrc(dfuh, "Could not get USB driver status")) + goto error; + if (dfuh->rc == 1) { + dfuh->rc = libusb_detach_kernel_driver(dfuh->devh, 0); + if (!dfu_libusb_chkrc(dfuh, "Could not detach USB driver")) + goto error; + dfuh->detached = 1; + } + } + + dfuh->rc = libusb_claim_interface(dfuh->devh, 0); + if (!dfu_libusb_chkrc(dfuh, "Could not claim USB interface")) + goto error; + + /* ok */ + snprintf(dfuh->descr, sizeof(dfuh->descr), + "[%04x:%04x] at bus %d, device %d, USB ver. %04x", + desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), + libusb_get_device_address(dev), desc.bcdUSB); + dfuh->found_pid = desc.idProduct; + break; + } + +bye: + if (devs) + libusb_free_device_list(devs, 1); + if (!dfuh->found_pid) + dfu_libusb_close(dfuh); + return dfuh->res; + +error: + goto bye; +} +#endif /* USE_LIBUSBAPI */ + +#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 + { "winapi", + dfu_winapi_open, + dfu_winapi_request, + dfu_winapi_reset, + dfu_winapi_close }, +#endif +#ifdef USE_LIBUSBAPI + { "libusb", + dfu_libusb_open, + dfu_libusb_request, + dfu_libusb_reset, + dfu_libusb_close }, +#endif +#ifdef __APPLE__ + { "IOKit", + dfu_iokit_open, + dfu_iokit_request, + dfu_iokit_reset, + dfu_iokit_close }, +#endif +}; +#define N_DFU_APIS (sizeof(api_list)/sizeof(struct dfuAPI)) + + +/* + * DFU API common functions + */ +static int DEBUG_DFUREQ = 0; + +static dfuAPIResult dfuapi_request(struct dfuDev *dfuh, + struct usbControlSetup *cs, void *data) +{ + if (!DEBUG_DFUREQ) + return dfuh->api->dfureq_fn(dfuh, cs, data); + + /* DEBUG */ + + /* previous state */ + unsigned char ste = 0; + struct usbControlSetup css = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(ste) }; + if (dfuh->api->dfureq_fn(dfuh, &css, &ste) != DFUAPISuccess) { + snprintf(dfuh->err + strlen(dfuh->err), sizeof(dfuh->err) - + strlen(dfuh->err), " [DEBUG_DFUREQ ERROR: state=%d]", ste); + goto error; + } + + 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, + (dfuh->res == DFUAPISuccess) ? "ok" : "ERROR"); + if (cs->bRequest == DFU_GETSTATE) + fprintf(stderr, " (state=%d)", *((unsigned char*)(data))); + if (cs->bRequest == DFU_GETSTATUS) { + struct usbStatusData *sd = (struct usbStatusData*)data; + fprintf(stderr, " (status=%d, polltmo=%d, state=%d)", sd->bStatus, + (sd->bwPollTimeout2 << 16) | (sd->bwPollTimeout1 << 8) | + (sd->bwPollTimeout0), sd->bState); + } + fputc('\n', stderr); + fflush(stderr); + +bye: + return dfuh->res; +error: + goto bye; +} + +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) }; + 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 dfuh->res; +} + +static dfuAPIResult dfuapi_req_getstate(struct dfuDev *dfuh, DFUState *state) +{ + unsigned char sts = 0; + struct usbControlSetup cs = { 0xA1, DFU_GETSTATE, 0, 0, sizeof(sts) }; + dfuapi_request(dfuh, &cs, &sts); + if (state) *state = sts; + return dfuh->res; +} + +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 dfuapi_request(dfuh, &cs, data); +} + +/* not used */ +#if 0 +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 dfuapi_request(dfuh, &cs, data); +} + +static dfuAPIResult dfuapi_req_clrstatus(struct dfuDev* dfuh) +{ + struct usbControlSetup cs = { 0x21, DFU_CLRSTATUS, 0, 0, 0 }; + return dfuapi_request(dfuh, &cs, NULL); +} + +static dfuAPIResult dfuapi_req_abort(struct dfuDev* dfuh) +{ + struct usbControlSetup cs = { 0x21, DFU_ABORT, 0, 0, 0 }; + return dfuapi_request(dfuh, &cs, NULL); +} + +/* not implemented on DFU8702 */ +static dfuAPIResult dfuapi_req_detach(struct dfuDev* dfuh, int tmo) +{ + struct usbControlSetup cs = { 0x21, DFU_DETACH, tmo, 0, 0 }; + return dfuapi_request(dfuh, &cs, NULL); +} +#endif + +static dfuAPIResult dfuapi_reset(struct dfuDev *dfuh) +{ + 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 */ + + if (dfuapi_req_getstatus(dfuh, status, poll_tmo, state) != DFUAPISuccess) + goto error; + + if (*state == dfuDNBUSY) { + if (*poll_tmo) + sleep_ms(*poll_tmo); + 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; + } + +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 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 poll_tmo; + DFUStatus status; + DFUState state; + + if (dfuapi_req_getstate(dfuh, &state) != DFUAPISuccess) + goto error; + + if (state != dfuIDLE) { + dfuapi_set_err(dfuh, "Could not start DFU download: not idle"); + goto error; + } + + blknum = 0; + remaining = size; + while (remaining) + { + len = (remaining < DFU_PKT_SZ) ? remaining : DFU_PKT_SZ; + + if (dfuapi_send_packet(dfuh, blknum, len, data + blknum*DFU_PKT_SZ, + &status, &poll_tmo, &state, NULL) != DFUAPISuccess) + goto error; + + if (state != dfuDNLOAD_IDLE) { + dfuapi_set_err(dfuh, "DFU download aborted: unexpected state"); + goto error; + } + + remaining -= len; + blknum++; + } + + /* send ZLP */ + 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; + } + + if (state != dfuMANIFEST) { + if (status == errFIRMWARE) + dfuapi_set_err(dfuh, "DFU download failed: corrupt firmware"); + else + dfuapi_set_err(dfuh, "DFU download failed: unexpected state"); + goto error; + } + + /* wait for manifest stage */ + if (poll_tmo) + sleep_ms(poll_tmo); + + if (dfuapi_req_getstatus(dfuh, &status, NULL, &state) != DFUAPISuccess) + goto ok; /* 1223 .dfu file */ + + /* XXX: next code never tested */ + + if (state != dfuMANIFEST_WAIT_RESET) { + if (status == errVERIFY) + dfuapi_set_err(dfuh, "DFU manifest failed: wrong FW verification"); + else + dfuapi_set_err(dfuh, "DFU manifest failed: unexpected state"); + goto error; + } + + if (dfuapi_reset(dfuh) != DFUAPISuccess) + goto error; + +ok: + return 1; +error: + return 0; +} + +/* exported functions */ +int ipoddfu_send(int pid, unsigned char *data, int size, + char* errstr, int errstrsize) +{ + struct dfuDev *dfuh; + unsigned char *buf; + uint32_t checksum; + int ret = 1; /* ok */ + + dfuh = dfuapi_create(); + + buf = malloc(size+4); + if (!buf) { + dfuapi_set_err(dfuh, "Could not allocate memory for DFU buffer"); + goto error; + } + + if (memcmp(data, IM3_IDENT, 4)) { + dfuapi_set_err(dfuh, "Bad DFU image data"); + goto error; + } + + crc32_init(); + checksum = crc32(data, size, 0); + memcpy(buf, data, size); + put_uint32le(buf+size, ~checksum); + + if (dfuapi_open(dfuh, pid) != DFUAPISuccess) + goto error; + + if (!ipoddfu_download_file(dfuh, buf, size+4)) + goto error; + +bye: + if (buf) free(buf); + dfuapi_destroy(dfuh); + return ret; + +error: + ret = 0; + if (errstr) + snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err); + goto bye; +} + +/* search for the DFU device and gets its DFUState */ +int ipoddfu_scan(int pid, int *state, int reset, + char* errstr, int errstrsize) +{ + struct dfuDev *dfuh; + int ret = 1; /* ok */ + + dfuh = dfuapi_create(); + + if (dfuapi_open(dfuh, pid) != DFUAPISuccess) + goto error; + + if (reset) + if (dfuapi_reset(dfuh) != DFUAPISuccess) + goto error; + + if (state) { + DFUState sts; + if (dfuapi_req_getstate(dfuh, &sts) != DFUAPISuccess) + goto error; + *state = (int)sts; + } + +bye: + dfuapi_destroy(dfuh); + return ret; + +error: + ret = 0; + if (errstr) + snprintf(errstr, errstrsize, "[ERR] %s", dfuh->err); + goto bye; +} + +void ipoddfu_debug(int debug) +{ + DEBUG_DFUREQ = debug; +} -- cgit v1.2.3