1/** 2 * \brief This file contains EHCI specific functions for the USB host controller 3 * driver interface. 4 */ 5 6/* 7 * Copyright (c) 2007-2013 ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <stdlib.h> 16#include <stdio.h> 17#include <barrelfish/barrelfish.h> 18 19// Mackerel device 20#include "ehci_device.h" 21 22// common USB declarations 23#include <usb/usb.h> 24 25// USB manager specific includes 26#include <usb_xfer.h> 27#include <usb_device.h> 28#include <usb_controller.h> 29#include <usb_hub.h> 30#include <usb_memory.h> 31 32// host controller specific includes 33#include "usb_ehci.h" 34#include "usb_ehci_root_hub.h" 35#include "usb_ehci_bus.h" 36#include "usb_ehci_xfer.h" 37#include "usb_ehci_pipe.h" 38#include "usb_ehci_queue.h" 39 40/** 41 * \brief wrapper function to perform the polling of the USB transfers 42 * 43 * \param hostc pointer to the generic host controller of type ehci 44 * 45 * Note: this functions is needed to translate the generic poll request 46 * into a host controller specific poll request. 47 */ 48static void usb_ehci_do_poll(usb_host_controller_t *hostc) 49{ 50 assert(hostc->hc_type == USB_EHCI); 51 52 usb_ehci_hc_t *hc = (usb_ehci_hc_t *) (hostc->hc_control); 53 54 usb_ehci_poll(hc); 55} 56 57/** 58 * \brief executes the polling of the usb transfers 59 * 60 * \param hc host ehci host controller to poll 61 * 62 * Note: this function iteratively polls all transfers currently added to the 63 * interrupt queue of this host controller. If a transfer changes its 64 * state to finished, the process is repeated 65 */ 66void usb_ehci_poll(usb_ehci_hc_t *hc) 67{ 68 69 USB_DEBUG_TR_ENTER; 70 71 struct usb_xfer *xfer = hc->controller->intr_queue.head.first; 72 uint8_t repeat = 0; 73 74 /* loop over all transferstill no changes */ 75 do { 76 repeat = 0; 77 while (xfer != NULL) { 78 /* TODO: Check if needed */ 79 if (xfer == ((xfer))->wait_entry.next) { 80 break; 81 } 82 83 if (usb_ehci_xfer_is_finished(xfer)) { 84 repeat = 1; // repeat the polling process 85 } 86 xfer = xfer->wait_entry.next; 87 } 88 } while (repeat); 89 90 USB_DEBUG_TR_RETURN; 91} 92 93/** 94 * \brief stores function pointers to the host controller driver interface 95 */ 96static struct usb_hcdi_bus_fn usb_ehci_bus_fn = { 97 .endpoint_init = usb_ehci_endpoint_init, 98 .xfer_setup = usb_ehci_xfer_setup, 99 .xfer_unsetup = usb_ehci_xfer_unsetup, 100 .device_resume = usb_ehci_device_resume, 101 .device_suspend = usb_ehci_device_suspend, 102 .set_hw_power = usb_ehci_set_power, 103 .set_hw_power_sleep = usb_ehci_sleep, 104 .roothub_exec = usb_ehci_roothub_exec, 105 .xfer_poll = usb_ehci_do_poll, 106 .xfer_finished = usb_ehci_xfer_is_finished, 107}; 108 109/** 110 * \brief this function returns a pointer to the ehci hcdi functions 111 * 112 * \return pointer to struct of function pointers 113 */ 114struct usb_hcdi_bus_fn *usb_ehci_get_bus_fn(void) 115{ 116 return (&usb_ehci_bus_fn); 117} 118 119/** 120 * \brief sets the ehci controller into sleep mode or wakes it up 121 * 122 * \param hc the host controller to sleep / wakeup 123 * \param state the state to put the host controller into 124 * 125 * Upon resuming all the connected devices are also resumed 126 */ 127void usb_ehci_sleep(struct usb_host_controller *hc, uint32_t state) 128{ 129 assert(hc->hc_type == USB_EHCI); 130 131 usb_ehci_hc_t *ehci_hc = hc->hc_control; 132 133 switch (state) { 134 case USB_POWER_MODE_SUSPEND: 135 case USB_POWER_MODE_OFF: 136 /* to suspend the hardware this is just a reset of the 137 * host controller 138 */ 139 usb_ehci_hc_reset(ehci_hc); 140 break; 141 case USB_POWER_MODE_RESUME: 142 /* resume: first reset the host controller hardware */ 143 usb_ehci_hc_reset(ehci_hc); 144 145 /* re-initialize the registers */ 146 usb_ehci_initialize_controller(ehci_hc); 147 148 /* poll the transfers */ 149 usb_ehci_do_poll(hc); 150 break; 151 default: 152 /* noop */ 153 break; 154 } 155} 156 157/** 158 * \brief sets the hardware to a specific power mode 159 * 160 * \param hc the host controller to change power mode 161 * 162 * This functio updates the ehci_usbcmd register to its a new value, if there 163 * are periodic or asynchronous transfers happening, these are finished first 164 */ 165void usb_ehci_set_power(struct usb_host_controller *hc) 166{ 167 assert(!"NYI: set power"); 168} 169 170/** 171 * \brief resumes a suspended USB device on this host controller 172 * 173 * \param device the usb devices to to resume 174 * 175 * All of the already set up usb xfers are also resumed 176 */ 177void usb_ehci_device_resume(struct usb_device *device) 178{ 179 assert(device->controller->hc_type == USB_EHCI); 180 181 struct usb_xfer_queue *queue = &device->controller->intr_queue; 182 struct usb_xfer *xfer = queue->head.first; 183 184 while (xfer) { 185 /* just handle the xfers that are belonging to this device */ 186 if (xfer->device == device) { 187 usb_ehci_hc_t *hc = device->controller->hc_control; 188 switch (xfer->type) { 189 case USB_TYPE_BULK: 190 case USB_TYPE_CTRL: 191 usb_ehci_enq_qh( 192 xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set], 193 hc->qh_async_last); 194 break; 195 case USB_TYPE_INTR: 196 usb_ehci_enq_qh( 197 xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set], 198 hc->qh_intr_last[xfer->intr_qh_pos]); 199 break; 200 default: 201 /* noop */ 202 break; 203 } 204 } 205 xfer = xfer->wait_entry.next; 206 } 207} 208 209/** 210 * \brief suspends a attached USB device by pausing all its transfers 211 * 212 * \param device the usb device to suspend 213 */ 214void usb_ehci_device_suspend(struct usb_device *device) 215{ 216 assert(device->controller->hc_type == USB_EHCI); 217 218 struct usb_xfer_queue *queue = &device->controller->intr_queue; 219 struct usb_xfer *xfer = queue->head.first; 220 221 while (xfer) { 222 if (xfer->device == device) { 223 usb_ehci_hc_t *hc = device->controller->hc_control; 224 switch (xfer->type) { 225 case USB_TYPE_BULK: 226 case USB_TYPE_CTRL: 227 usb_ehci_deq_qh( 228 xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set], 229 hc->qh_async_last); 230 break; 231 case USB_TYPE_INTR: 232 usb_ehci_deq_qh( 233 xfer->hcd_qh_start[xfer->flags_internal.curr_dma_set], 234 hc->qh_intr_last[xfer->intr_qh_pos]); 235 break; 236 default: 237 /* noop */ 238 break; 239 } 240 241 } 242 xfer = xfer->wait_entry.next; 243 } 244 245} 246 247 248/** 249 * \brief initializes a device endpoint with the values given by the descriptor 250 * 251 * \param device the device, this endpoint belongs 252 * \param ep_desc the descriptor of this endpoing 253 * \param ep the endpoint to initialize 254 */ 255void usb_ehci_endpoint_init(struct usb_device *device, 256 struct usb_endpoint_descriptor *ep_desc, struct usb_endpoint *ep) 257{ 258 assert(device->controller->hc_type == USB_EHCI); 259 260 usb_ehci_hc_t *hc = (usb_ehci_hc_t *) device->controller->hc_control; 261 262 /* 263 * CASE 1: The device is the root hub. Setting up endpoints for the root 264 * hub is not allowed, thus return 265 */ 266 if (device->device_index == hc->rh_device_address) { 267 return; 268 } 269 270 /* 271 * CASE 2: the attached device is not a high speed device and is directly 272 * connected to the root hub, this is not possible, since non 273 * full speed devices are handled by the companion controller. 274 * (root hub does not have a transaction translator) 275 */ 276 if (device->speed != USB_SPEED_HIGH) { 277 if ((device->hs_hub_address == 0) || (device->hs_hub_port_number == 0) 278 || (device->parent_hs_hub == NULL) 279 || (device->parent_hs_hub->hub == NULL)) { 280 return; 281 } 282 } 283 284 /* CASE 3: all fine setup the pipe functions depending on the EP type */ 285 switch (ep_desc->bmAttributes.xfer_type) { 286 case USB_ENDPOINT_TYPE_CONTROL: 287 ep->pipe_fn = usb_ehci_get_ctrl_pipe_fn(); 288 break; 289 case USB_ENDPOINT_TYPE_INTR: 290 ep->pipe_fn = usb_ehci_get_intr_pipe_fn(); 291 break; 292 case USB_ENDPOINT_TYPE_ISOCHR: 293 /* 294 * isochronus endpoints need differentiated handling depending on 295 * the device speed 296 */ 297 if (device->speed == USB_SPEED_HIGH) { 298 ep->pipe_fn = usb_ehci_get_hs_isoc_pipe_fn(); 299 } else if (device->speed == USB_SPEED_FULL) { 300 ep->pipe_fn = usb_ehci_get_fs_isoc_pipe_fn(); 301 } 302 break; 303 case USB_ENDPOINT_TYPE_BULK: 304 ep->pipe_fn = usb_ehci_get_bulk_pipe_fn(); 305 break; 306 default: 307 /* no-op */ 308 break; 309 } 310} 311 312