1 /*!
2 \file usbh_transc.c
3 \brief USB host mode transactions driver
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 "drv_usb_hw.h"
36 #include "usbh_pipe.h"
37 #include "usbh_transc.h"
38
39 /* local function prototypes ('static') */
40 static usb_urb_state usbh_urb_wait (usbh_host *puhost, uint8_t pp_num, uint32_t wait_time);
41 static void usbh_setup_transc (usbh_host *puhost);
42 static void usbh_data_in_transc (usbh_host *puhost);
43 static void usbh_data_out_transc (usbh_host *puhost);
44 static void usbh_status_in_transc (usbh_host *puhost);
45 static void usbh_status_out_transc (usbh_host *puhost);
46 static uint32_t usbh_request_submit (usb_core_driver *pudev, uint8_t pp_num);
47
48 /*!
49 \brief send the setup packet to the USB device
50 \param[in] pudev: pointer to usb core instance
51 \param[in] buf: data buffer which will be sent to USB device
52 \param[in] pp_num: pipe number
53 \param[out] none
54 \retval operation status
55 */
usbh_ctlsetup_send(usb_core_driver * pudev,uint8_t * buf,uint8_t pp_num)56 usbh_status usbh_ctlsetup_send (usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num)
57 {
58 usb_pipe *pp = &pudev->host.pipe[pp_num];
59
60 pp->DPID = PIPE_DPID_SETUP;
61 pp->xfer_buf = buf;
62 pp->xfer_len = USB_SETUP_PACKET_LEN;
63
64 return (usbh_status)usbh_request_submit (pudev, pp_num);
65 }
66
67 /*!
68 \brief send a data packet to the USB device
69 \param[in] pudev: pointer to usb core instance
70 \param[in] buf: data buffer which will be sent to USB device
71 \param[in] pp_num: pipe number
72 \param[in] len: length of the data to be sent
73 \param[out] none
74 \retval operation status
75 */
usbh_data_send(usb_core_driver * pudev,uint8_t * buf,uint8_t pp_num,uint16_t len)76 usbh_status usbh_data_send (usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num, uint16_t len)
77 {
78 usb_pipe *pp = &pudev->host.pipe[pp_num];
79
80 pp->xfer_buf = buf;
81 pp->xfer_len = len;
82
83 switch (pp->ep.type) {
84 case USB_EPTYPE_CTRL:
85 if (0U == len) {
86 pp->data_toggle_out = 1U;
87 }
88
89 pp->DPID = PIPE_DPID[pp->data_toggle_out];
90 break;
91
92 case USB_EPTYPE_INTR:
93 pp->DPID = PIPE_DPID[pp->data_toggle_out];
94
95 pp->data_toggle_out ^= 1U;
96 break;
97
98 case USB_EPTYPE_BULK:
99 pp->DPID = PIPE_DPID[pp->data_toggle_out];
100 break;
101
102 case USB_EPTYPE_ISOC:
103 pp->DPID = PIPE_DPID[0];
104 break;
105
106 default:
107 break;
108 }
109
110 usbh_request_submit (pudev, pp_num);
111
112 return USBH_OK;
113 }
114
115 /*!
116 \brief receive a data packet from the USB device
117 \param[in] pudev: pointer to usb core instance
118 \param[in] buf: data buffer which will be received from USB device
119 \param[in] pp_num: pipe number
120 \param[in] len: length of the data to be received
121 \param[out] none
122 \retval operation status
123 */
usbh_data_recev(usb_core_driver * pudev,uint8_t * buf,uint8_t pp_num,uint16_t len)124 usbh_status usbh_data_recev (usb_core_driver *pudev, uint8_t *buf, uint8_t pp_num, uint16_t len)
125 {
126 usb_pipe *pp = &pudev->host.pipe[pp_num];
127
128 pp->xfer_buf = buf;
129 pp->xfer_len = len;
130
131 switch (pp->ep.type) {
132 case USB_EPTYPE_CTRL:
133 pp->DPID = PIPE_DPID[1];
134 break;
135
136 case USB_EPTYPE_INTR:
137 pp->DPID = PIPE_DPID[pp->data_toggle_in];
138
139 /* Toggle DATA PID */
140 pp->data_toggle_in ^= 1U;
141 break;
142
143 case USB_EPTYPE_BULK:
144 pp->DPID = PIPE_DPID[pp->data_toggle_in];
145 break;
146
147 case USB_EPTYPE_ISOC:
148 pp->DPID = PIPE_DPID[0];
149 break;
150
151 default:
152 break;
153 }
154
155 usbh_request_submit (pudev, pp_num);
156
157 return USBH_OK;
158 }
159
160 /*!
161 \brief USB control transfer handler
162 \param[in] puhost: pointer to USB host
163 \param[out] none
164 \retval operation status
165 */
usbh_ctl_handler(usbh_host * puhost)166 usbh_status usbh_ctl_handler (usbh_host *puhost)
167 {
168 usbh_status status = USBH_BUSY;
169
170 switch (puhost->control.ctl_state) {
171 case CTL_SETUP:
172 usbh_setup_transc (puhost);
173 break;
174
175 case CTL_DATA_IN:
176 usbh_data_in_transc (puhost);
177 break;
178
179 case CTL_DATA_OUT:
180 usbh_data_out_transc (puhost);
181 break;
182
183 case CTL_STATUS_IN:
184 usbh_status_in_transc (puhost);
185 break;
186
187 case CTL_STATUS_OUT:
188 usbh_status_out_transc (puhost);
189 break;
190
191 case CTL_FINISH:
192 puhost->control.ctl_state = CTL_IDLE;
193
194 status = USBH_OK;
195 break;
196
197 case CTL_ERROR:
198 if (++puhost->control.error_count <= USBH_MAX_ERROR_COUNT) {
199 /* do the transmission again, starting from SETUP packet */
200 puhost->control.ctl_state = CTL_SETUP;
201 } else {
202 status = USBH_FAIL;
203 }
204 break;
205
206 default:
207 break;
208 }
209
210 return status;
211 }
212
213 /*!
214 \brief wait for USB URB(USB request block) state
215 \param[in] puhost: pointer to USB host
216 \param[in] pp_num: pipe number
217 \param[in] wait_time: wait time
218 \param[out] none
219 \retval USB URB state
220 */
usbh_urb_wait(usbh_host * puhost,uint8_t pp_num,uint32_t wait_time)221 static usb_urb_state usbh_urb_wait (usbh_host *puhost, uint8_t pp_num, uint32_t wait_time)
222 {
223 usb_urb_state urb_status = URB_IDLE;
224
225 while (URB_DONE != (urb_status = usbh_urbstate_get(puhost->data, pp_num))) {
226 if (URB_NOTREADY == urb_status) {
227 break;
228 } else if (URB_STALL == urb_status) {
229 puhost->control.ctl_state = CTL_SETUP;
230 break;
231 } else if (URB_ERROR == urb_status) {
232 puhost->control.ctl_state = CTL_ERROR;
233 break;
234 } else if ((wait_time > 0U) && ((usb_curframe_get(puhost->data)- puhost->control.timer) > wait_time)) {
235 /* timeout for in transfer */
236 puhost->control.ctl_state = CTL_ERROR;
237 break;
238 } else {
239 /* no operation, just wait */
240 }
241 }
242
243 return urb_status;
244 }
245
246 /*!
247 \brief USB setup transaction
248 \param[in] puhost: pointer to USB host
249 \param[out] none
250 \retval none
251 */
usbh_setup_transc(usbh_host * puhost)252 static void usbh_setup_transc (usbh_host *puhost)
253 {
254 /* send a SETUP packet */
255 usbh_ctlsetup_send (puhost->data,
256 puhost->control.setup.data,
257 puhost->control.pipe_out_num);
258
259 if (URB_DONE == usbh_urb_wait (puhost, puhost->control.pipe_out_num, 0U)) {
260 uint8_t dir = (puhost->control.setup.req.bmRequestType & USB_TRX_MASK);
261
262 if (puhost->control.setup.req.wLength) {
263 if (USB_TRX_IN == dir) {
264 puhost->control.ctl_state = CTL_DATA_IN;
265 } else {
266 puhost->control.ctl_state = CTL_DATA_OUT;
267 }
268 } else {
269 if (USB_TRX_IN == dir) {
270 puhost->control.ctl_state = CTL_STATUS_OUT;
271 } else {
272 puhost->control.ctl_state = CTL_STATUS_IN;
273 }
274 }
275
276 /* set the delay timer to enable timeout for data stage completion */
277 puhost->control.timer = (uint16_t)usb_curframe_get(puhost->data);
278 }
279 }
280
281 /*!
282 \brief USB data IN transaction
283 \param[in] puhost: pointer to USB host
284 \param[out] none
285 \retval none
286 */
usbh_data_in_transc(usbh_host * puhost)287 static void usbh_data_in_transc (usbh_host *puhost)
288 {
289 usbh_data_recev (puhost->data,
290 puhost->control.buf,
291 puhost->control.pipe_in_num,
292 puhost->control.ctl_len);
293
294 if (URB_DONE == usbh_urb_wait (puhost, puhost->control.pipe_in_num, DATA_STAGE_TIMEOUT)) {
295 puhost->control.ctl_state = CTL_STATUS_OUT;
296
297 puhost->control.timer = (uint16_t)usb_curframe_get(puhost->data);
298 }
299 }
300
301 /*!
302 \brief USB data OUT transaction
303 \param[in] puhost: pointer to USB host
304 \param[out] none
305 \retval none
306 */
usbh_data_out_transc(usbh_host * puhost)307 static void usbh_data_out_transc (usbh_host *puhost)
308 {
309 usbh_pipe_toggle_set(puhost->data, puhost->control.pipe_out_num, 1U);
310
311 usbh_data_send (puhost->data,
312 puhost->control.buf,
313 puhost->control.pipe_out_num,
314 puhost->control.ctl_len);
315
316 if (URB_DONE == usbh_urb_wait (puhost, puhost->control.pipe_out_num, DATA_STAGE_TIMEOUT)) {
317 puhost->control.ctl_state = CTL_STATUS_IN;
318
319 puhost->control.timer = (uint16_t)usb_curframe_get(puhost->data);
320 }
321 }
322
323 /*!
324 \brief USB status IN transaction
325 \param[in] puhost: pointer to USB host
326 \param[out] none
327 \retval none
328 */
usbh_status_in_transc(usbh_host * puhost)329 static void usbh_status_in_transc (usbh_host *puhost)
330 {
331 uint8_t pp_num = puhost->control.pipe_in_num;
332
333 usbh_data_recev (puhost->data, NULL, pp_num, 0U);
334
335 if (URB_DONE == usbh_urb_wait (puhost, pp_num, NODATA_STAGE_TIMEOUT)) {
336 puhost->control.ctl_state = CTL_FINISH;
337 }
338 }
339
340 /*!
341 \brief USB status OUT transaction
342 \param[in] puhost: pointer to USB host
343 \param[out] none
344 \retval none
345 */
usbh_status_out_transc(usbh_host * puhost)346 static void usbh_status_out_transc (usbh_host *puhost)
347 {
348 uint8_t pp_num = puhost->control.pipe_out_num;
349
350 usbh_data_send (puhost->data, NULL, pp_num, 0U);
351
352 if (URB_DONE == usbh_urb_wait (puhost, pp_num, NODATA_STAGE_TIMEOUT)) {
353 puhost->control.ctl_state = CTL_FINISH;
354 }
355 }
356
357 /*!
358 \brief prepare a pipe and start a transfer
359 \param[in] pudev: pointer to usb core instance
360 \param[in] pp_num: pipe number
361 \param[out] none
362 \retval operation status
363 */
usbh_request_submit(usb_core_driver * pudev,uint8_t pp_num)364 static uint32_t usbh_request_submit (usb_core_driver *pudev, uint8_t pp_num)
365 {
366 pudev->host.pipe[pp_num].urb_state = URB_IDLE;
367 pudev->host.pipe[pp_num].xfer_count = 0U;
368
369 return (uint32_t)usb_pipe_xfer (pudev, pp_num);
370 }
371