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