<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>