Skip to content

Commit 7eda016

Browse files
committed
STM32 DFU improvements
1 parent 22d8b69 commit 7eda016

File tree

8 files changed

+224
-167
lines changed

8 files changed

+224
-167
lines changed

examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFU.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import java.nio.file.Path;
1313

1414
/**
15-
* Command line application for upload firmware to STM32.
15+
* Command line application for uploading firmware to STM32 microcontrollers.
1616
* <p>
1717
* Only the STM32 variant of the DFU protocol is supported.
1818
* Only binary firmware format is supported (no .hex or .dfu files).
@@ -53,8 +53,8 @@ public static void main(String[] args) {
5353
System.exit(4);
5454
return;
5555
}
56-
var device = devices.get(0);
57-
System.out.printf("DFU device found with serial %s.%n", device.serialNumber());
56+
var device = devices.getFirst();
57+
System.out.printf("DFU device found with serial %s.%n", device.getSerialNumber());
5858

5959
// download and verify firmware
6060
try {
@@ -64,7 +64,7 @@ public static void main(String[] args) {
6464
System.out.println("Firmware successfully downloaded and verified");
6565

6666
device.startApplication();
67-
System.out.println("DFU mode exited and firmware started");
67+
System.out.println("DFU mode ended and firmware started");
6868

6969
device.close();
7070

examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUDevice.java

Lines changed: 64 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
*/
2424
public class DFUDevice {
2525

26-
private final UsbDevice usbDevice_;
27-
private final int interfaceNumber_;
28-
private final int transferSize_;
29-
private final Version dfuVersion_;
26+
private final UsbDevice usbDevice;
27+
private final int interfaceNumber;
28+
private final int transferSize;
29+
private final Version dfuVersion;
3030

31-
private List<Segment> segments_;
31+
private List<Segment> segments;
3232

3333
/**
3434
* Gets all connected DFU devices
@@ -89,73 +89,76 @@ public static int getDFUInterfaceNumber(UsbDevice device) {
8989
* @param usbDevice the USB device
9090
*/
9191
public DFUDevice(UsbDevice usbDevice) {
92-
usbDevice_ = usbDevice;
93-
interfaceNumber_ = getDFUInterfaceNumber(usbDevice);
92+
this.usbDevice = usbDevice;
93+
interfaceNumber = getDFUInterfaceNumber(usbDevice);
9494

9595
var configDesc = usbDevice.getConfigurationDescriptor();
9696
int offset = getDFUDescriptorOffset(configDesc);
9797
assert offset > 0;
9898

99-
transferSize_ = getInt16(configDesc, offset + 5);
100-
dfuVersion_ = new Version(getInt16(configDesc, offset + 7));
99+
transferSize = getInt16(configDesc, offset + 5);
100+
dfuVersion = new Version(getInt16(configDesc, offset + 7));
101101
}
102102

103103
/**
104104
* Gets the DFU protocol version.
105105
* @return the protocol version
106106
*/
107-
public Version dfuVersion() {
108-
return dfuVersion_;
107+
public Version getDfuVersion() {
108+
return dfuVersion;
109109
}
110110

111111
/**
112112
* Gets the device serial number.
113113
* @return the serial number
114114
*/
115-
public String serialNumber() {
116-
return usbDevice_.getSerialNumber();
115+
public String getSerialNumber() {
116+
return usbDevice.getSerialNumber();
117117
}
118118

119119
/**
120120
* Opens the DFU device for communication.
121121
*/
122122
public void open() {
123-
usbDevice_.open();
124-
usbDevice_.claimInterface(interfaceNumber_);
125-
segments_ = Segment.getSegments(usbDevice_, interfaceNumber_);
123+
usbDevice.open();
124+
usbDevice.claimInterface(interfaceNumber);
125+
segments = Segment.getSegments(usbDevice, interfaceNumber);
126126
clearErrorIfNeeded();
127127
}
128128

129129
/**
130130
* Closes the DFU device.
131131
*/
132132
public void close() {
133-
usbDevice_.close();
133+
usbDevice.close();
134134
}
135135

136136
/**
137137
* Clears an error status.
138138
*/
139139
public void clearStatus() {
140-
var setup = new UsbControlTransfer(CLASS, INTERFACE, DFURequest.CLEAR_STATUS.value(), 0, interfaceNumber_);
141-
usbDevice_.controlTransferOut(setup, null);
140+
var transfer = createDfuControlTransfer(DFURequest.CLEAR_STATUS, 0);
141+
usbDevice.controlTransferOut(transfer, null);
142142
}
143143

144144
/**
145145
* Aborts the download mode.
146146
*/
147147
public void abort() {
148-
var setup = new UsbControlTransfer(CLASS, INTERFACE, DFURequest.ABORT.value(), 0, interfaceNumber_);
149-
usbDevice_.controlTransferOut(setup, null);
148+
var transfer = createDfuControlTransfer(DFURequest.ABORT, 0);
149+
usbDevice.controlTransferOut(transfer, null);
150150
}
151151

152152
/**
153153
* Gets the full DFU status
154154
* @return the status
155155
*/
156156
public DFUStatus getStatus() {
157-
var setup = new UsbControlTransfer(CLASS, INTERFACE, DFURequest.GET_STATUS.value(), 0, interfaceNumber_);
158-
return DFUStatus.fromBytes(usbDevice_.controlTransferIn(setup, 6));
157+
var transfer = createDfuControlTransfer(DFURequest.GET_STATUS, 0);
158+
var response = usbDevice.controlTransferIn(transfer, 6);
159+
if (response.length != 6)
160+
throw new DFUException("Invalid response from GET_STATUS request");
161+
return DFUStatus.fromBytes(response);
159162
}
160163

161164
/**
@@ -168,7 +171,6 @@ public byte[] read(int address, int length) {
168171
expectState(DeviceState.DFU_IDLE, DeviceState.DFU_DNLOAD_IDLE);
169172
setAddress(address);
170173
exitDownloadMode();
171-
172174
expectState(DeviceState.DFU_IDLE, DeviceState.DFU_UPLOAD_IDLE);
173175

174176
var result = new byte[length];
@@ -177,17 +179,28 @@ public byte[] read(int address, int length) {
177179
int offset = 0;
178180
int blockNum = 2;
179181
while (offset < length) {
180-
int chunkSize = Math.min(transferSize_, length - offset);
181-
var setup = new UsbControlTransfer(CLASS, INTERFACE, DFURequest.UPLOAD.value(), blockNum, interfaceNumber_);
182-
var chunk = usbDevice_.controlTransferIn(setup, chunkSize);
182+
int chunkSize = Math.min(transferSize, length - offset);
183+
var transfer = createDfuControlTransfer(DFURequest.UPLOAD, blockNum);
184+
var chunk = usbDevice.controlTransferIn(transfer, chunkSize);
183185
System.arraycopy(chunk, 0, result, offset, chunkSize);
184186
offset += chunkSize;
185187
blockNum += 1;
186188
}
187189

188-
// request zero lenght chunk to exit out of upload mode
189-
var setup = new UsbControlTransfer(CLASS, INTERFACE, DFURequest.UPLOAD.value(), blockNum, interfaceNumber_);
190-
usbDevice_.controlTransferIn(setup, 0);
190+
// request zero length chunk to exit out of upload mode
191+
var transfer = createDfuControlTransfer(DFURequest.UPLOAD, blockNum);
192+
usbDevice.controlTransferIn(transfer, 0);
193+
194+
DFUStatus status;
195+
try {
196+
status = getStatus();
197+
} catch (DFUException e) {
198+
// Device might respond with an empty response; try again
199+
status = getStatus();
200+
}
201+
202+
if (status.state() != DeviceState.DFU_IDLE)
203+
throw new DFUException("Unexpected state after exiting from upload mode");
191204

192205
return result;
193206
}
@@ -201,13 +214,13 @@ public void verify(byte[] firmware) {
201214
public void download(byte[] firmware) {
202215
int length = firmware.length;
203216

204-
// validate start and end address exist and are writable
217+
// validate that start and end address exist and are writable
205218
int startAddress = STM32.FLASH_BASE_ADDRESS;
206219
var firstPage = getWritablePage(startAddress);
207220
getWritablePage(startAddress + length);
208221

209-
usbDevice_.selectAlternateSetting(interfaceNumber_, firstPage.segment().altSetting());
210-
System.out.printf("Target memory segment: %s%n", firstPage.segment().name());
222+
usbDevice.selectAlternateSetting(interfaceNumber, firstPage.segment().getAltSetting());
223+
System.out.printf("Target memory segment: %s%n", firstPage.segment().getName());
211224

212225
// erase if needed
213226
if (firstPage.isErasable())
@@ -219,14 +232,14 @@ public void download(byte[] firmware) {
219232
int offset = 0;
220233
int transaction = 2;
221234
while (offset < length) {
222-
int chunkSize = Math.min(length - offset, transferSize_);
235+
int chunkSize = Math.min(length - offset, transferSize);
223236

224237
byte[] chunk = new byte[chunkSize];
225238
System.arraycopy(firmware, offset, chunk, 0, chunkSize);
226239

227240
System.out.printf("Writing data at 0x%x (size 0x%x)%n", startAddress + offset, chunkSize);
228-
var setup = new UsbControlTransfer(CLASS, INTERFACE, DFURequest.DOWNLOAD.value(), transaction, interfaceNumber_);
229-
usbDevice_.controlTransferOut(setup, chunk);
241+
var transfer = createDfuControlTransfer(DFURequest.DOWNLOAD, transaction);
242+
usbDevice.controlTransferOut(transfer, chunk);
230243

231244
finishDownloadCommand("writing data");
232245

@@ -243,7 +256,7 @@ public void download(byte[] firmware) {
243256
* Only applicable to erasable sector, i.e. flash memory.
244257
* </p>
245258
* <p>
246-
* Only entire pages can be erase. If start and end address to not fall onto
259+
* Only entire pages can be erased. If start and end address to not fall onto
247260
* page boundaries, this method will extend the range to be erased.
248261
* </p>
249262
* @param startAddress the start address of the range
@@ -261,28 +274,28 @@ public void erase(int startAddress, int length) {
261274

262275
System.out.printf("Erasing page at 0x%x (size 0x%x)%n", page.startAddress(), page.pageSize());
263276
erasePage(page.startAddress());
264-
startAddress = page.endAddress();
277+
startAddress = page.getEndAddress();
265278
}
266279
}
267280

268281
public void erasePage(int address) {
269-
execDownloadCommandWithAddress((byte) 0x41, "erasing page", address);
282+
executeSpecialCommand((byte) 0x41, "erasing page", address);
270283
}
271284

272285
public void setAddress(int address) {
273-
execDownloadCommandWithAddress((byte) 0x21, "setting address", address);
286+
executeSpecialCommand((byte) 0x21, "setting address", address);
274287
}
275288

276-
private void execDownloadCommandWithAddress(byte command, String action, int address) {
277-
var setup = new UsbControlTransfer(CLASS, INTERFACE, DFURequest.DOWNLOAD.value(), 0, interfaceNumber_);
289+
private void executeSpecialCommand(byte command, String action, int address) {
290+
var transfer = createDfuControlTransfer(DFURequest.DOWNLOAD, 0);
278291
var data = new byte[] {
279292
command,
280293
(byte) address,
281294
(byte) (address >> 8),
282295
(byte) (address >> 16),
283296
(byte) (address >> 24)
284297
};
285-
usbDevice_.controlTransferOut(setup, data);
298+
usbDevice.controlTransferOut(transfer, data);
286299

287300
finishDownloadCommand(action);
288301
}
@@ -311,7 +324,7 @@ private Page getWritablePage(int address) {
311324
}
312325

313326
private Page findPage(int address) {
314-
return Segment.findPage(segments_, address);
327+
return Segment.findPage(segments, address);
315328
}
316329

317330
private void exitDownloadMode() {
@@ -327,8 +340,8 @@ private void exitDownloadMode() {
327340
public void startApplication() {
328341
expectState(DeviceState.DFU_IDLE, DeviceState.DFU_DNLOAD_IDLE);
329342

330-
var setup = new UsbControlTransfer(CLASS, INTERFACE, DFURequest.DOWNLOAD.value(), 2, interfaceNumber_);
331-
usbDevice_.controlTransferOut(setup, null);
343+
var transfer = createDfuControlTransfer(DFURequest.DOWNLOAD, 0);
344+
usbDevice.controlTransferOut(transfer, null);
332345

333346
var status = getStatus();
334347
if (status.state() != DeviceState.DFU_MANIFEST)
@@ -365,4 +378,9 @@ private static void sleep(int millis) {
365378
throw new DFUException("Sleep failed", e);
366379
}
367380
}
381+
382+
private UsbControlTransfer createDfuControlTransfer(DFURequest request, int value) {
383+
return new UsbControlTransfer(CLASS, INTERFACE, request.ordinal(), value, interfaceNumber);
384+
}
385+
368386
}

examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFURequest.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,5 @@ public enum DFURequest {
2020
GET_STATUS,
2121
CLEAR_STATUS,
2222
GET_STATE,
23-
ABORT;
24-
25-
public int value() {
26-
return ordinal();
27-
}
23+
ABORT
2824
}

examples/stm_dfu/src/main/java/net/codecrete/usb/dfu/DFUStatus.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@
1313
* See USB Device Class Specification for Device Firmware Upgrade, version 1.1.
1414
* </p>
1515
*/
16-
public record DFUStatus(DeviceStatus status, int pollTimeout, DeviceState state, int iString) {
16+
public record DFUStatus(DeviceStatus status, int pollTimeout, DeviceState state) {
1717

1818
public static DFUStatus fromBytes(byte[] data) {
1919
var status = DeviceStatus.fromValue(data[0]);
2020
var pollTimeout = (data[1] & 0xff) + 256 * (data[2] & 0xff) + 256 * 256 * (data[3] & 0xff);
2121
var state = DeviceState.fromValue(data[4]);
22-
var iString = data[5] & 0x55;
23-
return new DFUStatus(status, pollTimeout, state, iString);
22+
return new DFUStatus(status, pollTimeout, state);
2423
}
2524
}

0 commit comments

Comments
 (0)