/* * Copyright 2015 Umbrela Smart, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package co.umbrela.tools.stm32dfuprogrammer; import android.nfc.FormatException; import android.os.Environment; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @SuppressWarnings("unused") public class Dfu { private static final String TAG = "Dfu"; private final static int USB_DIR_OUT = 0; private final static int USB_DIR_IN = 128; //0x80 private final static int DFU_RequestType = 0x21; // '2' => Class request ; '1' => to interface private final static int STATE_IDLE = 0x00; private final static int STATE_DETACH = 0x01; private final static int STATE_DFU_IDLE = 0x02; private final static int STATE_DFU_DOWNLOAD_SYNC = 0x03; private final static int STATE_DFU_DOWNLOAD_BUSY = 0x04; private final static int STATE_DFU_DOWNLOAD_IDLE = 0x05; private final static int STATE_DFU_MANIFEST_SYNC = 0x06; private final static int STATE_DFU_MANIFEST = 0x07; private final static int STATE_DFU_MANIFEST_WAIT_RESET = 0x08; private final static int STATE_DFU_UPLOAD_IDLE = 0x09; private final static int STATE_DFU_ERROR = 0x0A; private final static int STATE_DFU_UPLOAD_SYNC = 0x91; private final static int STATE_DFU_UPLOAD_BUSY = 0x92; // DFU Commands, request ID code when using controlTransfers private final static int DFU_DETACH = 0x00; private final static int DFU_DNLOAD = 0x01; private final static int DFU_UPLOAD = 0x02; private final static int DFU_GETSTATUS = 0x03; private final static int DFU_CLRSTATUS = 0x04; private final static int DFU_GETSTATE = 0x05; private final static int DFU_ABORT = 0x06; public final static int ELEMENT1_OFFSET = 293; // constant offset in file array where image data starts public final static int TARGET_NAME_START = 22; public final static int TARGET_NAME_MAX_END = 276; public final static int TARGET_SIZE = 277; public final static int TARGET_NUM_ELEMENTS = 281; // Device specific parameters public static final String mInternalFlashString = "@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg"; // STM32F405RG, 1MB Flash, 192KB SRAM public static final int mInternalFlashSize = 1048575; public static final int mInternalFlashStartAddress = 0x08000000; public static final int mOptionByteStartAddress = 0x1FFFC000; private static final int OPT_BOR_1 = 0x08; private static final int OPT_BOR_2 = 0x04; private static final int OPT_BOR_3 = 0x00; private static final int OPT_BOR_OFF = 0x0C; private static final int OPT_WDG_SW = 0x20; private static final int OPT_nRST_STOP = 0x40; private static final int OPT_nRST_STDBY = 0x80; private static final int OPT_RDP_OFF = 0xAA00; private static final int OPT_RDP_1 = 0x3300; private final int deviceVid; private final int devicePid; private final DfuFile dfuFile; private Usb usb; private int deviceVersion; //STM bootloader version private final List<DfuListener> listeners = new ArrayList<>(); public interface DfuListener { void onStatusMsg(String msg); } public Dfu(int usbVendorId, int usbProductId) { this.deviceVid = usbVendorId; this.devicePid = usbProductId; dfuFile = new DfuFile(); } private void onStatusMsg(final String msg) { for (DfuListener listener : listeners) { listener.onStatusMsg(msg); } } public void setListener(final DfuListener listener) { if (listener == null) throw new IllegalArgumentException("Listener is null"); listeners.add(listener); } public void setUsb(Usb usb) { this.usb = usb; this.deviceVersion = this.usb.getDeviceVersion(); } /* One-Click Programming Method to fully flash the connected device This will try everything that it can do to program, if it throws execptions it failed on something it cannot fix. */ public boolean programFirmware(String filePath) throws Exception { final int MAX_ALLOWED_RETRIES = 5; openFile(filePath); verifyFile(); checkCompatibility(); if (isDeviceProtected()) { Log.i(TAG, "Device is protected"); Log.i(TAG, "Removing Read Protection"); removeReadProtection(); Log.i(TAG, "Device is resetting"); return false; // device will reset } for (int i = MAX_ALLOWED_RETRIES + 1; i > 0; i--) { if (isDeviceBlank()) break; if (i == 1) { throw new Exception("Cannot Mass Erase, REPLACE UNIT!"); } Log.i(TAG, "Device not blank, erasing"); massErase(); } writeImage(); for (int i = MAX_ALLOWED_RETRIES + 1; i > 0; i--) { if (isWrittenImageOk()) { Log.i(TAG, "Writing Option Bytes, will self-reset"); int selectOptions = OPT_RDP_OFF | OPT_WDG_SW | OPT_nRST_STOP | OPT_nRST_STDBY | OPT_BOR_1; // todo in production, OPT_RDP_1 must be set instead of OPT_RDP_OFF writeOptionBytes(selectOptions); // will reset device break; } if (i == 1) { throw new Exception("Cannot Write successfully, REPLACE UNIT!"); } Log.i(TAG, "Verification failed, retry"); massErase(); writeImage(); } return true; } private boolean isDeviceBlank() throws Exception { byte[] readContent = new byte[dfuFile.elementLength]; readImage(readContent); ByteBuffer read = ByteBuffer.wrap(readContent); // wrap whole array int hash = read.hashCode(); return (dfuFile.elementLength == Math.abs(hash)); } // similar to verify() private boolean isWrittenImageOk() throws Exception { byte[] deviceFirmware = new byte[dfuFile.elementLength]; long startTime = System.currentTimeMillis(); readImage(deviceFirmware); // create byte buffer and compare content ByteBuffer fileFw = ByteBuffer.wrap(dfuFile.file, ELEMENT1_OFFSET, dfuFile.elementLength); // set offset and limit of firmware ByteBuffer deviceFw = ByteBuffer.wrap(deviceFirmware); // wrap whole array boolean result = fileFw.equals(deviceFw); Log.i(TAG, "Verified completed in " + (System.currentTimeMillis() - startTime) + " ms"); return result; } public void massErase() { if (!isUsbConnected()) return; DfuStatus dfuStatus = new DfuStatus(); long startTime = System.currentTimeMillis(); // note current time try { do { clearStatus(); getStatus(dfuStatus); } while (dfuStatus.bState != STATE_DFU_IDLE); if (isDeviceProtected()) { removeReadProtection(); onStatusMsg("Read Protection removed. Device resets...Wait until it re-enumerates "); // XXX This will reset the device return; } massEraseCommand(); // sent erase command request getStatus(dfuStatus); // initiate erase command, returns 'download busy' even if invalid address or ROP int pollingTime = dfuStatus.bwPollTimeout; // note requested waiting time do { /* wait specified time before next getStatus call */ Thread.sleep(pollingTime); clearStatus(); getStatus(dfuStatus); } while (dfuStatus.bState != STATE_DFU_IDLE); onStatusMsg("Mass erase completed in " + (System.currentTimeMillis() - startTime) + " ms"); } catch (InterruptedException e) { e.printStackTrace(); } catch (Exception e) { onStatusMsg(e.toString()); } } public void fastOperations() { if (!isUsbConnected()) return; final DfuStatus dfuStatus = new DfuStatus(); final byte[] configBytes = new byte[4]; try { if (isDeviceProtected()) { onStatusMsg("Device is Read-Protected...First Mass Erase"); return; } readDeviceFeature(configBytes); if (configBytes[0] != 0x03) { configBytes[0] = 0x03; download(configBytes, 2); getStatus(dfuStatus); getStatus(dfuStatus); while (dfuStatus.bState != STATE_DFU_IDLE) { clearStatus(); getStatus(dfuStatus); } onStatusMsg("Fast Operations set (Parallelism x32)"); } else { onStatusMsg("Fast Operations was already set (Parallelism x32)"); } } catch (Exception e) { onStatusMsg(e.toString()); } } public void program() { if (!isUsbConnected()) return; try { if (isDeviceProtected()) { onStatusMsg("Device is Read-Protected...First Mass Erase"); return; } openFile(); verifyFile(); checkCompatibility(); onStatusMsg("File Path: " + dfuFile.filePath + "\n"); onStatusMsg("File Size: " + dfuFile.file.length + " Bytes \n"); onStatusMsg("ElementAddress: 0x" + Integer.toHexString(dfuFile.elementStartAddress)); onStatusMsg("\tElementSize: " + dfuFile.elementLength + " Bytes\n"); onStatusMsg("Start writing file in blocks of " + dfuFile.maxBlockSize + " Bytes \n"); long startTime = System.currentTimeMillis(); writeImage(); onStatusMsg("Programming completed in " + (System.currentTimeMillis() - startTime) + " ms\n"); } catch (Exception e) { e.printStackTrace(); onStatusMsg(e.toString()); } } public void verify() { if (!isUsbConnected()) return; try { if (isDeviceProtected()) { onStatusMsg("Device is Read-Protected...First Mass Erase"); return; } if (dfuFile.filePath == null) { openFile(); verifyFile(); checkCompatibility(); } byte[] deviceFirmware = new byte[dfuFile.elementLength]; readImage(deviceFirmware); // create byte buffer and compare content ByteBuffer fileFw = ByteBuffer.wrap(dfuFile.file, ELEMENT1_OFFSET, dfuFile.elementLength); // set offset and limit of firmware ByteBuffer deviceFw = ByteBuffer.wrap(deviceFirmware); // wrap whole array if (fileFw.equals(deviceFw)) { // compares type, length, content onStatusMsg("device firmware equals file firmware"); } else { onStatusMsg("device firmware does not equals file firmware"); } } catch (Exception e) { e.printStackTrace(); onStatusMsg(e.toString()); } } // check if usb device is active private boolean isUsbConnected() { if (usb != null && usb.isConnected()) { return true; } onStatusMsg("No device connected"); return false; } public void leaveDfuMode() { try { detach(mInternalFlashStartAddress); } catch (Exception e) { e.printStackTrace(); onStatusMsg(e.toString()); } } private void removeReadProtection() throws Exception { DfuStatus dfuStatus = new DfuStatus(); unProtectCommand(); getStatus(dfuStatus); if (dfuStatus.bState != STATE_DFU_DOWNLOAD_BUSY) { throw new Exception("Failed to execute unprotect command"); } usb.release(); // XXX device will self-reset Log.i(TAG, "USB was released"); } private void readDeviceFeature(byte[] configBytes) throws Exception { DfuStatus dfuStatus = new DfuStatus(); do { clearStatus(); getStatus(dfuStatus); } while (dfuStatus.bState != STATE_DFU_IDLE); setAddressPointer(0xFFFF0000); getStatus(dfuStatus); getStatus(dfuStatus); if (dfuStatus.bState == STATE_DFU_ERROR) { throw new Exception("Fast Operations not supported"); } while (dfuStatus.bState != STATE_DFU_IDLE) { clearStatus(); getStatus(dfuStatus); } upload(configBytes, configBytes.length, 2); getStatus(dfuStatus); while (dfuStatus.bState != STATE_DFU_IDLE) { clearStatus(); getStatus(dfuStatus); } } private void writeImage() throws Exception { int address = dfuFile.elementStartAddress; // flash start address int fileOffset = ELEMENT1_OFFSET; // index offset of file int blockSize = dfuFile.maxBlockSize; // max block size byte[] Block = new byte[blockSize]; int NumOfBlocks = dfuFile.elementLength / blockSize; int blockNum; for (blockNum = 0; blockNum < NumOfBlocks; blockNum++) { System.arraycopy(dfuFile.file, (blockNum * blockSize) + fileOffset, Block, 0, blockSize); // send out the block to device writeBlock(address, Block, blockNum); } // check if last block is partial int remainder = dfuFile.elementLength - (blockNum * blockSize); if (remainder > 0) { System.arraycopy(dfuFile.file, (blockNum * blockSize) + fileOffset, Block, 0, remainder); // Pad with 0xFF so our CRC matches the ST Bootloader and the ULink's CRC while (remainder < Block.length) { Block[remainder++] = (byte) 0xFF; } // send out the block to device writeBlock(address, Block, blockNum); } } private void readImage(byte[] deviceFw) throws Exception { DfuStatus dfuStatus = new DfuStatus(); int maxBlockSize = dfuFile.maxBlockSize; int startAddress = dfuFile.elementStartAddress; byte[] block = new byte[maxBlockSize]; int nBlock; int remLength = deviceFw.length; int numOfBlocks = remLength / maxBlockSize; do { clearStatus(); getStatus(dfuStatus); } while (dfuStatus.bState != STATE_DFU_IDLE); setAddressPointer(startAddress); getStatus(dfuStatus); // to execute getStatus(dfuStatus); //to verify if (dfuStatus.bState == STATE_DFU_ERROR) { throw new Exception("Start address not supported"); } // will read full and last partial blocks ( NOTE: last partial block will be read with maxkblocksize) for (nBlock = 0; nBlock <= numOfBlocks; nBlock++) { while (dfuStatus.bState != STATE_DFU_IDLE) { // todo if fails, maybe stop reading clearStatus(); getStatus(dfuStatus); } upload(block, maxBlockSize, nBlock + 2); getStatus(dfuStatus); if (remLength >= maxBlockSize) { remLength -= maxBlockSize; System.arraycopy(block, 0, deviceFw, (nBlock * maxBlockSize), maxBlockSize); } else { System.arraycopy(block, 0, deviceFw, (nBlock * maxBlockSize), remLength); } } } // this can be used if the filePath is known to .dfu file private void openFile(String filePath) throws Exception { if (filePath == null) { throw new FileNotFoundException("No file selected"); } File myFile = new File(filePath); if (!myFile.exists()) { throw new FileNotFoundException("Cannot find: " + myFile.toString()); } if (!myFile.canRead()) { throw new FormatException("Cannot open: " + myFile.toString()); } dfuFile.filePath = myFile.toString(); dfuFile.file = new byte[(int) myFile.length()]; //convert file into byte array FileInputStream fileInputStream = new FileInputStream(myFile); int readLength = fileInputStream.read(dfuFile.file); fileInputStream.close(); if (readLength != myFile.length()) { throw new IOException("Could Not Read File"); } } private void openFile() throws Exception { File extDownload; String myFilePath = null; String myFileName = null; FileInputStream fileInputStream; File myFile; if (Environment.getExternalStorageState() != null) // todo not sure if this works { extDownload = new File(Environment.getExternalStorageDirectory() + "/Download/"); if (extDownload.exists()) { String[] files = extDownload.list(); // todo support multiple dfu files in dir if (files.length > 0) { // will select first dfu file found in dir for (String file : files) { if (file.endsWith(".dfu")) { myFilePath = extDownload.toString(); myFileName = file; break; } } } } } if (myFileName == null) throw new Exception("No .dfu file found in Download Folder"); myFile = new File(myFilePath + "/" + myFileName); dfuFile.filePath = myFile.toString(); dfuFile.file = new byte[(int) myFile.length()]; //convert file into byte array fileInputStream = new FileInputStream(myFile); fileInputStream.read(dfuFile.file); fileInputStream.close(); } private void verifyFile() throws Exception { // todo for now i expect the file to be not corrupted int length = dfuFile.file.length; int crcIndex = length - 4; int crc = 0; crc |= dfuFile.file[crcIndex++] & 0xFF; crc |= (dfuFile.file[crcIndex++] & 0xFF) << 8; crc |= (dfuFile.file[crcIndex++] & 0xFF) << 16; crc |= (dfuFile.file[crcIndex] & 0xFF) << 24; // do crc check if (crc != calculateCRC(dfuFile.file)) { throw new FormatException("CRC Failed"); } // Check the prefix String prefix = new String(dfuFile.file, 0, 5); if (prefix.compareTo("DfuSe") != 0) { throw new FormatException("File signature error"); } // check dfuSe Version if (dfuFile.file[5] != 1) { throw new FormatException("DFU file version must be 1"); } // Check the suffix String suffix = new String(dfuFile.file, length - 8, 3); if (suffix.compareTo("UFD") != 0) { throw new FormatException("File suffix error"); } if ((dfuFile.file[length - 5] != 16) || (dfuFile.file[length - 10] != 0x1A) || (dfuFile.file[length - 9] != 0x01)) { throw new FormatException("File number error"); } // Now check the target prefix, we assume there is only one target in the file String target = new String(dfuFile.file, 11, 6); if (target.compareTo("Target") != 0) { throw new FormatException("Target signature error"); } if (0 != dfuFile.file[TARGET_NAME_START]) { String tempName = new String(dfuFile.file, TARGET_NAME_START, TARGET_NAME_MAX_END); int foundNullAt = tempName.indexOf(0); dfuFile.TargetName = tempName.substring(0, foundNullAt); } else { throw new FormatException("No Target Name Exist in File"); } Log.i(TAG, "Firmware Target Name: " + dfuFile.TargetName); dfuFile.TargetSize = dfuFile.file[TARGET_SIZE] & 0xFF; dfuFile.TargetSize |= (dfuFile.file[TARGET_SIZE + 1] & 0xFF) << 8; dfuFile.TargetSize |= (dfuFile.file[TARGET_SIZE + 2] & 0xFF) << 16; dfuFile.TargetSize |= (dfuFile.file[TARGET_SIZE + 3] & 0xFF) << 24; Log.i(TAG, "Firmware Target Size: " + dfuFile.TargetSize); dfuFile.NumElements = dfuFile.file[TARGET_NUM_ELEMENTS] & 0xFF; dfuFile.NumElements |= (dfuFile.file[TARGET_NUM_ELEMENTS + 1] & 0xFF) << 8; dfuFile.NumElements |= (dfuFile.file[TARGET_NUM_ELEMENTS + 2] & 0xFF) << 16; dfuFile.NumElements |= (dfuFile.file[TARGET_NUM_ELEMENTS + 3] & 0xFF) << 24; Log.i(TAG, "Firmware Num of Elements: " + dfuFile.NumElements); if (dfuFile.NumElements > 1) { throw new FormatException("Do not support multiple Elements inside Image"); /* If you get this error, that means that the C-compiler IDE is treating the Reset Vector ISR and the data ( your code) as two separate elements. This problem has been observed with The Atollic TrueStudio V5.5.2 The version of Atollic that works with this is v5.3.0 The version of DfuSe FileManager is v3.0.3 Refer to ST document UM0391 for more details on DfuSe format */ } // Get Element Flash start address and size dfuFile.elementStartAddress = dfuFile.file[285] & 0xFF; dfuFile.elementStartAddress |= (dfuFile.file[286] & 0xFF) << 8; dfuFile.elementStartAddress |= (dfuFile.file[287] & 0xFF) << 16; dfuFile.elementStartAddress |= (dfuFile.file[288] & 0xFF) << 24; dfuFile.elementLength = dfuFile.file[289] & 0xFF; dfuFile.elementLength |= (dfuFile.file[290] & 0xFF) << 8; dfuFile.elementLength |= (dfuFile.file[291] & 0xFF) << 16; dfuFile.elementLength |= (dfuFile.file[292] & 0xFF) << 24; if (dfuFile.elementLength < 512) { throw new FormatException("Element Size is too small"); } // Get VID, PID and version number dfuFile.VID = (dfuFile.file[length - 11] & 0xFF) << 8; dfuFile.VID |= (dfuFile.file[length - 12] & 0xFF); dfuFile.PID = (dfuFile.file[length - 13] & 0xFF) << 8; dfuFile.PID |= (dfuFile.file[length - 14] & 0xFF); dfuFile.BootVersion = (dfuFile.file[length - 15] & 0xFF) << 8; dfuFile.BootVersion |= (dfuFile.file[length - 16] & 0xFF); } private void checkCompatibility() throws Exception { if ((devicePid != dfuFile.PID) || (deviceVid != dfuFile.VID)) { throw new FormatException("PID/VID Miss match"); } deviceVersion = usb.getDeviceVersion(); // give warning and continue on if (deviceVersion != dfuFile.BootVersion) { onStatusMsg("Warning: Device BootVersion: " + Integer.toHexString(deviceVersion) + "\tFile BootVersion: " + Integer.toHexString(dfuFile.BootVersion) + "\n"); } if (dfuFile.elementStartAddress != mInternalFlashStartAddress) { // todo: this will fail with images for other memory sections, other than Internal Flash throw new FormatException("Firmware does not start at beginning of internal flash"); } if (deviceSizeLimit() < 0) { throw new Exception("Error: Could Not Retrieve Internal Flash String"); } if ((dfuFile.elementStartAddress + dfuFile.elementLength) >= (mInternalFlashStartAddress + mInternalFlashSize)) { throw new FormatException("Firmware image too large for target"); } switch (deviceVersion) { case 0x011A: case 0x0200: dfuFile.maxBlockSize = 1024; break; case 0x2100: case 0x2200: dfuFile.maxBlockSize = 2048; break; default: throw new Exception("Error: Unsupported bootloader version"); } Log.i(TAG, "Firmware ok and compatible"); } // todo this is limited to stm32f405RG and will fail for other future chips. private int deviceSizeLimit() { // retrieves and compares the Internal Flash Memory Size and compares to constant string int bmRequest = 0x80; // IN, standard request to usb device byte bRequest = (byte) 0x06; // USB_REQ_GET_DESCRIPTOR byte wLength = (byte) 127; // max string size byte[] descriptor = new byte[wLength]; /* This method can be used to retrieve any memory location size by incrementing the wValue in the defined range. ie. Size of: Internal Flash, Option Bytes, OTP Size, and Feature location */ int wValue = 0x0304; // possible strings range from 0x304-0x307 int len = usb.controlTransfer(bmRequest, bRequest, wValue, 0, descriptor, wLength, 500); if (len < 0) { return -1; } String decoded = new String(descriptor, Charset.forName("UTF-16LE")); if (decoded.contains(mInternalFlashString)) { return mInternalFlashSize; // size of stm32f405RG } else { return -1; } } private void writeBlock(int address, byte[] block, int blockNumber) throws Exception { DfuStatus dfuStatus = new DfuStatus(); do { clearStatus(); getStatus(dfuStatus); } while (dfuStatus.bState != STATE_DFU_IDLE); if (0 == blockNumber) { setAddressPointer(address); getStatus(dfuStatus); getStatus(dfuStatus); if (dfuStatus.bState == STATE_DFU_ERROR) { throw new Exception("Start address not supported"); } } do { clearStatus(); getStatus(dfuStatus); } while (dfuStatus.bState != STATE_DFU_IDLE); download(block, (blockNumber + 2)); getStatus(dfuStatus); // to execute if (dfuStatus.bState != STATE_DFU_DOWNLOAD_BUSY) { throw new Exception("error when downloading, was not busy "); } getStatus(dfuStatus); // to verify action if (dfuStatus.bState == STATE_DFU_ERROR) { throw new Exception("error when downloading, did not perform action"); } while (dfuStatus.bState != STATE_DFU_IDLE) { clearStatus(); getStatus(dfuStatus); } } private void detach(int Address) throws Exception { DfuStatus dfuStatus = new DfuStatus(); getStatus(dfuStatus); while (dfuStatus.bState != STATE_DFU_IDLE) { clearStatus(); getStatus(dfuStatus); } // Set the command pointer to the new application base address setAddressPointer(Address); getStatus(dfuStatus); while (dfuStatus.bState != STATE_DFU_IDLE) { clearStatus(); getStatus(dfuStatus); } // Issue the DFU detach command leaveDfu(); try { getStatus(dfuStatus); clearStatus(); getStatus(dfuStatus); } catch (Exception e) { // if caught, ignore since device might have disconnected already } } private boolean isDeviceProtected() throws Exception { DfuStatus dfuStatus = new DfuStatus(); boolean isProtected = false; do { clearStatus(); getStatus(dfuStatus); } while (dfuStatus.bState != STATE_DFU_IDLE); setAddressPointer(mInternalFlashStartAddress); getStatus(dfuStatus); // to execute getStatus(dfuStatus); // to verify if (dfuStatus.bState == STATE_DFU_ERROR) { isProtected = true; } while (dfuStatus.bState != STATE_DFU_IDLE) { clearStatus(); getStatus(dfuStatus); } return isProtected; } public void writeOptionBytes(int options) throws Exception { DfuStatus dfuStatus = new DfuStatus(); do { clearStatus(); getStatus(dfuStatus); } while (dfuStatus.bState != STATE_DFU_IDLE); setAddressPointer(mOptionByteStartAddress); getStatus(dfuStatus); getStatus(dfuStatus); if (dfuStatus.bState == STATE_DFU_ERROR) { throw new Exception("Option Byte Start address not supported"); } Log.i(TAG, "writing options: 0x" + Integer.toHexString(options)); byte[] buffer = new byte[2]; buffer[0] = (byte) (options & 0xFF); buffer[1] = (byte) ((options >> 8) & 0xFF); download(buffer); getStatus(dfuStatus); // device will reset } private void massEraseCommand() throws Exception { byte[] buffer = new byte[1]; buffer[0] = 0x41; download(buffer); } private void unProtectCommand() throws Exception { byte[] buffer = new byte[1]; buffer[0] = (byte) 0x92; download(buffer); } private void setAddressPointer(int Address) throws Exception { byte[] buffer = new byte[5]; buffer[0] = 0x21; buffer[1] = (byte) (Address & 0xFF); buffer[2] = (byte) ((Address >> 8) & 0xFF); buffer[3] = (byte) ((Address >> 16) & 0xFF); buffer[4] = (byte) ((Address >> 24) & 0xFF); download(buffer); } private void leaveDfu() throws Exception { download(null); } private void getStatus(DfuStatus status) throws Exception { byte[] buffer = new byte[6]; int length = usb.controlTransfer(DFU_RequestType | USB_DIR_IN, DFU_GETSTATUS, 0, 0, buffer, 6, 500); if (length < 0) { throw new Exception("USB Failed during getStatus"); } status.bStatus = buffer[0]; // state during request status.bState = buffer[4]; // state after request status.bwPollTimeout = (buffer[3] & 0xFF) << 16; status.bwPollTimeout |= (buffer[2] & 0xFF) << 8; status.bwPollTimeout |= (buffer[1] & 0xFF); } private void clearStatus() throws Exception { int length = usb.controlTransfer(DFU_RequestType, DFU_CLRSTATUS, 0, 0, null, 0, 0); if (length < 0) { throw new Exception("USB Failed during clearStatus"); } } // use for commands private void download(byte[] data) throws Exception { int len = usb.controlTransfer(DFU_RequestType, DFU_DNLOAD, 0, 0, data, data.length, 50); if (len < 0) { throw new Exception("USB Failed during command download"); } } // use for firmware download private void download(byte[] data, int nBlock) throws Exception { int len = usb.controlTransfer(DFU_RequestType, DFU_DNLOAD, nBlock, 0, data, data.length, 0); if (len < 0) { throw new Exception("USB failed during firmware download"); } } private void upload(byte[] data, int length, int blockNum) throws Exception { int len = usb.controlTransfer(DFU_RequestType | USB_DIR_IN, DFU_UPLOAD, blockNum, 0, data, length, 100); if (len < 0) { throw new Exception("USB comm failed during upload"); } } private static int calculateCRC(byte[] FileData) { int crc = -1; for (int i = 0; i < FileData.length - 4; i++) { crc = CRC_TABLE[(crc ^ FileData[i]) & 0xff] ^ (crc >>> 8); } return crc; } // stores the result of a GetStatus DFU request private class DfuStatus { byte bStatus; // state during request int bwPollTimeout; // minimum time in ms before next getStatus call should be made byte bState; // state after request } // holds all essential information for the Dfu File private class DfuFile { String filePath; byte[] file; int PID; int VID; int BootVersion; int maxBlockSize = 1024; int elementStartAddress; int elementLength; String TargetName; int TargetSize; int NumElements; } private final static int[] CRC_TABLE = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; }