Merge remote-tracking branch 'origin/master'

This commit is contained in:
rusefi 2020-07-05 20:51:21 -04:00
commit 0965158aa1
29 changed files with 838 additions and 237 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -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']

29
.github/workflows/build.yaml vendored Normal file
View File

@ -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

View File

@ -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

View File

@ -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'
}

BIN
lib/android.jar Normal file

Binary file not shown.

1
lib/android.md Normal file
View File

@ -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.

81
reference/F407usb.txt Normal file
View File

@ -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

View File

@ -0,0 +1,9 @@
package com.rusefi.dfu;
public interface BinaryImage {
byte[] getImage();
default int getImageSize() {
return getImage().length;
}
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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
};
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 +
'}';
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 +
'}';
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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");
}
}

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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);
}
}