hcdi.c revision d291d9f21e8c0417aec99de243dd48bc400002d0
/*
* 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
* 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"
/*
* USBA: Solaris USB Architecture support
*
* hcdi.c contains the code for client driver callbacks. A host controller
* driver registers/unregisters with usba through usba_hcdi_register/unregister.
*
* When the transfer has finished, the host controller driver will call into
* usba with the result. The call is usba_hcdi_cb().
*
* The callback queue is maintained in FIFO order. usba_hcdi_cb
* adds to the queue, and hcdi_cb_thread takes the callbacks off the queue
* and executes them.
*/
#define USBA_FRAMEWORK
#include <sys/ddi_impldefs.h>
/* function prototypes, XXXX use hcdi_ prefix? */
static void usba_hcdi_create_stats(usba_hcdi_t *, int);
static void usba_hcdi_destroy_stats(usba_hcdi_t *);
/* internal functions */
static void hcdi_cb_thread(void *);
static void hcdi_shared_cb_thread(void *);
usba_hcdi_t *);
static void hcdi_autoclearing(usba_req_wrapper_t *);
/* private function from USBAI */
void usba_pipe_clear(usb_pipe_handle_t);
/* for debug messages */
void
{
}
void
{
}
/*
* store hcdi structure in the dip
*/
void
{
}
/*
* retrieve hcdi structure from the dip
*/
{
return (ddi_get_driver_private(dip));
}
/*
* Called by an HCD to attach an instance of the driver
* make this instance known to USBA
* the HCD should initialize usba_hcdi structure prior
* to calling this interface
*/
int
{
char *datap;
return (USB_FAILURE);
}
/*
* Create a log_handle
*/
0);
/*
* Initialize the mutex. Use the iblock cookie passed in
* by the host controller driver.
*/
/* add soft interrupt */
DDI_SUCCESS) {
"usba_hcd_register: add soft interrupt failed");
return (USB_FAILURE);
}
DDI_SUCCESS) {
"usba_hcd_register: get soft interrupt priority failed");
return (USB_FAILURE);
}
/*
* Priority and iblock_cookie are one and the same
* (However, retaining hcdi_soft_iblock_cookie for now
* assigning it w/ priority. In future all iblock_cookie
* could just go)
*/
} else {
"illegal value (%s) for "
"ugen_default_binding property",
datap);
}
}
return (USB_SUCCESS);
}
/*
* Called by an HCD to detach an instance of the driver
*/
/*ARGSUSED*/
void
{
if (hcdi) {
/* Destroy the soft interrupt */
}
}
/*
* alloc usba_hcdi_ops structure
* called from the HCD attach routine
*/
{
return (usba_hcdi_ops);
}
/*
* dealloc usba_hcdi_ops structure
*/
void
{
if (hcdi_ops) {
}
}
/*
* Allocate the hotplug kstats structure
*/
void
{
char kstatname[KSTAT_STRLEN];
sizeof (hcdi_hotplug_stats_t) / sizeof (kstat_named_t),
return;
}
"Total Hotplug Successes", KSTAT_DATA_UINT64);
"Hotplug Successes", KSTAT_DATA_UINT64);
"Hotplug Total Failures", KSTAT_DATA_UINT64);
"Hotplug Failures", KSTAT_DATA_UINT64);
"Device Count", KSTAT_DATA_UINT64);
}
sizeof (hcdi_error_stats_t) / sizeof (kstat_named_t),
return;
}
"Bit Stuffing Violations", KSTAT_DATA_UINT64);
"Data Toggle PID Errors", KSTAT_DATA_UINT64);
"Endpoint Stalls", KSTAT_DATA_UINT64);
"Device Not Responding", KSTAT_DATA_UINT64);
"PID Check Bit Errors", KSTAT_DATA_UINT64);
"Invalid PID Errors", KSTAT_DATA_UINT64);
"Data Overruns", KSTAT_DATA_UINT64);
"Data Underruns", KSTAT_DATA_UINT64);
"Buffer Overruns", KSTAT_DATA_UINT64);
"Buffer Underruns", KSTAT_DATA_UINT64);
"Command Timed Out", KSTAT_DATA_UINT64);
"Not Accessed By Hardware", KSTAT_DATA_UINT64);
"Unspecified Error", KSTAT_DATA_UINT64);
#ifdef NOTYETNEEDED
"USB Failure", KSTAT_DATA_UINT64);
"No Resources", KSTAT_DATA_UINT64);
"No Bandwidth", KSTAT_DATA_UINT64);
"Pipe Reserved", KSTAT_DATA_UINT64);
"Pipe Unshareable", KSTAT_DATA_UINT64);
"Function Not Supported", KSTAT_DATA_UINT64);
"Pipe Error", KSTAT_DATA_UINT64);
"Pipe Busy", KSTAT_DATA_UINT64);
#endif
}
}
/*
* Do actual error stats
*/
void
{
return;
}
switch (completion_reason) {
case USB_CR_OK:
break;
case USB_CR_CRC:
break;
case USB_CR_BITSTUFFING:
break;
case USB_CR_DATA_TOGGLE_MM:
break;
case USB_CR_STALL:
break;
case USB_CR_DEV_NOT_RESP:
break;
case USB_CR_PID_CHECKFAILURE:
break;
case USB_CR_UNEXP_PID:
break;
case USB_CR_DATA_OVERRUN:
break;
case USB_CR_DATA_UNDERRUN:
break;
case USB_CR_BUFFER_OVERRUN:
break;
case USB_CR_BUFFER_UNDERRUN:
break;
case USB_CR_TIMEOUT:
break;
case USB_CR_NOT_ACCESSED:
break;
case USB_CR_NO_RESOURCES:
break;
case USB_CR_UNSPECIFIED_ERR:
break;
case USB_CR_STOPPED_POLLING:
break;
case USB_CR_PIPE_CLOSING:
break;
case USB_CR_PIPE_RESET:
break;
case USB_CR_NOT_SUPPORTED:
break;
case USB_CR_FLUSHED:
break;
default:
break;
}
}
/*
* Destroy the hotplug kstats structure
*/
static void
{
if (HCDI_HOTPLUG_STATS(hcdi)) {
}
if (HCDI_ERROR_STATS(hcdi)) {
}
}
/*
* HCD callback handling
*/
void
{
#ifdef DEBUG
"usba_hcdi_cb: "
"ph_data=0x%p req=0x%p state=%d ref=%d cnt=%d cr=%d",
#endif
/* Set the completion reason */
case USB_EP_ATTR_CONTROL:
((usb_ctrl_req_t *)req)->
break;
case USB_EP_ATTR_BULK:
((usb_bulk_req_t *)req)->
break;
case USB_EP_ATTR_INTR:
((usb_intr_req_t *)req)->
break;
case USB_EP_ATTR_ISOCH:
((usb_isoc_req_t *)req)->
break;
}
/*
* exception callbacks will still go thru a taskq thread
* but should occur after the soft interrupt callback
* By design of periodic pipes, polling will stop on any
* exception
*/
(completion_reason == USB_CR_OK)) {
ph_data->p_soft_intr++;
"usba_hcdi_cb: ddi_intr_trigger_softint failed");
return;
}
/*
* USBA_PH_FLAG_TQ_SHARE is for bulk and intr requests,
* USBA_PH_FLAG_USE_SOFT_INTR is only for isoch,
* so there are no conflicts.
*/
int iface;
if (iface < 0) {
/* we own the device, use the first taskq */
iface = 0;
}
NULL) {
}
return;
}
/* Add the callback to the pipehandles callback list */
/* only dispatch if there is no thread running */
if (ph_data->p_thread_id == 0) {
"usba_hcdi_cb: taskq_dispatch failed");
return;
}
} else {
}
}
}
/*
* thread to perform the callbacks
*/
static void
hcdi_cb_thread(void *arg)
{
/*
* hold the ph_data. we can't use usba_hold_ph_data() since
* it will return NULL if we are closing the pipe which would
* then leave all requests stuck in the cb_queue
*/
"hcdi_cb_thread: ph_data=0x%p ref=%d", ph_data,
/*
* wait till soft interrupt callbacks are taken care of
*/
while (ph_data->p_soft_intr) {
delay(1);
}
while ((req_wrp = (usba_req_wrapper_t *)
}
ph_data->p_thread_id = 0;
"hcdi_cb_thread done: ph_data=0x%p", ph_data);
}
static void
{
case USB_EP_ATTR_CONTROL:
break;
case USB_EP_ATTR_INTR:
break;
case USB_EP_ATTR_BULK:
break;
case USB_EP_ATTR_ISOCH:
break;
}
/*
* Normal callbacks:
*/
if (completion_reason == USB_CR_OK) {
} else {
"exception callback handling: attrs=0x%x", attrs);
/*
* In exception callback handling, if we were
* not able to clear stall, we need to modify
* pipe state. Also if auto-clearing is not set
* pipe state needs to be modified.
*/
if (!USBA_PIPE_CLOSING(pipe_state)) {
switch (completion_reason) {
case USB_CR_STOPPED_POLLING:
if (pipe_state ==
}
break;
case USB_CR_NOT_SUPPORTED:
break;
case USB_CR_PIPE_RESET:
case USB_CR_FLUSHED:
break;
default:
break;
}
}
if (attrs & USB_ATTRS_PIPE_RESET) {
if ((completion_reason != USB_CR_PIPE_RESET) &&
(pipe_state == USB_PIPE_STATE_ERROR)) {
}
}
usba_req_exc_cb(req_wrp, 0, 0);
}
/* Update the hcdi error kstats */
if (completion_reason) {
}
/*
* Once the callback is finished, release the pipe handle
* we start the next request first to avoid that the
* pipe gets closed while starting the next request
*/
}
/*
* thread to perform callbacks on the shared queue
*/
static void
hcdi_shared_cb_thread(void *arg)
{
/*
* hold the ph_data. we can't use usba_hold_ph_data() since
* it will return NULL if we are closing the pipe which would
* then leave all requests stuck in the cb_queue
*/
"hcdi_shared_cb_thread: ph_data=0x%p ref=%d req=0x%p",
/* do the callback */
"hcdi_cb_thread done: ph_data=0x%p", ph_data);
}
/*
* soft interrupt handler
*/
/*ARGSUSED*/
static uint_t
{
int count = 0;
while ((req_wrp = (usba_req_wrapper_t *)
/* hold the pipe */
/* do the callback */
/* decrement the soft interrupt count */
ph_data->p_soft_intr--;
/* release the pipe */
count++;
}
}
/*
* hcdi_autoclearing:
* This function is called under the taskq context. It
* resets the pipe, and clears the stall, if necessary
*/
static void
{
int rval;
"hcdi_autoclearing: wrp=0x%p", req_wrp);
/*
* first reset the pipe synchronously
*/
}
/* Do not clear if this request was a usb_get_status request */
if ((pipe_handle == def_pipe_handle) &&
"hcdi_autoclearing: usb_get_status failed, no clearing");
/* if default pipe and stall no auto clearing */
"hcdi_autoclearing: default pipe stalled, no clearing");
/* else do auto clearing */
} else if (((attrs & USB_ATTRS_AUTOCLEARING) ==
if (rval != USB_SUCCESS) {
"get status (STALL) failed: rval=%d", rval);
}
if ((rval != USB_SUCCESS) ||
(status & USB_EP_HALT_STATUS)) {
if ((rval = usb_pipe_sync_ctrl_xfer(
0,
0,
NULL, 0,
"auto clearing (STALL) failed: "
"rval=%d, cr=0x%x cb=0x%x",
} else {
}
} else {
}
}
}
/*
* usba_hcdi_get_req_private:
* This function is used to get the HCD private field
* maintained by USBA. HCD calls this function.
*
* Arguments:
* req - pointer to usb_*_req_t
*
* Return Values:
* wr_hcd_private field from wrapper
*/
{
return (wrp->wr_hcd_private);
}
/*
* usba_hcdi_set_req_private:
* This function is used to set the HCD private field
* maintained by USBA. HCD calls this function.
*
* Arguments:
* req - pointer to usb_*_req_t
* hcd_private - wr_hcd_private field from wrapper
*/
void
{
}
/* get data toggle information for this endpoint */
{
int ep_index;
return (toggle);
}
/* set data toggle information for this endpoint */
void
{
int ep_index;
}
/* get pipe_handle_impl ptr for this ep */
{
}