Harald Welte has submitted this change and it was merged. (
https://gerrit.osmocom.org/12507 )
Change subject: restructure and complete the USB-side DFU state machine
......................................................................
restructure and complete the USB-side DFU state machine
the USB control request are now handled in separate function
depending on the direction (IN or OUT).
download and manifestation are handled.
numerous additional state machine fixes are included.
Change-Id: I5237393c2789fdeddca2182da25ef417a2e71216
---
M usb/class/dfu/device/dfudf.c
1 file changed, 133 insertions(+), 44 deletions(-)
Approvals:
Jenkins Builder: Verified
Harald Welte: Looks good to me, approved
diff --git a/usb/class/dfu/device/dfudf.c b/usb/class/dfu/device/dfudf.c
index 42b708b..aefcab4 100644
--- a/usb/class/dfu/device/dfudf.c
+++ b/usb/class/dfu/device/dfudf.c
@@ -140,9 +140,136 @@
}
/**
+ * \brief Process the DFU IN request
+ * \param[in] ep Endpoint address.
+ * \param[in] req Pointer to the request.
+ * \param[in] stage Stage of the request.
+ * \return Operation status.
+ */
+static int32_t dfudf_in_req(uint8_t ep, struct usb_req *req, enum
usb_ctrl_stage stage)
+{
+ if (USB_DATA_STAGE == stage) { // the data stage is only for IN data,
which we sent
+ return ERR_NONE; // send the IN data
+ }
+
+ int32_t to_return = ERR_NONE;
+ uint8_t response[6]; // buffer for the response to this request
+ switch (req->bRequest) {
+ case USB_DFU_UPLOAD: // upload firmware from flash not supported
+ dfu_state = USB_DFU_STATE_DFU_ERROR; // unsupported class
request
+ to_return = ERR_UNSUPPORTED_OP; // stall control pipe (don't
reply to the request)
+ break;
+ case USB_DFU_GETSTATUS: // get status
+ response[0] = dfu_status; // set status
+ response[1] = 10; // set poll timeout (24 bits, in
milliseconds) to small value for periodical poll
+ response[2] = 0; // set poll timeout (24 bits, in milliseconds)
to small value for periodical poll
+ response[3] = 0; // set poll timeout (24 bits, in milliseconds)
to small value for periodical poll
+ response[4] = dfu_state; // set state
+ response[5] = 0; // string not used
+ to_return = usbdc_xfer(ep, response, 6, false); // send back
status
+ if (USB_DFU_STATE_DFU_DNLOAD_SYNC == dfu_state) { // download
has not completed
+ dfu_state = USB_DFU_STATE_DFU_DNBUSY; // switch to busy
state
+ } else if (USB_DFU_STATE_DFU_MANIFEST_SYNC == dfu_state) {
+ if (!dfu_manifestation_complete) {
+ dfu_state = USB_DFU_STATE_DFU_MANIFEST; // go
to manifest mode
+ } else if (usb_dfu_func_desc->bmAttributes &
USB_DFU_ATTRIBUTES_MANIFEST_TOLERANT) {
+ dfu_state = USB_DFU_STATE_DFU_IDLE; // go back
to idle mode
+ } else { // this should not happen (after manifestation
the state should be dfuMANIFEST-WAIT-RESET if we are not manifest tolerant)
+ dfu_state =
USB_DFU_STATE_DFU_MANIFEST_WAIT_RESET; // wait for reset
+ }
+ }
+ break;
+ case USB_DFU_GETSTATE: // get state
+ response[0] = dfu_state; // return state
+ to_return = usbdc_xfer(ep, response, 1, false); // send back
state
+ break;
+ default: // all other DFU class IN request
+ dfu_state = USB_DFU_STATE_DFU_ERROR; // unknown or unsupported
class request
+ to_return = ERR_INVALID_ARG; // stall control pipe (don't reply
to the request)
+ break;
+ }
+
+ return to_return;
+}
+
+/**
+ * \brief Process the DFU OUT request
+ * \param[in] ep Endpoint address.
+ * \param[in] req Pointer to the request.
+ * \param[in] stage Stage of the request.
+ * \return Operation status.
+ */
+static int32_t dfudf_out_req(uint8_t ep, struct usb_req *req, enum
usb_ctrl_stage stage)
+{
+ int32_t to_return = ERR_NONE;
+ switch (req->bRequest) {
+ case USB_DFU_DETACH: // detach makes only sense in DFU
run-time/application mode
+ dfu_state = USB_DFU_STATE_DFU_ERROR; // unsupported class
request
+ to_return = ERR_UNSUPPORTED_OP; // stall control pipe (don't
reply to the request)
+ break;
+ case USB_DFU_CLRSTATUS: // clear status
+ if (USB_DFU_STATE_DFU_ERROR == dfu_state || USB_DFU_STATUS_OK
!= dfu_status) { // only clear in case there is an error
+ dfu_status = USB_DFU_STATUS_OK; // clear error status
+ dfu_state = USB_DFU_STATE_DFU_IDLE; // put back in idle
state
+ }
+ to_return = usbdc_xfer(ep, NULL, 0, false); // send ACK
+ break;
+ case USB_DFU_ABORT: // abort current operation
+ dfu_download_progress = 0; // reset download progress
+ dfu_state = USB_DFU_STATE_DFU_IDLE; // put back in idle state
(nothing else to do)
+ to_return = usbdc_xfer(ep, NULL, 0, false); // send ACK
+ break;
+ case USB_DFU_DNLOAD: // download firmware on flash
+ if (!(usb_dfu_func_desc->bmAttributes & USB_REQ_DFU_DNLOAD)) {
// download is not enabled
+ dfu_state = USB_DFU_STATE_DFU_ERROR; // unsupported
class request
+ to_return = ERR_UNSUPPORTED_OP; // stall control pipe
(don't reply to the request)
+ } else if (USB_DFU_STATE_DFU_IDLE != dfu_state &&
USB_DFU_STATE_DFU_DNLOAD_IDLE != dfu_state) { // wrong state to request download
+ // warn about programming error
+ dfu_status = USB_DFU_STATUS_ERR_PROG;
+ dfu_state = USB_DFU_STATE_DFU_ERROR;
+ to_return = ERR_INVALID_ARG; // stall control pipe to
indicate error
+ } else if (USB_DFU_STATE_DFU_IDLE == dfu_state && (0 ==
req->wLength)) { // download request should not start empty
+ // warn about programming error
+ dfu_status = USB_DFU_STATUS_ERR_PROG;
+ dfu_state = USB_DFU_STATE_DFU_ERROR;
+ to_return = ERR_INVALID_ARG; // stall control pipe to
indicate error
+ } else if (USB_DFU_STATE_DFU_DNLOAD_IDLE == dfu_state && (0 ==
req->wLength)) { // download completed
+ dfu_manifestation_complete = false; // clear
manifestation status
+ dfu_state = USB_DFU_STATE_DFU_MANIFEST_SYNC; // prepare
for manifestation phase
+ to_return = usbdc_xfer(ep, NULL, 0, false); // send ACK
+ } else if (req->wLength > sizeof(dfu_download_data)) { // there
is more data to be flash then our buffer (the USB control buffer size should be
less or equal)
+ // warn about programming error
+ dfu_status = USB_DFU_STATUS_ERR_PROG;
+ dfu_state = USB_DFU_STATE_DFU_ERROR;
+ to_return = ERR_INVALID_ARG; // stall control pipe to
indicate error
+ } else { // there is data to be flash
+ if (USB_SETUP_STAGE == stage) { // there will be data
to be flash
+ to_return = usbdc_xfer(ep, dfu_download_data,
req->wLength, false); // send ack to the setup request to get the data
+ //to_return = usbdc_xfer(ep, NULL, 0, false);
// send ACK
+ } else { // now there is data to be flashed
+ dfu_download_progress = req->wValue *
sizeof(dfu_download_data); // remember which block to flash
+ dfu_download_length = 0;
+ //dfu_download_length = req->wLength; //
remember the data size to be flash
+ dfu_state = USB_DFU_STATE_DFU_DNLOAD_SYNC; //
go to sync state
+ to_return = usbdc_xfer(ep, NULL, 0, false); //
ACK the data
+ // we let the main application flash the data
because this can be long and would stall the USB ISR
+ }
+ }
+ break;
+ default: // all other DFU class OUT request
+ dfu_state = USB_DFU_STATE_DFU_ERROR; // unknown class request
+ to_return = ERR_INVALID_ARG; // stall control pipe (don't reply
to the request)
+ break;
+ }
+
+ return to_return;
+}
+
+/**
* \brief Process the CDC class request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
+ * \param[in] stage Stage of the request.
* \return Operation status.
*/
static int32_t dfudf_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage
stage)
@@ -151,54 +278,16 @@
return ERR_NOT_FOUND;
}
- int32_t to_return = ERR_NONE;
- uint8_t response[6]; // buffer for the response to this request
if ((req->wIndex == _dfudf_funcd.func_iface)) {
- // we don't verify the bmRequestType
- switch (req->bRequest) {
- case USB_DFU_GETSTATUS: // get status
- response[0] = dfu_status; // set status
- response[1] = 100; // set poll timeout (24 bits, in
milliseconds) to small value for periodical poll
- response[2] = 0; // set poll timeout (24 bits, in
milliseconds) to small value for periodical poll
- response[3] = 0; // set poll timeout (24 bits, in
milliseconds) to small value for periodical poll
- response[4] = dfu_state; // set state
- response[5] = 0; // string not used
- to_return = usbdc_xfer(ep, response, 6, false); // send
back status
- if (USB_DFU_STATE_DFU_DNLOAD_SYNC == dfu_state) {
- dfu_state = USB_DFU_STATE_DFU_DNBUSY; // switch
to busy state
- } else if (USB_DFU_STATE_DFU_MANIFEST_SYNC ==
dfu_state) {
- dfu_state = USB_DFU_STATE_DFU_MANIFEST; // go
to manifest mode
- dfu_state = USB_DFU_STATE_APP_DETACH;
- }
- break;
- case USB_DFU_CLRSTATUS: // clear status
- if (USB_DFU_STATE_DFU_ERROR == dfu_state ||
USB_DFU_STATUS_OK != dfu_status) { // only clear in case there is an error
- dfu_status = USB_DFU_STATUS_OK; // clear error
status
- dfu_state = USB_DFU_STATE_DFU_IDLE; // put back
in idle state
- }
- to_return = usbdc_xfer(ep, NULL, 0, true); // send ACK
- break;
- case USB_DFU_GETSTATE: // get state
- response[0] = dfu_state; // return state
- to_return = usbdc_xfer(ep, response, 1, false); // send
back state
- break;
- case USB_DFU_ABORT: // abort current operation
- dfu_state = USB_DFU_STATE_DFU_IDLE; // put back in idle
state (nothing else to do)
- to_return = usbdc_xfer(ep, NULL, 0, true); // send ACK
- //flash_pointer = (uint32_t)&__application_beginning;
// reset download location
- break;
- case USB_DFU_DETACH: // detach makes only sense in DFU
run-time/application mode
- case USB_DFU_UPLOAD: // upload firmware from flash not supported
- case USB_DFU_DNLOAD: // download firmware on flash TODO
implement
- default: // all other DFU class request
- dfu_state = USB_DFU_STATE_DFU_ERROR; // unknown or
unsupported class request
- to_return = ERR_UNSUPPORTED_OP; // stall control pipe
(don't reply to the request)
- break;
+ if (req->bmRequestType & USB_EP_DIR_IN) {
+ return dfudf_in_req(ep, req, stage);
+ } else {
+ return dfudf_out_req(ep, req, stage);
}
} else {
- to_return = ERR_NOT_FOUND;
+ return ERR_NOT_FOUND;
}
- return to_return;
+ return ERR_NOT_FOUND;
}
/** USB Device DFU Handler Struct */
--
To view, visit https://gerrit.osmocom.org/12507
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-asf4-dfu
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I5237393c2789fdeddca2182da25ef417a2e71216
Gerrit-Change-Number: 12507
Gerrit-PatchSet: 3
Gerrit-Owner: Kévin Redon <[email protected]>
Gerrit-Reviewer: Harald Welte <[email protected]>
Gerrit-Reviewer: Jenkins Builder (1000002)