1 /*!
2 \file usbd_msc_bbb.c
3 \brief USB BBB(Bulk/Bulk/Bulk) protocol core functions
4 \note BBB means Bulk-only transport protocol for USB MSC
5
6 \version 2020-08-04, V1.1.0, firmware for GD32VF103
7 */
8
9 /*
10 Copyright (c) 2020, GigaDevice Semiconductor Inc.
11
12 Redistribution and use in source and binary forms, with or without modification,
13 are permitted provided that the following conditions are met:
14
15 1. Redistributions of source code must retain the above copyright notice, this
16 list of conditions and the following disclaimer.
17 2. Redistributions in binary form must reproduce the above copyright notice,
18 this list of conditions and the following disclaimer in the documentation
19 and/or other materials provided with the distribution.
20 3. Neither the name of the copyright holder nor the names of its contributors
21 may be used to endorse or promote products derived from this software without
22 specific prior written permission.
23
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
33 OF SUCH DAMAGE.
34 */
35
36 #include "usbd_enum.h"
37 #include "usbd_msc_bbb.h"
38
39 /* local function prototypes ('static') */
40 static void msc_bbb_cbw_decode (usb_core_driver *pudev);
41 static void msc_bbb_data_send (usb_core_driver *pudev, uint8_t *pbuf, uint32_t Len);
42 static void msc_bbb_abort (usb_core_driver *pudev);
43
44 /*!
45 \brief initialize the bbb process
46 \param[in] pudev: pointer to USB device instance
47 \param[out] none
48 \retval none
49 */
msc_bbb_init(usb_core_driver * pudev)50 void msc_bbb_init (usb_core_driver *pudev)
51 {
52 uint8_t lun_num;
53
54 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
55
56 msc->bbb_state = BBB_IDLE;
57 msc->bbb_status = BBB_STATUS_NORMAL;
58
59 /* init the storage logic unit */
60 for(lun_num = 0U; lun_num < MEM_LUN_NUM; lun_num++) {
61 usbd_mem_fops->mem_init(lun_num);
62 }
63
64 /* flush the Rx FIFO */
65 usbd_fifo_flush (pudev, MSC_OUT_EP);
66
67 /* flush the Tx FIFO */
68 usbd_fifo_flush (pudev, MSC_IN_EP);
69
70 /* prepare endpoint to receive the first BBB CBW */
71 usbd_ep_recev (pudev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
72 }
73
74 /*!
75 \brief reset the BBB machine
76 \param[in] pudev: pointer to USB device instance
77 \param[out] none
78 \retval none
79 */
msc_bbb_reset(usb_core_driver * pudev)80 void msc_bbb_reset (usb_core_driver *pudev)
81 {
82 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
83
84 msc->bbb_state = BBB_IDLE;
85 msc->bbb_status = BBB_STATUS_RECOVERY;
86
87 /* prepare endpoint to receive the first BBB command */
88 usbd_ep_recev (pudev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
89 }
90
91 /*!
92 \brief de-initialize the BBB machine
93 \param[in] pudev: pointer to USB device instance
94 \param[out] none
95 \retval none
96 */
msc_bbb_deinit(usb_core_driver * pudev)97 void msc_bbb_deinit (usb_core_driver *pudev)
98 {
99 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
100
101 msc->bbb_state = BBB_IDLE;
102 }
103
104 /*!
105 \brief handle BBB data IN stage
106 \param[in] pudev: pointer to USB device instance
107 \param[in] ep_num: endpoint number
108 \param[out] none
109 \retval none
110 */
msc_bbb_data_in(usb_core_driver * pudev,uint8_t ep_num)111 void msc_bbb_data_in (usb_core_driver *pudev, uint8_t ep_num)
112 {
113 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
114
115 switch (msc->bbb_state) {
116 case BBB_DATA_IN:
117 if (scsi_process_cmd (pudev, msc->bbb_cbw.bCBWLUN, &msc->bbb_cbw.CBWCB[0]) < 0) {
118 msc_bbb_csw_send (pudev, CSW_CMD_FAILED);
119 }
120 break;
121
122 case BBB_SEND_DATA:
123 case BBB_LAST_DATA_IN:
124 msc_bbb_csw_send (pudev, CSW_CMD_PASSED);
125 break;
126
127 default:
128 break;
129 }
130 }
131
132 /*!
133 \brief handle BBB data OUT stage
134 \param[in] pudev: pointer to USB device instance
135 \param[in] ep_num: endpoint number
136 \param[out] none
137 \retval none
138 */
msc_bbb_data_out(usb_core_driver * pudev,uint8_t ep_num)139 void msc_bbb_data_out (usb_core_driver *pudev, uint8_t ep_num)
140 {
141 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
142
143 switch (msc->bbb_state) {
144 case BBB_IDLE:
145 msc_bbb_cbw_decode (pudev);
146 break;
147
148 case BBB_DATA_OUT:
149 if (scsi_process_cmd (pudev, msc->bbb_cbw.bCBWLUN, &msc->bbb_cbw.CBWCB[0]) < 0) {
150 msc_bbb_csw_send (pudev, CSW_CMD_FAILED);
151 }
152 break;
153
154 default:
155 break;
156 }
157 }
158
159 /*!
160 \brief send the CSW(command status wrapper)
161 \param[in] pudev: pointer to USB device instance
162 \param[in] csw_status: CSW status
163 \param[out] none
164 \retval none
165 */
msc_bbb_csw_send(usb_core_driver * pudev,uint8_t csw_status)166 void msc_bbb_csw_send (usb_core_driver *pudev, uint8_t csw_status)
167 {
168 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
169
170 msc->bbb_csw.dCSWSignature = BBB_CSW_SIGNATURE;
171 msc->bbb_csw.bCSWStatus = csw_status;
172 msc->bbb_state = BBB_IDLE;
173
174 usbd_ep_send (pudev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
175
176 /* prapare endpoint to receive next command */
177 usbd_ep_recev (pudev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
178 }
179
180 /*!
181 \brief complete the clear feature request
182 \param[in] pudev: pointer to USB device instance
183 \param[in] ep_num: endpoint number
184 \param[out] none
185 \retval none
186 */
msc_bbb_clrfeature(usb_core_driver * pudev,uint8_t ep_num)187 void msc_bbb_clrfeature (usb_core_driver *pudev, uint8_t ep_num)
188 {
189 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
190
191 if (msc->bbb_status == BBB_STATUS_ERROR)/* bad CBW signature */ {
192 usbd_ep_stall(pudev, MSC_IN_EP);
193
194 msc->bbb_status = BBB_STATUS_NORMAL;
195 } else if(((ep_num & 0x80U) == 0x80U) && (msc->bbb_status != BBB_STATUS_RECOVERY)) {
196 msc_bbb_csw_send (pudev, CSW_CMD_FAILED);
197 } else {
198
199 }
200 }
201
202 /*!
203 \brief decode the CBW command and set the BBB state machine accordingly
204 \param[in] pudev: pointer to USB device instance
205 \param[out] none
206 \retval none
207 */
msc_bbb_cbw_decode(usb_core_driver * pudev)208 static void msc_bbb_cbw_decode (usb_core_driver *pudev)
209 {
210 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
211
212 msc->bbb_csw.dCSWTag = msc->bbb_cbw.dCBWTag;
213 msc->bbb_csw.dCSWDataResidue = msc->bbb_cbw.dCBWDataTransferLength;
214
215 if ((BBB_CBW_LENGTH != usbd_rxcount_get (pudev, MSC_OUT_EP)) ||
216 (BBB_CBW_SIGNATURE != msc->bbb_cbw.dCBWSignature)||
217 (msc->bbb_cbw.bCBWLUN > 1U) ||
218 (msc->bbb_cbw.bCBWCBLength < 1U) ||
219 (msc->bbb_cbw.bCBWCBLength > 16U)) {
220 /* illegal command handler */
221 scsi_sense_code (pudev, msc->bbb_cbw.bCBWLUN, ILLEGAL_REQUEST, INVALID_CDB);
222
223 msc->bbb_status = BBB_STATUS_ERROR;
224
225 msc_bbb_abort (pudev);
226 } else {
227 if (scsi_process_cmd (pudev, msc->bbb_cbw.bCBWLUN, &msc->bbb_cbw.CBWCB[0]) < 0) {
228 msc_bbb_abort (pudev);
229 } else if ((BBB_DATA_IN != msc->bbb_state) &&
230 (BBB_DATA_OUT != msc->bbb_state) &&
231 (BBB_LAST_DATA_IN != msc->bbb_state)) { /* burst xfer handled internally */
232 if (msc->bbb_datalen > 0U) {
233 msc_bbb_data_send (pudev, msc->bbb_data, msc->bbb_datalen);
234 } else if (0U == msc->bbb_datalen) {
235 msc_bbb_csw_send (pudev, CSW_CMD_PASSED);
236 } else {
237
238 }
239 } else {
240
241 }
242 }
243 }
244
245 /*!
246 \brief send the requested data
247 \param[in] pudev: pointer to USB device instance
248 \param[in] buf: pointer to data buffer
249 \param[in] len: data length
250 \param[out] none
251 \retval none
252 */
msc_bbb_data_send(usb_core_driver * pudev,uint8_t * buf,uint32_t len)253 static void msc_bbb_data_send (usb_core_driver *pudev, uint8_t *buf, uint32_t len)
254 {
255 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
256
257 len = USB_MIN (msc->bbb_cbw.dCBWDataTransferLength, len);
258
259 msc->bbb_csw.dCSWDataResidue -= len;
260 msc->bbb_csw.bCSWStatus = CSW_CMD_PASSED;
261 msc->bbb_state = BBB_SEND_DATA;
262
263 usbd_ep_send (pudev, MSC_IN_EP, buf, len);
264 }
265
266 /*!
267 \brief abort the current transfer
268 \param[in] pudev: pointer to USB device instance
269 \param[out] none
270 \retval none
271 */
msc_bbb_abort(usb_core_driver * pudev)272 static void msc_bbb_abort (usb_core_driver *pudev)
273 {
274 usbd_msc_handler *msc = (usbd_msc_handler *)pudev->dev.class_data[USBD_MSC_INTERFACE];
275
276 if ((0U == msc->bbb_cbw.bmCBWFlags) &&
277 (0U != msc->bbb_cbw.dCBWDataTransferLength) &&
278 (BBB_STATUS_NORMAL == msc->bbb_status)) {
279 usbd_ep_stall(pudev, MSC_OUT_EP);
280 }
281
282 usbd_ep_stall(pudev, MSC_IN_EP);
283
284 if (msc->bbb_status == BBB_STATUS_ERROR) {
285 usbd_ep_recev (pudev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
286 }
287 }
288