uhciutil.c revision 932cf989e70cd9d0418891b72e973f56f41c28b1
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Universal Host Controller Driver (UHCI)
*
* The UHCI driver is a driver which interfaces to the Universal
* Serial Bus Driver (USBA) and the Host Controller (HC). The interface to
* the Host Controller is defined by the UHCI.
* This file contains misc functions.
*/
/* Globals */
extern ushort_t uhci_tree_bottom_nodes[];
extern void *uhci_statep;
/* function prototypes */
#ifdef DEBUG
#endif
/*
* uhci_build_interrupt_lattice:
*
* Construct the interrupt lattice tree using static Queue Head pointers.
* This interrupt lattice tree will have total of 63 queue heads and the
* Host Controller (HC) processes queue heads every frame.
*/
static void
{
uint16_t i, j, k;
"uhci_build_interrupt_lattice:");
/*
* Reserve the first 63 queue head structures in the pool as static
* queue heads & these are required for constructing interrupt
* lattice tree.
*/
for (i = 0; i < NUM_INTR_QH_LISTS; i++) {
list_array[i].node = i;
}
/* Build the interrupt lattice tree */
for (i = 0; i < half_list - 1; i++) {
/*
* The next pointer in the host controller queue head
* descriptor must contain an iommu address. Calculate
* the offset into the cpu address and add this to the
* starting iommu address.
*/
}
/*
* Initialize the interrupt list in the Frame list Table
* so that it points to the bottom of the tree.
*/
for (i = 0, j = 0; i < pow_2(TREE_HEIGHT); i++) {
for (k = 0; k < pow_2(VIRTUAL_TREE_HEIGHT); k++) {
addr | HC_QUEUE_HEAD);
}
}
/*
* Create a controller and bulk Queue heads
*/
/*
* Add a dummy TD to the static queue head 0. THis is used
* to generate an at the end of frame.
*/
/*
* Add a dummy td that is used to generate an interrupt for
* every 1024 frames.
*/
}
/*
* uhci_allocate_pools:
* Allocate the system memory for the Queue Heads Descriptor and
* for the Transfer Descriptor (TD) pools. Both QH and TD structures
* must be aligned to a 16 byte boundary.
*/
int
{
int i, result;
"uhci_allocate_pools:");
/* The host controller will be little endian */
/* Allocate the TD pool DMA handle */
return (USB_FAILURE);
}
/* Allocate the memory for the TD pool */
uhci_td_pool_size * sizeof (uhci_td_t),
&uhcip->uhci_td_pool_mem_handle)) {
return (USB_FAILURE);
}
/* Map the TD pool into the I/O address space */
uhci_td_pool_size * sizeof (uhci_td_t));
/* Process the result */
if (result == DDI_DMA_MAPPED) {
/* The cookie count should be 1 */
if (ccount != 1) {
"uhci_allocate_pools: More than 1 cookie");
return (USB_FAILURE);
}
} else {
"uhci_allocate_pools: Result = %d", result);
return (USB_FAILURE);
}
/* Initialize the TD pool */
for (i = 0; i < uhci_td_pool_size; i++) {
}
/* Allocate the TD pool DMA handle */
return (USB_FAILURE);
}
/* Allocate the memory for the QH pool */
uhci_qh_pool_size * sizeof (queue_head_t),
return (USB_FAILURE);
}
/* Process the result */
if (result == DDI_DMA_MAPPED) {
/* The cookie count should be 1 */
if (ccount != 1) {
"uhci_allocate_pools: More than 1 cookie");
return (USB_FAILURE);
}
} else {
return (USB_FAILURE);
}
uhci_qh_pool_size * sizeof (queue_head_t));
/* Initialize the QH pool */
for (i = 0; i < uhci_qh_pool_size; i ++) {
}
"uhci_allocate_pools: Completed");
return (USB_SUCCESS);
}
/*
* uhci_free_pools:
* Cleanup on attach failure or detach
*/
void
{
"uhci_free_pools:");
for (i = 0; i < uhci_td_pool_size; i ++) {
if ((flag != TD_FLAG_FREE) &&
}
}
}
}
/* Free the TD pool */
if (uhcip->uhci_td_pool_dma_handle) {
}
}
}
/* Free the QH pool */
if (uhcip->uhci_qh_pool_dma_handle) {
}
/* Free the Frame list Table area */
}
}
if (uhcip->uhci_flt_dma_handle) {
}
}
/*
* uhci_decode_ddi_dma_addr_bind_handle_result:
* Process the return values of ddi_dma_addr_bind_handle()
*/
void
{
char *msg;
"uhci_decode_ddi_dma_addr_bind_handle_result:");
switch (result) {
case DDI_DMA_PARTIAL_MAP:
msg = "Partial transfers not allowed";
break;
case DDI_DMA_INUSE:
msg = "Handle is in use";
break;
case DDI_DMA_NORESOURCES:
msg = "No resources";
break;
case DDI_DMA_NOMAPPING:
msg = "No mapping";
break;
case DDI_DMA_TOOBIG:
msg = "Object is too big";
break;
default:
msg = "Unknown dma error";
}
}
/*
* uhci_init_ctlr:
* Initialize the Host Controller (HC).
*/
int
{
/*
* When USB legacy mode is enabled, the BIOS manages the USB keyboard
* attached to the UHCI controller. It has been observed that some
* times the BIOS does not clear the interrupts in the legacy mode
* register in the PCI configuration space. So, disable the SMI intrs
* and route the intrs to PIRQD here.
*/
/*
* Disable all the interrupts.
*/
cmd_reg &= (~USBCMD_REG_HC_RUN);
/* Stop the controller */
/* Reset the host controller */
/* Wait 10ms for reset to complete */
Set_OpReg16(USBCMD, 0);
/* Set the frame number to zero */
Set_OpReg16(FRNUM, 0);
/* Initialize the Frame list base address area */
return (USB_FAILURE);
}
}
/* Save the contents of the Frame Interval Registers */
/* Set the Frame list base address */
/*
* Begin sending SOFs
* Set the Host Controller Functional State to Operational
*/
/*
* Verify the Command and interrupt enable registers,
* a sanity check whether actually initialized or not
*/
"uhci_init_ctlr: Controller initialization failed");
return (USB_FAILURE);
}
/*
* Set the ioc bit of the isoc intr td. This enables
* the generation of an interrupt for every 1024 frames.
*/
/* Set host controller soft state to operational */
"uhci_init_ctlr: Completed");
return (USB_SUCCESS);
}
/*
* uhci_uninit_ctlr:
* uninitialize the Host Controller (HC).
*/
void
{
if (uhcip->uhci_regs_handle) {
/* Disable all the interrupts. */
/* Complete the current transaction and then halt. */
Set_OpReg16(USBCMD, 0);
/* Wait for sometime */
}
}
/*
* uhci_map_regs:
* The Host Controller (HC) contains a set of on-chip operational
* registers and which should be mapped into a non-cacheable
* portion of the system addressable space.
*/
int
{
int index;
/* The host controller will be little endian */
return (USB_FAILURE);
}
break;
}
}
/*
* Deallocate the memory allocated by the ddi_prop_lookup_int_array
*/
return (USB_FAILURE);
}
/* Map in operational registers */
DDI_SUCCESS) {
"ddi_regs_map_setup: failed");
return (USB_FAILURE);
}
"uhci_map_regs: Config error");
return (USB_FAILURE);
}
/* Make sure Memory Access Enable and Master Enable are set */
}
/*
* Check whether I/O base address is configured and enabled.
*/
if (!(command_reg & PCI_COMM_IO)) {
"I/O Base address access disabled");
return (USB_FAILURE);
}
/*
* Get the IO base address of the controller
*/
"uhci_map_regs: Completed");
return (USB_SUCCESS);
}
void
{
/* Unmap the UHCI registers */
if (uhcip->uhci_regs_handle) {
/* Reset the host controller */
}
if (uhcip->uhci_config_handle) {
}
}
/*
* uhci_set_dma_attributes:
* Set the limits in the DMA attributes structure. Most of the values used
* in the DMA limit structres are the default values as specified by the
* Writing PCI device drivers document.
*/
void
{
"uhci_set_dma_attributes:");
/* Initialize the DMA attributes */
/* 32 bit addressing */
/*
* Setting the dam_att_align to 512, some times fails the
* binding handle. I dont know why ? But setting to 16 will
* be right for our case (16 byte alignment required per
* UHCI spec for TD descriptors).
*/
/* 16 byte alignment */
/*
* Since PCI specification is byte alignment, the
* burstsize field should be set to 1 for PCI devices.
*/
}
{
return ((x == 0) ? 1 : (1 << x));
}
{
int ret_val = 0;
while (x != 1) {
ret_val++;
x = x >> 1;
}
return (ret_val);
}
/*
* uhci_obtain_state:
*/
{
return (state);
}
/*
* uhci_alloc_hcdi_ops:
* The HCDI interfaces or entry points are the software interfaces used by
* the Universal Serial Bus Driver (USBA) to access the services of the
* Host Controller Driver (HCD). During HCD initialization, inform USBA
* about all available HCDI interfaces or entry points.
*/
{
"uhci_alloc_hcdi_ops:");
return (hcdi_ops);
}
/*
* uhci_init_frame_lst_table :
* Allocate the system memory and initialize Host Controller
* Frame list table area The starting of the Frame list Table
* area must be 4096 byte aligned.
*/
static int
{
int result;
"uhci_init_frame_lst_table:");
/* The host controller will be little endian */
/* 4K alignment required */
/* Create space for the HCCA block */
return (USB_FAILURE);
}
/* Reset to default 16 bytes */
return (USB_FAILURE);
}
/* Map the whole Frame list base area into the I/O address space */
if (result == DDI_DMA_MAPPED) {
/* The cookie count should be 1 */
if (ccount != 1) {
"uhci_init_frame_list_table: More than 1 cookie");
return (USB_FAILURE);
}
} else {
return (USB_FAILURE);
}
/* Initialize the interrupt lists */
return (USB_SUCCESS);
}
/*
* uhci_alloc_queue_head:
* Allocate a queue head
*/
{
int index;
"uhci_alloc_queue_head");
/* Allocate a dummy td first. */
"uhci_alloc_queue_head: allocate td from pool failed");
return (NULL);
}
/*
* The first 63 queue heads in the Queue Head (QH)
* buffer pool are reserved for building interrupt lattice
* tree. Search for a blank Queue head in the QH buffer pool.
*/
break;
}
}
"uhci_alloc_queue_head: Allocated %d", index);
if (index == uhci_qh_pool_size) {
"uhci_alloc_queue_head: All QH exhausted");
/* Free the dummy td allocated for this qh. */
return (NULL);
}
"uhci_alloc_queue_head: Allocated address 0x%p", queue_head);
return (queue_head);
}
/*
* uhci_allocate_bandwidth:
* Figure out whether or not this interval may be supported. Return
* the index into the lattice if it can be supported. Return
* allocation failure if it can not be supported.
*/
int
{
int bandwidth; /* Requested bandwidth */
uint_t i;
/*
* Calculate the length in bytes of a transaction on this
* periodic endpoint.
*/
/*
* If the length in bytes plus the allocated bandwidth exceeds
* the maximum, return bandwidth allocation failure.
*/
"uhci_allocate_bandwidth: "
"Reached maximum bandwidth value and cannot allocate "
return (USB_NO_BANDWIDTH);
}
/*
* ISOC xfers are not supported at this point type
*/
return (USB_SUCCESS);
}
/*
* This is an interrupt endpoint.
* Adjust bandwidth to be a power of 2
*/
/*
* If this bandwidth can't be supported,
* return allocation failure.
*/
if (bandwidth == USB_FAILURE) {
return (USB_FAILURE);
}
"The new bandwidth is %d", bandwidth);
/* Find the leaf with the smallest allocated bandwidth */
min_index = 0;
for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
min_index = i;
}
}
"The leaf with minimal bandwidth %d, "
/*
* Find the index into the lattice given the
* leaf with the smallest allocated bandwidth.
*/
"The height is %d", height);
/* check if there are isocs TDs scheduled for this frame */
} else {
}
for (i = 0; i < height; i++) {
}
"The real node is %d", *node);
/*
* Find the leftmost leaf in the subtree specified by the node.
*/
"Leftmost %d", leftmost);
(NUM_FRAME_LST_ENTRIES/bandwidth); i ++) {
"uhci_allocate_bandwidth: "
"Reached maximum bandwidth value and cannot "
"allocate bandwidth for Interrupt endpoint");
return (USB_NO_BANDWIDTH);
}
}
/*
* All the leaves for this node must be updated with the bandwidth.
*/
(NUM_FRAME_LST_ENTRIES/bandwidth); i ++) {
}
/* Find the leaf with the smallest allocated bandwidth */
min_index = 0;
for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
min_index = i;
}
}
/* Save the minimum for later use */
return (USB_SUCCESS);
}
/*
* uhci_deallocate_bandwidth:
* Deallocate bandwidth for the given node in the lattice
* and the length of transfer.
*/
void
{
uint_t i;
/* This routine is protected by the uhci_int_mutex */
/* Obtain the length */
/*
* If this is an isochronous endpoint, just delete endpoint's
* bandwidth from the total allocated isochronous bandwidth.
*/
return;
}
/* Obtain the node */
/* Adjust bandwidth to be a power of 2 */
/* Find the height in the tree */
/*
* Find the leftmost leaf in the subtree specified by the node
*/
/* Delete the bandwith from the appropriate lists */
i ++) {
}
/* Recompute the minimum */
for (i = 1; i < NUM_FRAME_LST_ENTRIES; i++) {
}
}
/* Save the minimum for later use */
}
/*
* uhci_compute_total_bandwidth:
*
* Given a periodic endpoint (interrupt or isochronous) determine the total
* bandwidth for one transaction. The UHCI host controller traverses the
* endpoint descriptor lists on a first-come-first-serve basis. When the HC
* services an endpoint, only a single transaction attempt is made. The HC
* moves to the next Endpoint Descriptor after the first transaction attempt
* rather than finishing the entire Transfer Descriptor. Therefore, when a
* Transfer Descriptor is inserted into the lattice, we will only count the
* number of bytes for one transaction.
*
* The following are the formulas used for calculating bandwidth in terms
* bytes and it is for the single USB full speed and low speed transaction
* respectively. The protocol overheads will be different for each of type
* of USB transfer and all these formulas & protocol overheads are derived
* from the 5.9.3 section of USB Specification & with the help of Bandwidth
* Analysis white paper which is posted on the USB developer forum.
*
* Full-Speed:
* Protocol overhead + ((MaxPacketSize * 7)/6 ) + Host_Delay
*
* Low-Speed:
* Protocol overhead + Hub LS overhead +
* (Low-Speed clock * ((MaxPacketSize * 7)/6 )) + Host_Delay
*/
static uint_t
{
/* Add Host Controller specific delay to required bandwidth */
/* Add bit-stuffing overhead */
/* Low Speed interrupt transaction */
if (port_status == USBA_LOW_SPEED_DEV) {
/* Low Speed interrupt transaction */
(LOW_SPEED_CLOCK * MaxPacketSize));
} else {
/* Full Speed transaction */
/* Full Speed interrupt transaction */
} else {
/* Isochronus and input transaction */
} else {
/* Isochronus and output transaction */
}
}
}
return (bandwidth);
}
/*
* uhci_bandwidth_adjust:
*/
static int
{
int i = 0;
/*
* Get the polling interval from the endpoint descriptor
*/
/*
* The bInterval value in the endpoint descriptor can range
* from 1 to 255ms. The interrupt lattice has 32 leaf nodes,
* and the host controller cycles through these nodes every
* 32ms. The longest polling interval that the controller
* supports is 32ms.
*/
/*
* Return an error if the polling interval is less than 1ms
* and greater than 255ms
*/
"uhci_bandwidth_adjust: Endpoint's poll interval must be "
"between %d and %d ms", MIN_POLL_INTERVAL,
return (USB_FAILURE);
}
/*
* According USB Specifications, a full-speed endpoint can
* specify a desired polling interval 1ms to 255ms and a low
* speed endpoints are limited to specifying only 10ms to
* 255ms. But some old keyboards & mice uses polling interval
* of 8ms. For compatibility purpose, we are using polling
* interval between 8ms & 255ms for low speed endpoints.
*/
if ((port_status == USBA_LOW_SPEED_DEV) &&
"uhci_bandwidth_adjust: Low speed endpoint's poll interval "
"must be >= %d ms, adjusted",
}
/*
* If polling interval is greater than 32ms,
* adjust polling interval equal to 32ms.
*/
if (interval > 32) {
interval = 32;
}
/*
* Find the nearest power of 2 that's less
* than interval.
*/
i++;
}
return (pow_2((i - 1)));
}
/*
* uhci_lattice_height:
* Given the requested bandwidth, find the height in the tree at
* which the nodes for this bandwidth fall. The height is measured
* as the number of nodes from the leaf to the level specified by
* bandwidth The root of the tree is at height TREE_HEIGHT.
*/
static uint_t
{
}
static uint_t
{
}
/*
* uhci_leftmost_leaf:
* Find the leftmost leaf in the subtree specified by the node.
* Height refers to number of nodes from the bottom of the tree
* to the node, including the node.
*/
static uint_t
{
return (node);
}
/*
* uhci_insert_qh:
* Add the Queue Head (QH) into the Host Controller's (HC)
* appropriate queue head list.
*/
void
{
"uhci_insert_qh:");
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_ISOCH:
"uhci_insert_qh: Illegal request");
break;
}
}
/*
* uhci_insert_ctrl_qh:
* Insert a control QH into the Host Controller's (HC) control QH list.
*/
static void
{
"uhci_insert_ctrl_qh:");
}
}
/*
* uhci_insert_bulk_qh:
* Insert a bulk QH into the Host Controller's (HC) bulk QH list.
*/
static void
{
"uhci_insert_bulk_qh:");
/* If there is already a loop, we should keep the loop. */
}
}
/*
* uhci_insert_intr_qh:
* Insert a periodic Queue head i.e Interrupt queue head into the
* Host Controller's (HC) interrupt lattice tree.
*/
static void
{
/* found during the opening */
/* of the pipe. */
"uhci_insert_intr_qh:");
/* Find the lattice queue head */
pp->pp_data_toggle = 0;
}
/*
* uhci_insert_intr_td:
* Create a TD and a data buffer for an interrupt endpoint.
*/
int
{
"uhci_insert_intr_td: req: 0x%p", req);
/* Get the interrupt pipe direction */
/* Get the current interrupt request pointer */
if (req) {
} else {
(((usb_intr_req_t *)pp->
}
/* Check the size of interrupt request */
if (length > UHCI_MAX_TD_XFER_SIZE) {
/* the length shouldn't exceed 8K */
"uhci_insert_intr_td: Intr request size 0x%lx is "
return (USB_INVALID_REQUEST);
}
"uhci_insert_intr_td: length: 0x%lx", length);
/* Allocate a transaction wrapper */
NULL) {
"uhci_insert_intr_td: TW allocation failed");
return (USB_NO_RESOURCES);
}
/*
* Initialize the callback and any callback
* data for when the td completes.
*/
/*
* If it is an Interrupt IN request and interrupt request is NULL,
* allocate the usb interrupt request structure for the current
* interrupt polling request.
*/
"uhci_insert_intr_td: Interrupt request structure "
"allocation failed");
/* free the transfer wrapper */
return (error);
}
}
intr_reqp->intr_timeout : 0;
/* DATA IN */
/* Insert the td onto the queue head */
if (error != USB_SUCCESS) {
/* free the transfer wrapper */
return (USB_NO_RESOURCES);
}
tw->tw_bytes_xfered = 0;
return (USB_SUCCESS);
}
/* DATA OUT */
/* Copy the data into the message */
}
/* set tw->tw_claim flag, so that nobody else works on this tw. */
buf_offs = 0;
/* Insert tds onto the queue head */
while (length > 0) {
if (error != USB_SUCCESS) {
/* no resource. */
break;
}
/* inserted all data. */
length = 0;
} else {
}
}
if (error != USB_SUCCESS) {
"uhci_insert_intr_td: allocate td failed, free resource");
/* remove all the tds */
}
return (error);
}
/* allow HC to xfer the tds of this tw */
}
tw->tw_bytes_xfered = 0;
return (error);
}
/*
* uhci_create_transfer_wrapper:
* Create a Transaction Wrapper (TW) for non-isoc transfer types.
* This involves the allocating of DMA resources.
*
* For non-isoc transfers, one DMA handle and one DMA buffer are
* allocated per transfer. The DMA buffer may contain multiple
* DMA cookies and the cookies should meet certain alignment
* requirement to be able to fit in the multiple TDs. The alignment
* needs to ensure:
* 1. the size of a cookie be larger than max TD length (0x500)
* 2. the size of a cookie be a multiple of wMaxPacketSize of the
*
* wMaxPacketSize for ctrl and bulk pipes may be 8, 16, 32 or 64 bytes.
* So the alignment should be a multiple of 64. wMaxPacketSize for intr
* pipes is a little different since it only specifies the max to be
* 64 bytes, but as long as an intr transfer is limited to max TD length,
* any alignment can work if the cookie size is larger than max TD length.
*
* Considering the above conditions, 2K alignment is used. 4K alignment
* should also be fine.
*/
static uhci_trans_wrapper_t *
{
int kmem_flag;
int (*dmamem_wait)(caddr_t);
"uhci_create_transfer_wrapper: length = 0x%lx flags = 0x%x",
/* isochronous pipe should not call into this function */
return (NULL);
}
/* SLEEP flag should not be used in interrupt context */
if (servicing_interrupt()) {
} else {
}
/* Allocate space for the transfer wrapper */
NULL) {
"uhci_create_transfer_wrapper: kmem_alloc failed");
return (NULL);
}
/* zero-length packet doesn't need to allocate dma memory */
if (length == 0) {
goto dmadone;
}
/* allow sg lists for transfer wrapper dma memory */
/* Store the transfer length */
/* Allocate the DMA handle */
"uhci_create_transfer_wrapper: Alloc handle failed");
return (NULL);
}
/* Allocate the memory */
"uhci_create_transfer_wrapper: dma_mem_alloc fail");
return (NULL);
}
/* Bind the handle */
"uhci_create_transfer_wrapper: Bind handle failed");
return (NULL);
}
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 a back pointer to the pipe private structure */
/* Store the transfer type - synchronous or asynchronous */
"uhci_create_transfer_wrapper: tw = 0x%p, ncookies = %u",
return (tw);
}
/*
* uhci_insert_hc_td:
* Insert a Transfer Descriptor (TD) on an QH.
*/
int
{
return (USB_NO_RESOURCES);
}
/*
* Fill in the current dummy td and
* add the new dummy to the end.
*/
/*
* Allow HC hardware xfer the td, except interrupt out td.
*/
}
/* Insert this td onto the tw */
} else {
/* Add the td to the end of the list */
}
/*
* Insert the TD on to the QH. When this occurs,
* the Host Controller will see the newly filled in TD
*/
} else {
}
return (USB_SUCCESS);
}
/*
* uhci_fill_in_td:
* Fill in the fields of a Transfer Descriptor (TD).
*/
static void
{
"uhci_fill_in_td: td 0x%p buf_offs 0x%x len 0x%lx "
/*
* If this is an isochronous TD, just return
*/
return;
}
/* The maximum transfer length of UHCI cannot exceed 0x500 bytes */
if (attrs & USB_ATTRS_SHORT_XFER_OK) {
}
}
/* Adjust the data toggle bit */
}
/*
* uhci_get_tw_paddr_by_offs:
* Walk through the DMA cookies of a TW buffer to retrieve
* the device address used for a TD.
*
* buffer_offset - the starting offset into the TW buffer, where the
* TD should transfer from. When a TW has more than
* one TD, the TDs must be filled in increasing order.
*/
static uint32_t
{
int rem_len;
"uhci_get_tw_paddr_by_offs: buf_offs 0x%x len 0x%lx",
/*
* TDs 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.
*/
if (length == 0) {
buf_addr = 0;
return (buf_addr);
}
/*
* Advance to the next DMA cookie until finding the cookie
* that buffer_offset falls in.
* It is very likely this loop will never repeat more than
* once. It is here just to accommodate the case buffer_offset
* is increased by multiple cookies during two consecutive
* calls into this function. In that case, the interim DMA
* buffer is allowed to be skipped.
*/
/*
* 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 TDs for current DMA cookie
*/
/* Calculate the beginning address of the buffer */
"uhci_get_tw_paddr_by_offs: dmac_addr 0x%p dmac_size "
tw->tw_cookie_idx);
return (buf_addr);
}
/*
* uhci_modify_td_active_bits:
* Sets active bit in all the tds of QH to INACTIVE so that
* the HC stops processing the TD's related to the QH.
*/
void
{
"uhci_modify_td_active_bits: tw head %p", (void *)tw_head);
while (td_head) {
} else {
}
}
}
}
/*
* uhci_insert_ctrl_td:
* Create a TD and a data buffer for a control Queue Head.
*/
int
{
/*
* If we have a control data phase, make the data buffer start
* on the next 64-byte boundary so as to ensure the DMA cookie
* can fit in the multiple TDs. The buffer in the range of
* [SETUP_SIZE, UHCI_CTRL_EPT_MAX_SIZE) is just for padding
* and not to be transferred.
*/
if (ctrl_reqp->ctrl_wLength) {
} else {
}
/* Allocate a transaction wrapper */
"uhci_insert_ctrl_td: TW allocation failed");
return (USB_NO_RESOURCES);
}
pp->pp_data_toggle = 0;
tw->tw_bytes_xfered = 0;
/*
* Initialize the callback and any callback
* data for when the td completes.
*/
tw->tw_ctrl_state = 0;
/* free the transfer wrapper */
return (USB_NO_RESOURCES);
}
return (USB_SUCCESS);
}
/*
* uhci_create_setup_pkt:
* create a setup packet to initiate a control transfer.
*
* OHCI driver has seen the case where devices fail if there is
* more than one control transfer to the device within a frame.
* So, the UHCI ensures that only one TD will be put on the control
* pipe to one device (to be consistent with OHCI driver).
*/
static int
{
int sdata;
"uhci_create_setup_pkt: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
/* Create the first four bytes of the setup packet */
/* Create the second four bytes */
/*
* The TD's are placed on the QH one at a time.
* Once this TD is placed on the done list, the
* data or status phase TD will be enqueued.
*/
return (USB_NO_RESOURCES);
}
/*
* If this control transfer has a data phase, record the
* direction. If the data phase is an OUT transaction ,
* copy the data into the buffer of the transfer wrapper.
*/
if (req->ctrl_wLength != 0) {
/* There is a data stage. Find the direction */
} else {
/* Copy the data into the buffer */
}
}
return (USB_SUCCESS);
}
/*
* uhci_create_stats:
* Allocate and initialize the uhci kstat structures
*/
void
{
int i;
char kstatname[KSTAT_STRLEN];
char *usbtypes[USB_N_COUNT_KSTATS] =
{"ctrl", "isoch", "bulk", "intr"};
sizeof (uhci_intrs_stats_t) / sizeof (kstat_named_t),
"HC Halted", KSTAT_DATA_UINT64);
"HC Process Errors", KSTAT_DATA_UINT64);
"Host Sys Errors", KSTAT_DATA_UINT64);
"Resume Detected", KSTAT_DATA_UINT64);
"USB Error", KSTAT_DATA_UINT64);
"USB Interrupts", KSTAT_DATA_UINT64);
"Total Interrupts", KSTAT_DATA_UINT64);
"Not Claimed", KSTAT_DATA_UINT64);
}
}
}
}
for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
}
}
}
}
/*
* uhci_destroy_stats:
* Clean up uhci kstat structures
*/
void
{
int i;
if (UHCI_INTRS_STATS(uhcip)) {
}
if (UHCI_TOTAL_STATS(uhcip)) {
}
for (i = 0; i < USB_N_COUNT_KSTATS; i++) {
if (uhcip->uhci_count_stats[i]) {
}
}
}
void
{
return;
}
switch (val) {
case USBSTS_REG_HC_HALTED:
break;
break;
case USBSTS_REG_HOST_SYS_ERR:
break;
case USBSTS_REG_RESUME_DETECT:
break;
case USBSTS_REG_USB_ERR_INTR:
break;
case USBSTS_REG_USB_INTR:
break;
default:
break;
}
}
void
{
switch (dir) {
case USB_EP_DIR_IN:
switch (type) {
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_ISOCH:
break;
}
break;
case USB_EP_DIR_OUT:
switch (type) {
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_ISOCH:
break;
}
break;
}
}
/*
* uhci_free_tw:
* Free the Transfer Wrapper (TW).
*/
void
{
int rval, i;
if (tw->tw_isoc_strtlen > 0) {
for (i = 0; i < tw->tw_ncookies; i++) {
}
}
}
/*
* uhci_deallocate_tw:
* Deallocate of a Transaction Wrapper (TW) and this involves
* the freeing of DMA resources.
*/
void
{
"uhci_deallocate_tw:");
/*
* If the transfer wrapper has no Host Controller (HC)
* Transfer Descriptors (TD) associated with it, then
* remove the transfer wrapper. The transfers are done
* in FIFO order, so this should be the first transfer
* wrapper on the list.
*/
return;
}
/*
* If pp->pp_tw_head is NULL, set the tail also to NULL.
*/
}
} else {
}
}
}
void
{
} else {
}
}
}
} else {
}
}
}
void
{
if (curr_reqp) {
} else {
}
} /* end of curr_reqp */
continue;
}
}
}
}
/*
* uhci_remove_qh:
* Remove the Queue Head from the Host Controller's
* appropriate QH list.
*/
void
{
"uhci_remove_qh:");
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_INTR:
break;
}
}
static void
{
}
/*
* uhci_remove_bulk_qh:
* Remove a bulk QH from the Host Controller's QH list. There may be a
* loop for bulk QHs, we must care about this while removing a bulk QH.
*/
static void
{
} else {
}
if (next_lattice_qh == NULL) {
} else {
}
}
static void
{
} else {
}
}
/*
* uhci_allocate_td_from_pool:
* Allocate a Transfer Descriptor (TD) from the TD buffer pool.
*/
static uhci_td_t *
{
int index;
/*
* Search for a blank Transfer Descriptor (TD)
* in the TD buffer pool.
*/
break;
}
}
if (index == uhci_td_pool_size) {
"uhci_allocate_td_from_pool: TD exhausted");
return (NULL);
}
"uhci_allocate_td_from_pool: Allocated %d", index);
/* Create a new dummy for the end of the TD list */
/* Mark the newly allocated TD as a dummy */
return (td);
}
/*
* uhci_insert_bulk_td:
*/
int
{
uint_t num_bulk_tds, i, j;
/*
* Create transfer wrapper
*/
"uhci_insert_bulk_td: TW allocation failed");
return (USB_NO_RESOURCES);
}
tw->tw_bytes_xfered = 0;
/* Get the bulk pipe direction */
/* If the DATA OUT, copy the data into transfer buffer. */
/* Copy the data into the message */
}
}
/* Get the max packet size. */
/*
* Calculate number of TD's to insert in the current frame interval.
* Max number TD's allowed (driver implementation) is 128
* in one frame interval. Once all the TD's are completed
* then the remaining TD's will be inserted into the lattice
* in the uhci_handle_bulk_td().
*/
} else {
num_bulk_tds++;
}
}
/*
* Allocate memory for the bulk xfer information structure
*/
if ((bulk_xfer_info = kmem_zalloc(
"uhci_insert_bulk_td: kmem_zalloc failed");
/* Free the transfer wrapper */
return (USB_FAILURE);
}
/* Allocate memory for the bulk TD's */
USB_SUCCESS) {
"uhci_insert_bulk_td: alloc_bulk_isoc_tds failed");
/* Free the transfer wrapper */
return (USB_FAILURE);
}
buf_offs = 0;
/* Fill up all the bulk TD's */
for (i = 0; i < bulk_xfer_info->num_pools; i++) {
}
/* fill in the last TD */
} else {
/* fill in the TD at the tail of a pool */
tmp_td = &bulk_td_ptr[j];
}
}
/*
* Point the end of the lattice tree to the start of the bulk xfers
* in the same frame. There are some bulk devices, which NAKs after
* completing each TD. As a result, the performance on such devices
* is very bad. This loop will provide a chance to execute NAk'ed
* bulk TDs again in the same frame.
*/
if (uhcip->uhci_pending_bulk_cmds++ == 0) {
"uhci_insert_bulk_td: count = %d no tds %d",
}
/* Insert on the bulk queue head for the execution by HC */
return (USB_SUCCESS);
}
/*
* uhci_fill_in_bulk_isoc_td
*
* offset - different meanings for bulk and isoc TDs:
* starting offset into the TW buffer for a bulk TD
* and the index into the isoc packet list for an isoc TD
*/
void
{
"uhci_fill_in_bulk_isoc_td: tw 0x%p offs 0x%x length 0x%x",
switch (UHCI_XFER_TYPE(ept)) {
case USB_EP_ATTR_ISOCH:
}
break;
case USB_EP_ATTR_BULK:
}
break;
}
/* Get the right buffer address for the current TD */
switch (UHCI_XFER_TYPE(ept)) {
case USB_EP_ATTR_ISOCH:
break;
case USB_EP_ATTR_BULK:
break;
}
/*
* Adjust the data toggle.
* The data toggle bit must always be 0 for isoc transfers.
* And set the "iso" bit in the TD for isoc transfers.
*/
pp->pp_data_toggle = 0;
} else {
}
} else {
}
} else {
/* Add the td to the end of the list */
}
}
/*
* uhci_alloc_bulk_isoc_tds:
* pool to store all the TDs if the system allows. Only when the
* first allocation fails, it tries to allocate several small
* pools with each pool limited in physical page size.
*/
static int
{
"uhci_alloc_bulk_isoc_tds: num_tds: 0x%x info: 0x%p",
/* allocate as a whole pool at the first time */
USB_SUCCESS) {
"alloc_memory_for_tds failed: num_tds %d num_pools %d",
/* reduce the td number per pool and alloc again */
if (num_tds % UHCI_MAX_TD_NUM_PER_POOL) {
}
USB_SUCCESS) {
"alloc_memory_for_tds failed: num_tds %d "
return (USB_NO_RESOURCES);
}
}
return (USB_SUCCESS);
}
/*
* uhci_alloc_memory_for_tds:
*/
static int
{
"uhci_alloc_memory_for_tds: num_tds: 0x%x info: 0x%p "
/* The host controller will be little endian */
/* Allocate the TD pool structures */
"uhci_alloc_memory_for_tds: alloc td_pools failed");
return (USB_FAILURE);
}
} else {
}
/* Allocate the bulk TD pool DMA handle */
for (j = 0; j < i; j++) {
}
(sizeof (uhci_bulk_isoc_td_pool_t) *
return (USB_FAILURE);
}
/* Allocate the memory for the bulk TD pool */
for (j = 0; j < i; j++) {
}
(sizeof (uhci_bulk_isoc_td_pool_t) *
return (USB_FAILURE);
}
/* Map the bulk TD pool into the I/O address space */
/* Process the result */
err = USB_SUCCESS;
if (result != DDI_DMA_MAPPED) {
"uhci_allocate_memory_for_tds: Result = %d",
result);
result);
err = USB_FAILURE;
}
/* The cookie count should be 1 */
"uhci_allocate_memory_for_tds: "
"More than 1 cookie");
err = USB_FAILURE;
}
if (err == USB_FAILURE) {
for (j = 0; j < i; j++) {
}
(sizeof (uhci_bulk_isoc_td_pool_t) *
return (USB_FAILURE);
}
}
return (USB_SUCCESS);
}
/*
* uhci_handle_bulk_td:
*
* Handles the completed bulk transfer descriptors
*/
void
{
/*
* Update the tw_bytes_pending, and tw_bytes_xfered
*/
/*
* Check whether there are any errors occurred in the xfer.
* If so, update the data_toggle for the queue head and
* return error to the upper layer.
*/
"uhci_handle_bulk_td: error; data toggle: 0x%x",
pp->pp_data_toggle);
return;
}
/*
* Update the tw_bytes_pending, and tw_bytes_xfered
*/
if (bytes_xfered != ZERO_LENGTH) {
}
/*
* Get Bulk pipe information and pipe handle
*/
/*
* Check whether data underrun occurred.
* If so, complete the transfer
* Update the data toggle bit
*/
"uhci_handle_bulk_td: Data underrun occured");
}
/*
* If the TD's in the current frame are completed, then check
* whether we have any more bytes to xfer. If so, insert TD's.
* If no more bytes needs to be transferred, then do callback to the
* upper layer.
* If the TD's in the current frame are not completed, then
* just delete the TD from the linked lists.
*/
"uhci_handle_bulk_td: completed TD data toggle: 0x%x",
if (--bulk_xfer_info->num_tds == 0) {
if ((tw->tw_bytes_pending) &&
paddr|(0x2));
}
} else {
num_bulk_tds++;
}
}
index = 0;
/* reuse the TDs to transfer more data */
while (td_count > 0) {
for (j = 0;
MaxPacketSize, tw);
}
if (td_count == 1) {
&bulk_td_ptr[j], current_dummy,
break;
} else {
tmp_td = &bulk_td_ptr[j];
bulk_td_ptr = (uhci_td_t *)
tmp_td, &bulk_td_ptr[0],
MaxPacketSize, tw);
td_count--;
index++;
}
}
} else {
if (tw->tw_bytes_pending) {
/* Update the element pointer */
/* Remove all the tds */
}
}
/* Data run occurred */
if (tw->tw_bytes_pending &&
(!(attrs & USB_ATTRS_SHORT_XFER_OK))) {
}
} else {
/* Data underrun occurred */
if (tw->tw_bytes_pending) {
"uhci_handle_bulk_td: "
"data underrun occurred");
} else {
}
} /* direction */
/* Deallocate DMA memory */
for (j = 0; j < bulk_xfer_info->num_pools; j++) {
(void) ddi_dma_unbind_handle(
}
(sizeof (uhci_bulk_isoc_td_pool_t) *
sizeof (uhci_bulk_isoc_xfer_t));
/*
* When there are no pending bulk commands, point the
* end of the lattice tree to NULL. This will make sure
* that the HC control does not loop anymore and PCI
* bus is not affected.
*/
if (--uhcip->uhci_pending_bulk_cmds == 0) {
"uhci_handle_bulk_td: count = %d",
}
}
} else {
}
}
void
{
"uhci_handle_bulk_td_errors: td = %p", (void *)td);
#ifdef DEBUG
#endif
/*
* Find the type of error occurred and return the error
* to the upper layer. And adjust the data toggle.
*/
/*
* If a timeout occurs before a transfer has completed,
* bit in the link_ptr for each td in the transfer.
* It then waits (at least) 1 ms so that any tds the controller might
* have been executing will have completed.
* So at this point element_ptr will point to either:
* 1) the next td for the transfer (which has not been executed,
* 2) the dummy td for this qh.
* So if the element_ptr does not point to the dummy td, we know
* it points to the next td that would have been executed.
* That td has the data toggle we want to save.
* so it doesn't matter which td we pass to uhci_parse_td_error
* for the error status.
*/
if (element_ptr != paddr_tail) {
paddr, &td_pool_ptr);
"uhci_handle_bulk_td_errors: next td = %p",
(void *)next_td);
} else {
}
/*
* Update the link pointer.
*/
/*
* Send up number of bytes transferred before the error condition.
*/
}
/*
* When there are no pending bulk commands, point the end of the
* lattice tree to NULL. This will make sure that the HC control
* does not loop anymore and PCI bus is not affected.
*/
if (--uhcip->uhci_pending_bulk_cmds == 0) {
"uhci_handle_bulk_td_errors: count = %d",
}
}
/*
* uhci_get_bulk_td_by_paddr:
* Obtain the address of the TD pool the physical address falls in.
*
* td_pool_pp - pointer to the address of the TD pool containing the paddr
*/
/* ARGSUSED */
static void
{
uint_t i = 0;
break;
}
i++;
}
}
void
int what)
{
return;
}
while (head) {
if (curr_reqp &&
((what == UHCI_IN_CLOSE) ||
(what == UHCI_IN_RESET))) {
} /* end of curr_reqp */
/*
* This will make sure that the HC
* does not loop anymore when there
* are no pending bulk commands.
*/
if (--uhcip->uhci_pending_bulk_cmds
== 0) {
"uhci_remove_bulk_tds_tws:"
" count = %d",
uhcip->
}
}
}
}
}
}
}
}
/*
* uhci_save_data_toggle ()
* Save the data toggle in the usba_device structure
*/
void
{
/* Save the data toggle in the usb devices structure. */
pp->pp_data_toggle);
}
/*
* uhci_create_isoc_transfer_wrapper:
* Create a Transaction Wrapper (TW) for isoc transfer.
* This involves the allocating of DMA resources.
*
* For isoc transfers, one isoc transfer includes multiple packets
* and each packet may have a different length. So each packet is
* transfered by one TD. We only know the individual packet length
* won't exceed 1023 bytes, but we don't know exactly the lengths.
* It is hard to make one physically discontiguous DMA buffer which
* intr transfers. It is also undesirable to make one physically
* contiguous DMA buffer for all the packets, since this may easily
* fail when the system is in low memory. So an individual DMA
* buffer is allocated for an individual isoc packet and each DMA
* buffer is physically contiguous. An extra structure is allocated
* to save the multiple DMA handles.
*/
static uhci_trans_wrapper_t *
{
int result;
int kmem_flag;
int (*dmamem_wait)(caddr_t);
return (NULL);
}
USB_EP_DIR_IN)) {
}
return (NULL);
}
"uhci_create_isoc_transfer_wrapper: length = 0x%lx flags = 0x%x",
/* SLEEP flag should not be used in interrupt context */
if (servicing_interrupt()) {
} else {
}
/* Allocate space for the transfer wrapper */
NULL) {
"uhci_create_isoc_transfer_wrapper: kmem_alloc failed");
return (NULL);
}
/* Allocate space for the isoc buffer handles */
"uhci_create_isoc_transfer_wrapper: kmem_alloc "
"isoc buffer failed");
return (NULL);
}
/* Store the transfer length */
for (i = 0; i < tmp_req->isoc_pkts_count; i++) {
/* Allocate the DMA handle */
DDI_SUCCESS) {
"uhci_create_isoc_transfer_wrapper: "
"Alloc handle %d failed", i);
for (j = 0; j < i; j++) {
}
return (NULL);
}
/* Allocate the memory */
DDI_SUCCESS) {
"uhci_create_isoc_transfer_wrapper: "
"dma_mem_alloc %d fail", i);
for (j = 0; j < i; j++) {
}
return (NULL);
}
/* Bind the handle */
continue;
} else {
"uhci_create_isoc_transfer_wrapper: "
"Bind handle %d failed", i);
if (result == DDI_DMA_MAPPED) {
}
for (j = 0; j < i; j++) {
}
return (NULL);
}
}
/*
* Only allow one wrapper to be added at a time. Insert the
* new transaction wrapper into the list for this pipe.
*/
} else {
}
/* Store a back pointer to the pipe private structure */
/* Store the transfer type - synchronous or asynchronous */
"uhci_create_isoc_transfer_wrapper: tw = 0x%p, ncookies = %u",
return (tw);
}
/*
* uhci_insert_isoc_td:
* - Create transfer wrapper
* - Allocate memory for the isoc td's
* - Fill up all the TD's and submit to the HC
* - Update all the linked lists
*/
int
{
int rval = USB_SUCCESS;
int error;
uint32_t expired_frames = 0;
"uhci_insert_isoc_td: ph = 0x%p isoc req = %p length = %lu",
/* Allocate a transfer wrapper */
"uhci_insert_isoc_td: TW allocation failed");
return (USB_NO_RESOURCES);
}
/* Save current isochronous request pointer */
/*
* Initialize the transfer wrapper. These values are useful
* for sending back the reply.
*/
/*
* If the transfer isoc send, then copy the data from the request
* to the transfer wrapper.
*/
uchar_t *p;
/* Copy the data into the message */
for (i = 0; i < isoc_req->isoc_pkts_count; i++) {
}
}
flags)) != USB_SUCCESS) {
"uhci_insert_isoc_td: isoc_req_t alloc failed");
return (rval);
}
}
/* Get the pointer to the isoc_xfer_info structure */
/*
* Allocate memory for isoc tds
*/
isoc_xfer_info)) != USB_SUCCESS) {
"uhci_alloc_bulk_isoc_td: Memory allocation failure");
}
return (rval);
}
/*
* Get the isoc td pool address, buffer address and
* max packet size that the device supports.
*/
index = 0;
/*
* Fill up the isoc tds
*/
for (i = 0; i < isoc_xfer_info->num_pools; i++) {
for (j = 0; j < td_pool_ptr->num_tds; j++) {
bytes_to_xfer, tw);
index++;
}
}
}
/*
* Get the starting frame number.
* The client drivers sets the flag USB_ATTRS_ISOC_XFER_ASAP to inform
* the HCD to care of starting frame number.
*
* Following code is very time critical. So, perform atomic execution.
*/
ddic = ddi_enter_critical();
/* Check available frames */
if (current_frame > start_frame) {
if ((current_frame + FRNUM_OFFSET) <
end_frame) {
} else {
}
}
} else {
}
if (start_frame == INVALID_FRNUM) {
} else if (current_frame > start_frame) {
}
}
if (rval != USB_SUCCESS) {
/* Exit the critical */
"uhci_insert_isoc_td: Invalid starting frame number");
}
while (tw->tw_hctd_head) {
}
for (i = 0; i < isoc_xfer_info->num_pools; i++) {
}
(sizeof (uhci_bulk_isoc_td_pool_t) *
return (rval);
}
for (i = 0; i < expired_frames; i++) {
&td_ptr, &td_pool_ptr);
}
/*
* Add the TD's to the HC list
*/
for (; i < isoc_req->isoc_pkts_count; i++) {
&td_ptr, &td_pool_ptr);
} else {
}
if (++start_frame == NUM_FRAME_LST_ENTRIES)
start_frame = 0;
}
"uhci_insert_isoc_td: current frame number 0x%llx, pipe frame num"
return (rval);
}
/*
* uhci_get_isoc_td_by_index:
* Obtain the addresses of the TD pool and the TD at the index.
*
* tdpp - pointer to the address of the TD at the isoc packet index
* td_pool_pp - pointer to the address of the TD pool containing
* the specified TD
*/
/* ARGSUSED */
static void
{
uint_t i = 0, j = 0;
j++;
} else {
i = index - i;
break;
}
}
}
/*
* uhci_handle_isoc_td:
* Handles the completed isoc tds
*/
void
{
"uhci_handle_isoc_td: td = 0x%p, pp = 0x%p, tw = 0x%p, req = 0x%p, "
/*
* Check whether there are any errors occurred. If so, update error
* count and return it to the upper.But never return a non zero
* completion reason.
*/
"uhci_handle_isoc_td: Error Occurred: TD Status = %x",
}
}
if (--isoc_xfer_info->num_tds != 0) {
"uhci_handle_isoc_td: Number of TDs %d",
return;
}
"uhci_handle_isoc_td: Drop message");
}
} else {
/* update kstats only for OUT. sendup_td_msg() does it for IN */
}
for (i = 0; i < isoc_xfer_info->num_pools; i++) {
}
(sizeof (uhci_bulk_isoc_td_pool_t) *
}
/*
* uhci_handle_isoc_receive:
* - Sends the isoc data to the client
* - Inserts another isoc receive request
*/
static int
{
"uhci_handle_isoc_receive: tw = 0x%p", tw);
/*
* -- check for pipe state being polling before
* inserting a new request. Check when is TD
* de-allocation being done? (so we can reuse the same TD)
*/
0) != USB_SUCCESS) {
"uhci_handle_isoc_receive: receive polling failed");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* uhci_delete_isoc_td:
* - Delete from the outstanding command queue
* - Delete from the tw queue
* - Delete from the isoc queue
* - Delete from the HOST CONTROLLER list
*/
static void
{
} else {
}
}
/*
* uhci_send_isoc_receive
* - Allocates usb_isoc_request
* - Updates the isoc request
* - Inserts the isoc td's into the HC processing list.
*/
int
{
"uhci_start_isoc_receive_polling: usb_flags = %x", usb_flags);
if (isoc_req) {
} else {
isoc_pkt_descr = ((usb_isoc_req_t *)
isoc_pkt_count = ((usb_isoc_req_t *)
isoc_pkts_length = ((usb_isoc_req_t *)
}
}
"uhci_start_isoc_receive_polling: isoc_pkts_length 0x%x "
"is not equal to the sum of all pkt lengths 0x%x in "
return (USB_FAILURE);
}
/* Check the size of isochronous request */
if (length > max_isoc_xfer_size) {
"uhci_start_isoc_receive_polling: "
"Max isoc request size = %lx, Given isoc req size = %lx",
return (USB_FAILURE);
}
/* Add the TD into the Host Controller's isoc list */
return (error);
}
/*
* uhci_remove_isoc_tds_tws
* This routine scans the pipe and removes all the td's
* and transfer wrappers and deallocates the memory
* associated with those td's and tw's.
*/
void
{
"uhci_remove_isoc_tds_tws: pp = %p", (void *)pp);
while (tw_head) {
tmp_tw);
}
while (td_head) {
}
if (isoc_req) {
}
for (i = 0; i < isoc_xfer_info->num_pools; i++) {
}
(sizeof (uhci_bulk_isoc_td_pool_t) *
}
}
}
/*
* uhci_isoc_update_sw_frame_number()
* to avoid code duplication, call uhci_get_sw_frame_number()
*/
void
{
(void) uhci_get_sw_frame_number(uhcip);
}
/*
* uhci_get_sw_frame_number:
* Hold the uhci_int_mutex before calling this routine.
*/
{
/*
* Check bit 10 in the software counter and hardware frame counter.
* If both are same, then don't increment the software frame counter
* (Bit 10 of hw frame counter toggle for every 1024 frames)
* The lower 11 bits of software counter contains the hardware frame
* counter value. The MSB (bit 10) of software counter is incremented
* for every 1024 frames either here or in get frame number routine.
*/
/* The MSB of hw counter did not toggle */
} else {
/*
* The hw counter wrapped around. And the interrupt handler
* did not get a chance to update the sw frame counter.
* So, update the sw frame counter and return correct frame no.
*/
}
"uhci_get_sw_frame_number: sw=%ld hd=%ld",
return (current_frnum);
}
/*
* uhci_cmd_timeout_hdlr:
* This routine will get called for every second. It checks for
* that exceeds the time out period specified by the pipe policy.
*/
void
uhci_cmd_timeout_hdlr(void *arg)
{
/*
* Check whether any of the control xfers are timed out.
* If so, complete those commands with time out as reason.
*/
while (head) {
/*
* If timeout out is zero, then dont timeout command.
*/
continue;
}
}
/* only do it for bulk and control TDs */
"Command timed out: td = %p", (void *)head);
/*
* Check finally whether the command completed
*/
}
}
}
if (flag) {
(void) uhci_wait_for_sof(uhcip);
}
while (head) {
}
while (tmp_td) {
}
}
}
/*
* Process the td which was completed before shifting from normal
* mode to polled mode
*/
} else if (flag) {
}
if (uhcip->uhci_cmd_timeout_id) {
(void *)uhcip, UHCI_ONE_SECOND);
}
}
/*
* uhci_wait_for_sof:
* Wait for the start of the next frame (implying any changes made in the
* lattice have now taken effect).
* To be sure this is the case, we wait for the completion of the current
* frame (which might have already been pending), then another complete
* frame to ensure everything has taken effect.
*/
int
{
int n, error;
"uhci_wait_for_sof: uhcip = %p", uhcip);
if (error != USB_SUCCESS) {
return (error);
}
for (n = 0; n < MAX_SOF_WAIT_COUNT; n++) {
if ((rval == -1) &&
}
}
}
/*
* uhci_allocate_periodic_in_resource:
* Allocate interrupt/isochronous request structure for the
* interrupt/isochronous IN transfer.
*/
int
{
"uhci_allocate_periodic_in_resource:\n\t"
/* Check the current periodic in request pointer */
if (tw->tw_curr_xfer_reqp) {
"uhci_allocate_periodic_in_resource: Interrupt "
"request structure already exists: "
"allocation failed");
return (USB_SUCCESS);
}
/* Get the client periodic in request pointer */
/*
* If it a periodic IN request and periodic request is NULL,
* allocate corresponding usb periodic IN request for the
* current periodic polling request and copy the information
* from the saved periodic request structure.
*/
/* Get the interrupt transfer length */
if (cur_intr_req == NULL) {
"uhci_allocate_periodic_in_resource: Interrupt "
"request structure allocation failed");
return (USB_NO_RESOURCES);
}
/* Check and save the timeout value */
} else {
NULL) {
"uhci_allocate_periodic_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
*/
}
ph->p_req_count++;
return (USB_SUCCESS);
}
/*
* uhci_deallocate_periodic_in_resource:
* Deallocate interrupt/isochronous request structure for the
* interrupt/isochronous IN transfer.
*/
void
{
"uhci_deallocate_periodic_in_resource: "
if (curr_xfer_reqp) {
/*
* Reset periodic in request usb isoch
* packet request pointers to null.
*/
ph->p_req_count--;
/*
* Free pre-allocated interrupt or isochronous requests.
*/
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_ISOCH:
break;
}
}
}
/*
* uhci_hcdi_callback()
* convenience wrapper around usba_hcdi_callback()
*/
void
{
} else {
}
}
/*
* uhci_state_is_operational:
*
* Check the Host controller state and return proper values.
*/
int
{
int val;
switch (uhcip->uhci_hc_soft_state) {
case UHCI_CTLR_INIT_STATE:
case UHCI_CTLR_SUSPEND_STATE:
val = USB_FAILURE;
break;
val = USB_SUCCESS;
break;
case UHCI_CTLR_ERROR_STATE:
break;
default:
val = USB_FAILURE;
break;
}
return (val);
}
#ifdef DEBUG
static void
{
#ifndef lint
#endif
"Queue Head Details:");
#ifndef lint
#endif
}
static void
{
}
#endif