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