uhcitgt.c revision fffe0b3067e1632c8db85485d8e8cabcad62e2fc
/*
* 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 Universal Host Controller Interface.
* This file contains the code for HCDI entry points.
*/
/* function prototypes */
/* Maximum bulk transfer size */
/*
* uhci_hcdi_pipe_open:
* Member of HCD Ops structure and called during client specific pipe open
* Add the pipe to the data structure representing the device and allocate
* bandwidth for the pipe if it is a interrupt or isochronous endpoint.
*/
int
{
"uhci_hcdi_pipe_open: addr = 0x%x, ep%d", usb_addr,
if (rval != USB_SUCCESS) {
return (rval);
}
/*
* Return failure immediately for any other pipe open on the root hub
* except control or interrupt pipe.
*/
if (usb_addr == ROOT_HUB_ADDR) {
case USB_EP_ATTR_CONTROL:
"uhci_hcdi_pipe_open: Root hub control pipe");
break;
case USB_EP_ATTR_INTR:
/*
* Set the state of the root hub interrupt
* pipe as IDLE.
*/
"uhci_hcdi_pipe_open: Root hub interrupt "
"pipe open succeeded");
return (USB_SUCCESS);
default:
"uhci_hcdi_pipe_open: Root hub pipe open failed");
return (USB_FAILURE);
}
}
/*
* A portion of the bandwidth is reserved for the non-periodic
* transfers i.e control and bulk transfers in each of one
* mill second frame period & usually it will be 10% of frame
* period. Hence there is no need to check for the available
* bandwidth before adding the control or bulk endpoints.
*
* There is a need to check for the available bandwidth before
* adding the periodic transfers i.e interrupt & isochronous, since
* all these periodic transfers are guaranteed transfers. Usually,
* 90% of the total frame time is reserved for periodic transfers.
*/
/* Zero Max Packet size endpoints are not supported */
"uhci_hcdi_pipe_open: Zero length packet");
return (USB_FAILURE);
}
if (error != USB_SUCCESS) {
"uhci_hcdi_pipe_open: Bandwidth allocation failed");
return (error);
}
}
/* Create the HCD pipe private structure */
"uhci_hcdi_pipe_open: pp allocation failure");
}
return (USB_NO_RESOURCES);
}
if (rval != USB_SUCCESS) {
return (rval);
}
/* Initialize frame number */
/* Set the state of pipe as IDLE */
/* Store a pointer to the pipe handle */
/* Store the pointer in the pipe handle */
/* Store a copy of the pipe policy */
/* don't check for ROOT_HUB here anymore */
/* Allocate the host controller endpoint descriptor */
"uhci_hcdi_pipe_open: QH allocation failed");
}
/*
* Deallocate the hcd private portion
* of the pipe handle.
*/
sizeof (uhci_pipe_private_t));
/*
* Set the private structure in the
* pipe handle equal to NULL.
*/
return (USB_NO_RESOURCES);
}
/*
* Insert the endpoint onto the host controller's
* appropriate endpoint list. The host controller
* will not schedule this endpoint until there are
* any TD's to process.
*/
}
/*
* Restore the data toggle from usb device structure.
*/
}
"uhci_hcdi_pipe_open: ph = 0x%p", ph);
return (USB_SUCCESS);
}
/*
* uhci_hcdi_pipe_close:
* Member of HCD Ops structure and called during the client specific pipe
* close. Remove the pipe to the data structure representing the device
* deallocate bandwidth for the pipe if it is an intr or isoch endpoint.
*/
int
{
"uhci_hcdi_pipe_close: addr = 0x%x, ep%d, flags = 0x%x", usb_addr,
/*
* Check whether the pipe is a root hub
*/
if (usb_addr == ROOT_HUB_ADDR) {
switch (UHCI_XFER_TYPE(eptd)) {
case USB_EP_ATTR_CONTROL:
"uhci_hcdi_pipe_close: Root hub control pipe "
"close succeeded");
break;
case USB_EP_ATTR_INTR:
USB_EP_NUM_MASK) == 1);
/* Do interrupt pipe cleanup */
"uhci_hcdi_pipe_close: Root hub interrupt "
"pipe close succeeded");
return (USB_SUCCESS);
}
} else {
/*
* Stop all the transactions if it is not the root hub.
*/
/*
* Stop polling on the pipe to prevent any subsequently
* queued tds (while we're waiting for SOF, below)
* from being executed
*/
}
/* Disable all outstanding tds */
/* Prevent this queue from being executed */
}
/* Wait for the next start of frame */
(void) uhci_wait_for_sof(uhcip);
switch (UHCI_XFER_TYPE(eptd)) {
case USB_EP_ATTR_INTR:
/* FALLTHROUGH */
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_ISOCH:
break;
default:
"uhci_hcdi_pipe_close: Unknown xfer type");
break;
}
/*
* Remove the endoint descriptor from Host Controller's
* appropriate endpoint list. Isochronous pipes dont have
* any queue heads attached to it.
*/
}
/*
* Do the callback for the original client
* periodic IN request.
*/
if (pp->pp_client_periodic_in_reqp) {
}
/* Deallocate bandwidth */
if (UHCI_PERIODIC_ENDPOINT(eptd)) {
}
}
/* Deallocate the hcd private portion of the pipe handle. */
"uhci_hcdi_pipe_close: ph = 0x%p", ph);
return (USB_SUCCESS);
}
/*
* uhci_hcdi_pipe_reset:
*/
int
{
"uhci_hcdi_pipe_reset: usb_flags = 0x%x", usb_flags);
/*
* Return failure immediately for any other pipe reset on the root
* hub except control or interrupt pipe.
*/
case USB_EP_ATTR_CONTROL:
"uhci_hcdi_pipe_reset: Pipe reset for root"
"hub control pipe successful");
break;
case USB_EP_ATTR_INTR:
/* Do interrupt pipe cleanup */
"uhci_hcdi_pipe_reset: Pipe reset for "
"root hub interrupt pipe successful");
break;
default:
"uhci_hcdi_pipe_reset: Root hub pipe reset failed");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* Set the active bit in to INACTIVE for all the remaining TD's of
* this end point. Set the active bit for the dummy td. This will
* generate an interrupt at the end of the frame. After receiving
* the interrupt, it is safe to to manipulate the lattice.
*/
/* Initialize the element pointer */
}
(void) uhci_wait_for_sof(uhcip);
/*
* Save the data toggle and clear the pipe.
*/
switch (UHCI_XFER_TYPE(eptd)) {
case USB_EP_ATTR_CONTROL:
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_ISOCH:
break;
default:
"uhci_hcdi_pipe_reset: Unknown xfer type");
break;
}
/*
* Do the callback for the original client
* periodic IN request.
*/
if (pp->pp_client_periodic_in_reqp) {
}
/*
* Since the endpoint is stripped of Transfer Descriptors (TD),
* reset the state of the periodic pipe to IDLE.
*/
return (USB_SUCCESS);
}
/*
* uhci_hcdi_pipe_ctrl_xfer:
*/
int
{
int error;
"uhci_hcdi_pipe_ctrl_xfer: req=0x%p, ph=0x%p, flags=0x%x",
if (error != USB_SUCCESS) {
return (error);
}
/*
* Check and handle root hub control request.
*/
return (error);
}
/* Insert the td's on the endpoint */
USB_SUCCESS) {
"uhci_hcdi_pipe_ctrl_xfer: No resources");
}
return (error);
}
/*
* uhci_hcdi_pipe_bulk_xfer:
*/
int
{
int error;
"uhci_hcdi_pipe_bulk_xfer: Flags = 0x%x", usb_flags);
/* Check the size of bulk request */
"uhci_hcdi_pipe_bulk_xfer: req size 0x%x is more than 0x%x",
return (USB_FAILURE);
}
if (error != USB_SUCCESS) {
return (error);
}
/* Add the TD into the Host Controller's bulk list */
usb_flags)) != USB_SUCCESS) {
"uhci_hcdi_pipe_bulk_xfer: uhci_insert_bulk_td failed");
}
return (error);
}
/*
* uhci_hcdi_bulk_transfer_size:
* Return maximum bulk transfer size
*/
int
{
int rval;
"uhci_hcdi_bulk_transfer_size:");
if (rval != USB_SUCCESS) {
return (rval);
}
return (USB_SUCCESS);
}
/*
* uhci_hcdi_pipe_intr_xfer:
*/
int
{
} else {
}
}
/*
* uhci_send_intr_data():
* send data to interrupt out pipe
*/
static int
{
int rval;
"uhci_send_intr_data:");
if (rval != USB_SUCCESS) {
return (rval);
}
/* Add the TD into the Host Controller's interrupt list */
USB_SUCCESS) {
"uhci_send_intr_data: No resources");
}
return (rval);
}
/*
* uhci_hcdi_pipe_stop_intr_polling()
*/
int
{
int rval;
"uhci_hcdi_pipe_stop_intr_polling: ph = 0x%p fl = 0x%x",
(void *)pipe_handle, flags);
return (rval);
}
/*
* uhci_hcdi_get_current_frame_number
* Get the current frame number.
* Return whether the request is handled successfully.
*/
int
{
int rval;
if (rval != USB_SUCCESS) {
return (rval);
}
"uhci_hcdi_get_current_frame_number: %llx", frame_number);
return (rval);
}
/*
* uhci_hcdi_get_max_isoc_pkts
* Get the maximum number of isoc packets per USB Isoch request.
* Return whether the request is handled successfully.
*/
int
{
int rval;
if (rval != USB_SUCCESS) {
return (rval);
}
"uhci_hcdi_get_max_isoc_pkts: 0x%x", UHCI_MAX_ISOC_PKTS);
return (rval);
}
/*
* uhci_hcdi_pipe_isoc_xfer:
*/
int
{
} else {
}
}
/*
* uhci_hcdi_pipe_stop_isoc_polling()
*/
int
{
int rval;
"uhci_hcdi_pipe_stop_isoc_polling: ph = 0x%p fl = 0x%x",
if (rval != USB_SUCCESS) {
return (rval);
}
return (rval);
}
/*
* uhci_start_periodic_pipe_polling:
*/
static int
{
int n, num_tds;
int error;
"uhci_start_periodic_pipe_polling: flags: 0x%x, ep%d",
if (error != USB_SUCCESS) {
return (error);
}
/* ONE_XFER not supported */
USB_ATTRS_ONE_XFER) == 0);
/* reset the client interrupt request pointer */
return (error);
}
"uhci_start_periodic_pipe_polling: "
"Start intr polling for root hub successful");
/* check if we need to send the reset data up? */
}
return (error);
}
/* save the original client's periodic IN request */
/*
*
* This pipe is uninitialized. If it is an isoc
* receive request, insert four times the same
* request so that we do not lose any frames.
*/
for (n = 0; n < 5; n++) {
if ((error = uhci_start_isoc_receive_polling(
"uhci_start_periodic_pipe_polling: "
"Start isoc polling failed %d", n);
return (error);
}
}
}
num_tds = 5;
} else {
num_tds = 1;
}
/*
* This pipe is uninitialized.
* Insert a TD on the interrupt ED.
*/
for (n = 0; n < num_tds; n++) {
flags)) != USB_SUCCESS) {
"uhci_start_periodic_pipe_polling: "
"Start polling failed");
return (error);
}
}
}
return (error);
}
/*
* uhci_hcdi_periodic_pipe_stop_polling:
*/
static int
{
"uhci_stop_periodic_pipe_polling: flags = 0x%x", flags);
/* Do interrupt pipe cleanup */
"uhci_stop_periodic_pipe_polling: Stop intr "
"polling for root hub successful");
} else {
"uhci_stop_periodic_pipe_polling: "
"Intr polling for root hub is already stopped");
}
return (USB_SUCCESS);
}
"uhci_stop_periodic_pipe_polling: Polling already stopped");
return (USB_SUCCESS);
}
/*
* Set the terminate bits in all the tds in the queue and
* in the element_ptr.
* Do not deallocate the bandwidth or tear down the DMA
*/
(void) uhci_wait_for_sof(uhcip);
} else {
}
if (pp->pp_client_periodic_in_reqp) {
}
return (USB_SUCCESS);
}
/*
* uhci_hcdi_pipe_send_isoc_data:
* Handles the isoc write request.
*/
static int
{
int error;
"uhci_pipe_send_isoc_data: isoc_req = %p flags = %x",
/* Calculate the maximum isochronous transfer size */
/* Check the size of isochronous request */
if (length > max_isoc_xfer_sz) {
"uhci_pipe_send_isoc_data: Maximum isoc request size %lx "
return (USB_INVALID_REQUEST);
}
/*
* Check whether we can insert these tds?
* At any point of time, we can insert maximum of 1024 isoc td's,
* size of frame list table.
*/
"uhci_pipe_send_isoc_data: request too big");
return (USB_INVALID_REQUEST);
}
/* Add the TD into the Host Controller's isoc list */
if (error != USB_SUCCESS) {
return (error);
}
"uhci_pipe_send_isoc_data: Unable to insert the isoc_req,"
"Error = %d", error);
}
return (error);
}
/*
* uhci_update_intr_td_data_toggle
* Update the data toggle and save in the usba_device structure
*/
static void
{
/* Find the next td that would have been executed */
/*
* If element_ptr points to the dummy td, then the data toggle in
* pp_data_toggle is correct. Otherwise update the data toggle in
* the pipe private
*/
if (element_ptr != paddr_tail) {
}
"uhci_update_intr_td_data_toggle: "
"pp %p toggle %x element ptr %x ptail %x",
}