mirror of https://github.com/rusefi/dfu_java.git
first byte exchange!
This commit is contained in:
parent
511e27d519
commit
11b0f3c809
|
@ -1,2 +1,6 @@
|
|||
# dfu_java
|
||||
stm32 java DFU implementation
|
||||
|
||||
|
||||
ST AN3156 Application note
|
||||
USB DFU protocol used in the STM32 bootloader
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue