first byte exchange!

This commit is contained in:
rusefi 2020-06-26 23:54:36 -04:00
parent 511e27d519
commit 11b0f3c809
10 changed files with 288 additions and 56 deletions

View File

@ -1,2 +1,6 @@
# dfu_java
stm32 java DFU implementation
ST AN3156 Application note
USB DFU protocol used in the STM32 bootloader

View File

@ -0,0 +1,21 @@
package com.rusefi.dfu;
public enum DfuCommmand {
DETACH(0),
DNLOAD(1),
UPLOAD(2),
GETSTATUS(3),
CLRSTATUS(4),
GETSTATE(5),
ABORT(6);
private final byte value;
DfuCommmand(int value) {
this.value = (byte) value;
}
public byte getValue() {
return this.value;
}
}

View File

@ -0,0 +1,12 @@
package com.rusefi.dfu;
import java.nio.ByteBuffer;
public interface DfuConnection {
int SECOND = 1000;
int DFU_TIMEOUT = 10 * DfuConnection.SECOND;
int receiveData(DfuCommmand command, short value, ByteBuffer data);
int sendData(DfuCommmand command, short value, ByteBuffer data);
}

View File

@ -1,52 +0,0 @@
package com.rusefi.dfu;
import org.apache.commons.logging.Log;
import org.usb4java.*;
public class DfuDeviceLocator {
private static final short ST_VENDOR = 0x0483;
private static final short ST_DFU_PRODUCT = (short) 0xdf11;
private static final Log log = LogUtil.getLog(DfuDeviceLocator.class);
public static Device findDevice() {
return findDevice(openContext(), ST_VENDOR, ST_DFU_PRODUCT);
}
public static Device findDevice(Context context, short vendorId, short productId) {
// Read the USB device list
DeviceList list = new DeviceList();
int result = LibUsb.getDeviceList(context, list);
if (result < 0)
throw new LibUsbException("Unable to get device list", result);
log.info(list.getSize() + " device(s) found");
try {
// Iterate over all devices and scan for the right one
for (Device device : list) {
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
// System.out.println("I see " + descriptor);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to read device descriptor", result);
if (descriptor.idVendor() == vendorId && descriptor.idProduct() == productId)
return device;
}
} finally {
// Ensure the allocated device list is freed
LibUsb.freeDeviceList(list, true);
}
// Device not found
return null;
}
public static Context openContext() {
Context context = new Context();
int result = LibUsb.init(context);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to initialize libusb.", result);
log.info("Welcome " + context);
return context;
}
}

View File

@ -0,0 +1,16 @@
package com.rusefi.dfu.commands;
import com.rusefi.dfu.DfuCommmand;
import com.rusefi.dfu.usb4java.USBDfuConnection;
import java.nio.ByteBuffer;
public class DfuCommandClearStatus {
public static void execute(USBDfuConnection session) {
ByteBuffer buffer = ByteBuffer.allocateDirect(0);
session.sendData(DfuCommmand.CLRSTATUS, (short) 0, buffer);
}
}

View File

@ -0,0 +1,53 @@
package com.rusefi.dfu.commands;
import com.rusefi.dfu.DfuCommmand;
import com.rusefi.dfu.usb4java.USBDfuConnection;
import java.nio.ByteBuffer;
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 != PACKET_SIZE)
throw new IllegalStateException("Got " + count);
buffer.rewind();
buffer.get(); // status
buffer.get(); // timeout
buffer.get(); // timeout
buffer.get(); // timeout
byte state = buffer.get();
return State.valueOf(state);
}
public enum State {
APP_IDLE(0x00),
APP_DETACH(0x01),
DFU_IDLE(0x02),
DFU_DOWNLOAD_SYNC(0x03),
DFU_DOWNLOAD_BUSY(0x04),
DFU_DOWNLOAD_IDLE(0x05),
DFU_MANIFEST_SYNC(0x06),
DFU_MANIFEST(0x07),
DFU_MANIFEST_WAIT_RESET(0x08),
DFU_UPLOAD_IDLE(0x09),
DFU_ERROR(0x0a);
private final byte value;
State(int value) {
this.value = (byte) value;
}
public static State valueOf(byte value) {
for (State state : State.values()) {
if (state.value == value) {
return state;
}
}
return null;
}
}
}

View File

