1 /*!
2     \file    usbh_msc_bbb.c
3     \brief   USB MSC BBB protocol related functions
4 
5     \version 2020-08-04, V1.1.0, firmware for GD32VF103
6 */
7 
8 /*
9     Copyright (c) 2020, GigaDevice Semiconductor Inc.
10 
11     Redistribution and use in source and binary forms, with or without modification,
12 are permitted provided that the following conditions are met:
13 
14     1. Redistributions of source code must retain the above copyright notice, this
15        list of conditions and the following disclaimer.
16     2. Redistributions in binary form must reproduce the above copyright notice,
17        this list of conditions and the following disclaimer in the documentation
18        and/or other materials provided with the distribution.
19     3. Neither the name of the copyright holder nor the names of its contributors
20        may be used to endorse or promote products derived from this software without
21        specific prior written permission.
22 
23     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32 OF SUCH DAMAGE.
33 */
34 
35 #include "usbh_pipe.h"
36 #include "usbh_msc_core.h"
37 #include "usbh_msc_scsi.h"
38 #include "usbh_msc_bbb.h"
39 #include "usbh_transc.h"
40 #include "drv_usbh_int.h"
41 
42 /*!
43     \brief      initialize the mass storage parameters
44     \param[in]  puhost: pointer to usb host handler
45     \param[out] none
46     \retval     none
47 */
usbh_msc_bot_init(usbh_host * puhost)48 void usbh_msc_bot_init (usbh_host *puhost)
49 {
50     usbh_msc_handler *msc = (usbh_msc_handler *)puhost->active_class->class_data;
51 
52     msc->bot.cbw.field.dCBWSignature = BBB_CBW_SIGNATURE;
53     msc->bot.cbw.field.dCBWTag = USBH_MSC_BOT_CBW_TAG;
54     msc->bot.state = BOT_SEND_CBW;
55     msc->bot.cmd_state = BOT_CMD_SEND;
56 }
57 
58 /*!
59     \brief      manage the different states of BOT transfer and updates the status to upper layer
60     \param[in]  puhost: pointer to usb host handler
61     \param[in]  lun: logic unit number
62     \param[out] none
63     \retval     operation status
64 */
usbh_msc_bot_process(usbh_host * puhost,uint8_t lun)65 usbh_status usbh_msc_bot_process (usbh_host *puhost, uint8_t lun)
66 {
67     bot_csw_status csw_status = BOT_CSW_CMD_FAILED;
68     usbh_status status = USBH_BUSY;
69     usbh_status error = USBH_BUSY;
70     usb_urb_state urb_status = URB_IDLE;
71     usbh_msc_handler *msc = (usbh_msc_handler *)puhost->active_class->class_data;
72 
73     switch (msc->bot.state) {
74     case BOT_SEND_CBW:
75         msc->bot.cbw.field.bCBWLUN = lun;
76         msc->bot.state = BOT_SEND_CBW_WAIT;
77         /* send CBW */
78         usbh_data_send (puhost->data,
79                         msc->bot.cbw.CBWArray,
80                         msc->pipe_out,
81                         BBB_CBW_LENGTH);
82         break;
83 
84     case BOT_SEND_CBW_WAIT:
85         urb_status = usbh_urbstate_get(puhost->data, msc->pipe_out);
86 
87         if (URB_DONE == urb_status) {
88             if (0U != msc->bot.cbw.field.dCBWDataTransferLength) {
89                 if (USB_TRX_IN == (msc->bot.cbw.field.bmCBWFlags & USB_TRX_MASK)) {
90                     msc->bot.state = BOT_DATA_IN;
91                 } else {
92                     msc->bot.state = BOT_DATA_OUT;
93                 }
94             } else {
95                 msc->bot.state = BOT_RECEIVE_CSW;
96             }
97 
98         } else if (URB_NOTREADY == urb_status) {
99             msc->bot.state = BOT_SEND_CBW;
100         } else {
101             if (URB_STALL == urb_status) {
102                 msc->bot.state = BOT_ERROR_OUT;
103             }
104         }
105         break;
106 
107     case BOT_DATA_IN:
108         usbh_data_recev (puhost->data,
109                          msc->bot.pbuf,
110                          msc->pipe_in,
111                          msc->ep_size_in);
112 
113         msc->bot.state = BOT_DATA_IN_WAIT;
114         break;
115 
116     case BOT_DATA_IN_WAIT:
117         urb_status = usbh_urbstate_get(puhost->data, msc->pipe_in);
118 
119         /* BOT DATA IN stage */
120         if (URB_DONE == urb_status) {
121             if (msc->bot.cbw.field.dCBWDataTransferLength > msc->ep_size_in) {
122                 msc->bot.pbuf += msc->ep_size_in;
123                 msc->bot.cbw.field.dCBWDataTransferLength -= msc->ep_size_in;
124             } else {
125                 msc->bot.cbw.field.dCBWDataTransferLength = 0U;
126             }
127 
128             if (msc->bot.cbw.field.dCBWDataTransferLength > 0U) {
129                 usbh_data_recev (puhost->data,
130                                  msc->bot.pbuf,
131                                  msc->pipe_in,
132                                  msc->ep_size_in);
133             } else {
134                 msc->bot.state = BOT_RECEIVE_CSW;
135             }
136         } else if(URB_STALL == urb_status) {
137             /* this is data stage stall condition */
138             msc->bot.state = BOT_ERROR_IN;
139         } else {
140             /* no operation */
141         }
142         break;
143 
144     case BOT_DATA_OUT:
145         usbh_data_send (puhost->data,
146                         msc->bot.pbuf,
147                         msc->pipe_out,
148                         msc->ep_size_out);
149 
150         msc->bot.state = BOT_DATA_OUT_WAIT;
151         break;
152 
153     case BOT_DATA_OUT_WAIT:
154         /* BOT DATA OUT stage */
155         urb_status = usbh_urbstate_get(puhost->data, msc->pipe_out);
156         if (URB_DONE == urb_status) {
157             if (msc->bot.cbw.field.dCBWDataTransferLength > msc->ep_size_out) {
158                 msc->bot.pbuf += msc->ep_size_out;
159                 msc->bot.cbw.field.dCBWDataTransferLength -= msc->ep_size_out;
160             }  else {
161                 msc->bot.cbw.field.dCBWDataTransferLength = 0; /* reset this value and keep in same state */
162             }
163 
164             if (msc->bot.cbw.field.dCBWDataTransferLength > 0) {
165                 usbh_data_send (puhost->data,
166                                 msc->bot.pbuf,
167                                 msc->pipe_out,
168                                 msc->ep_size_out);
169             } else {
170                 msc->bot.state = BOT_RECEIVE_CSW;
171             }
172         } else if (URB_NOTREADY == urb_status) {
173             msc->bot.state = BOT_DATA_OUT;
174         } else if (URB_STALL == urb_status) {
175             msc->bot.state = BOT_ERROR_OUT;
176         } else {
177             /* no operation */
178         }
179         break;
180 
181     case BOT_RECEIVE_CSW:
182         /* BOT CSW stage */
183         usbh_data_recev (puhost->data,
184                          msc->bot.csw.CSWArray,
185                          msc->pipe_in,
186                          BBB_CSW_LENGTH);
187 
188         msc->bot.state = BOT_RECEIVE_CSW_WAIT;
189         break;
190 
191     case BOT_RECEIVE_CSW_WAIT:
192         urb_status = usbh_urbstate_get(puhost->data, msc->pipe_in);
193 
194         /* decode CSW */
195         if (URB_DONE == urb_status) {
196             msc->bot.state = BOT_SEND_CBW;
197             msc->bot.cmd_state = BOT_CMD_SEND;
198 
199             csw_status = usbh_msc_csw_decode(puhost);
200             if (BOT_CSW_CMD_PASSED == csw_status) {
201                 status = USBH_OK;
202             } else {
203                 status = USBH_FAIL;
204             }
205         } else if (URB_STALL == urb_status) {
206             msc->bot.state = BOT_ERROR_IN;
207         } else {
208             /* no operation */
209         }
210         break;
211 
212     case BOT_ERROR_IN:
213         error = usbh_msc_bot_abort(puhost, USBH_MSC_DIR_IN);
214 
215         if (USBH_OK == error) {
216             msc->bot.state = BOT_RECEIVE_CSW;
217         } else if (USBH_UNRECOVERED_ERROR == status) {
218             /* this means that there is a stall error limit, do reset recovery */
219             msc->bot.state = BOT_UNRECOVERED_ERROR;
220         } else {
221             /* no operation */
222         }
223         break;
224 
225     case BOT_ERROR_OUT:
226         status = usbh_msc_bot_abort (puhost, USBH_MSC_DIR_OUT);
227 
228         if (USBH_OK == status) {
229             uint8_t toggle = usbh_pipe_toggle_get(puhost->data, msc->pipe_out);
230             usbh_pipe_toggle_set(puhost->data, msc->pipe_out, 1U - toggle);
231             usbh_pipe_toggle_set(puhost->data, msc->pipe_in, 0U);
232             msc->bot.state = BOT_ERROR_IN;
233         } else {
234             if (USBH_UNRECOVERED_ERROR == status) {
235                 msc->bot.state = BOT_UNRECOVERED_ERROR;
236             }
237         }
238         break;
239 
240     case BOT_UNRECOVERED_ERROR:
241         status = usbh_msc_bot_reset(puhost);
242         if (USBH_OK == status) {
243             msc->bot.state = BOT_SEND_CBW;
244         }
245         break;
246 
247     default:
248         break;
249     }
250 
251     return status;
252 }
253 
254 /*!
255     \brief      manages the different error handling for stall
256     \param[in]  puhost: pointer to usb host handler
257     \param[in]  direction: data IN or OUT
258     \param[out] none
259     \retval     operation status
260 */
usbh_msc_bot_abort(usbh_host * puhost,uint8_t direction)261 usbh_status usbh_msc_bot_abort (usbh_host *puhost, uint8_t direction)
262 {
263     usbh_status status = USBH_BUSY;
264     usbh_msc_handler *msc = (usbh_msc_handler *)puhost->active_class->class_data;
265 
266     switch (direction) {
267     case USBH_MSC_DIR_IN :
268         /* send clrfeture command on bulk IN endpoint */
269         status = usbh_clrfeature(puhost,
270                                  msc->ep_in,
271                                  msc->pipe_in);
272         break;
273 
274     case USBH_MSC_DIR_OUT :
275         /*send clrfeature command on bulk OUT endpoint */
276         status = usbh_clrfeature(puhost,
277                                  msc->ep_out,
278                                  msc->pipe_out);
279         break;
280 
281     default:
282         break;
283     }
284 
285     return status;
286 }
287 
288 /*!
289     \brief      reset msc bot transfer
290     \param[in]  puhost: pointer to usb host handler
291     \param[out] none
292     \retval     operation status
293 */
usbh_msc_bot_reset(usbh_host * puhost)294 usbh_status usbh_msc_bot_reset (usbh_host *puhost)
295 {
296     usbh_status status = USBH_BUSY;
297 
298     if (CTL_IDLE == puhost->control.ctl_state) {
299         puhost->control.setup.req = (usb_req) {
300             .bmRequestType = USB_TRX_OUT | USB_REQTYPE_CLASS | USB_RECPTYPE_ITF,
301             .bRequest      = BBB_RESET,
302             .wValue        = 0U,
303             .wIndex        = 0U,
304             .wLength       = 0U
305         };
306 
307         usbh_ctlstate_config (puhost, NULL, 0U);
308     }
309 
310     status = usbh_ctl_handler (puhost);
311 
312     return status;
313 }
314 
315 /*!
316     \brief      decode the CSW received by the device and updates the same to upper layer
317     \param[in]  puhost: pointer to usb host
318     \param[out] none
319     \retval     on success USBH_MSC_OK, on failure USBH_MSC_FAIL
320     \notes
321           Refer to USB Mass-Storage Class: BOT (www.usb.org)
322           6.3.1 Valid CSW Conditions :
323           The host shall consider the CSW valid when:
324           1. dCSWSignature is equal to 53425355h
325           2. the CSW is 13 (Dh) bytes in length,
326           3. dCSWTag matches the dCBWTag from the corresponding CBW.
327 */
usbh_msc_csw_decode(usbh_host * puhost)328 bot_csw_status usbh_msc_csw_decode (usbh_host *puhost)
329 {
330     bot_csw_status status = BOT_CSW_CMD_FAILED;
331     usbh_msc_handler *msc = (usbh_msc_handler *)puhost->active_class->class_data;
332 
333     /* checking if the transfer length is different than 13 */
334     if (BBB_CSW_LENGTH != usbh_xfercount_get (puhost->data, msc->pipe_in)) {
335         status = BOT_CSW_PHASE_ERROR;
336     } else {
337         /* CSW length is correct */
338 
339         /* check validity of the CSW Signature and CSWStatus */
340         if (BBB_CSW_SIGNATURE == msc->bot.csw.field.dCSWSignature) {
341             /* check condition 1. dCSWSignature is equal to 53425355h */
342             if (msc->bot.csw.field.dCSWTag == msc->bot.cbw.field.dCBWTag) {
343                 /* check condition 3. dCSWTag matches the dCBWTag from the corresponding CBW */
344                 if (0U == msc->bot.csw.field.bCSWStatus) {
345                     status = BOT_CSW_CMD_PASSED;
346                 } else if (1U == msc->bot.csw.field.bCSWStatus) {
347                     status = BOT_CSW_CMD_FAILED;
348                 } else if (2U == msc->bot.csw.field.bCSWStatus) {
349                     status = BOT_CSW_PHASE_ERROR;
350                 } else {
351                     /* no operation */
352                 }
353             }
354         } else {
355             /* If the CSW signature is not valid, we will return the phase error to
356                upper layers for reset recovery */
357             status = BOT_CSW_PHASE_ERROR;
358         }
359     }
360 
361     return status;
362 }
363