wa.c revision ff0e937b36dcde1a47ff7b00aa76a491c0dc07a8
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Wire Adapter Operations
* Both DWA and HWA have the same kind of functional components, the
* Wire Adapter. Functions defined in this file are to handle WA's
* class specific Descriptors, Requests, Notifications and Transfers.
* DWA or HWA specific descriptors, requests are not handled here.
*/
extern usb_log_handle_t whcdi_log_handle;
/* default rpipe PHY transfer speed */
/* function prototypes */
/*
* Parse Wire Adapter class desriptor.
* - see 8.4.3.7 & 8.5.2.7
*
* wa_descr - the parsed descriptors.
* altif_data - the passed in raw descriptor data.
*/
int
{
int i;
return (USB_INVALID_ARGS);
}
for (i = 0; i < altif_data->altif_n_cvs; i++) {
continue;
}
(void *)wa_descr,
if (count != USB_WA_DESCR_SIZE) {
return (USB_FAILURE);
} else {
return (USB_SUCCESS);
}
}
}
return (USB_FAILURE);
}
/* initialize rpipe structures */
void
{
int i;
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
}
}
/* deinitialize rpipe structures */
void
{
int i;
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
}
}
/*
* wusb_wa_data_init:
* WA interface validation
* Parse WA class descriptors
* Set up RPipes
* Set up callbacks
*/
int
{
int ifno;
int rval;
return (USB_INVALID_ARGS);
}
/* get inf descr and ept descrs from altif data */
/* T.8-44. Wire Adapter */
"wusb_init_wa_data: invalid interface subclass (0x%x)",
return (USB_FAILURE);
}
/* at least 3 EPs, INTR IN + BULK IN + BULK OUT */
"wusb_init_wa_data: invalid alt 0 for interface %d",
return (USB_FAILURE);
}
}
}
}
"wusb_init_wa_data: the minimum endpoint set is not "
"supported");
return (USB_FAILURE);
}
/* parse the WA descriptor */
USB_SUCCESS) {
"wusb_init_wa_data: parse wire adapter class descr failed");
return (rval);
}
/* initialize rpipe handlers */
sizeof (wusb_wa_rpipe_hdl_t)), KM_SLEEP);
/* init rpipes */
/* register callbacks */
return (USB_SUCCESS);
}
/* deinitialize data transfer related resources */
void
{
if (wa_data->wa_rpipe_hdl) {
sizeof (wusb_wa_rpipe_hdl_t));
}
}
{
"\tWRPipeIndex=%d wRequests=%d wBlocks=%d\n"
"\twMaxPacketSize=%d bHSHubAddress=%d\n"
"\tbHSHubPort=%d bSpeed=%d bDeviceAddress=%d\n"
"\tbEndpointAddress=0x%02x bDataSequence=%d\n"
"\tdwCurrentWindow=0x%08x bMaxDataSequence=%d",
"(cont'ed)bInterval=%d bOverTheAirInterval=%d\n"
"\tbmAttribute=0x%02x bmCharacter=0x%02x\n"
"\tbmRetryOptions=0x%02x wNumTransactionErrors=%d\n",
}
/* get rpipe descr of a certain index, refer to WUSB 1.0/8.3.1.4 */
int
{
int rval;
/*
* This descriptor is critical for later operations to succeed.
* So, we must wait here.
*/
USB_DESCR_TYPE_RPIPE << 8,
idx,
&data, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
"wusb_wa_get_rpipe_descr: rval=%d, cr=%d, "
goto done;
}
"wusb_wa_get_rpipe_descr: return size %d",
rval = USB_FAILURE;
goto done;
}
if (count == USB_PARSE_ERROR) {
"wusb_wa_get_rpipe_descr: parse error");
rval = USB_FAILURE;
goto done;
}
return (USB_SUCCESS);
done:
if (data) {
}
return (rval);
}
/*
* Get All the RPipes' descriptors of an HWA
* - WA RPipe descriptor are not returned as part of the
* cofiguration descriptor. We have to get it separately.
* - See section 8.4.3.19 and 8.5.2.11
*/
int
{
int i, rval;
return (USB_INVALID_ARGS);
}
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
if (rval != USB_SUCCESS) {
"wusb_wa_get_rpipe_descrs: fail to get rpipe "
"descr for idx %d", i);
return (rval);
}
}
return (USB_SUCCESS);
}
/*
* Get Wire Adapter's Status
* See section 8.3.1.6
*/
int
{
int rval = USB_SUCCESS;
return (USB_INVALID_ARGS);
}
0,
&data, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
"wusb_get_wa_status: can't retrieve status");
goto done;
}
done:
if (data) {
}
return (rval);
}
/*
* Reset WA
* See 8.3.1.9
*/
int
{
int rval, i;
return (USB_INVALID_ARGS);
}
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
"wusb_wa_reset: can't reset wa, rval = %d, cr=%d", rval,
return (rval);
}
for (i = 0; i < 10; i++) {
if (rval != USB_SUCCESS) {
"wusb_wa_reset: can't get status, rval = %d",
rval);
return (rval);
}
if (!(status & WA_HC_RESET_IN_PROGRESS)) {
return (USB_SUCCESS);
}
}
return (USB_FAILURE);
}
/*
* Enable wire adapter.
* See 8.3.1.9
*/
int
{
int rval, i;
return (USB_INVALID_ARGS);
}
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
"wusb_wa_enable: can't enable WA, rval = %d, cr=%d",
return (rval);
}
for (i = 0; i < 10; i++) {
if (rval != USB_SUCCESS) {
"wusb_wa_enable: can't get status, rval = %d",
rval);
return (rval);
}
if (status & WA_HC_ENABLED) {
return (USB_SUCCESS);
}
}
return (USB_FAILURE);
}
/*
* Disable WA. Clear a fearture.
* See Section 8.3.1.3
*/
int
{
int rval, i;
return (USB_INVALID_ARGS);
}
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
"wusb_wa_disable: can't disable wa, rval = %d, cr = %d",
return (rval);
}
for (i = 0; i < 10; i++) {
if (rval != USB_SUCCESS) {
"wusb_wa_disable: can't get status, rval = %d",
rval);
return (rval);
}
if (!(status & WA_HC_ENABLED)) {
return (USB_SUCCESS);
}
}
return (USB_FAILURE);
}
/*
* Open the two bulk endpoints and one interrupt IN endpoint, defined in
* a WA's data transfer interface. See 8.1.2
*/
int
{
int rval;
return (USB_SUCCESS);
}
&wa_data->wa_intr_ph);
if (rval != USB_SUCCESS) {
"wusb_wa_open_pipes: can't open intr pipe, rval = %d",
rval);
return (rval);
}
&wa_data->wa_bulkin_ph);
if (rval != USB_SUCCESS) {
"wusb_wa_open_pipes: can't open bulkin pipe, rval = %d",
rval);
return (rval);
}
&wa_data->wa_bulkout_ph);
if (rval != USB_SUCCESS) {
"wusb_wa_open_pipes: can't open bulkout pipe, rval = %d",
rval);
return (rval);
}
/* mark the state stopped until listening is started on the pipes */
/* no listening on this pipe, just mark it active */
return (USB_SUCCESS);
}
/*
* Close WA's pipes.
*/
void
{
return;
}
}
}
/*
* start listening for transfer completion notifications or device
* notifications on the notification ept
*/
int
{
int rval;
return (USB_INVALID_PIPE);
}
if (!reqp) {
return (USB_NO_RESOURCES);
}
flag)) != USB_SUCCESS) {
"wusb_wa_start_nep: intr xfer fail, rval = %d",
rval);
return (rval);
}
/* pipe state is active while the listening is on */
return (USB_SUCCESS);
}
/*
* stop the notification ept from listening
*/
void
{
return;
}
/* stop intr in without closing the pipe */
}
/*
* allocate a rpipe for transfers on a pipe
* - Find a free RPipe
*
* For now, one rpipe is associated with only one usba pipe once
* the pipe is opened. In the future, the rpipe needs to be
* multiplexed between asynchronous endpoints
* input:
* type: 0 - ctrl, 1 - isoc, 2 - bulk, 3 - intr
*
*/
/* ARGSUSED */
int
{
int i;
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
/* find the first unused rpipe */
continue;
}
/* check if the rpipe supports the ept transfer type */
return (USB_SUCCESS);
}
}
"wusb_wa_get_rpipe: no matching rpipe is found");
return (USB_FAILURE);
}
/*
* Decrease a RPipe's reference count.
* - if count == 0, mark it as free RPipe.
*/
int
{
return (USB_FAILURE);
}
return (USB_FAILURE);
}
}
hdl->rp_block_chg = 0;
}
return (USB_SUCCESS);
}
/*
* Set a RPipe's Descriptor and make the rpipe configured
* See section 8.3.1.7
*/
int
{
int rval;
uint8_t *p;
"wusb_wa_set_rpipe_descr: RPipe Descriptors");
USB_DESCR_TYPE_RPIPE << 8,
&data, 0,
&completion_reason, &cb_flags, 0);
"wusb_wa_set_rpipe_descr: rval = %d", rval);
return (rval);
}
/* ept companion descr for the default ctrl pipe, refer to WUSB 1.0/4.8.1 */
1, 2,
};
/*
* Get the Endpoint Companion Descriptor for the pipe
* ph_data - the specified pipe
* ep_comp - the companion descriptor returned
*/
int
{
int i, j;
/* default ctrl endpoint */
if (ep->bEndpointAddress == 0) {
return (USB_SUCCESS);
}
USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
return (USB_FAILURE);
}
/* retrieve ept companion descr from the dev data */
for (j = 0; j < altif_data->altif_n_ep; j++) {
sizeof (usb_ep_descr_t)) == 0) {
return (USB_SUCCESS);
}
}
}
return (USB_FAILURE);
}
/* to check if the specified PHY speed is supported by the device */
int
{
};
if (speed >= WUSB_PHY_TX_RATE_RES) {
return (0);
}
/* this speed is not supported by the device */
return (0);
}
return (1);
}
/*
* Set up a RPipe
* - Associate a RPipe and a pipe handle. Hence, an endpoint has
* RPipe to transfer data.
* - Set this RPipe to bDeviceAddress:bEndpointAddress
*
* wa - wa data
* ph - wa's default control pipe
* ph_data - client driver's usba pipe to be opened
* hdl - RPipe handle
*/
int
{
int rval;
"wusb_wa_set_rpipe_target: ph_data = 0x%p rp_hdl = 0x%p",
/* Get client device's Endpoint companion descriptor */
USB_SUCCESS) {
"wusb_wa_set_rpipe_target: get companion ep descr failed,"
" rval = %d", rval);
return (rval);
}
/* set the rpipe to unconfigured state */
"wusb_wa_set_rpipe_target: reset rpipe failed, rval = %d",
rval);
return (rval);
}
"wusb_wa_set_rpipe_target: get rpipe status failed, "
"rval = %d", rval);
return (rval);
}
if (rp_status & WA_RPIPE_CONFIGURED) {
"wusb_wa_set_rpipe_target: reset rpipe unsuccessful");
return (USB_FAILURE);
}
/* should be 0x200 for default ctrl pipe, refer to wusb 1.0/4.8.1 */
/*
* set rpipe descr values
*
* Try to use an average block value first. If it's too small,
* then try to allocate the minimum block size to accomodate one
* packet. If the required number of block is not available, return
* failure.
*/
/* alloc enough blocks to accomodate one packet */
}
/* WA don't have so many blocks to fulfill this reqirement */
return (USB_FAILURE);
}
/* we're satisfied */
}
"wusb_wa_set_rpipe_target: wBlocks=%d, maxblock=%d, numR=%d, av=%d",
/*
* DEVICE INDEX
* device info index should be zero based, refer
* to WUSB 1.0/8.5.3.7
*/
/*
* default ctrl pipe uses PHY base signaling rate
* refer to wusb 1.0/4.8.1
*/
if (ep->bEndpointAddress == 0) {
} else {
} else {
/* use a must-supported speed */
}
}
/* set rpipe descr */
if (rval != USB_SUCCESS) {
"wusb_wa_set_rpipe_target: set rpipe descr failed, "
"rval = %d", rval);
return (rval);
}
/* check rpipe status, must be configured and idle */
"wusb_wa_set_rpipe_target: get rpipe status failed, "
"rval = %d", rval);
return (rval);
}
"wusb_wa_set_rpipe_target: set rpipe descr unsuccessful");
return (USB_FAILURE);
}
return (rval);
}
/*
* Abort a RPipe
* - See Section 8.3.1.1
* - Aborts all transfers pending on the given pipe
*/
int
{
int rval;
"wusb_wa_rpipe_abort: rp_hdl = 0x%p", (void *)hdl);
/* only abort when there is active transfer */
return (USB_SUCCESS);
}
0,
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
"wusb_wa_rpipe_abort: abort failed, rval = %d", rval);
return (rval);
}
return (USB_SUCCESS);
}
/*
* Clear status on the remote device's endpoint, specifically clear the
* RPipe's target endpoint sequence number. See 4.5.3, 4.6.4 and Tab.8-49
* for reference of data sequence.
*
* NOTE AGAIN:
* The device endpoint will not respond to host request if the RPipe is
* reset or re-targeted, while device endpoint is not reset!
*/
void
{
return;
}
"wusb_wa_clear_dev_ep:clear endpoint = 0x%02x", ept_addr);
if (ept_addr != 0) {
/* only clear non-default endpoints */
}
}
/*
* Reset a RPipe
* - Reset a RPipe to a known state
* - Pending transfers must be drained or aborted before this
* operation.
* - See Section 8.3.1.10
*
* dip - the WA's devinfo
* ph - RPipe's targeted remote device's endpoint pipe.
* hdl - RPipe's handle
*
* flag = 1, reset the RPipe descriptor to its initial state and
* also clear remote device endpoint
* = 0, not reset the RPipe descriptor. Caller should use 0 flag
* if it's the first time to open a pipe, because we don't have
* a valid ph yet before successfully opening a pipe by using
* usb_pipe_open().
*/
int
{
int rval = 0;
usb_cb_flags_t cb_flags = 0;
"wusb_wa_rpipe_reset: rp_hdl = 0x%p, ep=0x%02x, flag = %d",
/* get WA's default pipe */
0,
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
"wusb_wa_rpipe_reset: reset failed, rval=%d"
" cr=%d cb=0x%02x",
return (rval);
}
if (flag == 0) {
/* do nothing else, just return, the rpipe is unconfigured */
return (USB_SUCCESS);
}
"wusb_wa_rpipe_reset: need to clear dev pipe and reset RP descr");
/* set rpipe descr and make the rpipe configured */
if (rval != USB_SUCCESS) {
"wusb_wa_rpipe_reset: set descr failed, rval = %d", rval);
return (rval);
}
}
"wusb_wa_rpipe_reset: end");
return (USB_SUCCESS);
}
/* get rpipe status, refer to WUSB 1.0/8.3.1.5 */
int
{
int rval;
0,
idx,
1,
&data, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
"wusb_wa_get_rpipe_status: fail, rval=%d, cr=%d, "
} else {
"wusb_wa_get_rpipe_status: status = %x", *status);
}
return (rval);
}
/*
* WA specific operations end
*/
/* Transfer related routines */
{
"wusb_wa_alloc_tw: ph = 0x%p rp_hdl = 0x%p ",
/* compute the rpipe buffer size */
if (seg_len < maxpktsize) {
"wusb_wa_alloc_tw: fail, segment len(%d) "
return (NULL);
}
/*
* the transfer length for each segment is a multiple of the
* wMaxPacketSize except the last segment, and the length
* cannot exceed the rpipe buffer size
*/
if (datalen) {
} else {
seg_count = 1;
}
if (seg_count > WA_MAX_SEG_COUNT) {
"wusb_wa_alloc_tw: fail, seg count(%d)"
return (NULL);
}
KM_NOSLEEP)) == NULL) {
return (NULL);
}
/* allocation, not visible to other threads */
KM_NOSLEEP)) == NULL) {
return (NULL);
}
/* assign a unique ID for each transfer */
return (NULL);
}
wr->wr_has_aborted = 0;
"wusb_wa_alloc_tw: wr = 0x%p id = %x nseg = %d", (void*)wr,
return (wr);
}
/* create transfer wrapper for a ctrl request, return NULL on failure */
{
"wusb_wa_create_ctrl_wrapper: ph = 0x%p rp_hdl = 0x%p reqp = 0x%p",
"wusb_wa_create_ctrl_wrapper: fail to create tw for %p",
(void *)ctrl_reqp);
return (NULL);
}
/* not visible to other threads yet */
} else {
}
"wusb_wa_create_ctrl_wrapper: wr = 0x%p nseg = %d", (void *)wr,
return (wr);
}
/*
* create transfer wrapper for a bulk request, return NULL on failure
* - split the request into multiple segments
* - every segment is N * wMaxPacketSize
* - segment length <= bRPipeBlockSize * wBlocks
*/
{
"wusb_wa_create_bulk_wrapper: ph = 0x%p rp_hdl = 0x%p reqp = 0x%p",
"wusb_wa_create_bulk_wrapper: fail to create tw for %p",
(void *)bulk_reqp);
return (NULL);
}
/* no locking needed */
} else {
}
"wusb_wa_create_bulk_wrapper: wr = 0x%p nseg = %d", (void *)wr,
return (wr);
}
/*
* create transfer wrapper for a intr request, return NULL on failure
* - split the request into multiple segments
* - every segment is N * wMaxPacketSize
* - segment length <= bRPipeBlockSize * wBlocks
*/
{
"wusb_wa_create_intr_wrapper: ph = 0x%p rp_hdl = 0x%p reqp = 0x%p",
/* duplicate client's intr request */
if (curr_intr_reqp == NULL) {
"wusb_wa_create_intr_wrapper: fail to create reqp");
return (NULL);
}
} else { /* OUT */
}
"wusb_wa_create_bulk_wrapper: fail to create tw for %p",
(void *)intr_reqp);
return (NULL);
}
/* no locking needed */
} else {
}
"wusb_wa_create_intr_wrapper: wr = 0x%p nseg = %d", (void *)wr,
return (wr);
}
/*
* Setup the transfer request structure for a segment
* len = transfer request structure length
* - see section 8.3.3.1 and 8.3.3.2
*/
void
{
"wusb_wa_setup_trans_req: wr = 0x%p len = %d segnum = 0x%x",
/*
* 8-byte setupdata only for the first segment of a ctrl
* transfer request
*/
/* what is the unsecured flag for ? */
/* only send baSetupDate on the first segment */
}
"wusb_wa_setup_trans_req: Ctrl segment = %02x",
"wusb_wa_setup_trans_req: Ctrl Setup Data: "
"%02x %02x %02x %02x %02x %02x %02x %02x",
}
}
/*
* WA bulk pipe callbacks
* wusb_wa_trans_bulk_cb: transfer request stage normal callback
* wusb_wa_trans_bulk_exc_cb: transfer request stage exceptional callback
*
* wusb_wa_data_bulk_cb: transfer data stage normal callback
* wusb_wa_data_bulk_exc_cb: transfer data stage exceptional callback
*
* see WUSB1.0 8.3.3 for details
*/
void
{
"wusb_wa_trans_bulk_cb: ph=%p req=0x%p cr=%d", (void*)ph,
/* callback returned, this seg can be freed */
seg->seg_trans_req_state = 0;
}
void
{
"wusb_wa_trans_bulk_exc_cb: ph=%p req=0x%p cr=%d", (void *)ph,
/* callback returned, this seg can be freed */
seg->seg_trans_req_state = 0;
}
void
{
"wusb_wa_data_bulk_cb: ph=%p req=0x%p cr=%d", (void *)ph,
/* callback returned, this seg can be freed */
seg->seg_data_req_state = 0;
}
void
{
"wusb_wa_data_bulk_exc_cb: ph=%p req=0x%p cr=%d", (void *)ph,
/* callback returned, this seg can be freed */
seg->seg_data_req_state = 0;
}
/*
* Setup all the transfer request segments, including the transfer request
* stage and data stage for out transfer.
* len = total size of payload data to transfer
* - for every segment, allocate a new bulk request for Transfer
* Request. Fill the request with the segment and wrapper data.
* - for every segment, allocate a new bulk request for data stage.
*
*/
int
{
int i, rval;
uint8_t *p;
"wusb_wa_setup_segs: wr = 0x%p len = %d data = 0x%p", (void *)wr,
"wusb_wa_setup_segs: invalid wr");
return (USB_INVALID_ARGS);
}
}
} else {
}
/*
* set seg_id, all segs are the same or unique ??
* now make all segs share the same id
*/
/* alloc transfer request and set values */
case WA_XFER_REQ_TYPE_CTRL:
break;
break;
default:
trans_req_len = 0;
break;
}
if (trans_req_len == 0) {
"wusb_wa_setup_segs: trans len error");
goto error;
}
/* alloc transfer request for the ith seg */
"wusb_wa_setup_segs: can't alloc_bulk_req");
goto error;
}
/* setup the ith transfer request */
/* alloc request for data stage */
"wusb_wa_setup_segs: can't alloc_bulk_req"
" for data");
goto error;
}
/* setup the ith data transfer */
/*
* Copy data from client driver to bulk request for
* an OUT endpoint.
*/
/*
* cannot increase data->b_rptr,
* or scsa2usb panic at bulk out
*/
bcopy(p,
}
}
}
/* zero timeout means to wait infinitely */
/*
* if this is the first time this WR to be transfered,
* we'll add it to its rpipe handle's timeout queue
*/
if (wr->wr_timeout > 0) {
/* Add this new wrapper to the head of RPipe's timeout list */
if (hdl->rp_timeout_list) {
}
}
return (USB_SUCCESS);
"wusb_wa_setup_segs: fail, rval = %d", rval);
return (rval);
}
/* allocate transfer wrapper and setup all transfer segments */
{
"wusb_wa_alloc_ctrl_resources failed");
return (NULL);
}
"wusb_wa_alloc_ctrl_resources failed to setup segs");
return (NULL);
}
return (wr);
}
/* allocate transfer wrapper and setup all transfer segments */
{
"wusb_wa_alloc_bulk_resources: failed to create wr");
return (NULL);
}
"wusb_wa_alloc_bulk_resources:failed to setup segs");
return (NULL);
}
return (wr);
}
/*
* allocate transfer wrapper and setup all transfer segments
* if it's an IN request, duplicate it.
*/
{
"wusb_wa_alloc_intr_resources: failed to create wr");
return (NULL);
}
"wusb_wa_alloc_intr_resources: failed to setup segs");
return (NULL);
}
return (wr);
}
/* free the bulk request structures for all segments */
void
{
int i;
"wusb_wa_free_segs: wr = 0x%p, segs=%p", (void *)wr,
(void *)wr->wr_seg_array);
return;
}
}
/* free the bulk req for transfer request */
}
}
/* free the bulk req for data transfer */
}
}
}
/* free transfer wrapper */
void
{
"wusb_wa_free_trans_wrapper: wr = 0x%p", (void *)wr);
"wusb_wa_free_trans_wrapper: NULL wrapper");
return;
}
}
}
}
/* abort a transfer, refer to WUSB 1.0/8.3.3.5 */
void
{
uint8_t *p;
int rval;
"wusb_wa_abort_req: wr = 0x%p", (void *)wr);
"wusb_wa_abort_req: alloc bulk req failed");
return;
}
p[0] = WA_ABORT_REQ_LEN;
p[1] = WA_XFER_REQ_TYPE_ABORT;
if (rval != USB_SUCCESS) {
"wusb_wa_abort_req: send abort req failed, rval = %d",
rval);
}
}
static void
{
int ret = 0; /* debug only */
"remove_wr_from_timeout_list: %p", (void *)tw);
if (hdl->rp_timeout_list) {
ret = 1;
} else {
}
ret = 1;
}
}
}
/* debug only */
"remove_wr_from_timeout_list: %p, on the list:%d",
}
/* start timer on a rpipe */
void
{
"wusb_wa_start_xfer_timer: rpipe hdl = 0x%p", (void *)hdl);
/*
* wr_timeout is in Seconds
*/
/*
* Start the rpipe's timer only if currently timer is not
* running and if there are transfers on the rpipe.
* The timer will be per rpipe.
*
* The RPipe's timer expires every 1s. When this timer expires, the
* handler gets called and will decrease every pending transfer
* wrapper's timeout value.
*/
}
}
/* transfer timeout handler */
void
wusb_wa_xfer_timeout_handler(void *arg)
{
int rval;
"wusb_wa_xfer_timeout_handler: rphdl = 0x%p ", (void *)hdl);
/*
* Check whether still timeout handler is valid.
*/
if (hdl->rp_timer_id != 0) {
/* Reset the timer id to zero */
hdl->rp_timer_id = 0;
} else {
return;
}
/*
* Check each transfer wrapper on this RPipe's timeout queue
* Actually, due to USBA's limitation and queueing, there's only one
* usba_request submitted to HCD at a specific pipe. Hence, only one
* WR can be on this RPipe's list at any moment.
*/
while (wr) {
"wusb_wa_xfer_timeout_handler: rhdl=0x%p"
/*
* 1 second passed. Decrease every transfer wrapper's
* timeout value. If the timeout < 0 (expired), remove this
* wrapper from the timeout list and put it on the
* expire_list.
*/
wr->wr_timeout--;
if (wr->wr_timeout <= 0) {
"wusb_wa_xfer_timeout_handler: 0x%p time out",
(void *)wr);
/* remove it from the rpipe's timeout list */
/* put it on the expired list */
expire_list = wr;
}
}
/* Restart this RPipe's timer */
/* timeout handling */
wr = expire_list;
while (wr) {
/* other thread shouldn't continue processing it */
&rp_status);
if (rval != USB_SUCCESS) {
/* reset WA perhaps? */
"wusb_wa_xfer_timeout_handler: fail to get"
" rpipe status, rval = %d", rval);
goto continuing;
}
"wusb_wa_xfer_timeout_handler: rpstat=0x%02x, wr=0x%p,"
if (!(rp_status & WA_RPIPE_IDLE)) {
/*
* If RP is not idle, then it must be processing this WR.
* Abort this request to make the RPipe idle.
*/
"wusb_wa_xfer_timeout_handler: rp not idle");
"wusb_wa_xfer_timeout_handler: abort rpipe"
" fail rval = %d", rval);
if (rval == 0) {
/*
* wait for the result thread to get
* Aborted result. If this wr hasn't been
* aborted, wait it.
*/
if ((wr->wr_has_aborted == 0) &&
>= 0)) {
/* 100ms, random number, long enough? */
/* the result thread has processed it */
goto continuing;
}
"wusb_wa_xfer_timeout_handler: result"
" thread can't get the aborted request");
}
}
/*
* 1)The Rpipe is idle, OR,
* 2)rpipe_abort fails, OR,
* 3)The result thread hasn't got an aborted result in 100ms,
* most likely the result is lost. We can not depend on WA to
* return result for this aborted request. The WA seems not
* always returning such result. This will cause some hcdi
* ops hang.
*/
/* release this WR's occupied req */
}
}
/* stop timer */
void
{
"wusb_wa_stop_xfer_timer: wr = 0x%p", (void *)wr);
if (hdl->rp_timer_id == 0) {
return;
}
hdl->rp_timer_id = 0;
}
/*
* send transfer request and data to the bulk out pipe
*
* General transfer function for WA transfer, see Section 8.3.3.
*/
/* ARGSUSED */
int
{
int i, rval;
"wusb_wa_wr_xfer: wr = 0x%p", (void *)wr);
}
"wusb_wa_wr_xfer: curr_seg = %d, avail_req = %d", curr_seg,
hdl->rp_avail_reqs);
/*
* For every segment,
* Step 1: contruct a bulk req containing Transfer
* Request(T8-12 and T8-10)
* Step 2: alloc another bulk req if there's any data
* for OUT endpoints.
*
* For IN endpoints, the data is returned in the
* GetResult thread.
* Just throw as many as maximum available requests to the RPipe.
* If the avail_req is zero, wait!
*
* When a request is finished, the avail_req will be increased
* in the result thread.
*/
"wusb_wa_wr_xfer: wr=%p curr_seg = %d, avail_req = %d,"
/* waiting for available requests if wr is still good */
}
/* send only one segment */
break;
}
/* wr transfer error, don't continue */
break;
}
/* send ith transfer request */
if (rval != USB_SUCCESS) {
"wusb_wa_wr_xfer: send transfer request %d failed,"
"rv=%d", i, rval);
if (i == 0) {
/* no xfer in processing */
return (rval);
}
break;
}
"wusb_wa_wr_xfer: seg(%d) request(0x%p) sent,"
hdl->rp_avail_reqs--;
/* Get data in the GetResult thread for IN eps */
wr->wr_curr_seg++;
/* only send data for out request */
continue;
}
/* no data stage */
wr->wr_curr_seg++;
continue;
}
/* send ith data asynchronously */
if (rval != USB_SUCCESS) {
"wusb_wa_wr_xfer: send transfer data %d failed",
i);
/* not inc rp_avail_reqs until callback */
break;
}
"wusb_wa_wr_xfer: seg(%d) data(0x%p) sent, avail_req = %d",
wr->wr_curr_seg++;
}
/* start timer */
/*
* return success even if the xfer is not complete, the callback
* will only continue sending segs when (wr_error_state = 0 &&
* wr_curr_seg < wr_nsegs)
*/
return (USB_SUCCESS);
}
/*
* submit wr according to rpipe status
* - check RPipe state
* - call general WA transfer function to do transfer
*
* usba only submits one transfer to the host controller per pipe at a time
* and starts next when the previous one completed. So the hwahc now
* assumes one transfer per rpipe at a time. This won't be necessary to
* change unless the usba scheme is changed.
*/
int
{
int rval;
case WA_RPIPE_STATE_IDLE:
break;
case WA_RPIPE_STATE_ACTIVE:
/* only allow one req at a time, this should not happen */
default:
break;
}
if (rval != USB_SUCCESS) {
}
"wusb_wa_submit_ctrl_wr:fail, reqp=0x%p, rpstat=%d, rv=%d",
}
/* In other cases, wr will be freed in callback */
return (rval);
}
/*
* Transfer a control request:
* - allocate a transfer wrapper(TW) for this request
* - submit this TW
*/
int
{
int rval;
"wusb_wa_ctrl_xfer: ph = 0x%p reqp = 0x%p",
"wusb_wa_ctrl_req: alloc ctrl resource failed");
return (USB_NO_RESOURCES);
}
if (rval != USB_SUCCESS) {
"wusb_wa_submit_ctrl_wr: submit ctrl req failed, rval = %d",
rval);
}
return (rval);
}
/*
* submit wr according to rpipe status
*
* usba only submits one transfer to the host controller per pipe at a time
* and starts next when the previous one completed. So the hwahc now
* assumes one transfer per rpipe at a time. This won't be necessary to
* change unless the usba scheme is changed.
*/
int
{
int rval;
case WA_RPIPE_STATE_IDLE:
break;
case WA_RPIPE_STATE_ACTIVE:
/* only allow one req at a time, this should not happen */
default:
break;
}
if (rval != USB_SUCCESS) {
}
}
/* In other cases, wr will be freed in callback */
return (rval);
}
/*
* WA general bulk transfer
* - allocate bulk resources
* - submit the bulk request
*/
int
{
int rval;
"wusb_wa_bulk_xfer: ph = 0x%p reqp = 0x%p",
"wusb_wa_bulk_xfer: alloc bulk resource failed");
return (USB_NO_RESOURCES);
}
if (rval != USB_SUCCESS) {
"wusb_wa_bulk_req: submit bulk req failed, rval = %d",
rval);
}
return (rval);
}
/*
* submit wr according to rpipe status
*
* usba only submits one transfer to the host controller per pipe at a time
* and starts next when the previous one completed. So the hwahc now
* assumes one transfer per rpipe at a time. This won't be necessary to
* change unless the usba scheme is changed.
*/
int
{
int rval;
case WA_RPIPE_STATE_IDLE:
break;
case WA_RPIPE_STATE_ACTIVE:
/* only allow one req at a time, this should not happen */
default:
break;
}
if (rval != USB_SUCCESS) {
}
}
"wusb_wa_submit_intr_wr: submit intr req, rval = %d", rval);
/* In other cases, wr will be freed in callback */
return (rval);
}
/*
* do intr xfer
*
* Now only one time intr transfer is supported. intr polling is not
* supported.
*/
int
{
int rval;
"wusb_wa_intr_xfer: ph = 0x%p reqp = 0x%p",
"wusb_wa_intr_req: alloc intr resource failed");
return (USB_NO_RESOURCES);
}
if (rval != USB_SUCCESS) {
"wusb_wa_intr_req: submit intr req failed, rval = %d",
rval);
return (rval);
}
/*
* have successfully duplicate and queue one more request on
* the pipe. Increase the pipe request count.
*/
/*
* this count will be decremented by usba_req_normal_cb
* or usba_req_exc_cb (called by hcdi_do_cb <-- usba_hcdi_cb)
*/
ph->p_req_count++;
}
return (rval);
}
/*
* For an IN transfer request, receive transfer data on bulk-in ept
* The bulk_req has been allocated when allocating transfer resources
*/
int
{
int rval;
if (len == 0) {
return (USB_SUCCESS);
}
/* adjust bulk in length to actual length */
return (rval);
}
/*
* to retrieve a transfer_wrapper by dwTransferID
*
* Though to search a list looks not so efficient, we have to give up
* id32_lookup(). When a transfer segment is throwed to HWA device, we
* can't anticipate when the result will be returned, even if we try to
* abort it. If we have freed the transfer wrapper due to timeout, then
* after a moment, that TW's segment is accomplished by hardware. If
* id32_lookup() is used to look up corresponding TW, we'll get an invalid
* address. Unfortunately, id32_lookup() can't judge validity of its
* returned address.
*/
{
uint16_t i;
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
/* all outstanding TWs are put on the timeout list */
while (tw) {
return (tw);
}
}
}
return (NULL);
}
/* endlessly wait for transfer result on bulk-in ept and handle the result */
int
{
int rval;
uint8_t *p;
"wusb_wa_get_xfer_result: started, wa=0x%p", (void*)wa_data);
/* grab lock before accessing wa_data */
"wusb_wa_get_xfer_result: alloc bulk req failed");
return (USB_NO_RESOURCES);
}
req->bulk_timeout = 0;
/* Get the Transfer Result head, see Table 8-14 */
"wusb_wa_get_xfer_result: bulk xfer failed or "
"null data returned, rval=%d, req->bulk_data = %p",
return (rval);
}
"wusb_wa_get_xfer_result: received data len = %d",
(p[1] != WA_RESULT_TYPE_TRANSFER)) {
"wusb_wa_get_xfer_result: invalid xfer result, "
"len = %d, p0 = 0x%x, p1 = 0x%x, p6 = 0x%x",
return (USB_SUCCESS); /* don't stop this thread */
}
/* Transfer result. Section 8.3.3.4 */
sizeof (wa_xfer_result_t));
"wusb_wa_get_xfer_result: id = 0x%x len = 0x%x nseg = 0x%02x"
/*
* The result is just ignored since the transfer request
* has completed
*/
"wusb_wa_get_xfer_result: TransferID not found");
return (USB_SUCCESS);
}
/* this id's corresponding WR may have been freed by timeout handler */
"wusb_wa_get_xfer_result: wr == deadbeef or NULL");
return (USB_SUCCESS);
}
/* bit 7 is last segment flag */
"wusb_wa_get_xfer_result: error - "
" bTransferSegment(%d) > segment coutnts(%d)",
goto err;
}
/*
* if this is the last segment, we should not continue.
* IMPT: we expect the WA deliver result sequentially.
*/
wr->wr_seg_done++;
hdl->rp_avail_reqs++;
"wusb_wa_get_xfer_result: wr = %p, rp=%p, avail_req=%d", (void*)wr,
if (status & 0x40) {
status = 0; /* ignore warning, see Tab8-15 */
}
/* Error bit set */
if (status & 0x80) {
/* don't change timeout error */
}
/*
* The timeout handler is waiting, but the result thread will
* process this wr.
*/
}
/* seg error, don't proceed with this WR */
goto err;
}
"wusb_wa_get_xfer_result: status = 0x%02x dir=%s",
/*
* for an IN endpoint and data length > 0 and no error, read in
* the real data. Otherwise, for OUT EP, or data length = 0, or
* segment error, don't read.
*/
(act_len > 0) &&
/* receive data */
if (rval != USB_SUCCESS) {
"wusb_wa_get_xfer_result: can't get seg data:%d",
rval);
goto err;
}
"wusb_wa_get_xfer_result: get (%dB) data for IN ep",
act_len);
}
/* check if the whole transfer has completed */
"wusb_wa_get_xfer_result: ended");
return (USB_SUCCESS);
err:
"wusb_wa_get_xfer_result: segment(%02x) error, abort wr 0x%p,"
/* if it's timeout, just return the TIMEOUT error */
cr = USB_CR_TIMEOUT;
} else {
}
"wusb_wa_get_xfer_result: error end, cr=%d",
cr);
return (USB_SUCCESS);
}
static void
{
"wusb_wa_handle_error: start");
/* still segments pending, abort them */
"wusb_wa_handle_error: segment err, abort other segs");
}
"wusb_wa_handle_error: error end, cr=%d",
cr);
}
/*
* Check if current request is done, if yes, do callback and move on to
* next request; if there is any uncleared error, do callback to cleanup
* the pipe
*/
void
{
int i, rval;
"wusb_wa_check_req_done: wr = 0x%p, lastseg=%02x",
/* not done: submitted segs not finished and lastseg not set */
return;
}
"wusb_wa_check_req_done: tw(%p) aborted somewhere",
(void*)wr);
goto reset;
}
/* check if there is any error */
for (i = 0; i < wr->wr_curr_seg; i++) {
/* what about short xfer? need to fix */
"wusb_wa_check_req_done: seg fail, status=%02x",
seg->seg_status);
goto reset;
}
/* device has told this is the last segment, we're done */
"wusb_wa_check_req_done: last seg");
goto done;
}
}
/* check if current request has completed */
/*
* Transfer another segment.
*
*/
/* send the remained segments */
"wusb_wa_check_req_done: req not completed, restart");
if (rval != USB_SUCCESS) {
goto reset;
}
return;
}
done:
/* release the occupied requests */
/* Need to move on to next request? usba will do this */
"wusb_wa_check_req_done: ended");
return;
/* not necessary to reset the RPipe */
/* if it's timeout, just return the TIMEOUT error */
cr = USB_CR_TIMEOUT;
"wusb_wa_check_req_done: reset end");
}
/*
* callback for ctrl transfer
*
* reset_flag: not support yet
*/
void
{
int i;
"wusb_wa_handle_ctrl: wr = 0x%p, cr = 0x%x, flag=%d",
/* do callback */
return;
}
/* copy received data to original req buffer */
bulk_req = (usb_bulk_req_t *)
/* short xfer */
break;
}
/* last segment, finish */
break;
}
}
/* do callback */
}
/*
* callback for bulk transfer
*
* reset_flag: not support yet
*/
void
{
int i;
"wusb_wa_handle_bulk: wr = 0x%p, cr = 0x%x, flag=%d",
/* do callback */
return;
}
/* copy received data to original req buffer */
bulk_req = (usb_bulk_req_t *)
/* short xfer */
break;
}
/* last segment, finish */
break;
}
}
/* do callback */
}
int
{
"wa_submit_periodic_req: wa_data=0x%p, ph=0x%p",
}
/*
* callback for intr transfer
*
* reset_flag: not support yet
*/
void
{
int i;
int rval;
"wusb_wa_handle_intr: wr = 0x%p, cr = 0x%x, flag=%d",
/* do callback */
return;
}
/* copy data to client's buffer */
/* copy received data to original req buffer */
bulk_req = (usb_bulk_req_t *)
/* short xfer */
break;
}
break;
}
}
if (attrs & USB_ATTRS_ONE_XFER) {
/* client requires ONE_XFER request, return */
"wusb_wa_handle_intr: ONE_XFER set");
goto finish;
}
/* polling mode */
if (rval != USB_SUCCESS) {
"wusb_wa_handle_intr: polling, fail to resubmit req");
goto finish;
}
"wusb_wa_handle_intr: polling, resubmit request, rv=%d", rval);
/* do callback */
"wusb_wa_handle_intr: end");
}
/*
* free transfer wrapper
* call host controller driver callback for completion handling
*
* This callback will call WA's specific callback function.
* The callback functions should call usba_hcdi_cb() to pass request
* back to client driver.
*/
void
{
"wusb_wa_callback: wr=0x%p, cr=0x%x, ph=0x%p, req = 0x%p",
if (cr == USB_CR_FLUSHED) {
/*
* the wr is aborted. mark the rpipe as error,
* so that the periodic xfer callbacks will not submit
* further requests.
*/
}
/*
* need to consider carefully when to free wrapper
* if the rpipe is reset, what to do with current wr in processing?
*/
"wusb_wa_callback: hwahc callback finish for wr= 0x%p, free it",
(void*)wr);
}
static struct {
} sts2cr[] = {
{0xff, 0} /* end */
};
/* translate transfer status to USB completion reason */
{
int i;
/* cares about bits5:0 in WUSB 1.0 */
} else {
}
}
}
return (USB_CR_UNSPECIFIED_ERR);
}