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