mirror of https://github.com/rusefi/dfu_java.git
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
0965158aa1
|
@ -0,0 +1,12 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: rusefi
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
@ -0,0 +1,29 @@
|
|||
name: Java DfuSe
|
||||
|
||||
on: [push,pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '8'
|
||||
|
||||
- name: Test Compiler
|
||||
run: javac -version
|
||||
|
||||
- name: Build
|
||||
working-directory: ./
|
||||
run: bash ./gradlew build
|
||||
|
||||
- name: Upload JAR
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dfu_java
|
||||
path: ./build/libs/dfu_java.jar
|
||||
|
|
@ -17,4 +17,6 @@ Zadig is a Windows application that installs generic USB drivers, such as WinUSB
|
|||
|
||||
Uses precompiled https://github.com/j123b567/java-intelhex-parser
|
||||
|
||||
This implementation would not happen without http://dfu-util.sourceforge.net/dfuse.html and https://github.com/kairyu/flop
|
||||
This implementation would not happen without http://dfu-util.sourceforge.net/dfuse.html and https://github.com/kairyu/flop
|
||||
|
||||
https://github.com/UmbrelaSmart/android-stm32-dfu-programmer is also nice
|
|
@ -12,8 +12,9 @@ dependencies {
|
|||
// windows/linux/darwin
|
||||
compile group: 'org.usb4java', name: 'usb4java', version: '1.3.0'
|
||||
// todo: someone one day should move android into sub-project
|
||||
compile group: 'com.google.android', name: 'android', version: '4.0.1.2'
|
||||
implementation files('lib/android.jar')
|
||||
|
||||
|
||||
testImplementation group: 'junit', name: 'junit', version: '4.13'
|
||||
testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
mvn central has android jars up to version 4 (year 2015) I was not able to find a source for newer API jars. We need Android 5 here.
|
|
@ -0,0 +1,81 @@
|
|||
1 configuration(s)
|
||||
Config: Configuration Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 2
|
||||
wTotalLength 54
|
||||
bNumInterfaces 1
|
||||
bConfigurationValue 1
|
||||
iConfiguration 0
|
||||
bmAttributes 0xc0
|
||||
Self Powered
|
||||
bMaxPower 100mA
|
||||
extralen 0
|
||||
extra:
|
||||
|
||||
Interface:
|
||||
numAltsetting 4
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 0
|
||||
bInterfaceClass 254 Application
|
||||
bInterfaceSubClass 1
|
||||
bInterfaceProtocol 2
|
||||
iInterface 4
|
||||
extralen 0
|
||||
extra:
|
||||
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 1
|
||||
bNumEndpoints 0
|
||||
bInterfaceClass 254 Application
|
||||
bInterfaceSubClass 1
|
||||
bInterfaceProtocol 2
|
||||
iInterface 5
|
||||
extralen 0
|
||||
extra:
|
||||
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 2
|
||||
bNumEndpoints 0
|
||||
bInterfaceClass 254 Application
|
||||
bInterfaceSubClass 1
|
||||
bInterfaceProtocol 2
|
||||
iInterface 6
|
||||
extralen 0
|
||||
extra:
|
||||
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 3
|
||||
bNumEndpoints 0
|
||||
bInterfaceClass 254 Application
|
||||
bInterfaceSubClass 1
|
||||
bInterfaceProtocol 2
|
||||
iInterface 7
|
||||
extralen 9
|
||||
extra:
|
||||
09 21 0b ff 00 00 08 1a 01
|
||||
1 interface(s)
|
||||
Interface #0 setting #0:
|
||||
Setting 0: 0 4 class fe, subclass 1, protocol: 2
|
||||
Descriptor @Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg
|
||||
Interface #0 setting #1:
|
||||
Setting 1: 0 5 class fe, subclass 1, protocol: 2
|
||||
Descriptor @Option Bytes /0x1FFFC000/01*016 e
|
||||
Interface #0 setting #2:
|
||||
Setting 2: 0 6 class fe, subclass 1, protocol: 2
|
||||
Descriptor @OTP Memory /0x1FFF7800/01*512 e,01*016 e
|
||||
Interface #0 setting #3:
|
||||
Setting 3: 0 7 class fe, subclass 1, protocol: 2
|
||||
Descriptor @Device Feature/0xFFFF0000/01*004 e
|
|
@ -0,0 +1,9 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
public interface BinaryImage {
|
||||
byte[] getImage();
|
||||
|
||||
default int getImageSize() {
|
||||
return getImage().length;
|
||||
}
|
||||
}
|
|
@ -1,33 +1,11 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
import com.rusefi.dfu.commands.DfuCommandGetStatus;
|
||||
import com.rusefi.dfu.usb4java.USBDfuConnection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface DfuConnection {
|
||||
int SECOND = 1000;
|
||||
int DFU_TIMEOUT = 10 * DfuConnection.SECOND;
|
||||
|
||||
static void waitStatus(USBDfuConnection device) {
|
||||
DfuCommandGetStatus.State state = DfuCommandGetStatus.read(device);
|
||||
System.out.println(" state " + state);
|
||||
while (state == DfuCommandGetStatus.State.DFU_DOWNLOAD_BUSY || state == DfuCommandGetStatus.State.DFU_ERROR) {
|
||||
sleep(106);
|
||||
state = DfuCommandGetStatus.read(device);
|
||||
System.out.println(" state " + state);
|
||||
}
|
||||
}
|
||||
|
||||
static void sleep(int millis) {
|
||||
System.out.println("Sleep " + millis);
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
FlashRange getFlashRange();
|
||||
|
||||
int getTransferSize();
|
||||
|
@ -35,4 +13,6 @@ public interface DfuConnection {
|
|||
int receiveData(DfuCommmand command, short wValue, ByteBuffer data);
|
||||
|
||||
int sendData(DfuCommmand command, short wValue, ByteBuffer data);
|
||||
|
||||
ByteBuffer allocateBuffer(int capacity);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
import com.rusefi.dfu.commands.DfuCommandGetStatus;
|
||||
|
||||
public class DfuConnectionUtil {
|
||||
public static void waitStatus(DfuLogic.Logger logger, DfuConnection device) {
|
||||
DfuCommandGetStatus.DeviceStatus state = DfuCommandGetStatus.read(logger, device);
|
||||
logger.info("First state " + state);
|
||||
long enter = System.currentTimeMillis();
|
||||
while (state.getState() == DfuCommandGetStatus.State.DFU_DOWNLOAD_BUSY || state.getState() == DfuCommandGetStatus.State.DFU_ERROR) {
|
||||
state = DfuCommandGetStatus.read(logger, device);
|
||||
logger.info("Loop state " + state);
|
||||
if (System.currentTimeMillis() - enter > 10 * DfuConnection.SECOND)
|
||||
throw new IllegalStateException("State does not look good");
|
||||
}
|
||||
}
|
||||
|
||||
public static void sleep(DfuLogic.Logger logger, int millis) {
|
||||
if (millis == 0)
|
||||
return;
|
||||
logger.info("Sleep " + millis);
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Inspired by https://github.com/UmbrelaSmart/android-stm32-dfu-programmer
|
||||
*/
|
||||
public class DfuImage implements BinaryImage {
|
||||
// private final static int ELEMENT1_OFFSET = 293; // constant offset in file array where image data starts
|
||||
private final static int TARGET_NAME_START = 22;
|
||||
private final static int TARGET_NAME_MAX_END = 276;
|
||||
private final static int TARGET_SIZE = 277;
|
||||
private final static int TARGET_NUM_ELEMENTS = 281;
|
||||
|
||||
private byte[] content;
|
||||
private int PID;
|
||||
private int VID;
|
||||
|
||||
private int elementStartAddress;
|
||||
private int elementLength;
|
||||
|
||||
private String TargetName;
|
||||
private int TargetSize;
|
||||
private int NumElements;
|
||||
|
||||
public DfuImage read(String fileName) {
|
||||
File file = new File(fileName);
|
||||
this.content = new byte[(int) file.length()];
|
||||
FileInputStream fileInputStream;
|
||||
try {
|
||||
fileInputStream = new FileInputStream(fileName);
|
||||
fileInputStream.read(this.content);
|
||||
fileInputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
verifyFile();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getImage() {
|
||||
return content;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void verifyFile() {
|
||||
|
||||
// todo for now i expect the file to be not corrupted
|
||||
|
||||
int length = this.content.length;
|
||||
|
||||
int crcIndex = length - 4;
|
||||
int crc = 0;
|
||||
crc |= this.content[crcIndex++] & 0xFF;
|
||||
crc |= (this.content[crcIndex++] & 0xFF) << 8;
|
||||
crc |= (this.content[crcIndex++] & 0xFF) << 16;
|
||||
crc |= (this.content[crcIndex] & 0xFF) << 24;
|
||||
// do crc check
|
||||
if (crc != calculateCRC(this.content)) {
|
||||
throw new IllegalArgumentException("CRC Failed");
|
||||
}
|
||||
|
||||
// Check the prefix
|
||||
String prefix = new String(this.content, 0, 5);
|
||||
if (prefix.compareTo("DfuSe") != 0) {
|
||||
throw new IllegalArgumentException("File signature error");
|
||||
}
|
||||
|
||||
// check dfuSe Version
|
||||
if (this.content[5] != 1) {
|
||||
throw new IllegalArgumentException("DFU file version must be 1");
|
||||
}
|
||||
|
||||
// Check the suffix
|
||||
String suffix = new String(this.content, length - 8, 3);
|
||||
if (suffix.compareTo("UFD") != 0) {
|
||||
throw new IllegalArgumentException("File suffix error");
|
||||
}
|
||||
if ((this.content[length - 5] != 16) || (this.content[length - 10] != 0x1A) || (this.content[length - 9] != 0x01)) {
|
||||
throw new IllegalArgumentException("File number error");
|
||||
}
|
||||
|
||||
// Now check the target prefix, we assume there is only one target in the file
|
||||
String target = new String(this.content, 11, 6);
|
||||
if (target.compareTo("Target") != 0) {
|
||||
throw new IllegalArgumentException("Target signature error");
|
||||
}
|
||||
|
||||
if (0 != this.content[TARGET_NAME_START]) {
|
||||
String tempName = new String(this.content, TARGET_NAME_START, TARGET_NAME_MAX_END);
|
||||
int foundNullAt = tempName.indexOf(0);
|
||||
this.TargetName = tempName.substring(0, foundNullAt);
|
||||
} else {
|
||||
throw new IllegalArgumentException("No Target Name Exist in File");
|
||||
}
|
||||
System.out.println("Firmware Target Name: " + this.TargetName);
|
||||
|
||||
this.TargetSize = this.content[TARGET_SIZE] & 0xFF;
|
||||
this.TargetSize |= (this.content[TARGET_SIZE + 1] & 0xFF) << 8;
|
||||
this.TargetSize |= (this.content[TARGET_SIZE + 2] & 0xFF) << 16;
|
||||
this.TargetSize |= (this.content[TARGET_SIZE + 3] & 0xFF) << 24;
|
||||
|
||||
System.out.println("Firmware Target Size: " + this.TargetSize);
|
||||
|
||||
this.NumElements = this.content[TARGET_NUM_ELEMENTS] & 0xFF;
|
||||
this.NumElements |= (this.content[TARGET_NUM_ELEMENTS + 1] & 0xFF) << 8;
|
||||
this.NumElements |= (this.content[TARGET_NUM_ELEMENTS + 2] & 0xFF) << 16;
|
||||
this.NumElements |= (this.content[TARGET_NUM_ELEMENTS + 3] & 0xFF) << 24;
|
||||
|
||||
System.out.println("Firmware Num of Elements: " + this.NumElements);
|
||||
|
||||
if (this.NumElements > 1) {
|
||||
/* 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
|
||||
*/
|
||||
throw new IllegalArgumentException("Do not support multiple Elements inside Image");
|
||||
}
|
||||
|
||||
// Get Element Flash start address and size
|
||||
elementStartAddress = this.content[285] & 0xFF;
|
||||
elementStartAddress |= (this.content[286] & 0xFF) << 8;
|
||||
elementStartAddress |= (this.content[287] & 0xFF) << 16;
|
||||
elementStartAddress |= (this.content[288] & 0xFF) << 24;
|
||||
|
||||
this.elementLength = this.content[289] & 0xFF;
|
||||
this.elementLength |= (this.content[290] & 0xFF) << 8;
|
||||
this.elementLength |= (this.content[291] & 0xFF) << 16;
|
||||
this.elementLength |= (this.content[292] & 0xFF) << 24;
|
||||
|
||||
if (this.elementLength < 512) {
|
||||
throw new IllegalArgumentException("Element Size is too small");
|
||||
}
|
||||
|
||||
System.out.println(String.format("%x %x", elementStartAddress, elementLength));
|
||||
|
||||
// Get VID, PID and version number
|
||||
this.VID = (this.content[length - 11] & 0xFF) << 8;
|
||||
this.VID |= (this.content[length - 12] & 0xFF);
|
||||
this.PID = (this.content[length - 13] & 0xFF) << 8;
|
||||
this.PID |= (this.content[length - 14] & 0xFF);
|
||||
int bootVersion = (this.content[length - 15] & 0xFF) << 8;
|
||||
bootVersion |= (this.content[length - 16] & 0xFF);
|
||||
|
||||
System.out.println(String.format("VID=%x PID=%x", VID, PID));
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
}
|
|
@ -1,38 +1,93 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
import com.rusefi.dfu.commands.DfuSeCommandErasePage;
|
||||
import com.rusefi.dfu.commands.DfuSeCommandSetAddress;
|
||||
import com.rusefi.dfu.usb4java.USBDfuConnection;
|
||||
import com.rusefi.dfu.commands.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
public class DfuLogic {
|
||||
public static final short ST_VENDOR = 0x0483;
|
||||
public static final short ST_DFU_PRODUCT = (short) 0xdf11;
|
||||
public static final byte USB_CLASS_APP_SPECIFIC = (byte) 0xfe;
|
||||
public static final int ST_DFU_PRODUCT = 0xdf11;
|
||||
public static final int USB_CLASS_APP_SPECIFIC = 0xfe;
|
||||
public static final byte DFU_SUBCLASS = 0x01;
|
||||
public static final byte USB_DT_DFU = 0x21;
|
||||
public static final String FLASH_TAG = "Flash";
|
||||
|
||||
static void uploadImage(USBDfuConnection device, HexImage image) {
|
||||
// todo: smarter erase handling!
|
||||
DfuSeCommandErasePage.execute(device, 0x08000000);
|
||||
DfuSeCommandErasePage.execute(device, 0x08004000);
|
||||
DfuSeCommandErasePage.execute(device, 0x08008000);
|
||||
DfuSeCommandErasePage.execute(device, 0x0800C000);
|
||||
DfuSeCommandErasePage.execute(device, 0x08010000);
|
||||
DfuSeCommandErasePage.execute(device, 0x08020000);
|
||||
DfuSeCommandErasePage.execute(device, 0x08040000);
|
||||
public static void uploadImage(Logger logger, DfuConnection device, BinaryImage image, FlashRange range) {
|
||||
DfuLogic.startup(logger, device);
|
||||
actuallyUploadImage(logger, device, image, range);
|
||||
DfuLogic.leaveDFU(logger, device);
|
||||
}
|
||||
|
||||
for (int offset = 0; offset < image.getMaxOffset() - image.getRange().getBaseAddress(); offset += device.getTransferSize()) {
|
||||
DfuSeCommandSetAddress.execute(device, device.getFlashRange().getBaseAddress() + offset);
|
||||
DfuConnection.waitStatus(device);
|
||||
public static void actuallyUploadImage(Logger logger, DfuConnection device, BinaryImage image, FlashRange range) {
|
||||
long enter = System.currentTimeMillis();
|
||||
List<Integer> erasePages = range.pagesForSize(image.getImageSize());
|
||||
// todo: smarted start address logic
|
||||
int eraseAddress = 0x08000000;
|
||||
for (Integer erasePage : erasePages) {
|
||||
DfuSeCommandErasePage.execute(logger, device, eraseAddress);
|
||||
eraseAddress += erasePage;
|
||||
}
|
||||
logger.info(String.format("Erased up to %x", eraseAddress));
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(device.getTransferSize());
|
||||
buffer.put(image.getImage(), offset, device.getTransferSize());
|
||||
for (int offset = 0; offset < image.getImage().length; offset += device.getTransferSize()) {
|
||||
DfuSeCommandSetAddress.execute(logger, device, device.getFlashRange().getBaseAddress() + offset);
|
||||
DfuConnectionUtil.waitStatus(logger, device);
|
||||
|
||||
ByteBuffer buffer = device.allocateBuffer(device.getTransferSize());
|
||||
// last transfer would usually be smaller than transfer size
|
||||
int size = Math.min(device.getTransferSize(), image.getImage().length - offset);
|
||||
buffer.put(image.getImage(), offset, size);
|
||||
device.sendData(DfuCommmand.DNLOAD, DfuSeCommand.W_DNLOAD, buffer);
|
||||
// AN3156 USB DFU protocol used in the STM32 bootloader
|
||||
// "The Write memory operation is effectively executed only when a DFU_GETSTATUS request is issued by the host. "
|
||||
DfuConnection.waitStatus(device);
|
||||
DfuConnectionUtil.waitStatus(logger, device);
|
||||
}
|
||||
logger.info("Uploaded " + image.getImage().length + " bytes in " + (System.currentTimeMillis() - enter) + " ms");
|
||||
}
|
||||
|
||||
public static void leaveDFU(Logger logger, DfuConnection device) {
|
||||
logger.info("Leaving DFU");
|
||||
device.sendData(DfuCommmand.DNLOAD, DfuSeCommand.W_DNLOAD, device.allocateBuffer(0));
|
||||
// The DFU Leave operation is effectively executed only when a DFU_GETSTATUS request is
|
||||
// issued by the host.
|
||||
DfuConnectionUtil.waitStatus(logger, device);
|
||||
logger.info("DONE");
|
||||
}
|
||||
|
||||
public static void startup(Logger logger, DfuConnection device) {
|
||||
DfuCommandGetStatus.DeviceStatus state = DfuCommandGetStatus.read(logger, device);
|
||||
logger.info("DFU state: " + state);
|
||||
switch (state.getState()) {
|
||||
case DFU_IDLE:
|
||||
// best status
|
||||
logger.info("startup status " + state.getStatus());
|
||||
break;
|
||||
case DFU_ERROR:
|
||||
DfuCommandClearStatus.execute(device);
|
||||
break;
|
||||
case DFU_DOWNLOAD_SYNC:
|
||||
case DFU_DOWNLOAD_IDLE:
|
||||
case DFU_UPLOAD_IDLE:
|
||||
case DFU_MANIFEST_SYNC:
|
||||
case DFU_DOWNLOAD_BUSY:
|
||||
case DFU_MANIFEST:
|
||||
DfuCommandAbort.execute(device);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected state " + state);
|
||||
}
|
||||
state = DfuCommandGetStatus.read(logger, device);
|
||||
if (state.getState() != DfuCommandGetStatus.State.DFU_IDLE)
|
||||
throw new IllegalStateException("Not idle on start-up: " + state.getState());
|
||||
}
|
||||
|
||||
public interface Logger {
|
||||
Logger VOID = message -> {
|
||||
};
|
||||
|
||||
Logger CONSOLE = System.out::println;
|
||||
|
||||
void info(String message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ST chips send their memory layout in USB description string
|
||||
|
@ -25,14 +27,15 @@ public class DfuSeFlashDescriptor {
|
|||
int baseAddress = Integer.parseInt(baseAddressString, 16);
|
||||
System.out.printf("Base address %x\n", baseAddress);
|
||||
|
||||
int totalLength = parseRegions(topLevelSections[2].trim());
|
||||
List<Integer> pages = parseRegions(topLevelSections[2].trim());
|
||||
|
||||
return new FlashRange(baseAddress, totalLength);
|
||||
return new FlashRange(baseAddress, pages);
|
||||
}
|
||||
|
||||
private static int parseRegions(String regions) {
|
||||
private static List<Integer> parseRegions(String regions) {
|
||||
List<Integer> pages = new ArrayList<>();
|
||||
|
||||
String[] sections = regions.split(",");
|
||||
int totalSize = 0;
|
||||
for (String section : sections) {
|
||||
System.out.println("Region " + section);
|
||||
String parts[] = section.split("\\*");
|
||||
|
@ -45,8 +48,9 @@ public class DfuSeFlashDescriptor {
|
|||
|
||||
System.out.println("Count " + count + " size " + pageSize);
|
||||
|
||||
totalSize += count * 1024 * pageSize;
|
||||
for (int i = 0; i < count; i++)
|
||||
pages.add(1024 * pageSize);
|
||||
}
|
||||
return totalSize;
|
||||
return pages;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FlashRange {
|
||||
private final int baseAddress;
|
||||
private final List<Integer> pages;
|
||||
private final int totalLength;
|
||||
|
||||
public FlashRange(int baseAddress, int totalLength) {
|
||||
public FlashRange(int baseAddress, List<Integer> pages) {
|
||||
this.baseAddress = baseAddress;
|
||||
this.totalLength = totalLength;
|
||||
this.pages = pages;
|
||||
int t = 0;
|
||||
for (Integer page : pages)
|
||||
t += page;
|
||||
this.totalLength = t;
|
||||
}
|
||||
|
||||
public int getBaseAddress() {
|
||||
|
@ -17,6 +25,10 @@ public class FlashRange {
|
|||
return totalLength;
|
||||
}
|
||||
|
||||
public List<Integer> getPages() {
|
||||
return pages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FlashRange{" +
|
||||
|
@ -24,4 +36,17 @@ public class FlashRange {
|
|||
", totalLength=" + totalLength +
|
||||
'}';
|
||||
}
|
||||
|
||||
public List<Integer> pagesForSize(int size) {
|
||||
int total = 0;
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for (Integer page : pages) {
|
||||
if (total < size) {
|
||||
result.add(page);
|
||||
total += page;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,23 +6,20 @@ import cz.jaybee.intelhex.Parser;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class HexImage extends AtomicInteger {
|
||||
public class HexImage implements BinaryImage {
|
||||
private final byte[] image;
|
||||
private final FlashRange range;
|
||||
private final int totalBytes;
|
||||
private final int maxOffset;
|
||||
|
||||
public HexImage(byte[] image, FlashRange range, int totalBytes, int maxOffset) {
|
||||
public HexImage(byte[] image) {
|
||||
this.image = image;
|
||||
this.range = range;
|
||||
this.totalBytes = totalBytes;
|
||||
this.maxOffset = maxOffset;
|
||||
}
|
||||
|
||||
static HexImage loadHexToBuffer(InputStream is, FlashRange range) throws IntelHexException, IOException {
|
||||
byte[] image = new byte[range.getTotalLength()];
|
||||
static HexImage loadHexToBuffer(InputStream is, FlashRange flashRange) throws IntelHexException, IOException {
|
||||
Objects.requireNonNull(flashRange, "flashRange");
|
||||
byte[] image = new byte[flashRange.getTotalLength()];
|
||||
|
||||
// create IntelHexParserObject
|
||||
Parser ihp = new Parser(is);
|
||||
|
@ -39,12 +36,12 @@ public class HexImage extends AtomicInteger {
|
|||
|
||||
maxOffset.set((int) Math.max(maxOffset.get(), address + data.length));
|
||||
|
||||
if (address < range.getBaseAddress() || address + data.length > range.getBaseAddress() + range.getTotalLength())
|
||||
throw new IllegalStateException(String.format("Image data out of range: %x@%x not withiin %s",
|
||||
if (address < flashRange.getBaseAddress() || address + data.length > flashRange.getBaseAddress() + flashRange.getTotalLength())
|
||||
throw new IllegalStateException(String.format("Image data out of range: %x@%x not within %s",
|
||||
data.length,
|
||||
address,
|
||||
range.toString()));
|
||||
System.arraycopy(data, 0, image, (int) (address - range.getBaseAddress()), data.length);
|
||||
flashRange.toString()));
|
||||
System.arraycopy(data, 0, image, (int) (address - flashRange.getBaseAddress()), data.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,32 +51,19 @@ public class HexImage extends AtomicInteger {
|
|||
});
|
||||
ihp.parse();
|
||||
|
||||
return new HexImage(image, range, totalBytesReceived.get(), maxOffset.get());
|
||||
int imageSize = maxOffset.get() - flashRange.getBaseAddress();
|
||||
return new HexImage(Arrays.copyOfRange(image, 0, imageSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public int getTotalBytes() {
|
||||
return totalBytes;
|
||||
}
|
||||
|
||||
public int getMaxOffset() {
|
||||
return maxOffset;
|
||||
}
|
||||
|
||||
public FlashRange getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HexImage{" +
|
||||
"image=" + image.length +
|
||||
", range=" + range +
|
||||
", totalBytes=" + totalBytes +
|
||||
", maxOffset=" + maxOffset +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,22 @@ import com.rusefi.dfu.FlashRange;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static android.hardware.usb.UsbConstants.USB_DIR_IN;
|
||||
import static android.hardware.usb.UsbConstants.USB_DIR_OUT;
|
||||
|
||||
public class AndroidDfuConnection implements DfuConnection {
|
||||
private final UsbDeviceConnection usbDeviceConnection;
|
||||
private final int interfaceNumber;
|
||||
private final int transferSize;
|
||||
private final FlashRange flashRange;
|
||||
|
||||
public AndroidDfuConnection(UsbDeviceConnection usbDeviceConnection, FlashRange flashRange) {
|
||||
private static final byte REQUEST_TYPE_CLASS = 32;
|
||||
private static final byte RECIPIENT_INTERFACE = 0x01;
|
||||
|
||||
public AndroidDfuConnection(UsbDeviceConnection usbDeviceConnection, int interfaceNumber, int transferSize, FlashRange flashRange) {
|
||||
this.usbDeviceConnection = usbDeviceConnection;
|
||||
this.interfaceNumber = interfaceNumber;
|
||||
this.transferSize = transferSize;
|
||||
this.flashRange = flashRange;
|
||||
}
|
||||
|
||||
|
@ -23,17 +33,29 @@ public class AndroidDfuConnection implements DfuConnection {
|
|||
|
||||
@Override
|
||||
public int getTransferSize() {
|
||||
return 0;
|
||||
return transferSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int receiveData(DfuCommmand command, short wValue, ByteBuffer data) {
|
||||
//return usbDeviceConnection.controlTransfer();
|
||||
return 0;
|
||||
return transfer(usbDeviceConnection, USB_DIR_IN, command.getValue(), wValue, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendData(DfuCommmand command, short wValue, ByteBuffer data) {
|
||||
return 0;
|
||||
return transfer(usbDeviceConnection, USB_DIR_OUT, command.getValue(), wValue, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer allocateBuffer(int capacity) {
|
||||
// on Android direct buffer comes with arrayOffset which is just too much trouble
|
||||
return ByteBuffer.allocate(capacity);
|
||||
}
|
||||
|
||||
private int transfer(UsbDeviceConnection connection, int direction, int request, short wValue, ByteBuffer byteBuffer) {
|
||||
if (!byteBuffer.hasArray() || byteBuffer.arrayOffset() != 0)
|
||||
throw new IllegalArgumentException("Need a simpler ByteArray");
|
||||
return connection.controlTransfer(REQUEST_TYPE_CLASS | RECIPIENT_INTERFACE | direction, request,
|
||||
wValue, interfaceNumber, byteBuffer.array(), byteBuffer.limit(), DFU_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package com.rusefi.dfu.android;
|
||||
|
||||
import android.hardware.usb.*;
|
||||
|
||||
import com.rusefi.dfu.DfuLogic;
|
||||
import com.rusefi.dfu.LogUtil;
|
||||
import org.apache.commons.logging.Log;
|
||||
import com.rusefi.dfu.DfuSeFlashDescriptor;
|
||||
import com.rusefi.dfu.FlashRange;
|
||||
|
||||
public class DfuDeviceLocator {
|
||||
private static final Log log = LogUtil.getLog(DfuDeviceLocator.class);
|
||||
// private static final Log log = LogUtil.getLog(DfuDeviceLocator.class);
|
||||
|
||||
private static UsbDevice findDevice(UsbManager usbManager) {
|
||||
public static UsbDevice findDevice(UsbManager usbManager) {
|
||||
for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
|
||||
if (usbDevice.getVendorId() == DfuLogic.ST_VENDOR && usbDevice.getProductId() == DfuLogic.ST_DFU_PRODUCT) {
|
||||
return usbDevice;
|
||||
|
@ -17,32 +18,63 @@ public class DfuDeviceLocator {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
public short xxx(UsbManager usbManager) {
|
||||
UsbDevice dfuDevice = findDevice(usbManager);
|
||||
|
||||
for (int interfaceIndex = 0; interfaceIndex < dfuDevice.getInterfaceCount(); interfaceIndex++) {
|
||||
UsbInterface usbInterface = dfuDevice.getInterface(interfaceIndex);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public Result openDfu(UsbManager usbManager, UsbDevice dfuDevice) {
|
||||
for (int interfaceIndex = 0; interfaceIndex < dfuDevice.getInterfaceCount(); interfaceIndex++) {
|
||||
UsbInterface usbInterface = dfuDevice.getInterface(interfaceIndex);
|
||||
String stringDescriptor = usbInterface.getName();
|
||||
if (usbInterface.getInterfaceClass() == DfuLogic.USB_CLASS_APP_SPECIFIC &&
|
||||
usbInterface.getInterfaceSubclass() == DfuLogic.DFU_SUBCLASS) {
|
||||
log.debug(String.format("Found DFU interface: " + usbInterface));
|
||||
usbInterface.getInterfaceSubclass() == DfuLogic.DFU_SUBCLASS &&
|
||||
stringDescriptor.contains(DfuLogic.FLASH_TAG)) {
|
||||
// log.debug(String.format("Found DFU interface: " + usbInterface));
|
||||
|
||||
FlashRange flashRange = DfuSeFlashDescriptor.parse(stringDescriptor);
|
||||
|
||||
UsbDeviceConnection connection = usbManager.openDevice(dfuDevice);
|
||||
|
||||
//UsbInterface intf = dfuDevice.getInterface(0);
|
||||
connection.claimInterface(usbInterface, true);
|
||||
|
||||
byte[] rawDescs = connection.getRawDescriptors();
|
||||
// todo: need proper handling of this rawDesc area since I have no clue what's the format
|
||||
if (rawDescs.length != 72)
|
||||
throw new IllegalStateException("Unexpected RawDescriptors length");
|
||||
if (rawDescs[64] != DfuLogic.USB_DT_DFU)
|
||||
throw new IllegalStateException("Unexpected USB_DT_DFU");
|
||||
int transferSize = rawDescs[69] * 256 + rawDescs[68];
|
||||
|
||||
return new Result(connection, interfaceIndex, flashRange, transferSize);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
private final UsbDeviceConnection connection;
|
||||
private final int interfaceIndex;
|
||||
private final FlashRange flashRange;
|
||||
private final int transferSize;
|
||||
|
||||
public Result(UsbDeviceConnection connection, int interfaceIndex, FlashRange flashRange, int transferSize) {
|
||||
this.connection = connection;
|
||||
this.interfaceIndex = interfaceIndex;
|
||||
this.flashRange = flashRange;
|
||||
this.transferSize = transferSize;
|
||||
}
|
||||
|
||||
public UsbDeviceConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public FlashRange getFlashRange() {
|
||||
return flashRange;
|
||||
}
|
||||
|
||||
public int getInterfaceIndex() {
|
||||
return interfaceIndex;
|
||||
}
|
||||
|
||||
public int getTransferSize() {
|
||||
return transferSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package com.rusefi.dfu.commands;
|
||||
|
||||
import com.rusefi.dfu.DfuCommmand;
|
||||
import com.rusefi.dfu.usb4java.USBDfuConnection;
|
||||
import com.rusefi.dfu.DfuConnection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class DfuCommandAbort {
|
||||
public static void execute(USBDfuConnection session) {
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(0);
|
||||
session.sendData(DfuCommmand.ABORT, (short) 0, buffer);
|
||||
public static void execute(DfuConnection connection) {
|
||||
ByteBuffer buffer = connection.allocateBuffer(0);
|
||||
connection.sendData(DfuCommmand.ABORT, (short) 0, buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package com.rusefi.dfu.commands;
|
||||
|
||||
import com.rusefi.dfu.DfuCommmand;
|
||||
import com.rusefi.dfu.usb4java.USBDfuConnection;
|
||||
import com.rusefi.dfu.DfuConnection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class DfuCommandClearStatus {
|
||||
public static void execute(USBDfuConnection session) {
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(0);
|
||||
public static void execute(DfuConnection session) {
|
||||
ByteBuffer buffer = session.allocateBuffer(0);
|
||||
session.sendData(DfuCommmand.CLRSTATUS, (short) 0, buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,67 @@
|
|||
package com.rusefi.dfu.commands;
|
||||
|
||||
import com.rusefi.dfu.DfuCommmand;
|
||||
import com.rusefi.dfu.usb4java.USBDfuConnection;
|
||||
import com.rusefi.dfu.DfuConnection;
|
||||
import com.rusefi.dfu.DfuConnectionUtil;
|
||||
import com.rusefi.dfu.DfuLogic;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class DfuCommandGetStatus {
|
||||
private static final int PACKET_SIZE = 6;
|
||||
|
||||
public static State read(USBDfuConnection session) {
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(PACKET_SIZE);
|
||||
int count = session.receiveData(DfuCommmand.GETSTATUS, (short) 0, buffer);
|
||||
if (count == 0)
|
||||
return State.DFU_ERROR;
|
||||
private final static AtomicInteger STATUS_COUNTER = new AtomicInteger();
|
||||
|
||||
public static DeviceStatus read(DfuLogic.Logger logger, DfuConnection connection) {
|
||||
ByteBuffer buffer = connection.allocateBuffer(PACKET_SIZE);
|
||||
int count = connection.receiveData(DfuCommmand.GETSTATUS, (short) 0, buffer);
|
||||
if (count != PACKET_SIZE)
|
||||
throw new IllegalStateException("Got " + count);
|
||||
return new DeviceStatus(null, State.DFU_ERROR);
|
||||
buffer.rewind();
|
||||
buffer.get(); // status
|
||||
buffer.get(); // timeout
|
||||
buffer.get(); // timeout
|
||||
buffer.get(); // timeout
|
||||
byte state = buffer.get();
|
||||
return State.valueOf(state);
|
||||
Status status = Status.valueOf(buffer.get()); // status
|
||||
int timeout = buffer.get();
|
||||
timeout = timeout | (buffer.get() << 8);
|
||||
timeout = timeout | (buffer.get() << 8);
|
||||
byte stateCode = buffer.get();
|
||||
DfuConnectionUtil.sleep(logger, timeout);
|
||||
State state = State.valueOf(stateCode);
|
||||
logger.info(STATUS_COUNTER.incrementAndGet() + " GETSTATUS " + status + " timeout=" + timeout + " " + state);
|
||||
return new DeviceStatus(status, state);
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
OK(0x00),
|
||||
ERROR_TARGET(0x01),
|
||||
ERROR_FILE(0x02),
|
||||
ERROR_WRITE(0x03),
|
||||
ERROR_ERASE(0x04),
|
||||
ERROR_CHECK_ERASED(0x05),
|
||||
ERROR_PROG(0x06),
|
||||
ERROR_VERIFY(0x07),
|
||||
ERROR_ADDRESS(0x08),
|
||||
ERROR_NOTDONE(0x09),
|
||||
ERROR_FIRMWARE(0x0a),
|
||||
ERROR_VENDOR(0x0b),
|
||||
ERROR_USBR(0x0c),
|
||||
ERROR_POR(0x0d),
|
||||
ERROR_UNKNOWN(0x0e),
|
||||
ERROR_STALLEDPKT(0x0f);
|
||||
|
||||
private final byte value;
|
||||
|
||||
Status(int value) {
|
||||
this.value = (byte) value;
|
||||
}
|
||||
|
||||
public static Status valueOf(byte value) {
|
||||
for (Status s : Status.values()) {
|
||||
if (s.value == value) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum State {
|
||||
|
@ -52,4 +92,30 @@ public class DfuCommandGetStatus {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeviceStatus {
|
||||
private final Status status;
|
||||
private final State state;
|
||||
|
||||
public DeviceStatus(Status status, State state) {
|
||||
this.status = status;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DeviceStatus{" +
|
||||
"status=" + status +
|
||||
", state=" + state +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
package com.rusefi.dfu.commands;
|
||||
|
||||
import com.rusefi.dfu.DfuCommmand;
|
||||
import com.rusefi.dfu.DfuConnection;
|
||||
import com.rusefi.dfu.DfuSeCommand;
|
||||
import com.rusefi.dfu.LogUtil;
|
||||
import com.rusefi.dfu.usb4java.USBDfuConnection;
|
||||
import org.apache.commons.logging.Log;
|
||||
import com.rusefi.dfu.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class DfuSeCommandErasePage {
|
||||
private static final Log log = LogUtil.getLog(DfuSeCommandErasePage.class);
|
||||
|
||||
public static void execute(USBDfuConnection session, int address) {
|
||||
log.info(String.format("SetAddress %x", address));
|
||||
ByteBuffer buffer = DfuSeCommandSetAddress.createSpecialCommandBuffer(DfuSeCommand.SE_ERASE_PAGE, address);
|
||||
session.sendData(DfuCommmand.DNLOAD, DfuSeCommand.W_SPECIAL, buffer);
|
||||
DfuConnection.waitStatus(session);
|
||||
public static void execute(DfuLogic.Logger logger, DfuConnection connection, int address) {
|
||||
logger.info(String.format("SetAddress %x", address));
|
||||
ByteBuffer buffer = DfuSeCommandSetAddress.createSpecialCommandBuffer(connection, DfuSeCommand.SE_ERASE_PAGE, address);
|
||||
connection.sendData(DfuCommmand.DNLOAD, DfuSeCommand.W_SPECIAL, buffer);
|
||||
DfuConnectionUtil.waitStatus(logger, connection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
package com.rusefi.dfu.commands;
|
||||
|
||||
import com.rusefi.dfu.DfuCommmand;
|
||||
import com.rusefi.dfu.DfuConnection;
|
||||
import com.rusefi.dfu.DfuLogic;
|
||||
import com.rusefi.dfu.DfuSeCommand;
|
||||
import com.rusefi.dfu.LogUtil;
|
||||
import com.rusefi.dfu.usb4java.USBDfuConnection;
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class DfuSeCommandSetAddress {
|
||||
private static final Log log = LogUtil.getLog(DfuSeCommandSetAddress.class);
|
||||
|
||||
public static void execute(USBDfuConnection session, int address) {
|
||||
log.info(String.format("SetAddress %x", address));
|
||||
ByteBuffer buffer = createSpecialCommandBuffer(DfuSeCommand.SE_SET_ADDRESS, address);
|
||||
session.sendData(DfuCommmand.DNLOAD, DfuSeCommand.W_SPECIAL, buffer);
|
||||
public static void execute(DfuLogic.Logger logger, DfuConnection connection, int address) {
|
||||
logger.info(String.format("SetAddress %x", address));
|
||||
ByteBuffer buffer = createSpecialCommandBuffer(connection, DfuSeCommand.SE_SET_ADDRESS, address);
|
||||
connection.sendData(DfuCommmand.DNLOAD, DfuSeCommand.W_SPECIAL, buffer);
|
||||
}
|
||||
|
||||
protected static ByteBuffer createSpecialCommandBuffer(byte command, int address) {
|
||||
ByteBuffer buffer = createBuffer(5);
|
||||
protected static ByteBuffer createSpecialCommandBuffer(DfuConnection connection, byte command, int address) {
|
||||
ByteBuffer buffer = createBuffer(connection, 5);
|
||||
buffer.put(command);
|
||||
// buffer.rewind();
|
||||
// byte[] t = new byte[4];
|
||||
|
@ -28,7 +25,7 @@ public class DfuSeCommandSetAddress {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
protected static ByteBuffer createBuffer(int capacity) {
|
||||
return ByteBuffer.allocateDirect(capacity).order(ByteOrder.LITTLE_ENDIAN);
|
||||
protected static ByteBuffer createBuffer(DfuConnection connection, int capacity) {
|
||||
return connection.allocateBuffer(capacity).order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
package com.rusefi.dfu.usb4java;
|
||||
|
||||
import com.rusefi.dfu.DfuLogic;
|
||||
import com.rusefi.dfu.DfuSeFlashDescriptor;
|
||||
import com.rusefi.dfu.FlashRange;
|
||||
import com.rusefi.dfu.LogUtil;
|
||||
import com.rusefi.dfu.commands.DfuCommandAbort;
|
||||
import com.rusefi.dfu.commands.DfuCommandClearStatus;
|
||||
import com.rusefi.dfu.commands.DfuCommandGetStatus;
|
||||
import com.rusefi.dfu.*;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.usb4java.*;
|
||||
|
||||
|
@ -16,6 +10,8 @@ public class DfuDeviceLocator {
|
|||
|
||||
private static final Log log = LogUtil.getLog(DfuDeviceLocator.class);
|
||||
|
||||
private static StringBuilder usbInfo = new StringBuilder();
|
||||
|
||||
public static Context openContext() {
|
||||
Context context = new Context();
|
||||
int result = LibUsb.init(context);
|
||||
|
@ -25,11 +21,11 @@ public class DfuDeviceLocator {
|
|||
return context;
|
||||
}
|
||||
|
||||
public static USBDfuConnection findDevice() {
|
||||
return findDevice(openContext(), DfuLogic.ST_VENDOR, DfuLogic.ST_DFU_PRODUCT);
|
||||
public static USBDfuConnection findDevice(DfuLogic.Logger logger) {
|
||||
return findDevice(logger, openContext(), DfuLogic.ST_VENDOR, (short) DfuLogic.ST_DFU_PRODUCT);
|
||||
}
|
||||
|
||||
private static USBDfuConnection findDevice(Context context, short vendorId, short productId) {
|
||||
private static USBDfuConnection findDevice(DfuLogic.Logger logger, Context context, short vendorId, short productId) {
|
||||
// Read the USB device list
|
||||
DeviceList list = new DeviceList();
|
||||
int result = LibUsb.getDeviceList(context, list);
|
||||
|
@ -46,7 +42,7 @@ public class DfuDeviceLocator {
|
|||
if (result != LibUsb.SUCCESS)
|
||||
throw new LibUsbException("getDeviceDescriptor", result);
|
||||
if (descriptor.idVendor() == vendorId && descriptor.idProduct() == productId) {
|
||||
return findDfuInterface(device, descriptor);
|
||||
return findDfuInterface(logger, device, descriptor);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -62,13 +58,14 @@ public class DfuDeviceLocator {
|
|||
return (((x & 0xff) << 8) | ((x >> 8) & 0xff));
|
||||
}
|
||||
|
||||
public static USBDfuConnection findDfuInterface(Device device, DeviceDescriptor deviceDescriptor) {
|
||||
public static USBDfuConnection findDfuInterface(DfuLogic.Logger logger, Device device, DeviceDescriptor deviceDescriptor) {
|
||||
byte numConfigurations = deviceDescriptor.bNumConfigurations();
|
||||
log.info(numConfigurations + " configuration(s)");
|
||||
|
||||
appendInfo(numConfigurations + " configuration(s)");
|
||||
|
||||
DeviceHandle deviceHandle = open(device);
|
||||
int transferSize = 0;
|
||||
FlashRange flashRange = null;
|
||||
FlashRange flashRange;
|
||||
|
||||
for (int configurationIndex = 0; configurationIndex < numConfigurations; configurationIndex++) {
|
||||
ConfigDescriptor config = new ConfigDescriptor();
|
||||
|
@ -77,35 +74,14 @@ public class DfuDeviceLocator {
|
|||
throw new LibUsbException("getConfigDescriptor", result);
|
||||
}
|
||||
|
||||
System.out.println("Config " + config);
|
||||
System.out.println("Config Done");
|
||||
appendInfo("Config: " + config);
|
||||
|
||||
byte numInterfaces = config.bNumInterfaces();
|
||||
log.info(numInterfaces + " interface(s)");
|
||||
appendInfo(numInterfaces + " interface(s)");
|
||||
|
||||
for (int interfaceIndex = 0; interfaceIndex < numInterfaces; interfaceIndex++) {
|
||||
Interface iface = config.iface()[interfaceIndex];
|
||||
transferSize = scanTransferSize(config, numInterfaces);
|
||||
|
||||
for (int s = 0; s < iface.numAltsetting(); s++) {
|
||||
InterfaceDescriptor setting = iface.altsetting()[s];
|
||||
System.out.println("setting " + setting);
|
||||
|
||||
ByteBuffer extra = setting.extra();
|
||||
|
||||
if (extra.limit() > 2) {
|
||||
int len = extra.get();
|
||||
byte type = extra.get();
|
||||
if (type == DfuLogic.USB_DT_DFU) {
|
||||
System.out.println(len + " " + type);
|
||||
extra.get(); // bmAttributes
|
||||
extra.get(); // wDetachTimeOut
|
||||
extra.get();
|
||||
transferSize = swap16(extra.getShort());
|
||||
System.out.println("transferSize " + transferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scanDescriptor(deviceHandle, config, numInterfaces);
|
||||
|
||||
for (int interfaceIndex = 0; interfaceIndex < numInterfaces; interfaceIndex++) {
|
||||
Interface iface = config.iface()[interfaceIndex];
|
||||
|
@ -113,56 +89,31 @@ public class DfuDeviceLocator {
|
|||
for (int s = 0; s < iface.numAltsetting(); s++) {
|
||||
InterfaceDescriptor setting = iface.altsetting()[s];
|
||||
|
||||
log.info("Settings " + setting);
|
||||
byte interfaceNumber = setting.bInterfaceNumber();
|
||||
log.info(String.format("Setting %d: %x %x class %x, subclass %x, protocol: %x", s,
|
||||
interfaceNumber,
|
||||
setting.iInterface(),
|
||||
setting.bInterfaceClass(), setting.bInterfaceSubClass(),
|
||||
setting.bInterfaceProtocol()
|
||||
));
|
||||
|
||||
if (setting.bInterfaceClass() == DfuLogic.USB_CLASS_APP_SPECIFIC &&
|
||||
log.info("Settings " + setting + " " + interfaceNumber);
|
||||
|
||||
if (setting.bInterfaceClass() == (byte) DfuLogic.USB_CLASS_APP_SPECIFIC &&
|
||||
setting.bInterfaceSubClass() == DfuLogic.DFU_SUBCLASS) {
|
||||
log.debug(String.format("Found DFU interface: %d", interfaceNumber));
|
||||
|
||||
String stringDescriptor = LibUsb.getStringDescriptor(deviceHandle, setting.iInterface());
|
||||
log.info("StringDescriptor: " + stringDescriptor);
|
||||
if (stringDescriptor.contains("Flash"))
|
||||
if (stringDescriptor.contains(DfuLogic.FLASH_TAG)) {
|
||||
|
||||
flashRange = DfuSeFlashDescriptor.parse(stringDescriptor);
|
||||
|
||||
result = LibUsb.claimInterface(deviceHandle, interfaceNumber);
|
||||
if (result != LibUsb.SUCCESS) {
|
||||
throw new LibUsbException("claimInterface", result);
|
||||
result = LibUsb.claimInterface(deviceHandle, interfaceNumber);
|
||||
if (result != LibUsb.SUCCESS) {
|
||||
throw new LibUsbException("claimInterface", result);
|
||||
}
|
||||
|
||||
USBDfuConnection session = new USBDfuConnection(deviceHandle, interfaceNumber, transferSize, flashRange);
|
||||
|
||||
System.out.printf("info:\n" + usbInfo);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
USBDfuConnection session = new USBDfuConnection(deviceHandle, interfaceNumber, transferSize, flashRange);
|
||||
|
||||
DfuCommandGetStatus.State state = DfuCommandGetStatus.read(session);
|
||||
log.info("DFU state: " + state);
|
||||
switch (state) {
|
||||
case DFU_IDLE:
|
||||
// best status
|
||||
break;
|
||||
case DFU_ERROR:
|
||||
DfuCommandClearStatus.execute(session);
|
||||
break;
|
||||
case DFU_DOWNLOAD_SYNC:
|
||||
case DFU_DOWNLOAD_IDLE:
|
||||
case DFU_UPLOAD_IDLE:
|
||||
case DFU_MANIFEST_SYNC:
|
||||
case DFU_DOWNLOAD_BUSY:
|
||||
case DFU_MANIFEST:
|
||||
DfuCommandAbort.execute(session);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected state " + state);
|
||||
}
|
||||
state = DfuCommandGetStatus.read(session);
|
||||
if (state != DfuCommandGetStatus.State.DFU_IDLE)
|
||||
throw new IllegalStateException("Not idle");
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +121,61 @@ public class DfuDeviceLocator {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static void scanDescriptor(DeviceHandle deviceHandle, ConfigDescriptor config, byte numInterfaces) {
|
||||
for (int interfaceIndex = 0; interfaceIndex < numInterfaces; interfaceIndex++) {
|
||||
Interface iface = config.iface()[interfaceIndex];
|
||||
|
||||
for (int s = 0; s < iface.numAltsetting(); s++) {
|
||||
InterfaceDescriptor setting = iface.altsetting()[s];
|
||||
appendInfo("Interface #" + interfaceIndex + " setting #" + s + ":");
|
||||
|
||||
byte interfaceNumber = setting.bInterfaceNumber();
|
||||
appendInfo(String.format("Setting %d: %x %x class %x, subclass %x, protocol: %x", s,
|
||||
interfaceNumber,
|
||||
setting.iInterface(),
|
||||
setting.bInterfaceClass(),
|
||||
setting.bInterfaceSubClass(),
|
||||
setting.bInterfaceProtocol()
|
||||
));
|
||||
String stringDescriptor = LibUsb.getStringDescriptor(deviceHandle, setting.iInterface());
|
||||
appendInfo("Descriptor " + stringDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int scanTransferSize(ConfigDescriptor config, byte numInterfaces) {
|
||||
int transferSize = 0;
|
||||
for (int interfaceIndex = 0; interfaceIndex < numInterfaces; interfaceIndex++) {
|
||||
Interface iface = config.iface()[interfaceIndex];
|
||||
|
||||
for (int s = 0; s < iface.numAltsetting(); s++) {
|
||||
InterfaceDescriptor setting = iface.altsetting()[s];
|
||||
System.out.println("setting " + setting);
|
||||
|
||||
ByteBuffer extra = setting.extra();
|
||||
|
||||
if (extra.limit() > 2) {
|
||||
int len = extra.get();
|
||||
byte type = extra.get();
|
||||
if (type == DfuLogic.USB_DT_DFU) {
|
||||
System.out.println(len + " " + type);
|
||||
extra.get(); // bmAttributes
|
||||
extra.get(); // wDetachTimeOut
|
||||
extra.get();
|
||||
transferSize = swap16(extra.getShort());
|
||||
System.out.println("transferSize " + transferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return transferSize;
|
||||
}
|
||||
|
||||
private static void appendInfo(String message) {
|
||||
log.info(message);
|
||||
usbInfo.append(message).append("\n");
|
||||
}
|
||||
|
||||
private static DeviceHandle open(Device device) {
|
||||
DeviceHandle deviceHandle = new DeviceHandle();
|
||||
int result = LibUsb.open(device, deviceHandle);
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.usb4java.DeviceHandle;
|
|||
import org.usb4java.LibUsb;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
public class USBDfuConnection implements DfuConnection {
|
||||
private final DeviceHandle deviceHandle;
|
||||
|
@ -17,6 +18,7 @@ public class USBDfuConnection implements DfuConnection {
|
|||
public USBDfuConnection(DeviceHandle deviceHandle, byte interfaceNumber, int transferSize, FlashRange flashRange) {
|
||||
if (transferSize == 0)
|
||||
throw new IllegalArgumentException("transfer size not detected");
|
||||
Objects.requireNonNull(flashRange, "flashRange");
|
||||
this.deviceHandle = deviceHandle;
|
||||
this.interfaceNumber = interfaceNumber;
|
||||
this.transferSize = transferSize;
|
||||
|
@ -43,6 +45,12 @@ public class USBDfuConnection implements DfuConnection {
|
|||
return transfer(command, wValue, data, LibUsb.ENDPOINT_OUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer allocateBuffer(int capacity) {
|
||||
// usb4java requires direct buffer
|
||||
return ByteBuffer.allocateDirect(capacity);
|
||||
}
|
||||
|
||||
private int transfer(DfuCommmand command, short wValue, ByteBuffer data, byte mode) {
|
||||
return LibUsb.controlTransfer(
|
||||
deviceHandle,
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class DfuLogicTest {
|
||||
@Test
|
||||
public void test() {
|
||||
FlashRange range = new FlashRange(0, Arrays.asList(20, 40, 80, 800));
|
||||
|
||||
// todo: migrate to mockito?
|
||||
DfuConnection device = new DfuConnection() {
|
||||
@Override
|
||||
public FlashRange getFlashRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransferSize() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int receiveData(DfuCommmand command, short wValue, ByteBuffer data) {
|
||||
return data.limit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer allocateBuffer(int capacity) {
|
||||
return ByteBuffer.allocate(capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendData(DfuCommmand command, short wValue, ByteBuffer data) {
|
||||
return data.limit();
|
||||
}
|
||||
};
|
||||
|
||||
BinaryImage image = () -> new byte[150];
|
||||
|
||||
DfuLogic.actuallyUploadImage(DfuLogic.Logger.VOID, device, image, range);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.rusefi.dfu;
|
||||
|
||||
public class DfuReaderSandbox {
|
||||
public static void main(String[] args) {
|
||||
DfuImage dfuImage = new DfuImage();
|
||||
dfuImage.read("rusefi.dfu");
|
||||
}
|
||||
}
|
|
@ -10,5 +10,10 @@ public class DfuSeFlashDescriptorTest {
|
|||
FlashRange range = DfuSeFlashDescriptor.parse("@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg");
|
||||
assertEquals(0x8000000, range.getBaseAddress());
|
||||
assertEquals(0x100000, range.getTotalLength());
|
||||
|
||||
assertEquals(1, range.pagesForSize(1).size());
|
||||
assertEquals(1, range.pagesForSize(16 * 1024).size());
|
||||
|
||||
assertEquals(2, range.pagesForSize(17 * 1024).size());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import cz.jaybee.intelhex.IntelHexException;
|
|||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A test tool focused only on HEX file input
|
||||
|
@ -11,7 +12,7 @@ import java.io.IOException;
|
|||
public class HexReaderSandbox {
|
||||
public static void main(String[] args) throws IOException, IntelHexException {
|
||||
|
||||
FlashRange range = new FlashRange(0x8000000, 0x100000);
|
||||
FlashRange range = new FlashRange(0x8000000, Arrays.asList(0x100000));
|
||||
|
||||
HexImage image = HexImage.loadHexToBuffer(new FileInputStream("rusefi.hex"), range);
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ public class Sandbox {
|
|||
public static void main(String[] args) throws IOException, IntelHexException {
|
||||
log.info("Hello sandbox");
|
||||
|
||||
USBDfuConnection device = DfuDeviceLocator.findDevice();
|
||||
DfuLogic.Logger logger = DfuLogic.Logger.CONSOLE;
|
||||
USBDfuConnection device = DfuDeviceLocator.findDevice(logger);
|
||||
if (device == null) {
|
||||
System.err.println("No DFU devices found");
|
||||
return;
|
||||
|
@ -22,10 +23,9 @@ public class Sandbox {
|
|||
|
||||
HexImage image = HexImage.loadHexToBuffer(new FileInputStream("rusefi.hex"), device.getFlashRange());
|
||||
|
||||
|
||||
DfuLogic.uploadImage(device, image);
|
||||
//DfuImage image = new DfuImage().read("rusefi_disco.dfu");
|
||||
DfuLogic.uploadImage(logger, device, image, device.getFlashRange());
|
||||
|
||||
log.info("STM32 DFU " + device);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue