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