1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ddk/debug.h>
6
7#include "dwc3.h"
8#include "dwc3-regs.h"
9#include "dwc3-types.h"
10
11#include <stdio.h>
12#include <string.h>
13
14#define EP0_LOCK(dwc)   (&(dwc)->eps[EP0_OUT].lock)
15
16static void dwc3_queue_setup_locked(dwc3_t* dwc) {
17    io_buffer_cache_flush_invalidate(&dwc->ep0_buffer, 0, sizeof(usb_setup_t));
18    dwc3_ep_start_transfer(dwc, EP0_OUT, TRB_TRBCTL_SETUP, io_buffer_phys(&dwc->ep0_buffer),
19                           sizeof(usb_setup_t), false);
20    dwc->ep0_state = EP0_STATE_SETUP;
21}
22
23zx_status_t dwc3_ep0_init(dwc3_t* dwc) {
24    // fifo only needed for physical endpoint 0
25    zx_status_t status = dwc3_ep_fifo_init(dwc, EP0_OUT);
26    if (status != ZX_OK) {
27        return status;
28    }
29
30    for (unsigned i = EP0_OUT; i <= EP0_IN; i++) {
31        dwc3_endpoint_t* ep = &dwc->eps[i];
32        ep->enabled = false;
33        ep->max_packet_size = EP0_MAX_PACKET_SIZE;
34        ep->type = USB_ENDPOINT_CONTROL;
35        ep->interval = 0;
36    }
37
38    return ZX_OK;
39}
40
41void dwc3_ep0_reset(dwc3_t* dwc) {
42    mtx_lock(EP0_LOCK(dwc));
43    dwc3_cmd_ep_end_transfer(dwc, EP0_OUT);
44    dwc->ep0_state = EP0_STATE_NONE;
45    mtx_unlock(EP0_LOCK(dwc));
46}
47
48void dwc3_ep0_start(dwc3_t* dwc) {
49    mtx_lock(EP0_LOCK(dwc));
50    dwc3_cmd_start_new_config(dwc, EP0_OUT, 0);
51    dwc3_ep_set_config(dwc, EP0_OUT, true);
52    dwc3_ep_set_config(dwc, EP0_IN, true);
53
54    dwc3_queue_setup_locked(dwc);
55    mtx_unlock(EP0_LOCK(dwc));
56}
57
58static zx_status_t dwc3_handle_setup(dwc3_t* dwc, usb_setup_t* setup, void* buffer, size_t length,
59                                     size_t* out_actual) {
60    zx_status_t status;
61
62    if (setup->bmRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) {
63        // handle some special setup requests in this driver
64        switch (setup->bRequest) {
65        case USB_REQ_SET_ADDRESS:
66            zxlogf(TRACE, "SET_ADDRESS %d\n", setup->wValue);
67            dwc3_set_address(dwc, setup->wValue);
68            *out_actual = 0;
69            return ZX_OK;
70        case USB_REQ_SET_CONFIGURATION:
71            zxlogf(TRACE, "SET_CONFIGURATION %d\n", setup->wValue);
72            dwc3_reset_configuration(dwc);
73            dwc->configured = false;
74            status = usb_dci_control(&dwc->dci_intf, setup, buffer, length, out_actual);
75            if (status == ZX_OK && setup->wValue) {
76                dwc->configured = true;
77                dwc3_start_eps(dwc);
78            }
79            return status;
80        default:
81            // fall through to usb_dci_control()
82            break;
83        }
84    } else if (setup->bmRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) &&
85               setup->bRequest == USB_REQ_SET_INTERFACE) {
86        zxlogf(TRACE, "SET_INTERFACE %d\n", setup->wValue);
87        dwc3_reset_configuration(dwc);
88        dwc->configured = false;
89        status = usb_dci_control(&dwc->dci_intf, setup, buffer, length, out_actual);
90        if (status == ZX_OK) {
91            dwc->configured = true;
92            dwc3_start_eps(dwc);
93        }
94        return status;
95    }
96
97    return usb_dci_control(&dwc->dci_intf, setup, buffer, length, out_actual);
98}
99
100void dwc3_ep0_xfer_not_ready(dwc3_t* dwc, unsigned ep_num, unsigned stage) {
101    mtx_lock(EP0_LOCK(dwc));
102
103    switch (dwc->ep0_state) {
104    case EP0_STATE_SETUP:
105        if (stage == DEPEVT_XFER_NOT_READY_STAGE_DATA ||
106            stage == DEPEVT_XFER_NOT_READY_STAGE_STATUS) {
107            // Stall if we receive xfer not ready data/status while waiting for setup to complete
108           dwc3_cmd_ep_set_stall(dwc, EP0_OUT);
109           dwc3_queue_setup_locked(dwc);
110        }
111        break;
112    case EP0_STATE_DATA_OUT:
113        if (ep_num == EP0_IN && stage == DEPEVT_XFER_NOT_READY_STAGE_DATA) {
114            // end transfer and stall if we receive xfer not ready in the opposite direction
115            dwc3_cmd_ep_end_transfer(dwc, EP0_OUT);
116            dwc3_cmd_ep_set_stall(dwc, EP0_OUT);
117            dwc3_queue_setup_locked(dwc);
118        }
119        break;
120    case EP0_STATE_DATA_IN:
121        if (ep_num == EP0_OUT && stage == DEPEVT_XFER_NOT_READY_STAGE_DATA) {
122            // end transfer and stall if we receive xfer not ready in the opposite direction
123            dwc3_cmd_ep_end_transfer(dwc, EP0_IN);
124            dwc3_cmd_ep_set_stall(dwc, EP0_OUT);
125            dwc3_queue_setup_locked(dwc);
126        }
127        break;
128    case EP0_STATE_WAIT_NRDY_OUT:
129        if (ep_num == EP0_OUT) {
130            if (dwc->cur_setup.wLength > 0) {
131                dwc3_ep_start_transfer(dwc, EP0_OUT, TRB_TRBCTL_STATUS_3, 0, 0, false);
132            } else {
133                dwc3_ep_start_transfer(dwc, EP0_OUT, TRB_TRBCTL_STATUS_2, 0, 0, false);
134            }
135            dwc->ep0_state = EP0_STATE_STATUS;
136        }
137        break;
138    case EP0_STATE_WAIT_NRDY_IN:
139        if (ep_num == EP0_IN) {
140            if (dwc->cur_setup.wLength > 0) {
141                dwc3_ep_start_transfer(dwc, EP0_IN, TRB_TRBCTL_STATUS_3, 0, 0, false);
142            } else {
143                dwc3_ep_start_transfer(dwc, EP0_IN, TRB_TRBCTL_STATUS_2, 0, 0, false);
144            }
145            dwc->ep0_state = EP0_STATE_STATUS;
146        }
147        break;
148    default:
149        zxlogf(ERROR, "dwc3_ep0_xfer_not_ready unhandled state %u\n", dwc->ep0_state);
150        break;
151    }
152
153    mtx_unlock(EP0_LOCK(dwc));
154}
155
156void dwc3_ep0_xfer_complete(dwc3_t* dwc, unsigned ep_num) {
157    mtx_lock(EP0_LOCK(dwc));
158
159    switch (dwc->ep0_state) {
160    case EP0_STATE_SETUP: {
161        usb_setup_t* setup = &dwc->cur_setup;
162
163        void* vaddr = io_buffer_virt(&dwc->ep0_buffer);
164        zx_paddr_t paddr = io_buffer_phys(&dwc->ep0_buffer);
165        memcpy(setup, vaddr, sizeof(*setup));
166
167        zxlogf(TRACE, "got setup: type: 0x%02X req: %d value: %d index: %d length: %d\n",
168                setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex,
169                setup->wLength);
170
171        bool is_out = ((setup->bmRequestType & USB_DIR_MASK) == USB_DIR_OUT);
172        if (setup->wLength > 0 && is_out) {
173            // queue a read for the data phase
174            dwc3_ep_start_transfer(dwc, EP0_OUT, TRB_TRBCTL_CONTROL_DATA, paddr, setup->wLength,
175                                   false);
176            dwc->ep0_state = EP0_STATE_DATA_OUT;
177        } else {
178            size_t actual;
179            zx_status_t status = dwc3_handle_setup(dwc, setup, vaddr, dwc->ep0_buffer.size,
180                                                   &actual);
181            zxlogf(TRACE, "dwc3_handle_setup returned %d actual %zu\n", status, actual);
182            if (status != ZX_OK) {
183                dwc3_cmd_ep_set_stall(dwc, EP0_OUT);
184                dwc3_queue_setup_locked(dwc);
185                break;
186            }
187
188            if (setup->wLength > 0) {
189                // queue a write for the data phase
190                io_buffer_cache_flush(&dwc->ep0_buffer, 0, actual);
191                dwc3_ep_start_transfer(dwc, EP0_IN, TRB_TRBCTL_CONTROL_DATA, paddr, actual, false);
192                dwc->ep0_state = EP0_STATE_DATA_IN;
193            } else {
194                dwc->ep0_state = EP0_STATE_WAIT_NRDY_IN;
195            }
196       }
197       break;
198    }
199    case EP0_STATE_DATA_OUT:
200        dwc->ep0_state = EP0_STATE_WAIT_NRDY_IN;
201        break;
202    case EP0_STATE_DATA_IN:
203        dwc->ep0_state = EP0_STATE_WAIT_NRDY_OUT;
204        break;
205    case EP0_STATE_STATUS:
206        dwc3_queue_setup_locked(dwc);
207        break;
208    default:
209        break;
210    }
211
212    mtx_unlock(EP0_LOCK(dwc));
213}
214