ehci_polled.c revision 9b58c2ad19d1f06f07604b0f61b7ff38f757c8fa
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* EHCI Host Controller Driver (EHCI)
*
* The EHCI driver is a software driver which interfaces to the Universal
* Serial Bus layer (USBA) and the Host Controller (HC). The interface to
* the Host Controller is defined by the EHCI Host Controller Interface.
*
* This module contains the specific EHCI code used in POLLED mode. This
* code is in a separate file since it will never become part of the EHCI
* driver.
*/
#ifndef __sparc
extern void invalidate_cache();
#endif
/*
* Internal Function Prototypes
*/
/* Polled initialization routines */
static int ehci_polled_init(
/* Polled deinitialization routines */
/* Polled save state routines */
/* Polled restore state routines */
static void ehci_polled_stop_processing(
static void ehci_polled_start_processing(
/* Polled read routines */
static int ehci_polled_process_active_intr_qtd_list(
static int ehci_polled_handle_normal_qtd(
ehci_qtd_t *qtd);
static void ehci_polled_insert_intr_qtd(
ehci_qtd_t *qtd);
static void ehci_polled_insert_bulk_qtd(
static void ehci_polled_fill_in_qtd(
static void ehci_polled_insert_qtd_on_tw(
ehci_qtd_t *qtd);
static void ehci_polled_insert_qtd_into_active_intr_qtd_list(
static void ehci_polled_remove_qtd_from_active_intr_qtd_list(
static void ehci_polled_traverse_qtds(
static void ehci_polled_finish_interrupt(
static int ehci_polled_create_tw(
static void ehci_polled_insert_async_qh(
static void ehci_polled_remove_async_qh(
/*
* POLLED entry points
*
* These functions are entry points into the POLLED code.
*/
/*
* ehci_hcdi_polled_input_init:
*
* This is the initialization routine for handling the USB input device
* in POLLED mode. This routine is not called from POLLED mode, so
* it is OK to acquire mutexes.
*/
int
{
int ret;
/*
* Grab the ehci_int_mutex so that things don't change on us
* if an interrupt comes in.
*/
if (ret != USB_SUCCESS) {
/* Allow interrupts to continue */
return (ret);
}
/*
* Mark the structure so that if we are using it, we don't free
* the structures if one of them is unplugged.
*/
/* increase the counter for keyboard connected */
/*
* This is the buffer we will copy characters into. It will be
* copied into at this layer, so we need to keep track of it.
*/
/*
* This is a software workaround to fix schizo hardware bug.
* Existence of "no-prom-cdma-sync" property means consistent
* dma sync should not be done while in prom or polled mode.
*/
DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
}
/* Allow interrupts to continue */
return (USB_SUCCESS);
}
/*
* ehci_hcdi_polled_input_fini:
*/
int
{
int ret;
/*
* Reset the POLLED_INPUT_MODE flag so that we can tell if
* this structure is in use in the ehci_polled_fini routine.
*/
/* decrease the counter for keyboard disconnected */
/* Free the buffer that we copied data into */
return (ret);
}
/*
* ehci_hcdi_polled_input_enter:
*
* This is where we enter into POLLED mode. This routine sets up
* everything so that calls to ehci_hcdi_polled_read will return
* characters.
*/
int
{
int pipe_attr;
#ifndef lint
#endif
#ifndef lint
#endif
/*
* If the controller is already switched over, just return
*/
return (USB_SUCCESS);
}
switch (pipe_attr) {
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_BULK:
#ifndef lint
#endif
/* Wait for few milliseconds */
#ifndef lint
#endif
/* Wait for few milliseconds */
break;
default:
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* ehci_hcdi_polled_input_exit:
*
* This is where we exit POLLED mode. This routine restores
* everything that is needed to continue operation.
*/
int
{
int pipe_attr;
#ifndef lint
#endif
#ifndef lint
#endif
/*
* If there are still outstanding "enters", just return
*/
if (ehci_polledp->ehci_polled_entry > 0) {
return (USB_SUCCESS);
}
switch (pipe_attr & USB_EP_ATTR_MASK) {
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_BULK:
#ifndef lint
#endif
/* Wait for few milliseconds */
#ifndef lint
#endif
/* Wait for few milliseconds */
break;
default:
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* ehci_hcdi_polled_read:
*
* Get a key character
*/
int
{
int pipe_attr;
#ifndef lint
#endif
*num_characters = 0;
if (pipe_attr == USB_EP_ATTR_BULK) {
}
/*
* Check whether any frame list rollover interrupt is pending
* and if it is pending, process this interrupt.
*/
if (intr & EHCI_INTR_FRAME_LIST_ROLLOVER) {
/* Check any frame list rollover interrupt is pending */
}
/* Process any QTD's on the active interrupt qtd list */
#ifndef lint
#endif
return (USB_SUCCESS);
}
/*
* ehci_hcdi_polled_output_init:
*
* This is the initialization routine for handling the USB serial output
* in POLLED mode. This routine is not called from POLLED mode, so
* it is OK to acquire mutexes.
*/
int
{
int ret;
/*
* Grab the ehci_int_mutex so that things don't change on us
* if an interrupt comes in.
*/
if (ret != USB_SUCCESS) {
/* Allow interrupts to continue */
return (ret);
}
/*
* Mark the structure so that if we are using it, we don't free
* the structures if one of them is unplugged.
*/
/*
* Insert the Endpoint Descriptor to appropriate endpoint list.
*/
/*
* This is a software workaround to fix schizo hardware bug.
* Existence of "no-prom-cdma-sync" property means consistent
* dma sync should not be done while in prom or polled mode.
*/
DDI_PROP_NOTPROM, "no-prom-cdma-sync")) {
}
/* Allow interrupts to continue */
return (USB_SUCCESS);
}
/*
* ehci_hcdi_polled_output_fini:
*/
int
{
int ret;
/* Remove the Endpoint Descriptor. */
/*
* Reset the POLLED_INPUT_MODE flag so that we can tell if
* this structure is in use in the ehci_polled_fini routine.
*/
return (ret);
}
/*
* ehci_hcdi_polled_output_enter:
*
* everything is done in input enter
*/
/*ARGSUSED*/
int
{
return (USB_SUCCESS);
}
/*
* ehci_hcdi_polled_output_exit:
*
* everything is done in input exit
*/
/*ARGSUSED*/
int
{
return (USB_SUCCESS);
}
/*
* ehci_hcdi_polled_write:
* Put a key character.
*/
int
{
int intr;
#ifndef lint
#endif
/* Disable all list processing */
/* Wait for few milliseconds */
/* copy transmit buffer */
if (num_characters > POLLED_RAW_BUF_SIZE) {
}
/* Enable async list processing */
/* Wait for few milliseconds */
#ifndef __sparc
#else
;
#endif
}
/*
* Check whether any frame list rollover interrupt is pending
* and if it is pending, process this interrupt.
*/
if (intr & EHCI_INTR_FRAME_LIST_ROLLOVER) {
}
/* Check for any USB transaction completion notification */
/* Acknowledge the USB and USB error interrupt */
}
#ifndef lint
#endif
return (USB_SUCCESS);
}
/*
* Internal Functions
*/
/*
* Polled initialization routines
*/
/*
* ehci_polled_init:
*
* support.
*/
static int
{
int pipe_attr;
/*
* We have already initialized this structure. If the structure
* has already been initialized, then we don't need to redo it.
*/
if (console_info->uci_private) {
return (USB_SUCCESS);
}
/* Allocate and intitialize a state structure */
ehci_polledp = (ehci_polled_t *)
/*
* Store away the ehcip so that we can get to it when we are in
* POLLED mode. We don't want to have to call ehci_obtain_state
* every time we want to access this structure.
*/
/*
* Save usb device and endpoint number information from the usb
* pipe handle.
*/
/*
* Allocate memory to make duplicate of original usb pipe handle.
*/
/*
* Copy the USB handle into the new pipe handle. Also
* create new lock for the new pipe handle.
*/
(void *)ehci_polledp->ehci_polled_input_pipe_handle,
sizeof (usba_pipe_handle_data_t));
/*
* uint64_t typecast to make sure amd64 can compile
*/
/*
* Create a new ehci pipe private structure
*/
pp = (ehci_pipe_private_t *)
/*
* Store the pointer in the pipe handle. This structure was also
* just allocated.
*/
/*
* Store a pointer to the pipe handle. This structure was just
* allocated and it is not in use yet. The locking is there to
* satisfy warlock.
*/
/*
* Allocate a dummy for the interrupt table. This dummy will be
* put into the action when we switch interrupt tables during
* ehci_hcdi_polled_enter. Dummy is placed on the unused lattice
* entries. When the QH is allocated we will replace dummy QH by
* valid interrupt QH in one or more locations in the interrupt
* lattice depending on the requested polling interval. Also we
* will hang a dummy QTD to the QH & dummy QTD is used to indicate
* the end of the QTD chain.
*/
return (USB_NO_RESOURCES);
}
/*
* Allocate the endpoint. This QH will be inserted in
* to the lattice chain for the device. This endpoint
* will have the QTDs hanging off of it for the processing.
*/
return (USB_NO_RESOURCES);
}
/* Set the state of pipe as idle */
/* Set polled mode flag */
/* Insert the endpoint onto the pipe handle */
switch (pipe_attr) {
case USB_EP_ATTR_INTR:
/*
* Set soft interrupt handler flag in the normal mode usb
* pipe handle.
*/
/*
* Insert a Interrupt polling request onto the endpoint.
*
* There will now be two QTDs on the QH, one is the dummy QTD
* that was allocated above in the ehci_alloc_qh and this
* new one.
*/
return (USB_NO_RESOURCES);
}
/* Get the given new interrupt qtd */
/* Insert this qtd into active interrupt QTD list */
qtd);
break;
case USB_EP_ATTR_BULK:
USB_FLAGS_SLEEP)) != USB_SUCCESS) {
return (USB_NO_RESOURCES);
}
break;
default:
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* Polled deinitialization routines
*/
/*
* ehci_polled_fini:
*/
static int
{
/* If the structure is already in use, then don't free it */
return (USB_SUCCESS);
}
pp = (ehci_pipe_private_t *)
/* Deallocate all the pre-allocated interrupt requests */
/*
* Traverse the list of QTD's on this endpoint and these QTD's
* have outstanding transfer requests. Since list processing
* is stopped, these QTDs can be deallocated.
*/
/* Free DMA resources */
/*
* Deallocate the endpoint descriptors that we allocated
* with ehci_alloc_qh.
*/
if (ehci_polledp->ehci_polled_dummy_qh) {
}
if (ehci_polledp->ehci_polled_qh) {
}
/*
* Destroy everything about the pipe that we allocated in
* ehci_polled_duplicate_pipe_handle
*/
sizeof (usba_pipe_handle_data_t));
/*
* We use this field to determine if a QTD is for input or not,
* so NULL the pointer so we don't check deallocated data.
*/
/*
* Finally, free off the structure that we use to keep track
* of all this.
*/
return (USB_SUCCESS);
}
/*
* Polled save state routines
*/
/*
* ehci_polled_save_state:
*/
static void
{
int i;
#ifndef lint
#endif
/*
* If either of these two flags are set, then we have already
* saved off the state information and setup the controller.
*/
#ifndef lint
#endif
return;
}
/*
* Check if the number of keyboard reach the max number we can
* support in polled mode
*/
#ifndef lint
#endif
return;
}
/* Get the endpoint addr. */
/* Get the normal mode usb pipe handle */
/*
* The first enter keyboard entry should save info of the normal mode,
* disable all list processing and interrupt, initialize the
* frame list table with dummy QHs.
*/
/*
* Save the current normal mode ehci registers and later this
* saved register copy is used to replace some of required ehci
* registers before switching from polled mode to normal mode.
*/
/* Save current ehci registers */
/* Disable all list processing and interrupts */
/* Wait for few milliseconds */
/* Save any unprocessed normal mode ehci interrupts */
/*
* Save the current interrupt lattice and replace this lattice
* with an lattice used in POLLED mode. We will restore lattice
* back when we exit from the POLLED mode.
*/
for (i = 0; i < EHCI_NUM_PERIODIC_FRAME_LISTS; i++) {
}
/*
* Fill in the lattice with dummy QHs. These QHs are used so the
* controller can tell that it is at the end of the QH list.
*/
for (i = 0; i < EHCI_NUM_PERIODIC_FRAME_LISTS; i++) {
}
}
/* Get the polled mode ehci pipe private structure */
polled_pp = (ehci_pipe_private_t *)
/*
* Before replacing the lattice, adjust the data togggle on the
* on the ehci's interrupt ed
*/
/*
* If normal mode interrupt pipe endpoint is active, get the data
* toggle from the this interrupt endpoint through the corresponding
* interrupt pipe handle. Else get the data toggle information from
* the usb device structure and this information is saved during the
* normal mode interrupt pipe close. Use this data toggle information
* to fix the data toggle of polled mode interrupt endpoint.
*/
if (ph) {
/* Get the normal mode ehci pipe private structure */
} else {
}
if (polled_toggle != real_toggle) {
if (real_toggle == DATA0) {
} else {
}
}
/*
* Check whether Halt bit is set in the QH and if so clear the
* halt bit.
*/
/* Clear the halt bit */
}
/*
* Initialize the qh overlay area
*/
for (i = 0; i < 5; i++) {
}
/*
* Now, add the endpoint to the lattice that we will hang our
* QTD's off of. We need to poll this device at every 8 ms and
* hence add this QH needs 4 entries in interrupt lattice.
*/
i = i + LS_MIN_POLL_INTERVAL) {
}
/* The first enter keyboard entry enable interrupts and periodic list */
/* Enable USB and Frame list rollover interrupts */
/* Enable the periodic list */
/* Wait for few milliseconds */
}
#ifndef lint
#endif
}
/*
* Polled restore state routines
*/
/*
* ehci_polled_restore_state:
*/
static void
{
int i;
#ifndef lint
#endif
/*
* If this flag is set, then we are still using this structure,
* so don't restore any controller state information yet.
*/
#ifndef lint
#endif
return;
}
/* Get the endpoint addr */
/* Get the normal mode usb pipe handle */
/* Disable list processing and other things */
/* Get the polled mode ehci pipe private structure */
polled_pp = (ehci_pipe_private_t *)
/*
* Before replacing the lattice, adjust the data togggle
* on the on the ehci's interrupt ed
*/
/*
* If normal mode interrupt pipe endpoint is active, fix the
* data toggle for this interrupt endpoint by getting the data
* toggle information from the polled interrupt endpoint. Else
* save the data toggle information in usb device structure.
*/
if (ph) {
/* Get the normal mode ehci pipe private structure */
if (polled_toggle != real_toggle) {
if (polled_toggle == DATA0) {
} else {
}
}
} else {
}
/*
* Only the last leave keyboard entry restore the save frame
* list table and start processing.
*/
if (ehcip->ehci_polled_enter_count == 0) {
/* Replace the lattice */
for (i = 0; i < EHCI_NUM_PERIODIC_FRAME_LISTS; i++) {
}
}
#ifndef lint
#endif
}
/*
* ehci_polled_stop_processing:
*/
static void
{
/* First inactive this QH */
/* Only first leave keyboard entry turn off periodic list processing */
/* Wait for few milliseconds */
}
/*
* Now clear all required fields of QH
* including inactive bit.
*/
/*
* Now look up at the QTD's that are in the active qtd list &
* re-insert them back into the QH's QTD list.
*/
}
/*
* ehci_polled_start_processing:
*/
static void
{
/* Enable all required EHCI interrupts */
/* Enable all reuired list processing */
/* Wait for few milliseconds */
}
/*
* Polled read routines
*/
/*
* ehci_polled_process_active_intr_qtd_list:
*
* This routine takes the QTD's off of the input done head and processes
* them. It returns the number of characters that have been copied for
* input.
*/
static int
{
uint_t num_characters = 0;
/* Sync QH and QTD pool */
}
/* Create done qtd list */
/*
* Traverse the list of transfer descriptors. We can't destroy
* the qtd_next pointers of these QTDs because we are using it
* to traverse the done list. Therefore, we can not put these
* QTD's back on the QH until we are done processing all of them.
*/
while (qtd) {
/* Get next active QTD from the active QTD list */
/* Obtain the transfer wrapper from the QTD */
/* Get ehci pipe from transfer wrapper */
/* Look at the status */
/*
* Check to see if there is an error. If there is error
* clear the halt condition in the Endpoint Descriptor
* (QH) associated with this Transfer Descriptor (QTD).
*/
/* Clear the halt bit */
} else if (pipe_dir == USB_EP_DIR_IN) {
qtd);
}
/* Insert this qtd back into QH's qtd list */
switch (pipe_attr) {
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_BULK:
} else {
}
break;
}
}
return (num_characters);
}
/*
* ehci_polled_handle_normal_qtd:
*/
static int
{
/* Obtain the transfer wrapper from the QTD */
/*
* If "Total bytes of xfer" in control field of qtd is not equal to 0,
* then we received less data from the usb device than requested by us.
* In that case, get the actual received data size.
*/
if (residue) {
}
/* Sync IO buffer */
}
/* Copy the data into the message */
return ((int)length);
}
/*
* ehci_polled_insert_intr_qtd:
*
* Insert a Transfer Descriptor (QTD) on an Endpoint Descriptor (QH).
*/
static void
{
/* Obtain the transfer wrapper from the QTD */
/* Obtain the endpoint and interrupt request */
/*
* Take this QTD off the transfer wrapper's list since
* the pipe is FIFO, this must be the first QTD on the
* list.
*/
/*
* If the head becomes NULL, then there are no more
* active QTD's for this transfer wrapper. Also set
* the tail to NULL.
*/
}
/* Convert current valid QTD as new dummy QTD */
/* Rename qtd as new_dummy_qtd */
new_dummy_qtd = qtd;
/* Get the current and next dummy QTDs */
/* Update QH's dummy qtd field */
/* Update next dummy's next qtd pointer */
/*
* Fill in the current dummy qtd and
* add the new dummy to the end.
*/
/* Insert this qtd onto the tw */
/* Insert this qtd into active interrupt QTD list */
}
static void
{
if (new_dummy_qtd == NULL) {
return;
}
/* Get the current and next dummy QTDs */
/* Update QH's dummy qtd field */
/* Update next dummy's next qtd pointer */
/*
* Fill in the current dummy qtd and
* add the new dummy to the end.
*/
/* Insert this qtd into active interrupt QTD list */
}
/*
* ehci_polled_fill_in_qtd:
*
* Fill in the fields of a Transfer Descriptor (QTD).
* The "Buffer Pointer" fields of a QTD are retrieved from the TW
* it is associated with.
*
* Unlike the it's ehci_fill_in_qtd counterpart, we do not
* set the alternative ptr in polled mode. There is not need
* for it in polled mode, because it doesn't need to cleanup
* short xfer conditions.
*
* Note:
* qtd_dma_offs - the starting offset into the TW buffer, where the QTD
* should transfer from. It should be 4K aligned. And when
* a TW has more than one QTDs, the QTDs must be filled in
* increasing order.
* qtd_length - the total bytes to transfer.
*/
static void
{
uint_t i = 0;
int rem_len;
/* Assert that the qtd to be filled in is a dummy */
/* Change QTD's state Active */
/* Set the total length data tarnsfer */
/*
* QTDs must be filled in increasing DMA offset order.
* tw_dma_offs is initialized to be 0 at TW creation and
* is only increased in this function.
*/
/*
* Save the starting dma buffer offset used and
* length of data that will be transfered in
* the current QTD.
*/
while (buf_len) {
/*
* Advance to the next DMA cookie until finding the cookie
* that qtd_dma_offs falls in.
* It is very likely this loop will never repeat more than
* once. It is here just to accommodate the case qtd_dma_offs
* is increased by multiple cookies during two consecutive
* calls into this function. In that case, the interim DMA
* buffer is allowed to be skipped.
*/
qtd_dma_offs) {
/*
* tw_dma_offs always points to the starting offset
* of a cookie
*/
tw->tw_cookie_idx++;
}
/*
* Counting the remained buffer length to be filled in
* the QTD for current DMA cookie
*/
/* Update the beginning of the buffer */
if (buf_len <= EHCI_MAX_QTD_BUF_SIZE) {
break;
} else {
}
i++;
}
/*
* For control, bulk and interrupt QTD, now
* enable current QTD by setting active bit.
*/
}
/*
* ehci_polled_insert_qtd_on_tw:
*
* The transfer wrapper keeps a list of all Transfer Descriptors (QTD) that
* are allocated for this transfer. Insert a QTD onto this list. The list
* of QTD's does not include the dummy QTD that is at the end of the list of
* QTD's for the endpoint.
*/
static void
{
/*
* Set the next pointer to NULL because
* this is the last QTD on list.
*/
} else {
/* Add the qtd to the end of the list */
}
}
/*
* ehci_polled_create_done_qtd_list:
*
* Create done qtd list from active qtd list.
*/
static ehci_qtd_t *
{
"ehci_polled_create_done_qtd_list:");
while (curr_qtd) {
/* Get next qtd from the active qtd list */
/* Check this QTD has been processed by Host Controller */
/* Remove this QTD from active QTD list */
if (done_qtd_list) {
} else {
}
}
}
return (done_qtd_list);
}
/*
* ehci_polled_insert_qtd_into_active_intr_qtd_list:
*
* Insert current QTD into active interrupt QTD list.
*/
static void
{
/* Insert this qtd into active intr qtd list */
if (curr_qtd) {
while (next_qtd) {
}
} else {
}
}
/*
* ehci_polled_remove_qtd_from_active_intr_qtd_list:
*
* Remove current QTD from the active QTD list.
*/
void
{
}
if (prev_qtd) {
} else {
}
if (next_qtd) {
}
}
}
/*
* ehci_polled_traverse_qtds:
*
* Traverse the list of QTDs for given pipe using transfer wrapper. Since
* the endpoint is marked as Halted, the Host Controller (HC) is no longer
* accessing these QTDs. Remove all the QTDs that are attached to endpoint.
*/
static void
{
/* Process the transfer wrappers for this pipe */
while (next_tw) {
/* Walk through each QTD for this transfer wrapper */
while (qtd) {
/* Remove this QTD from active QTD list */
ehci_polledp, qtd);
/* Deallocate this QTD */
}
}
/* Clear current qtd pointer */
/* Update the next qtd pointer in the QH */
}
/*
* ehci_polled_finish_interrupt:
*/
static void
{
/* Acknowledge the interrupt */
/*
* Read interrupt status register to make sure that any PIO
* store to clear the ISR has made it on the PCI bus before
* returning from its interrupt handler.
*/
(void) Get_OpReg(ehci_status);
}
static int
{
/* Get the required qtd counts */
KM_NOSLEEP)) == NULL) {
return (USB_FAILURE);
}
/* allow sg lists for transfer wrapper dma memory */
/* Allocate the DMA handle */
DDI_SUCCESS) {
return (USB_FAILURE);
}
/* Allocate the memory */
DDI_SUCCESS) {
return (USB_FAILURE);
}
/* Bind the handle */
return (USB_FAILURE);
}
/* The cookie count should be 1 */
if (ccount != 1) {
return (USB_FAILURE);
}
} else {
return (USB_FAILURE);
}
tw->tw_cookie_idx = 0;
tw->tw_dma_offs = 0;
/*
* Only allow one wrapper to be added at a time. Insert the
* new transaction wrapper into the list for this pipe.
*/
} else {
}
/* Store the transfer length */
/* Store a back pointer to the pipe private structure */
/* Store the transfer type - synchronous or asynchronous */
/* Get and Store 32bit ID */
"ehci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
return (USB_SUCCESS);
}
/*
* ehci_polled_insert_async_qh:
*
* Insert a bulk endpoint into the Host Controller's (HC)
* Asynchronous schedule endpoint list.
*/
static void
{
/* Make sure this QH is not already in the list */
/* Obtain a ptr to the head of the Async schedule list */
if (async_head_qh == NULL) {
/* Set this QH to be the "head" of the circular list */
/* Set new QH's link and previous pointer to itself */
/* Set the head ptr to the new endpoint */
/*
* For some reason this register might get nulled out by
* the Uli M1575 South Bridge. To workaround the hardware
* problem, check the value after write and retry if the
* last write fails.
*
* If the ASYNCLISTADDR remains "stuck" after
* EHCI_MAX_RETRY retries, then the M1575 is broken
* and is stuck in an inconsistent state and is about
* to crash the machine with a trn_oor panic when it
* does a DMA read from 0x0. It is better to panic
* now rather than wait for the trn_oor crash; this
* way Customer Service will have a clean signature
* that indicts the M1575 chip rather than a
* mysterious and hard-to-diagnose trn_oor panic.
*/
int retry = 0;
if (retry >= EHCI_MAX_RETRY)
" ASYNCLISTADDR write failed.");
"ehci_insert_async_qh: ASYNCLISTADDR "
"write failed, retry=%d", retry);
}
} else {
/* Ensure this QH's "H" bit is not set */
/* Set new QH's link and previous pointers */
/* Set next QH's prev pointer */
/* Set QH Head's link pointer points to new QH */
}
}
/*
* ehci_remove_async_qh:
*
* Asynchronous schedule endpoint list.
*/
static void
{
/* Make sure this QH is in the list */
/*
* If next QH and current QH are the same, then this is the last
* QH on the Asynchronous Schedule list.
*/
/*
* Null our pointer to the async sched list, but do not
* touch the host controller's list_addr.
*/
} else {
/* If this QH is the HEAD then find another one to replace it */
}
}
/* qh_prev to indicate it is no longer in the circular list */
}