ehci_isoch_util.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* 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 EHCI driver isochronous code, which handles all
* Checking of status of USB transfers, error recovery and callbacks.
*/
#include <sys/usb/hcd/ehci/ehcid.h>
#include <sys/usb/hcd/ehci/ehci_xfer.h>
#include <sys/usb/hcd/ehci/ehci_util.h>
/* Adjustable variables for the size of isoc pools */
int ehci_itd_pool_size = EHCI_ITD_POOL_SIZE;
/*
* pool functions
*/
int ehci_allocate_isoc_pools(
ehci_state_t *ehcip);
int ehci_get_itd_pool_size();
/*
* Isochronous Transfer Wrapper Functions
*/
ehci_isoc_xwrapper_t *ehci_allocate_itw_resources(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
size_t itw_length,
usb_flags_t usb_flags,
size_t pkt_count);
static ehci_isoc_xwrapper_t *ehci_allocate_itw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
size_t length,
usb_flags_t usb_flags);
void ehci_deallocate_itw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw);
static void ehci_free_itw_dma(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw);
/*
* transfer descriptor functions
*/
static ehci_itd_t *ehci_allocate_itd(
ehci_state_t *ehcip);
void ehci_deallocate_itd(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *old_itd);
uint_t ehci_calc_num_itds(
ehci_isoc_xwrapper_t *itw,
size_t pkt_count);
int ehci_allocate_itds_for_itw(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
uint_t itd_count);
static void ehci_deallocate_itds_for_itw(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw);
void ehci_insert_itd_on_itw(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd);
void ehci_insert_itd_into_active_list(
ehci_state_t *ehcip,
ehci_itd_t *itd);
void ehci_remove_itd_from_active_list(
ehci_state_t *ehcip,
ehci_itd_t *itd);
ehci_itd_t *ehci_create_done_itd_list(
ehci_state_t *ehcip);
int ehci_insert_isoc_to_pfl(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw);
void ehci_remove_isoc_from_pfl(
ehci_state_t *ehcip,
ehci_itd_t *curr_itd);
/*
* Isochronous in resource functions
*/
int ehci_allocate_isoc_in_resource(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *tw,
usb_flags_t flags);
void ehci_deallocate_isoc_in_resource(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw);
/*
* memory addr functions
*/
uint32_t ehci_itd_cpu_to_iommu(
ehci_state_t *ehcip,
ehci_itd_t *addr);
ehci_itd_t *ehci_itd_iommu_to_cpu(
ehci_state_t *ehcip,
uintptr_t addr);
/*
* Error parsing functions
*/
void ehci_parse_isoc_error(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd);
static usb_cr_t ehci_parse_itd_error(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd);
static usb_cr_t ehci_parse_sitd_error(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd);
/*
* print functions
*/
void ehci_print_itd(
ehci_state_t *ehcip,
ehci_itd_t *itd);
/*
* ehci_allocate_isoc_pools:
*
* Allocate the system memory for itd which are for low/full/high speed
* Transfer Descriptors. Must be aligned to a 32 byte boundary.
*/
int
ehci_allocate_isoc_pools(ehci_state_t *ehcip)
{
ddi_device_acc_attr_t dev_attr;
size_t real_length;
int result;
uint_t ccount;
int i;
USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_pools:");
/* Byte alignment */
ehcip->ehci_dma_attr.dma_attr_align = EHCI_DMA_ATTR_TD_QH_ALIGNMENT;
/* Allocate the itd pool DMA handle */
result = ddi_dma_alloc_handle(ehcip->ehci_dip,
&ehcip->ehci_dma_attr,
DDI_DMA_SLEEP,
0,
&ehcip->ehci_itd_pool_dma_handle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_pools: Alloc handle failed");
return (DDI_FAILURE);
}
/* The host controller will be little endian */
dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
/* Allocate the memory */
result = ddi_dma_mem_alloc(ehcip->ehci_itd_pool_dma_handle,
ehci_itd_pool_size * sizeof (ehci_itd_t),
&dev_attr,
DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
0,
(caddr_t *)&ehcip->ehci_itd_pool_addr,
&real_length,
&ehcip->ehci_itd_pool_mem_handle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_pools: Alloc memory failed");
return (DDI_FAILURE);
}
/* Map the ITD pool into the I/O address space */
result = ddi_dma_addr_bind_handle(
ehcip->ehci_itd_pool_dma_handle,
NULL,
(caddr_t)ehcip->ehci_itd_pool_addr,
real_length,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
DDI_DMA_SLEEP,
NULL,
&ehcip->ehci_itd_pool_cookie,
&ccount);
bzero((void *)ehcip->ehci_itd_pool_addr,
ehci_itd_pool_size * sizeof (ehci_itd_t));
/* Process the result */
if (result == DDI_DMA_MAPPED) {
/* The cookie count should be 1 */
if (ccount != 1) {
USB_DPRINTF_L2(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_pools: More than 1 cookie");
return (DDI_FAILURE);
}
} else {
USB_DPRINTF_L4(PRINT_MASK_ATTA, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_pools: Result = %d", result);
ehci_decode_ddi_dma_addr_bind_handle_result(ehcip, result);
return (DDI_FAILURE);
}
/*
* DMA addresses for ITD pools are bound
*/
ehcip->ehci_dma_addr_bind_flag |= EHCI_ITD_POOL_BOUND;
/* Initialize the ITD pool */
for (i = 0; i < ehci_itd_pool_size; i++) {
Set_ITD(ehcip->ehci_itd_pool_addr[i].itd_state,
EHCI_ITD_FREE);
}
return (DDI_SUCCESS);
}
int
ehci_get_itd_pool_size()
{
return (ehci_itd_pool_size);
}
/*
* Isochronous Transfer Wrapper Functions
*/
/*
* ehci_allocate_itw:
*
* Allocate an iTW and n iTD from the iTD buffer pool and places it into the
* ITW. It does an all or nothing transaction.
*
* Calculates the number of iTD needed based on pipe speed.
* For LOW/FULL speed devices, 1 iTD is needed for each packet.
* For HIGH speed device, 1 iTD is needed for 8 to 24 packets, depending on
* the multiplier for "HIGH BANDWIDTH" transfers look at 4.7 in EHCI spec.
*
* Returns NULL if there is insufficient resources otherwise ITW.
*/
ehci_isoc_xwrapper_t *
ehci_allocate_itw_resources(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
size_t itw_length,
usb_flags_t usb_flags,
size_t pkt_count)
{
uint_t itd_count;
ehci_isoc_xwrapper_t *itw;
itw = ehci_allocate_itw(ehcip, pp, itw_length, usb_flags);
if (itw == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_itw_resources: Unable to allocate ITW");
} else {
itd_count = ehci_calc_num_itds(itw, pkt_count);
if (ehci_allocate_itds_for_itw(ehcip, itw, itd_count) ==
USB_SUCCESS) {
itw->itw_num_itds = itd_count;
} else {
ehci_deallocate_itw(ehcip, pp, itw);
itw = NULL;
}
}
return (itw);
}
/*
* ehci_allocate_itw:
*
* Creates a Isochronous Transfer Wrapper (itw) and populate it with this
* endpoint's data. This involves the allocation of DMA resources.
*
* ITW Fields not set by this function:
* - will be populated itds are allocated
* num_ids
* itd_head
* itd_tail
* curr_xfer_reqp
* curr_isoc_pktp
* itw_itd_free_list
* - Should be set by the calling function
* itw_handle_callback_value
*/
static ehci_isoc_xwrapper_t *
ehci_allocate_itw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
size_t length,
usb_flags_t usb_flags)
{
ddi_device_acc_attr_t dev_attr;
int result;
size_t real_length;
uint_t ccount; /* Cookie count */
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usba_device_t *usba_device = ph->p_usba_device;
usb_ep_descr_t *endpoint = &ph->p_ep;
ehci_isoc_xwrapper_t *itw;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_itw: length = 0x%x flags = 0x%x",
length, usb_flags);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
/* Allocate space for the transfer wrapper */
itw = kmem_zalloc(sizeof (ehci_isoc_xwrapper_t), KM_NOSLEEP);
if (itw == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_itw: kmem_zalloc failed");
return (NULL);
}
ehcip->ehci_dma_attr.dma_attr_align = EHCI_DMA_ATTR_ALIGNMENT;
/* Allocate the DMA handle */
result = ddi_dma_alloc_handle(ehcip->ehci_dip,
&ehcip->ehci_dma_attr,
DDI_DMA_DONTWAIT,
0,
&itw->itw_dmahandle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_create_transfer_wrapper: Alloc handle failed");
kmem_free(itw, sizeof (ehci_isoc_xwrapper_t));
return (NULL);
}
/* no need for swapping the raw data in the buffers */
dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
/* Allocate the memory */
result = ddi_dma_mem_alloc(itw->itw_dmahandle,
length,
&dev_attr,
DDI_DMA_CONSISTENT,
DDI_DMA_DONTWAIT,
NULL,
(caddr_t *)&itw->itw_buf,
&real_length,
&itw->itw_accesshandle);
if (result != DDI_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_create_transfer_wrapper: dma_mem_alloc fail");
ddi_dma_free_handle(&itw->itw_dmahandle);
kmem_free(itw, sizeof (ehci_isoc_xwrapper_t));
return (NULL);
}
ASSERT(real_length >= length);
/* Bind the handle */
result = ddi_dma_addr_bind_handle(itw->itw_dmahandle,
NULL,
(caddr_t)itw->itw_buf,
real_length,
DDI_DMA_RDWR|DDI_DMA_CONSISTENT,
DDI_DMA_DONTWAIT,
NULL,
&itw->itw_cookie,
&ccount);
if (result == DDI_DMA_MAPPED) {
/* The cookie count should be 1 */
if (ccount != 1) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_create_transfer_wrapper: More than 1 cookie");
result = ddi_dma_unbind_handle(itw->itw_dmahandle);
ASSERT(result == DDI_SUCCESS);
ddi_dma_mem_free(&itw->itw_accesshandle);
ddi_dma_free_handle(&itw->itw_dmahandle);
kmem_free(itw, sizeof (ehci_isoc_xwrapper_t));
return (NULL);
}
} else {
ehci_decode_ddi_dma_addr_bind_handle_result(ehcip, result);
ddi_dma_mem_free(&itw->itw_accesshandle);
ddi_dma_free_handle(&itw->itw_dmahandle);
kmem_free(itw, sizeof (ehci_isoc_xwrapper_t));
return (NULL);
}
/* Store a back pointer to the pipe private structure */
itw->itw_pipe_private = pp;
if (pp->pp_itw_head == NULL) {
pp->pp_itw_head = itw;
pp->pp_itw_tail = itw;
} else {
pp->pp_itw_tail->itw_next = itw;
pp->pp_itw_tail = itw;
}
/*
* Store transfer information
* itw_buf has been allocated and will be set later
*/
itw->itw_length = length;
itw->itw_flags = usb_flags;
itw->itw_port_status = usba_device->usb_port_status;
itw->itw_direction = endpoint->bEndpointAddress & USB_EP_DIR_MASK;
/*
* Store the endpoint information that will be used by the
* transfer descriptors later.
*/
mutex_enter(&usba_device->usb_mutex);
itw->itw_hub_addr = usba_device->usb_hs_hub_addr;
itw->itw_hub_port = usba_device->usb_hs_hub_port;
itw->itw_endpoint_num = endpoint->bEndpointAddress & USB_EP_NUM_MASK;
itw->itw_device_addr = usba_device->usb_addr;
mutex_exit(&usba_device->usb_mutex);
/* Get and Store 32bit ID */
itw->itw_id = EHCI_GET_ID((void *)itw);
ASSERT(itw->itw_id != NULL);
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_create_itw: itw = 0x%p", itw);
return (itw);
}
/*
* ehci_deallocate_itw:
*
* Deallocate of a Isochronous Transaction Wrapper (TW) and this involves the
* freeing of DMA resources.
*/
void
ehci_deallocate_itw(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw)
{
ehci_isoc_xwrapper_t *prev, *next;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_deallocate_itw: itw = 0x%p", itw);
/*
* If the transfer wrapper has no Host Controller (HC)
* Transfer Descriptors (ITD) associated with it, then
* remove the transfer wrapper.
*/
if (itw->itw_itd_head) {
ASSERT(itw->itw_itd_tail != NULL);
return;
}
ASSERT(itw->itw_itd_tail == NULL);
/* Make sure we return all the unused itd's to the pool as well */
ehci_deallocate_itds_for_itw(ehcip, itw);
/*
* If pp->pp_tw_head and pp->pp_tw_tail are pointing to
* given TW then set the head and tail equal to NULL.
* Otherwise search for this TW in the linked TW's list
* and then remove this TW from the list.
*/
if (pp->pp_itw_head == itw) {
if (pp->pp_itw_tail == itw) {
pp->pp_itw_head = NULL;
pp->pp_itw_tail = NULL;
} else {
pp->pp_itw_head = itw->itw_next;
}
} else {
prev = pp->pp_itw_head;
next = prev->itw_next;
while (next && (next != itw)) {
prev = next;
next = next->itw_next;
}
if (next == itw) {
prev->itw_next = next->itw_next;
if (pp->pp_itw_tail == itw) {
pp->pp_itw_tail = prev;
}
}
}
/* Free this iTWs dma resources */
ehci_free_itw_dma(ehcip, pp, itw);
}
/*
* ehci_free_itw_dma:
*
* Free the Isochronous Transfer Wrapper dma resources.
*/
/*ARGSUSED*/
static void
ehci_free_itw_dma(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw)
{
int rval;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_free_itw_dma: itw = 0x%p", itw);
ASSERT(itw != NULL);
ASSERT(itw->itw_id != NULL);
/* Free 32bit ID */
EHCI_FREE_ID((uint32_t)itw->itw_id);
rval = ddi_dma_unbind_handle(itw->itw_dmahandle);
ASSERT(rval == DDI_SUCCESS);
ddi_dma_mem_free(&itw->itw_accesshandle);
ddi_dma_free_handle(&itw->itw_dmahandle);
/* Free transfer wrapper */
kmem_free(itw, sizeof (ehci_isoc_xwrapper_t));
}
/*
* transfer descriptor functions
*/
/*
* ehci_allocate_itd:
*
* Allocate a Transfer Descriptor (iTD) from the iTD buffer pool.
*/
static ehci_itd_t *
ehci_allocate_itd(ehci_state_t *ehcip)
{
int i, state;
ehci_itd_t *itd;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
/*
* Search for a blank Transfer Descriptor (iTD)
* in the iTD buffer pool.
*/
for (i = 0; i < ehci_itd_pool_size; i ++) {
state = Get_ITD(ehcip->ehci_itd_pool_addr[i].itd_state);
if (state == EHCI_ITD_FREE) {
break;
}
}
if (i >= ehci_itd_pool_size) {
USB_DPRINTF_L2(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_allocate_itd: ITD exhausted");
return (NULL);
}
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_allocate_itd: Allocated %d", i);
/* Create a new dummy for the end of the ITD list */
itd = &ehcip->ehci_itd_pool_addr[i];
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_itd: itd 0x%p", (void *)itd);
/* Mark the newly allocated ITD as a empty */
Set_ITD(itd->itd_state, EHCI_ITD_DUMMY);
return (itd);
}
/*
* ehci_deallocate_itd:
*
* Deallocate a Host Controller's (HC) Transfer Descriptor (ITD).
*
*/
void
ehci_deallocate_itd(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *old_itd)
{
ehci_itd_t *itd, *next_itd;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_deallocate_itd: old_itd = 0x%p", (void *)old_itd);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(Get_ITD(old_itd->itd_trans_wrapper) == itw->itw_id);
/* If it has been marked RECLAIM it has already been removed */
if (Get_ITD(old_itd->itd_state) != EHCI_ITD_RECLAIM) {
ehci_remove_isoc_from_pfl(ehcip, old_itd);
}
/* Make sure the ITD is not in the PFL */
ASSERT(Get_ITD_FRAME(old_itd->itd_frame_number) == 0);
/* Remove the itd from the itw */
itd = itw->itw_itd_head;
if (old_itd != itd) {
next_itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(itd->itd_itw_next_itd));
while (next_itd != old_itd) {
itd = next_itd;
next_itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(itd->itd_itw_next_itd));
}
Set_ITD(itd->itd_itw_next_itd, old_itd->itd_itw_next_itd);
if (itd->itd_itw_next_itd == NULL) {
itw->itw_itd_tail = itd;
}
} else {
itw->itw_itd_head = ehci_itd_iommu_to_cpu(
ehcip, Get_ITD(old_itd->itd_itw_next_itd));
if (itw->itw_itd_head == NULL) {
itw->itw_itd_tail = NULL;
}
}
bzero((char *)old_itd, sizeof (ehci_itd_t));
Set_ITD(old_itd->itd_state, EHCI_ITD_FREE);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"Dealloc_itd: itd 0x%p", (void *)old_itd);
}
/*
* ehci_calc_num_itds:
*
* Calculates how many ITDs are needed for this request.
* The calculation is based on weather it is an HIGH speed
* transaction of a FULL/LOW transaction.
*
* For FULL/LOW transaction more itds are necessary if it
* spans frames.
*/
uint_t
ehci_calc_num_itds(
ehci_isoc_xwrapper_t *itw,
size_t pkt_count)
{
uint_t multiplier, itd_count;
/* Allocate the appropriate isoc resources */
if (itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
/* Multiplier needs to be passed in somehow */
multiplier = 1 * 8;
itd_count = pkt_count / multiplier;
if (pkt_count % multiplier) {
itd_count++;
}
} else {
itd_count = pkt_count;
}
return (pkt_count);
}
/*
* ehci_allocate_itds_for_itw:
*
* Allocate n Transfer Descriptors (TD) from the TD buffer pool and places it
* into the TW.
*
* Returns USB_NO_RESOURCES if it was not able to allocate all the requested TD
* otherwise USB_SUCCESS.
*/
int
ehci_allocate_itds_for_itw(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
uint_t itd_count)
{
ehci_itd_t *itd;
uint32_t itd_addr;
int i;
int error = USB_SUCCESS;
for (i = 0; i < itd_count; i += 1) {
itd = ehci_allocate_itd(ehcip);
if (itd == NULL) {
error = USB_NO_RESOURCES;
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_itds_for_itw: "
"Unable to allocate %d ITDs",
itd_count);
break;
}
if (i > 0) {
itd_addr = ehci_itd_cpu_to_iommu(ehcip,
itw->itw_itd_free_list);
Set_ITD(itd->itd_link_ptr, itd_addr);
}
itw->itw_itd_free_list = itd;
}
return (error);
}
/*
* ehci_deallocate_itds_for_itw:
*
* Free all allocated resources for Transaction Wrapper (TW).
* Does not free the iTW itself.
*/
static void
ehci_deallocate_itds_for_itw(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw)
{
ehci_itd_t *itd = NULL;
ehci_itd_t *temp_itd = NULL;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_free_itw_itd_resources: itw = 0x%p", itw);
itd = itw->itw_itd_free_list;
while (itd != NULL) {
/* Save the pointer to the next itd before destroying it */
temp_itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(itd->itd_link_ptr));
ehci_deallocate_itd(ehcip, itw, itd);
itd = temp_itd;
}
itw->itw_itd_free_list = NULL;
}
/*
* ehci_insert_itd_on_itw:
*
* The transfer wrapper keeps a list of all Transfer Descriptors (iTD) that
* are allocated for this transfer. Insert a iTD onto this list.
*/
void ehci_insert_itd_on_itw(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd)
{
/*
* Set the next pointer to NULL because
* this is the last ITD on list.
*/
Set_ITD(itd->itd_itw_next_itd, NULL);
if (itw->itw_itd_head == NULL) {
ASSERT(itw->itw_itd_tail == NULL);
itw->itw_itd_head = itd;
itw->itw_itd_tail = itd;
} else {
ehci_itd_t *dummy = (ehci_itd_t *)itw->itw_itd_tail;
ASSERT(dummy != NULL);
ASSERT(Get_ITD(itd->itd_state) == EHCI_ITD_ACTIVE);
/* Add the itd to the end of the list */
Set_ITD(dummy->itd_itw_next_itd,
ehci_itd_cpu_to_iommu(ehcip, itd));
itw->itw_itd_tail = itd;
}
Set_ITD(itd->itd_trans_wrapper, (uint32_t)itw->itw_id);
}
/*
* ehci_insert_itd_into_active_list:
*
* Add current ITD into the active ITD list in reverse order.
* When he done list is created, remove it in the reverse order.
*/
void
ehci_insert_itd_into_active_list(
ehci_state_t *ehcip,
ehci_itd_t *itd)
{
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(itd != NULL);
Set_ITD(itd->itd_next_active_itd,
ehci_itd_cpu_to_iommu(ehcip, ehcip->ehci_active_itd_list));
ehcip->ehci_active_itd_list = itd;
}
/*
* ehci_remove_itd_from_active_list:
*
* Remove current ITD from the active ITD list.
*/
void
ehci_remove_itd_from_active_list(
ehci_state_t *ehcip,
ehci_itd_t *itd)
{
ehci_itd_t *curr_itd, *next_itd;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(itd != NULL);
curr_itd = ehcip->ehci_active_itd_list;
if (curr_itd == itd) {
ehcip->ehci_active_itd_list =
ehci_itd_iommu_to_cpu(ehcip, itd->itd_next_active_itd);
itd->itd_next_active_itd = NULL;
return;
}
next_itd = ehci_itd_iommu_to_cpu(ehcip, curr_itd->itd_next_active_itd);
while (next_itd != itd) {
curr_itd = next_itd;
if (curr_itd) {
next_itd = ehci_itd_iommu_to_cpu(ehcip,
curr_itd->itd_next_active_itd);
} else {
break;
}
}
if ((curr_itd) && (next_itd == itd)) {
Set_ITD(curr_itd->itd_next_active_itd,
Get_ITD(itd->itd_next_active_itd));
Set_ITD(itd->itd_next_active_itd, NULL);
} else {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_remove_itd_from_active_list: "
"Unable to find ITD in active_itd_list");
}
}
/*
* ehci_create_done_itd_list:
*
* Traverse the active list and create a done list and remove them
* from the active list.
*/
ehci_itd_t *
ehci_create_done_itd_list(
ehci_state_t *ehcip)
{
usb_frame_number_t current_frame_number;
usb_frame_number_t itd_frame_number, itd_reclaim_number;
ehci_itd_t *curr_itd = NULL, *next_itd = NULL;
ehci_itd_t *done_itd_list = NULL;
uint_t state;
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_create_done_itd_list:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
/*
* Get the current frame number.
* Only process itd that were inserted before the current
* frame number.
*/
current_frame_number = ehci_get_current_frame_number(ehcip);
curr_itd = ehcip->ehci_active_itd_list;
while (curr_itd) {
/* Get next itd from the active itd list */
next_itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(curr_itd->itd_next_active_itd));
/*
* If haven't past the frame number that the ITD was
* suppose to be executed, don't touch it. Just in
* case it is being processed by the HCD and cause
* a race condition.
*/
itd_frame_number = Get_ITD_FRAME(curr_itd->itd_frame_number);
itd_reclaim_number =
Get_ITD_FRAME(curr_itd->itd_reclaim_number);
/* Get the ITD state */
state = Get_ITD(curr_itd->itd_state);
if (((state == EHCI_ITD_ACTIVE) &&
(itd_frame_number < current_frame_number)) ||
((state == EHCI_ITD_RECLAIM) &&
(itd_reclaim_number < current_frame_number))) {
/* Remove this ITD from active ITD list */
ehci_remove_itd_from_active_list(ehcip, curr_itd);
/*
* Create the done list in reverse order, since the
* active list was also created in reverse order.
*/
Set_ITD(curr_itd->itd_next_active_itd,
ehci_itd_cpu_to_iommu(ehcip, done_itd_list));
done_itd_list = curr_itd;
}
curr_itd = next_itd;
}
return (done_itd_list);
}
/*
* ehci_insert_isoc_to_pfl:
*
* Insert a ITD request into the Host Controller's isochronous list.
* All the ITDs in the ITW will be added the PFL at once. Either all
* of them will make it or none of them will.
*/
int
ehci_insert_isoc_to_pfl(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw)
{
usb_isoc_req_t *isoc_reqp = itw->itw_curr_xfer_reqp;
usb_frame_number_t current_frame_number, start_frame_number;
uint_t ddic, pfl_number;
ehci_periodic_frame_list_t *periodic_frame_list =
ehcip->ehci_periodic_frame_list_tablep;
uint32_t addr, port_status;
ehci_itd_t *itd;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_isoc_to_pfl: "
"isoc flags 0x%x", isoc_reqp->isoc_attributes);
/*
* Enter critical, while programming the usb frame number
* and inserting current isochronous TD into the ED's list.
*/
ddic = ddi_enter_critical();
/* Get the current frame number */
current_frame_number = ehci_get_current_frame_number(ehcip);
/*
* Check the given isochronous flags and get the frame number
* to insert the itd into.
*/
switch (isoc_reqp->isoc_attributes &
(USB_ATTRS_ISOC_START_FRAME | USB_ATTRS_ISOC_XFER_ASAP)) {
case USB_ATTRS_ISOC_START_FRAME:
/* Starting frame number is specified */
if (pp->pp_flag & EHCI_ISOC_XFER_CONTINUE) {
/* Get the starting usb frame number */
start_frame_number = pp->pp_next_frame_number;
} else {
/* Check for the Starting usb frame number */
if ((isoc_reqp->isoc_frame_no == 0) ||
((isoc_reqp->isoc_frame_no +
isoc_reqp->isoc_pkts_count) <
current_frame_number)) {
/* Exit the critical */
ddi_exit_critical(ddic);
USB_DPRINTF_L2(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_insert_isoc_to_pfl:"
"Invalid starting frame number");
return (USB_INVALID_START_FRAME);
}
/* Get the starting usb frame number */
start_frame_number = isoc_reqp->isoc_frame_no;
pp->pp_next_frame_number = 0;
}
break;
case USB_ATTRS_ISOC_XFER_ASAP:
/* ehci has to specify starting frame number */
if ((pp->pp_next_frame_number) &&
(pp->pp_next_frame_number > current_frame_number)) {
/*
* Get the next usb frame number.
*/
start_frame_number = pp->pp_next_frame_number;
} else {
/*
* Add appropriate offset to the current usb
* frame number and use it as a starting frame
* number.
*/
start_frame_number =
current_frame_number + EHCI_FRAME_OFFSET;
}
if (!(pp->pp_flag & EHCI_ISOC_XFER_CONTINUE)) {
isoc_reqp->isoc_frame_no = start_frame_number;
}
break;
default:
/* Exit the critical */
ddi_exit_critical(ddic);
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_insert_isoc_to_pfl: Either starting "
"frame number or ASAP flags are not set, attrs = 0x%x",
isoc_reqp->isoc_attributes);
return (USB_NO_FRAME_NUMBER);
}
if (itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
port_status = 0;
} else {
port_status = EHCI_ITD_LINK_REF_SITD;
}
itd = itw->itw_itd_head;
while (itd) {
/* Find the appropriate frame list to put the itd into */
pfl_number = start_frame_number % EHCI_NUM_PERIODIC_FRAME_LISTS;
addr = Get_PFLT(periodic_frame_list->
ehci_periodic_frame_list_table[pfl_number]);
Set_ITD(itd->itd_link_ptr, addr);
/* Set the link_ref correctly as ITD or SITD. */
addr = ehci_itd_cpu_to_iommu(ehcip, itd) & EHCI_ITD_LINK_PTR;
addr |= port_status;
Set_PFLT(periodic_frame_list->
ehci_periodic_frame_list_table[pfl_number], addr);
/* Save which frame the ITD was inserted into */
Set_ITD_FRAME(itd->itd_frame_number, start_frame_number);
ehci_insert_itd_into_active_list(ehcip, itd);
/* Get the next ITD in the ITW */
itd = ehci_itd_iommu_to_cpu(ehcip,
Get_ITD(itd->itd_itw_next_itd));
start_frame_number++;
}
/* Exit the critical */
ddi_exit_critical(ddic);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_isoc_to_pfl: "
"current frame number 0x%llx start frame number 0x%llx num itds %d",
current_frame_number, start_frame_number, itw->itw_num_itds);
/*
* Increment this saved frame number by current number
* of data packets needs to be transfer.
*/
pp->pp_next_frame_number = start_frame_number;
/*
* Set EHCI_ISOC_XFER_CONTINUE flag in order to send other
* isochronous packets, part of the current isoch request
* in the subsequent frames.
*/
pp->pp_flag |= EHCI_ISOC_XFER_CONTINUE;
return (USB_SUCCESS);
}
/*
* ehci_remove_isoc_to_pfl:
*
* Remove an ITD request from the Host Controller's isochronous list.
* If we can't find it, something has gone wrong.
*/
void
ehci_remove_isoc_from_pfl(
ehci_state_t *ehcip,
ehci_itd_t *curr_itd)
{
ehci_periodic_frame_list_t *periodic_frame_list;
uint_t pfl_number;
uint32_t next_addr, curr_itd_addr;
uint32_t link_ref;
ehci_itd_t *prev_itd = NULL;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_remove_isoc_from_pfl:");
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
/* Get the address of the current itd */
curr_itd_addr = ehci_itd_cpu_to_iommu(ehcip, curr_itd);
/*
* Remove this ITD from the PFL
* But first we need to find it in the PFL
*/
periodic_frame_list = ehcip->ehci_periodic_frame_list_tablep;
pfl_number = Get_ITD_FRAME(curr_itd->itd_frame_number) %
EHCI_NUM_PERIODIC_FRAME_LISTS;
USB_DPRINTF_L4(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_remove_isoc_from_pfl: itd = 0x%p pfl number 0x%x",
curr_itd, pfl_number);
next_addr = Get_PFLT(periodic_frame_list->
ehci_periodic_frame_list_table[pfl_number]);
while ((next_addr & EHCI_ITD_LINK_PTR) !=
(curr_itd_addr & EHCI_ITD_LINK_PTR)) {
link_ref = next_addr & EHCI_ITD_LINK_REF;
if ((link_ref == EHCI_ITD_LINK_REF_ITD) ||
(link_ref == EHCI_ITD_LINK_REF_SITD)) {
prev_itd = ehci_itd_iommu_to_cpu(ehcip,
(next_addr & EHCI_ITD_LINK_PTR));
next_addr = Get_ITD(prev_itd->itd_link_ptr);
} else {
break;
}
}
/*
* If the next itd is the current itd, that means we found it.
* Set the previous's ITD link ptr to the Curr_ITD's link ptr.
* But do not touch the Curr_ITD's link ptr.
*/
if ((next_addr & EHCI_ITD_LINK_PTR) ==
(curr_itd_addr & EHCI_ITD_LINK_PTR)) {
next_addr = Get_ITD(curr_itd->itd_link_ptr);
if (prev_itd == NULL) {
/* This means PFL points to this ITD */
Set_PFLT(periodic_frame_list->
ehci_periodic_frame_list_table[pfl_number],
next_addr);
} else {
/* Set the previous ITD's itd_link_ptr */
Set_ITD(prev_itd->itd_link_ptr, next_addr);
}
Set_ITD_FRAME(curr_itd->itd_frame_number, 0);
} else {
ASSERT((next_addr & EHCI_ITD_LINK_PTR) ==
(curr_itd_addr & EHCI_ITD_LINK_PTR));
USB_DPRINTF_L3(PRINT_MASK_ALLOC, ehcip->ehci_log_hdl,
"ehci_remove_isoc_from_pfl: Unable to find ITD in PFL");
}
}
/*
* Isochronous in resource functions
*/
/*
* ehci_allocate_periodic_in_resource
*
* Allocate interrupt request structure for the interrupt IN transfer.
*/
int
ehci_allocate_isoc_in_resource(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw,
usb_flags_t flags)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
usb_isoc_req_t *orig_isoc_reqp, *clone_isoc_reqp;
USB_DPRINTF_L4(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_in_resource:"
"pp = 0x%p itw = 0x%p flags = 0x%x", pp, itw, flags);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT(itw->itw_curr_xfer_reqp == NULL);
/* Get the client periodic in request pointer */
orig_isoc_reqp = (usb_isoc_req_t *)(pp->pp_client_periodic_in_reqp);
ASSERT(orig_isoc_reqp != NULL);
clone_isoc_reqp = usba_hcdi_dup_isoc_req(ph->p_dip,
orig_isoc_reqp, flags);
if (clone_isoc_reqp == NULL) {
USB_DPRINTF_L2(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_allocate_isoc_in_resource: Isochronous"
"request structure allocation failed");
return (USB_NO_RESOURCES);
}
/*
* Save the client's isochronous request pointer and
* length of isochronous transfer in transfer wrapper.
* The dup'ed request is saved in pp_client_periodic_in_reqp
*/
itw->itw_curr_xfer_reqp = orig_isoc_reqp;
itw->itw_length = clone_isoc_reqp->isoc_pkts_length;
pp->pp_client_periodic_in_reqp = (usb_opaque_t)clone_isoc_reqp;
mutex_enter(&ph->p_mutex);
ph->p_req_count++;
mutex_exit(&ph->p_mutex);
pp->pp_state = EHCI_PIPE_STATE_ACTIVE;
return (USB_SUCCESS);
}
/*
* ehci_deallocate_isoc_in_resource
*
* Deallocate interrupt request structure for the interrupt IN transfer.
*/
void
ehci_deallocate_isoc_in_resource(
ehci_state_t *ehcip,
ehci_pipe_private_t *pp,
ehci_isoc_xwrapper_t *itw)
{
usba_pipe_handle_data_t *ph = pp->pp_pipe_handle;
uchar_t ep_attr = ph->p_ep.bmAttributes;
usb_isoc_req_t *isoc_reqp;
USB_DPRINTF_L4(PRINT_MASK_LISTS,
ehcip->ehci_log_hdl,
"ehci_deallocate_isoc_in_resource: "
"pp = 0x%p itw = 0x%p", pp, itw);
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
ASSERT((ep_attr & USB_EP_ATTR_MASK) == USB_EP_ATTR_ISOCH);
isoc_reqp = itw->itw_curr_xfer_reqp;
/* Check the current periodic in request pointer */
if (isoc_reqp) {
itw->itw_curr_xfer_reqp = NULL;
itw->itw_curr_isoc_pktp = NULL;
mutex_enter(&ph->p_mutex);
ph->p_req_count--;
mutex_exit(&ph->p_mutex);
usb_free_isoc_req(isoc_reqp);
/* Set periodic in pipe state to idle */
pp->pp_state = EHCI_PIPE_STATE_IDLE;
}
}
/*
* ehci_itd_cpu_to_iommu:
*
* This function converts for the given Transfer Descriptor (ITD) CPU address
* to IO address.
*/
uint32_t
ehci_itd_cpu_to_iommu(
ehci_state_t *ehcip,
ehci_itd_t *addr)
{
uint32_t td;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (addr == NULL) {
return (NULL);
}
td = (uint32_t)ehcip->ehci_itd_pool_cookie.dmac_address +
(uint32_t)((uintptr_t)addr -
(uintptr_t)(ehcip->ehci_itd_pool_addr));
ASSERT(((uint32_t) (sizeof (ehci_itd_t) *
(addr - ehcip->ehci_itd_pool_addr))) ==
((uint32_t)((uintptr_t)addr - (uintptr_t)
(ehcip->ehci_itd_pool_addr))));
ASSERT(td >= ehcip->ehci_itd_pool_cookie.dmac_address);
ASSERT(td <= ehcip->ehci_itd_pool_cookie.dmac_address +
sizeof (ehci_itd_t) * ehci_itd_pool_size);
return (td);
}
/*
* ehci_itd_iommu_to_cpu:
*
* This function converts for the given Transfer Descriptor (ITD) IO address
* to CPU address.
*/
ehci_itd_t *
ehci_itd_iommu_to_cpu(
ehci_state_t *ehcip,
uintptr_t addr)
{
ehci_itd_t *itd;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
if (addr == NULL) {
return (NULL);
}
itd = (ehci_itd_t *)((uintptr_t)
(addr - ehcip->ehci_itd_pool_cookie.dmac_address) +
(uintptr_t)ehcip->ehci_itd_pool_addr);
ASSERT(itd >= ehcip->ehci_itd_pool_addr);
ASSERT((uintptr_t)itd <= (uintptr_t)ehcip->ehci_itd_pool_addr +
(uintptr_t)(sizeof (ehci_itd_t) * ehci_itd_pool_size));
return (itd);
}
/*
* Error parsing functions
*/
void ehci_parse_isoc_error(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd) {
usb_isoc_req_t *isoc_reqp;
usb_isoc_pkt_descr_t *isoc_pkt_descr;
usb_cr_t error;
ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
isoc_reqp = itw->itw_curr_xfer_reqp;
isoc_pkt_descr = itw->itw_curr_isoc_pktp;
if (itw->itw_port_status == USBA_HIGH_SPEED_DEV) {
error = ehci_parse_itd_error(ehcip, itw, itd);
} else {
error = ehci_parse_sitd_error(ehcip, itw, itd);
}
/* Write the status of isoc data packet */
isoc_pkt_descr->isoc_pkt_status = error;
if (error != USB_CR_OK) {
isoc_reqp->isoc_error_count++;
USB_DPRINTF_L2(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_parse_isoc_error: Error %d Device Address %d "
"Endpoint number %d", error, itw->itw_device_addr,
itw->itw_endpoint_num);
}
itw->itw_curr_isoc_pktp++;
}
/* ARGSUSED */
static usb_cr_t ehci_parse_itd_error(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd) {
return (USB_NOT_SUPPORTED);
}
static usb_cr_t ehci_parse_sitd_error(
ehci_state_t *ehcip,
ehci_isoc_xwrapper_t *itw,
ehci_itd_t *itd) {
uint32_t status;
usb_cr_t error;
usb_isoc_pkt_descr_t *isoc_pkt_descr;
uint32_t residue;
isoc_pkt_descr = itw->itw_curr_isoc_pktp;
status = Get_ITD_BODY(itd, EHCI_SITD_XFER_STATE) &
EHCI_SITD_XFER_STATUS_MASK;
switch (status) {
case EHCI_SITD_XFER_ACTIVE:
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_sitd_error: NOT ACCESSED");
error = USB_CR_NOT_ACCESSED;
break;
case EHCI_SITD_XFER_ERROR:
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_sitd_error: TT ERROR");
error = USB_CR_UNSPECIFIED_ERR;
break;
case EHCI_SITD_XFER_DATA_BUFFER_ERR:
if (itw->itw_direction == USB_EP_DIR_OUT) {
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_sitd_error: BUFFER Underrun");
error = USB_CR_BUFFER_UNDERRUN;
} else {
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_sitd_error: BUFFER Overrun");
error = USB_CR_BUFFER_OVERRUN;
}
break;
case EHCI_SITD_XFER_BABBLE:
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_sitd_error: BABBLE");
error = USB_CR_DATA_OVERRUN;
break;
case EHCI_SITD_XFER_XACT_ERROR:
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_sitd_error: XACT ERROR");
error = USB_CR_DEV_NOT_RESP;
break;
case EHCI_SITD_XFER_MISSED_UFRAME:
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_sitd_error: MISSED UFRAME");
error = USB_CR_NOT_ACCESSED;
break;
default:
USB_DPRINTF_L4(PRINT_MASK_INTR, ehcip->ehci_log_hdl,
"ehci_check_for_sitd_error: NO ERROR");
error = USB_CR_OK;
break;
}
/* This is HCD specific and may not have this information */
residue =
(Get_ITD_BODY(itd, EHCI_SITD_XFER_STATE) &
EHCI_SITD_XFER_TOTAL_MASK) >>
EHCI_SITD_XFER_TOTAL_SHIFT;
/*
* Subtract the residue from the isoc_pkt_descr that
* was set when this ITD was inserted.
*/
isoc_pkt_descr->isoc_pkt_actual_length -= residue;
return (error);
}
/*
* debug print functions
*/
void
ehci_print_itd(
ehci_state_t *ehcip,
ehci_itd_t *itd)
{
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"ehci_print_itd: itd = 0x%p", (void *)itd);
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_link_ptr: 0x%x ", Get_ITD(itd->itd_link_ptr));
if ((Get_ITD(itd->itd_link_ptr) & EHCI_ITD_LINK_REF) ==
EHCI_ITD_LINK_REF_ITD) {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_ctrl0: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_CTRL0]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_ctrl1: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_CTRL1]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_ctrl2: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_CTRL2]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_ctrl3: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_CTRL3]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_ctrl4: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_CTRL4]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_ctrl5: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_CTRL5]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_ctrl6: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_CTRL6]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_ctrl7: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_CTRL7]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_buffer0: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_BUFFER0]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_buffer1: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_BUFFER1]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_buffer2: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_BUFFER2]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_buffer3: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_BUFFER3]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_buffer4: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_BUFFER4]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_buffer5: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_BUFFER5]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_buffer6: 0x%x ",
Get_ITD(itd->itd_body[EHCI_ITD_BUFFER6]));
} else {
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\tsitd_ctrl: 0x%x ",
Get_ITD(itd->itd_body[EHCI_SITD_CTRL]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\tsitd_uframe_sched: 0x%x ",
Get_ITD(itd->itd_body[EHCI_SITD_UFRAME_SCHED]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\tsitd_xfer_state: 0x%x ",
Get_ITD(itd->itd_body[EHCI_SITD_XFER_STATE]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\tsitd_buffer0: 0x%x ",
Get_ITD(itd->itd_body[EHCI_SITD_BUFFER0]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\tsitd_buffer1: 0x%x ",
Get_ITD(itd->itd_body[EHCI_SITD_BUFFER1]));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\tsitd_prev_sitd: 0x%x ",
Get_ITD(itd->itd_body[EHCI_SITD_PREV_SITD]));
}
/* HCD private fields */
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_trans_wrapper: 0x%x ",
Get_ITD(itd->itd_trans_wrapper));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_itw_next_itd: 0x%x ",
Get_ITD(itd->itd_itw_next_itd));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_state: 0x%x ",
Get_ITD(itd->itd_state));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_frame_number: 0x%x ",
Get_ITD(itd->itd_frame_number));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_reclaim_number: 0x%x ",
Get_ITD(itd->itd_reclaim_number));
USB_DPRINTF_L3(PRINT_MASK_LISTS, ehcip->ehci_log_hdl,
"\titd_next_active_itd: 0x%x ",
Get_ITD(itd->itd_next_active_itd));
}