@ -0,0 +1,136 @@
package com.rusefi.dfu.usb4java;
import com.rusefi.dfu.commands.DfuCommandClearStatus;
import com.rusefi.dfu.commands.DfuCommandGetStatus;
import org.apache.commons.logging.Log;
import org.usb4java.*;
public class DfuDeviceLocator {
private static final short ST_VENDOR = 0x0483;
private static final short ST_DFU_PRODUCT = (short) 0xdf11;
private static final byte USB_CLASS_APP_SPECIFIC = (byte) 0xfe;
private static final byte DFU_SUBCLASS = 0x01;
private static final Log log = LogUtil.getLog(DfuDeviceLocator.class);
public static Context openContext() {
Context context = new Context();
int result = LibUsb.init(context);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("init", result);
log.info("Welcome " + context);
return context;
}
public static USBDfuConnection findDevice() {
return findDevice(openContext(), ST_VENDOR, ST_DFU_PRODUCT);
}
private static USBDfuConnection findDevice(Context context, short vendorId, short productId) {
// Read the USB device list
DeviceList list = new DeviceList();
int result = LibUsb.getDeviceList(context, list);
if (result < 0)
throw new LibUsbException("getDeviceList", result);
log.info(list.getSize() + " device(s) found");
try {
// Iterate over all devices and scan for the right one
for (Device device : list) {
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
// System.out.println("I see " + descriptor);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("getDeviceDescriptor", result);
if (descriptor.idVendor() == vendorId && descriptor.idProduct() == productId) {
return findDfuInterface(device, descriptor);
}
}
} finally {
// Ensure the allocated device list is freed
LibUsb.freeDeviceList(list, true);
}
// Device not found
return null;
}
public static USBDfuConnection findDfuInterface(Device device, DeviceDescriptor deviceDescriptor) {
byte numConfigurations = deviceDescriptor.bNumConfigurations();
log.info(numConfigurations + " configuration(s)");
for (int configurationIndex = 0; configurationIndex < numConfigurations; configurationIndex++) {
ConfigDescriptor config = new ConfigDescriptor();
int result = LibUsb.getConfigDescriptor(device, (byte) configurationIndex, config);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("getConfigDescriptor", result);
}
byte numInterfaces = config.bNumInterfaces();
log.info(numInterfaces + " interface(s)");
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];
log.info("Set " + 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() == USB_CLASS_APP_SPECIFIC &&
setting.bInterfaceSubClass() == DFU_SUBCLASS) {
log.debug(String.format("Found DFU interface: %d", interfaceNumber));
DeviceHandle deviceHandle = open(device);
result = LibUsb.claimInterface(deviceHandle, interfaceNumber);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("claimInterface", result);
}
USBDfuConnection session = new USBDfuConnection(deviceHandle, interfaceNumber);
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;
default:
throw new IllegalStateException("Unexpected state " + state);
}
state = DfuCommandGetStatus.read(session);
if (state != DfuCommandGetStatus.State.DFU_IDLE)
throw new IllegalStateException("Not idle");
return session;
}
}
}
}
return null;
}
private static DeviceHandle open(Device device) {
DeviceHandle deviceHandle = new DeviceHandle();
int result = LibUsb.open(device, deviceHandle);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("open", result);
}
result = LibUsb.setConfiguration(deviceHandle, 1);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("setConfiguration", result);
}
return deviceHandle;
}
}

View File

@ -1,4 +1,4 @@
package com.rusefi.dfu;
package com.rusefi.dfu.usb4java;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -6,7 +6,7 @@ import org.apache.commons.logging.LogFactory;
public class LogUtil {
// todo: one day I would love to learn how to enable trace level with commons logging + java logging
static Log getLog(Class<?> clazz) {
public static Log getLog(Class<?> clazz) {
return LogFactory.getLog(clazz);
}
}

View File

@ -0,0 +1,39 @@
package com.rusefi.dfu.usb4java;
import com.rusefi.dfu.DfuCommmand;
import com.rusefi.dfu.DfuConnection;
import org.usb4java.DeviceHandle;
import org.usb4java.LibUsb;
import java.nio.ByteBuffer;
public class USBDfuConnection implements DfuConnection {
private final DeviceHandle deviceHandle;
private final byte interfaceNumber;
public USBDfuConnection(DeviceHandle deviceHandle, byte interfaceNumber) {
this.deviceHandle = deviceHandle;
this.interfaceNumber = interfaceNumber;
}
@Override
public int receiveData(DfuCommmand command, short value, ByteBuffer data) {
return transfer(command, value, data, LibUsb.ENDPOINT_IN);
}
@Override
public int sendData(DfuCommmand command, short value, ByteBuffer data) {
return transfer(command, value, data, LibUsb.ENDPOINT_OUT);
}
private int transfer(DfuCommmand command, short value, ByteBuffer data, byte mode) {
return LibUsb.controlTransfer(
deviceHandle,
(byte) (mode | LibUsb.REQUEST_TYPE_CLASS | LibUsb.RECIPIENT_INTERFACE),
command.getValue(),
value,
interfaceNumber,
data,
DFU_TIMEOUT);
}
}

View File

@ -1,7 +1,9 @@
package com.rusefi.dfu;
import com.rusefi.dfu.usb4java.DfuDeviceLocator;
import com.rusefi.dfu.usb4java.LogUtil;
import com.rusefi.dfu.usb4java.USBDfuConnection;
import org.apache.commons.logging.Log;
import org.usb4java.Device;
public class Sandbox {
private static final Log log = LogUtil.getLog(Sandbox.class);
@ -9,7 +11,8 @@ public class Sandbox {
public static void main(String[] args) {
log.info("Hello sandbox");
Device device = DfuDeviceLocator.findDevice();
USBDfuConnection device = DfuDeviceLocator.findDevice();
log.info("STM32 DFU " + device);
}
}