wa.c revision d3d50737e566cade9a08d73d2af95105ac7cd960
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 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.
*/
#include <sys/usb/hwa/hwahc/hwahc.h>
#include <sys/usb/hwa/hwahc/hwahc_util.h>
#include <sys/usb/usba/wa.h>
#include <sys/usb/usba/wusba.h>
#include <sys/usb/usba/whcdi.h>
#include <sys/usb/usba.h>
#include <sys/usb/usba/usba_impl.h>
#include <sys/usb/usba/usba_devdb.h> /* usba_devdb_refresh */
#include <sys/usb/hubd/hubdvar.h>
#include <sys/usb/hubd/hubd_impl.h> /* hubd_ioctl_data_t */
#include <sys/strsubr.h> /* allocb_wait */
#include <sys/strsun.h> /* MBLKL macro */
extern usb_log_handle_t whcdi_log_handle;
/* default rpipe PHY transfer speed */
static uint8_t rp_default_speed = WUSB_PHY_TX_RATE_106;
/* function prototypes */
static void wusb_wa_remove_wr_from_timeout_list(wusb_wa_rpipe_hdl_t *hdl,
wusb_wa_trans_wrapper_t *tw);
static void wusb_wa_handle_error(wusb_wa_data_t *wa_data,
wusb_wa_trans_wrapper_t *wr, usb_cr_t cr);
/*
* 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
wusb_parse_wa_descr(usb_wa_descr_t *wa_descr, usb_alt_if_data_t *altif_data)
{
usb_cvs_data_t *cvs_data;
int i;
size_t count;
if ((wa_descr == NULL) || (altif_data == NULL)) {
return (USB_INVALID_ARGS);
}
for (i = 0; i < altif_data->altif_n_cvs; i++) {
cvs_data = &altif_data->altif_cvs[i];
if (cvs_data->cvs_buf == NULL) {
continue;
}
if (cvs_data->cvs_buf[1] == USB_DESCR_TYPE_WA) {
count = usb_parse_data("ccsccsscccc",
cvs_data->cvs_buf, cvs_data->cvs_buf_len,
(void *)wa_descr,
(size_t)USB_WA_DESCR_SIZE);
if (count != USB_WA_DESCR_SIZE) {
return (USB_FAILURE);
} else {
return (USB_SUCCESS);
}
}
}
return (USB_FAILURE);
}
/* initialize rpipe structures */
void
wusb_wa_rpipes_init(wusb_wa_data_t *wa_data)
{
int i;
wusb_wa_rpipe_hdl_t *hdl;
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
hdl = &wa_data->wa_rpipe_hdl[i];
mutex_init(&hdl->rp_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&hdl->rp_cv, NULL, CV_DRIVER, NULL);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*hdl));
hdl->rp_state = WA_RPIPE_STATE_FREE;
hdl->rp_refcnt = 0;
hdl->rp_timeout_list = NULL;
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*hdl));
}
}
/* deinitialize rpipe structures */
void
wusb_wa_rpipes_fini(wusb_wa_data_t *wa_data)
{
int i;
wusb_wa_rpipe_hdl_t *hdl;
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
hdl = &wa_data->wa_rpipe_hdl[i];
mutex_destroy(&hdl->rp_mutex);
cv_destroy(&hdl->rp_cv);
}
}
/*
* wusb_wa_data_init:
* WA interface validation
* Parse WA class descriptors
* Set up RPipes
* Set up callbacks
*/
int
wusb_wa_data_init(dev_info_t *dip, wusb_wa_data_t *wa_data, wusb_wa_cb_t *cbs,
usb_client_dev_data_t *dev_data,
uint_t mask, usb_log_handle_t handle)
{
usb_alt_if_data_t *altif_data;
usb_ep_data_t *ep_data;
int ifno;
int rval;
if ((wa_data == NULL) || (dev_data == NULL)) {
return (USB_INVALID_ARGS);
}
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wa_data));
/* get inf descr and ept descrs from altif data */
altif_data = &dev_data->dev_curr_cfg->
cfg_if[dev_data->dev_curr_if].if_alt[0];
/* T.8-44. Wire Adapter */
if (altif_data->altif_descr.bInterfaceSubClass !=
USB_SUBCLS_WUSB_2) {
USB_DPRINTF_L2(mask, handle,
"wusb_init_wa_data: invalid interface subclass (0x%x)",
altif_data->altif_descr.bInterfaceSubClass);
return (USB_FAILURE);
}
/* at least 3 EPs, INTR IN + BULK IN + BULK OUT */
if (altif_data->altif_n_ep < 3) {
USB_DPRINTF_L2(mask, handle,
"wusb_init_wa_data: invalid alt 0 for interface %d",
dev_data->dev_curr_if);
return (USB_FAILURE);
}
wa_data->wa_ifno = ifno = dev_data->dev_curr_if;
wa_data->wa_if_descr = altif_data->altif_descr;
if ((ep_data = usb_lookup_ep_data(dip, dev_data, ifno, 0, 0,
USB_EP_ATTR_BULK, USB_EP_DIR_OUT)) != NULL) {
wa_data->wa_bulkout_ept = ep_data->ep_descr;
}
if ((ep_data = usb_lookup_ep_data(dip, dev_data, ifno, 0, 0,
USB_EP_ATTR_BULK, USB_EP_DIR_IN)) != NULL) {
wa_data->wa_bulkin_ept = ep_data->ep_descr;
}
if ((ep_data = usb_lookup_ep_data(dip, dev_data, ifno, 0, 0,
USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) {
wa_data->wa_intr_ept = ep_data->ep_descr;
}
if ((wa_data->wa_bulkout_ept.bLength == 0) ||
(wa_data->wa_bulkin_ept.bLength == 0) ||
(wa_data->wa_intr_ept.bLength == 0)) {
USB_DPRINTF_L2(mask, handle,
"wusb_init_wa_data: the minimum endpoint set is not "
"supported");
return (USB_FAILURE);
}
/* parse the WA descriptor */
if ((rval = wusb_parse_wa_descr(&wa_data->wa_descr, altif_data)) !=
USB_SUCCESS) {
USB_DPRINTF_L2(mask, handle,
"wusb_init_wa_data: parse wire adapter class descr failed");
return (rval);
}
wa_data->wa_avail_blocks = wa_data->wa_descr.wRPipeMaxBlock;
wa_data->wa_dip = dip;
/* initialize rpipe handlers */
wa_data->wa_num_rpipes = wa_data->wa_descr.wNumRPipes;
wa_data->wa_rpipe_hdl = kmem_zalloc((wa_data->wa_num_rpipes *
sizeof (wusb_wa_rpipe_hdl_t)), KM_SLEEP);
/* init rpipes */
wusb_wa_rpipes_init(wa_data);
/* register callbacks */
wa_data->pipe_periodic_req = cbs->pipe_periodic_req;
wa_data->intr_cb = cbs->intr_cb;
wa_data->intr_exc_cb = cbs->intr_exc_cb;
wa_data->rpipe_xfer_cb = cbs->rpipe_xfer_cb;
mutex_init(&wa_data->wa_mutex, NULL, MUTEX_DRIVER, NULL);
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*wa_data));
return (USB_SUCCESS);
}
/* deinitialize data transfer related resources */
void
wusb_wa_data_fini(wusb_wa_data_t *wa_data)
{
mutex_enter(&wa_data->wa_mutex);
if (wa_data->wa_rpipe_hdl) {
wusb_wa_rpipes_fini(wa_data);
kmem_free(wa_data->wa_rpipe_hdl, wa_data->wa_num_rpipes *
sizeof (wusb_wa_rpipe_hdl_t));
}
mutex_exit(&wa_data->wa_mutex);
mutex_destroy(&wa_data->wa_mutex);
}
void wusb_wa_dump_rpipe_descr(usb_wa_rpipe_descr_t *pd, uint_t mask,
usb_log_handle_t handle)
{
USB_DPRINTF_L4(mask, handle, "RPipe Descriptor:\n"
"\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",
pd->wRPipeIndex, pd->wRequests, pd->wBlocks, pd->wMaxPacketSize,
pd->wa_value.hwa_value.bMaxBurst,
pd->wa_value.hwa_value.bDeviceInfoIndex,
pd->bSpeed, pd->bDeviceAddress,
pd->bEndpointAddress, pd->bDataSequence, pd->dwCurrentWindow,
pd->bMaxDataSequence);
USB_DPRINTF_L4(mask, handle,
"(cont'ed)bInterval=%d bOverTheAirInterval=%d\n"
"\tbmAttribute=0x%02x bmCharacter=0x%02x\n"
"\tbmRetryOptions=0x%02x wNumTransactionErrors=%d\n",
pd->bInterval, pd->bOverTheAirInterval,
pd->bmAttribute, pd->bmCharacteristics, pd->bmRetryOptions,
pd->wNumTransactionErrors);
}
/* get rpipe descr of a certain index, refer to WUSB 1.0/8.3.1.4 */
int
wusb_wa_get_rpipe_descr(dev_info_t *dip, usb_pipe_handle_t ph,
uint16_t idx, usb_wa_rpipe_descr_t *descr,
uint_t mask, usb_log_handle_t handle)
{
mblk_t *data = NULL;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
size_t count;
int rval;
/*
* This descriptor is critical for later operations to succeed.
* So, we must wait here.
*/
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
WA_CLASS_RPIPE_REQ_IN_TYPE,
USB_REQ_GET_DESCR,
USB_DESCR_TYPE_RPIPE << 8,
idx,
USB_RPIPE_DESCR_SIZE,
&data, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(mask, handle,
"wusb_wa_get_rpipe_descr: rval=%d, cr=%d, "
"cb=0x%x", rval, completion_reason, cb_flags);
goto done;
}
if (MBLKL(data) != USB_RPIPE_DESCR_SIZE) {
USB_DPRINTF_L2(mask, handle,
"wusb_wa_get_rpipe_descr: return size %d",
(int)MBLKL(data));
rval = USB_FAILURE;
goto done;
}
count = usb_parse_data("2c4s6cl6cs", data->b_rptr,
USB_RPIPE_DESCR_SIZE, descr, sizeof (usb_wa_rpipe_descr_t));
if (count == USB_PARSE_ERROR) {
USB_DPRINTF_L2(mask, handle,
"wusb_wa_get_rpipe_descr: parse error");
rval = USB_FAILURE;
goto done;
}
wusb_wa_dump_rpipe_descr(descr, mask, handle);
freemsg(data);
data = NULL;
return (USB_SUCCESS);
done:
if (data) {
freemsg(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
wusb_wa_get_rpipe_descrs(wusb_wa_data_t *wa_data, usb_pipe_handle_t ph,
uint_t mask, usb_log_handle_t handle)
{
dev_info_t *dip = wa_data->wa_dip;
int i, rval;
if ((dip == NULL) || (ph == NULL)) {
return (USB_INVALID_ARGS);
}
/* called at initialization, no other threads yet */
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wa_data));
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
rval = wusb_wa_get_rpipe_descr(dip, ph, i,
&wa_data->wa_rpipe_hdl[i].rp_descr, mask, handle);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(mask, handle,
"wusb_wa_get_rpipe_descrs: fail to get rpipe "
"descr for idx %d", i);
return (rval);
}
}
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*wa_data));
return (USB_SUCCESS);
}
/*
* Get Wire Adapter's Status
* See section 8.3.1.6
*/
int
wusb_get_wa_status(wusb_wa_data_t *wa_data, usb_pipe_handle_t ph,
uint32_t *status)
{
dev_info_t *dip = wa_data->wa_dip;
int rval = USB_SUCCESS;
mblk_t *data = NULL;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
if ((dip == NULL) || (ph == NULL)) {
return (USB_INVALID_ARGS);
}
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
WUSB_CLASS_IF_REQ_IN_TYPE,
USB_REQ_GET_STATUS,
0,
wa_data->wa_ifno,
WA_GET_WA_STATUS_LEN,
&data, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_get_wa_status: can't retrieve status");
goto done;
}
*status = (*(data->b_rptr + 3) << 24) | (*(data->b_rptr + 2) << 16) |
(*(data->b_rptr + 1) << 8) | *(data->b_rptr);
done:
if (data) {
freemsg(data);
}
return (rval);
}
/*
* Reset WA
* See 8.3.1.9
*/
int
wusb_wa_reset(wusb_wa_data_t *wa_data, usb_pipe_handle_t ph)
{
dev_info_t *dip = wa_data->wa_dip;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
int rval, i;
uint32_t status;
if ((dip == NULL) || (ph == NULL)) {
return (USB_INVALID_ARGS);
}
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
WUSB_CLASS_IF_REQ_OUT_TYPE,
USB_REQ_SET_FEATURE,
WA_DEV_RESET,
wa_data->wa_ifno,
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_reset: can't reset wa, rval = %d, cr=%d", rval,
completion_reason);
return (rval);
}
for (i = 0; i < 10; i++) {
delay(drv_usectohz(50000));
rval = wusb_get_wa_status(wa_data, ph, &status);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_enable(wusb_wa_data_t *wa_data, usb_pipe_handle_t ph)
{
dev_info_t *dip = wa_data->wa_dip;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
int rval, i;
uint32_t status;
if ((dip == NULL) || (ph == NULL)) {
return (USB_INVALID_ARGS);
}
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
WUSB_CLASS_IF_REQ_OUT_TYPE,
USB_REQ_SET_FEATURE,
WA_DEV_ENABLE,
wa_data->wa_ifno,
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_enable: can't enable WA, rval = %d, cr=%d",
rval, completion_reason);
return (rval);
}
for (i = 0; i < 10; i++) {
delay(drv_usectohz(50000));
rval = wusb_get_wa_status(wa_data, ph, &status);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_disable(wusb_wa_data_t *wa_data, usb_pipe_handle_t ph)
{
dev_info_t *dip = wa_data->wa_dip;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
int rval, i;
uint32_t status;
if ((dip == NULL) || (ph == NULL)) {
return (USB_INVALID_ARGS);
}
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
WUSB_CLASS_IF_REQ_OUT_TYPE,
USB_REQ_CLEAR_FEATURE,
WA_DEV_ENABLE,
wa_data->wa_ifno,
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_disable: can't disable wa, rval = %d, cr = %d",
rval, completion_reason);
return (rval);
}
for (i = 0; i < 10; i++) {
delay(drv_usectohz(50000));
rval = wusb_get_wa_status(wa_data, ph, &status);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_open_pipes(wusb_wa_data_t *wa_data)
{
int rval;
mutex_enter(&wa_data->wa_mutex);
if (wa_data->wa_state & WA_PIPES_OPENED) {
mutex_exit(&wa_data->wa_mutex);
return (USB_SUCCESS);
}
wa_data->wa_pipe_policy.pp_max_async_reqs = 1;
mutex_exit(&wa_data->wa_mutex);
rval = usb_pipe_open(wa_data->wa_dip, &wa_data->wa_intr_ept,
&wa_data->wa_pipe_policy, USB_FLAGS_SLEEP,
&wa_data->wa_intr_ph);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_open_pipes: can't open intr pipe, rval = %d",
rval);
return (rval);
}
rval = usb_pipe_open(wa_data->wa_dip, &wa_data->wa_bulkin_ept,
&wa_data->wa_pipe_policy, USB_FLAGS_SLEEP,
&wa_data->wa_bulkin_ph);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_open_pipes: can't open bulkin pipe, rval = %d",
rval);
usb_pipe_close(wa_data->wa_dip, wa_data->wa_intr_ph,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&wa_data->wa_mutex);
wa_data->wa_intr_ph = NULL;
mutex_exit(&wa_data->wa_mutex);
return (rval);
}
rval = usb_pipe_open(wa_data->wa_dip, &wa_data->wa_bulkout_ept,
&wa_data->wa_pipe_policy, USB_FLAGS_SLEEP,
&wa_data->wa_bulkout_ph);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_open_pipes: can't open bulkout pipe, rval = %d",
rval);
usb_pipe_close(wa_data->wa_dip, wa_data->wa_intr_ph,
USB_FLAGS_SLEEP, NULL, NULL);
usb_pipe_close(wa_data->wa_dip, wa_data->wa_bulkin_ph,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&wa_data->wa_mutex);
wa_data->wa_intr_ph = NULL;
wa_data->wa_bulkin_ph = NULL;
mutex_exit(&wa_data->wa_mutex);
return (rval);
}
mutex_enter(&wa_data->wa_mutex);
/* mark the state stopped until listening is started on the pipes */
wa_data->wa_intr_pipe_state = WA_PIPE_STOPPED;
wa_data->wa_bulkin_pipe_state = WA_PIPE_STOPPED;
/* no listening on this pipe, just mark it active */
wa_data->wa_bulkout_pipe_state = WA_PIPE_ACTIVE;
wa_data->wa_state |= WA_PIPES_OPENED;
mutex_exit(&wa_data->wa_mutex);
return (USB_SUCCESS);
}
/*
* Close WA's pipes.
*/
void
wusb_wa_close_pipes(wusb_wa_data_t *wa_data)
{
mutex_enter(&wa_data->wa_mutex);
if ((wa_data->wa_state & WA_PIPES_OPENED) == 0) {
mutex_exit(&wa_data->wa_mutex);
return;
}
mutex_exit(&wa_data->wa_mutex);
usb_pipe_close(wa_data->wa_dip, wa_data->wa_intr_ph,
USB_FLAGS_SLEEP, NULL, NULL);
if (wa_data->wa_bulkin_ph != NULL) {
usb_pipe_close(wa_data->wa_dip, wa_data->wa_bulkin_ph,
USB_FLAGS_SLEEP, NULL, NULL);
}
usb_pipe_close(wa_data->wa_dip, wa_data->wa_bulkout_ph,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&wa_data->wa_mutex);
wa_data->wa_intr_ph = NULL;
wa_data->wa_bulkin_ph = NULL;
wa_data->wa_bulkout_ph = NULL;
wa_data->wa_intr_pipe_state = WA_PIPE_CLOSED;
wa_data->wa_bulkin_pipe_state = WA_PIPE_CLOSED;
wa_data->wa_bulkout_pipe_state = WA_PIPE_CLOSED;
wa_data->wa_state &= ~WA_PIPES_OPENED;
mutex_exit(&wa_data->wa_mutex);
}
/*
* start listening for transfer completion notifications or device
* notifications on the notification ept
*/
int
wusb_wa_start_nep(wusb_wa_data_t *wa_data, usb_flags_t flag)
{
int rval;
usb_intr_req_t *reqp;
mutex_enter(&wa_data->wa_mutex);
if ((wa_data->wa_intr_ph == NULL) ||
(wa_data->wa_intr_pipe_state != WA_PIPE_STOPPED)) {
mutex_exit(&wa_data->wa_mutex);
return (USB_INVALID_PIPE);
}
reqp = usb_alloc_intr_req(wa_data->wa_dip, 0, flag);
if (!reqp) {
mutex_exit(&wa_data->wa_mutex);
return (USB_NO_RESOURCES);
}
reqp->intr_client_private = (usb_opaque_t)wa_data;
reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
USB_ATTRS_AUTOCLEARING;
reqp->intr_len = wa_data->wa_intr_ept.wMaxPacketSize;
reqp->intr_cb = wa_data->intr_cb;
reqp->intr_exc_cb = wa_data->intr_exc_cb;
mutex_exit(&wa_data->wa_mutex);
if ((rval = usb_pipe_intr_xfer(wa_data->wa_intr_ph, reqp,
flag)) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_start_nep: intr xfer fail, rval = %d",
rval);
usb_free_intr_req(reqp);
return (rval);
}
mutex_enter(&wa_data->wa_mutex);
/* pipe state is active while the listening is on */
wa_data->wa_intr_pipe_state = WA_PIPE_ACTIVE;
mutex_exit(&wa_data->wa_mutex);
return (USB_SUCCESS);
}
/*
* stop the notification ept from listening
*/
void
wusb_wa_stop_nep(wusb_wa_data_t *wa_data)
{
mutex_enter(&wa_data->wa_mutex);
if ((wa_data->wa_intr_ph == NULL) ||
(wa_data->wa_intr_pipe_state != WA_PIPE_ACTIVE)) {
mutex_exit(&wa_data->wa_mutex);
return;
}
wa_data->wa_intr_pipe_state = WA_PIPE_STOPPED;
mutex_exit(&wa_data->wa_mutex);
/* stop intr in without closing the pipe */
usb_pipe_stop_intr_polling(wa_data->wa_intr_ph, USB_FLAGS_SLEEP);
}
/*
* 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
wusb_wa_get_rpipe(wusb_wa_data_t *wa_data, usb_pipe_handle_t ph,
uint8_t type, wusb_wa_rpipe_hdl_t **hdl,
uint_t mask, usb_log_handle_t handle)
{
int i;
wusb_wa_rpipe_hdl_t *thdl;
uint8_t rp_type;
uint8_t ep_type = 1 << type;
*hdl = NULL;
mutex_enter(&wa_data->wa_mutex);
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
/* find the first unused rpipe */
thdl = &wa_data->wa_rpipe_hdl[i];
mutex_enter(&thdl->rp_mutex);
if (thdl->rp_state != WA_RPIPE_STATE_FREE) {
mutex_exit(&thdl->rp_mutex);
continue;
}
/* check if the rpipe supports the ept transfer type */
rp_type = (thdl->rp_descr.bmCharacteristics &
USB_RPIPE_CHA_MASK);
if (rp_type & ep_type) {
thdl->rp_refcnt++;
thdl->rp_state = WA_RPIPE_STATE_IDLE;
thdl->rp_avail_reqs = thdl->rp_descr.wRequests;
*hdl = thdl;
mutex_exit(&thdl->rp_mutex);
mutex_exit(&wa_data->wa_mutex);
return (USB_SUCCESS);
}
mutex_exit(&thdl->rp_mutex);
}
USB_DPRINTF_L2(mask, handle,
"wusb_wa_get_rpipe: no matching rpipe is found");
mutex_exit(&wa_data->wa_mutex);
return (USB_FAILURE);
}
/*
* Decrease a RPipe's reference count.
* - if count == 0, mark it as free RPipe.
*/
int
wusb_wa_release_rpipe(wusb_wa_data_t *wa, wusb_wa_rpipe_hdl_t *hdl)
{
if (hdl == NULL) {
return (USB_FAILURE);
}
mutex_enter(&wa->wa_mutex);
mutex_enter(&hdl->rp_mutex);
if (hdl->rp_refcnt == 0) {
mutex_exit(&hdl->rp_mutex);
mutex_exit(&wa->wa_mutex);
return (USB_FAILURE);
}
if (--hdl->rp_refcnt == 0) {
hdl->rp_state = WA_RPIPE_STATE_FREE;
}
if (hdl->rp_block_chg == 1) {
wa->wa_avail_blocks += hdl->rp_descr.wBlocks;
hdl->rp_descr.wBlocks = 0; /* to prevent misadd upon re-call */
hdl->rp_block_chg = 0;
}
mutex_exit(&hdl->rp_mutex);
mutex_exit(&wa->wa_mutex);
return (USB_SUCCESS);
}
/*
* Set a RPipe's Descriptor and make the rpipe configured
* See section 8.3.1.7
*/
int
wusb_wa_set_rpipe_descr(dev_info_t *dip, usb_pipe_handle_t ph,
usb_wa_rpipe_descr_t *rp_descr)
{
mblk_t *data = NULL;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
int rval;
uint8_t *p;
data = allocb_wait(USB_RPIPE_DESCR_SIZE, BPRI_LO, STR_NOSIG, NULL);
p = data->b_wptr;
p[0] = rp_descr->bLength;
p[1] = rp_descr->bDescriptorType;
p[2] = rp_descr->wRPipeIndex;
p[3] = rp_descr->wRPipeIndex >> 8;
p[4] = rp_descr->wRequests;
p[5] = rp_descr->wRequests >> 8;
p[6] = rp_descr->wBlocks;
p[7] = rp_descr->wBlocks >> 8;
p[8] = rp_descr->wMaxPacketSize;
p[9] = rp_descr->wMaxPacketSize >> 8;
p[10] = rp_descr->wa_value.hwa_value.bMaxBurst;
p[11] = rp_descr->wa_value.hwa_value.bDeviceInfoIndex;
p[12] = rp_descr->bSpeed;
p[13] = rp_descr->bDeviceAddress;
p[14] = rp_descr->bEndpointAddress;
p[15] = rp_descr->bDataSequence;
p[16] = rp_descr->dwCurrentWindow;
p[17] = rp_descr->dwCurrentWindow >> 8;
p[18] = rp_descr->dwCurrentWindow >> 16;
p[19] = rp_descr->dwCurrentWindow >> 24;
p[20] = rp_descr->bMaxDataSequence;
p[21] = rp_descr->bInterval;
p[22] = rp_descr->bOverTheAirInterval;
p[23] = rp_descr->bmAttribute;
p[24] = rp_descr->bmCharacteristics;
p[25] = rp_descr->bmRetryOptions;
p[26] = rp_descr->wNumTransactionErrors;
p[27] = rp_descr->wNumTransactionErrors >> 8;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_descr: RPipe Descriptors");
wusb_wa_dump_rpipe_descr(rp_descr, DPRINT_MASK_WHCDI, whcdi_log_handle);
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
WA_CLASS_RPIPE_REQ_OUT_TYPE,
USB_REQ_SET_DESCR,
USB_DESCR_TYPE_RPIPE << 8,
rp_descr->wRPipeIndex,
USB_RPIPE_DESCR_SIZE,
&data, 0,
&completion_reason, &cb_flags, 0);
freemsg(data);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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 */
usb_ep_comp_descr_t ep_comp0 = {
sizeof (usb_ep_comp_descr_t), USB_DESCR_TYPE_WIRELESS_EP_COMP,
1, 2,
};
/*
* Get the Endpoint Companion Descriptor for the pipe
* ph_data - the specified pipe
* ep_comp - the companion descriptor returned
*/
int
wusb_wa_get_ep_comp_descr(usba_pipe_handle_data_t *ph_data,
usb_ep_comp_descr_t *ep_comp)
{
usb_ep_descr_t *ep = &ph_data->p_ep;
usb_client_dev_data_t *dev_data;
usb_if_data_t *if_data;
usb_alt_if_data_t *altif_data;
usb_ep_data_t *ep_data;
int i, j;
/* default ctrl endpoint */
if (ep->bEndpointAddress == 0) {
*ep_comp = ep_comp0;
return (USB_SUCCESS);
}
if (usb_get_dev_data(ph_data->p_dip, &dev_data,
USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
return (USB_FAILURE);
}
/* retrieve ept companion descr from the dev data */
if_data = &dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if];
for (i = 0; i < if_data->if_n_alt; i++) {
altif_data = &if_data->if_alt[i];
for (j = 0; j < altif_data->altif_n_ep; j++) {
ep_data = &altif_data->altif_ep[j];
if (memcmp(&ep_data->ep_descr, ep,
sizeof (usb_ep_descr_t)) == 0) {
*ep_comp = ep_data->ep_comp_descr;
usb_free_dev_data(ph_data->p_dip, dev_data);
return (USB_SUCCESS);
}
}
}
usb_free_dev_data(ph_data->p_dip, dev_data);
return (USB_FAILURE);
}
/* to check if the specified PHY speed is supported by the device */
int
wusb_wa_is_speed_valid(usba_device_t *ud, uint8_t speed)
{
usb_uwb_cap_descr_t *uwb_descr = ud->usb_wireless_data->uwb_descr;
uint8_t valid_spd[WUSB_PHY_TX_RATE_RES] = {
WUSB_DATA_RATE_BIT_53, WUSB_DATA_RATE_BIT_106,
WUSB_DATA_RATE_BIT_160, WUSB_DATA_RATE_BIT_200,
WUSB_DATA_RATE_BIT_320, WUSB_DATA_RATE_BIT_400,
WUSB_DATA_RATE_BIT_480, 0
};
if (speed >= WUSB_PHY_TX_RATE_RES) {
return (0);
}
/* this speed is not supported by the device */
if (valid_spd[speed] != (uwb_descr->wPHYRates & valid_spd[speed])) {
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
wusb_wa_set_rpipe_target(dev_info_t *dip, wusb_wa_data_t *wa,
usb_pipe_handle_t ph, usba_pipe_handle_data_t *ph_data,
wusb_wa_rpipe_hdl_t *hdl)
{
int rval;
usb_ep_comp_descr_t ep_comp;
usb_ep_descr_t *ep = &ph_data->p_ep;
usba_device_t *usba_device;
uint8_t rp_status;
usb_wa_descr_t *wa_desc = &wa->wa_descr;
uint16_t blockcnt;
uint16_t maxsize;
uint16_t seg_len;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_target: ph_data = 0x%p rp_hdl = 0x%p",
(void*)ph_data, (void*)hdl);
/* Get client device's Endpoint companion descriptor */
if ((rval = wusb_wa_get_ep_comp_descr(ph_data, &ep_comp)) !=
USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_target: get companion ep descr failed,"
" rval = %d", rval);
return (rval);
}
/* set the rpipe to unconfigured state */
if ((rval = wusb_wa_rpipe_reset(dip, ph_data, hdl, 0)) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_target: reset rpipe failed, rval = %d",
rval);
return (rval);
}
if ((rval = wusb_wa_get_rpipe_status(dip, ph,
hdl->rp_descr.wRPipeIndex, &rp_status)) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_target: get rpipe status failed, "
"rval = %d", rval);
return (rval);
}
if (rp_status & WA_RPIPE_CONFIGURED) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_target: reset rpipe unsuccessful");
return (USB_FAILURE);
}
mutex_enter(&wa->wa_mutex);
usba_device = usba_get_usba_device(ph_data->p_dip);
mutex_enter(&hdl->rp_mutex);
/* should be 0x200 for default ctrl pipe, refer to wusb 1.0/4.8.1 */
hdl->rp_descr.wMaxPacketSize = ep->wMaxPacketSize;
/*
* 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.
*/
if (hdl->rp_descr.wBlocks == 0) {
blockcnt = wa_desc->wRPipeMaxBlock/wa_desc->wNumRPipes;
maxsize = 1 << (wa_desc->bRPipeBlockSize - 1);
seg_len = blockcnt * maxsize;
/* alloc enough blocks to accomodate one packet */
if (ep->wMaxPacketSize > seg_len) {
blockcnt = (ep->wMaxPacketSize + maxsize -1)/maxsize;
}
/* WA don't have so many blocks to fulfill this reqirement */
if (wa->wa_avail_blocks < blockcnt) {
mutex_exit(&hdl->rp_mutex);
mutex_exit(&wa->wa_mutex);
return (USB_FAILURE);
}
/* we're satisfied */
hdl->rp_descr.wBlocks = blockcnt;
hdl->rp_block_chg = 1; /* the wBlocks is changed */
wa->wa_avail_blocks -= blockcnt;
}
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_target: wBlocks=%d, maxblock=%d, numR=%d, av=%d",
hdl->rp_descr.wBlocks, wa_desc->wRPipeMaxBlock, wa_desc->wNumRPipes,
wa->wa_avail_blocks);
hdl->rp_descr.wa_value.hwa_value.bMaxBurst = ep_comp.bMaxBurst;
/*
* DEVICE INDEX
* device info index should be zero based, refer
* to WUSB 1.0/8.5.3.7
*/
hdl->rp_descr.wa_value.hwa_value.bDeviceInfoIndex =
usba_device->usb_port - 1;
/*
* default ctrl pipe uses PHY base signaling rate
* refer to wusb 1.0/4.8.1
*/
if (ep->bEndpointAddress == 0) {
hdl->rp_descr.bSpeed = WUSB_PHY_TX_RATE_53;
} else {
if (wusb_wa_is_speed_valid(usba_device, rp_default_speed)) {
hdl->rp_descr.bSpeed = rp_default_speed;
} else {
/* use a must-supported speed */
hdl->rp_descr.bSpeed = WUSB_PHY_TX_RATE_106;
}
}
hdl->rp_descr.bDeviceAddress = usba_device->usb_addr;
hdl->rp_descr.bEndpointAddress = ep->bEndpointAddress;
hdl->rp_descr.bDataSequence = 0;
hdl->rp_descr.dwCurrentWindow = 1;
hdl->rp_descr.bMaxDataSequence = ep_comp.bMaxSequence - 1;
hdl->rp_descr.bInterval = ep->bInterval;
hdl->rp_descr.bOverTheAirInterval = ep_comp.bOverTheAirInterval;
hdl->rp_descr.bmAttribute = ep->bmAttributes & 0x03;
hdl->rp_descr.bmRetryOptions = 0; /* keep retrying */
hdl->rp_descr.wNumTransactionErrors = 0;
mutex_exit(&hdl->rp_mutex);
mutex_exit(&wa->wa_mutex);
/* set rpipe descr */
rval = wusb_wa_set_rpipe_descr(dip, ph, &hdl->rp_descr);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_target: set rpipe descr failed, "
"rval = %d", rval);
return (rval);
}
/* check rpipe status, must be configured and idle */
if ((rval = wusb_wa_get_rpipe_status(dip, ph,
hdl->rp_descr.wRPipeIndex, &rp_status)) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_set_rpipe_target: get rpipe status failed, "
"rval = %d", rval);
return (rval);
}
if (rp_status != (WA_RPIPE_CONFIGURED | WA_RPIPE_IDLE)) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_rpipe_abort(dev_info_t *dip, usb_pipe_handle_t ph,
wusb_wa_rpipe_hdl_t *hdl)
{
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
int rval;
mutex_enter(&hdl->rp_mutex);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_rpipe_abort: rp_hdl = 0x%p", (void *)hdl);
/* only abort when there is active transfer */
if (hdl->rp_state != WA_RPIPE_STATE_ACTIVE) {
mutex_exit(&hdl->rp_mutex);
return (USB_SUCCESS);
}
mutex_exit(&hdl->rp_mutex);
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
WA_CLASS_RPIPE_REQ_OUT_TYPE,
WA_REQ_ABORT_RPIPE,
0,
hdl->rp_descr.wRPipeIndex,
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_clear_dev_ep(usba_pipe_handle_data_t *ph)
{
uint8_t ept_addr;
if (ph == NULL) {
return;
}
ept_addr = ph->p_ep.bEndpointAddress;
USB_DPRINTF_L4(PRINT_MASK_HCDI, whcdi_log_handle,
"wusb_wa_clear_dev_ep:clear endpoint = 0x%02x", ept_addr);
if (ept_addr != 0) {
/* only clear non-default endpoints */
(void) usb_clr_feature(ph->p_dip, USB_DEV_REQ_RCPT_EP, 0,
ept_addr, USB_FLAGS_SLEEP, NULL, NULL);
}
}
/*
* 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
wusb_wa_rpipe_reset(dev_info_t *dip, usba_pipe_handle_data_t *ph,
wusb_wa_rpipe_hdl_t *hdl, int flag)
{
int rval = 0;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags = 0;
usb_pipe_handle_t default_ph;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_rpipe_reset: rp_hdl = 0x%p, ep=0x%02x, flag = %d",
(void *)hdl, ph->p_ep.bEndpointAddress, flag);
/* get WA's default pipe */
default_ph = usba_get_dflt_pipe_handle(dip);
rval = usb_pipe_sync_ctrl_xfer(dip, default_ph,
WA_CLASS_RPIPE_REQ_OUT_TYPE,
WA_REQ_RESET_RPIPE,
0,
hdl->rp_descr.wRPipeIndex,
0,
NULL, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_rpipe_reset: reset failed, rval=%d"
" cr=%d cb=0x%02x",
rval, (int)completion_reason, (int)cb_flags);
return (rval);
}
if (flag == 0) {
/* do nothing else, just return, the rpipe is unconfigured */
return (USB_SUCCESS);
}
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_rpipe_reset: need to clear dev pipe and reset RP descr");
/* set rpipe descr and make the rpipe configured */
rval = wusb_wa_set_rpipe_descr(dip, default_ph, &hdl->rp_descr);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_rpipe_reset: set descr failed, rval = %d", rval);
return (rval);
}
mutex_enter(&hdl->rp_mutex);
hdl->rp_avail_reqs = hdl->rp_descr.wRequests;
if (hdl->rp_state == WA_RPIPE_STATE_ERROR) {
hdl->rp_state = WA_RPIPE_STATE_IDLE;
}
mutex_exit(&hdl->rp_mutex);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_rpipe_reset: end");
return (USB_SUCCESS);
}
/* get rpipe status, refer to WUSB 1.0/8.3.1.5 */
int
wusb_wa_get_rpipe_status(dev_info_t *dip, usb_pipe_handle_t ph, uint16_t idx,
uint8_t *status)
{
mblk_t *data = NULL;
usb_cr_t completion_reason;
usb_cb_flags_t cb_flags;
int rval;
rval = usb_pipe_sync_ctrl_xfer(dip, ph,
WA_CLASS_RPIPE_REQ_IN_TYPE,
USB_REQ_GET_STATUS,
0,
idx,
1,
&data, 0,
&completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_rpipe_status: fail, rval=%d, cr=%d, "
"cb=0x%x", rval, completion_reason, cb_flags);
} else {
*status = *data->b_rptr;
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_rpipe_status: status = %x", *status);
freemsg(data);
}
return (rval);
}
/*
* WA specific operations end
*/
/* Transfer related routines */
wusb_wa_trans_wrapper_t *
wusb_wa_alloc_tw(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, uint32_t datalen, usb_flags_t usb_flags)
{
uint_t seg_count;
uint32_t seg_len, maxpktsize;
wusb_wa_trans_wrapper_t *wr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_tw: ph = 0x%p rp_hdl = 0x%p ",
(void*)ph, (void*)hdl);
mutex_enter(&hdl->rp_mutex);
/* compute the rpipe buffer size */
seg_len = hdl->rp_descr.wBlocks *
(1 << (wa_data->wa_descr.bRPipeBlockSize - 1));
maxpktsize = hdl->rp_descr.wMaxPacketSize;
mutex_exit(&hdl->rp_mutex);
if (seg_len < maxpktsize) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_tw: fail, segment len(%d) "
"< wMaxPacketSize(%d) ", seg_len, maxpktsize);
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
*/
seg_len = (seg_len / maxpktsize) * maxpktsize;
if (datalen) {
seg_count = (datalen + seg_len - 1) / seg_len;
} else {
seg_count = 1;
}
if (seg_count > WA_MAX_SEG_COUNT) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_tw: fail, seg count(%d)"
" > Max allowed number(%d) ", seg_count, WA_MAX_SEG_COUNT);
return (NULL);
}
if ((wr = kmem_zalloc(sizeof (wusb_wa_trans_wrapper_t),
KM_NOSLEEP)) == NULL) {
return (NULL);
}
/* allocation, not visible to other threads */
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wr));
if ((wr->wr_seg_array = kmem_zalloc(sizeof (wusb_wa_seg_t) * seg_count,
KM_NOSLEEP)) == NULL) {
kmem_free(wr, sizeof (wusb_wa_trans_wrapper_t));
return (NULL);
}
/* assign a unique ID for each transfer */
wr->wr_id = WA_GET_ID(wr);
if (wr->wr_id == 0) {
kmem_free(wr->wr_seg_array, sizeof (wusb_wa_seg_t) *
seg_count);
kmem_free(wr, sizeof (wusb_wa_trans_wrapper_t));
return (NULL);
}
wr->wr_ph = ph;
wr->wr_rp = hdl;
wr->wr_wa_data = wa_data;
wr->wr_flags = usb_flags;
wr->wr_nsegs = (uint8_t)seg_count;
wr->wr_max_seglen = seg_len;
wr->wr_has_aborted = 0;
cv_init(&wr->wr_cv, NULL, CV_DRIVER, NULL);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_tw: wr = 0x%p id = %x nseg = %d", (void*)wr,
wr->wr_id, wr->wr_nsegs);
return (wr);
}
/* create transfer wrapper for a ctrl request, return NULL on failure */
wusb_wa_trans_wrapper_t *
wusb_wa_create_ctrl_wrapper(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags)
{
wusb_wa_trans_wrapper_t *wr = NULL;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_ctrl_wrapper: ph = 0x%p rp_hdl = 0x%p reqp = 0x%p",
(void *)ph, (void*)hdl, (void *)ctrl_reqp);
wr = wusb_wa_alloc_tw(wa_data, hdl, ph, ctrl_reqp->ctrl_wLength,
usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_ctrl_wrapper: fail to create tw for %p",
(void *)ctrl_reqp);
return (NULL);
}
/* not visible to other threads yet */
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wr));
if (ctrl_reqp->ctrl_bmRequestType & USB_DEV_REQ_DEV_TO_HOST) {
wr->wr_dir = WA_DIR_IN;
} else {
wr->wr_dir = WA_DIR_OUT;
}
wr->wr_type = WA_XFER_REQ_TYPE_CTRL;
wr->wr_reqp = (usb_opaque_t)ctrl_reqp;
wr->wr_timeout = (ctrl_reqp->ctrl_timeout == 0) ?
WA_RPIPE_DEFAULT_TIMEOUT : ctrl_reqp->ctrl_timeout;
wr->wr_cb = wusb_wa_handle_ctrl;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_ctrl_wrapper: wr = 0x%p nseg = %d", (void *)wr,
wr->wr_nsegs);
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*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_trans_wrapper_t *
wusb_wa_create_bulk_wrapper(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags)
{
wusb_wa_trans_wrapper_t *wr = NULL;
usb_ep_descr_t *epdt = &ph->p_ep;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_bulk_wrapper: ph = 0x%p rp_hdl = 0x%p reqp = 0x%p",
(void *)ph, (void *)hdl, (void *)bulk_reqp);
wr = wusb_wa_alloc_tw(wa_data, hdl, ph, bulk_reqp->bulk_len,
usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_bulk_wrapper: fail to create tw for %p",
(void *)bulk_reqp);
return (NULL);
}
/* no locking needed */
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wr));
if ((epdt->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
wr->wr_dir = WA_DIR_IN;
} else {
wr->wr_dir = WA_DIR_OUT;
}
wr->wr_type = WA_XFER_REQ_TYPE_BULK_INTR;
wr->wr_reqp = (usb_opaque_t)bulk_reqp;
wr->wr_timeout = (bulk_reqp->bulk_timeout == 0) ?
WA_RPIPE_DEFAULT_TIMEOUT : bulk_reqp->bulk_timeout;
wr->wr_cb = wusb_wa_handle_bulk;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_bulk_wrapper: wr = 0x%p nseg = %d", (void *)wr,
wr->wr_nsegs);
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*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_trans_wrapper_t *
wusb_wa_create_intr_wrapper(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_intr_req_t *intr_reqp,
usb_flags_t usb_flags)
{
wusb_wa_trans_wrapper_t *wr;
usb_ep_descr_t *epdt = &ph->p_ep;
uint32_t tw_len;
usb_intr_req_t *curr_intr_reqp;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_intr_wrapper: ph = 0x%p rp_hdl = 0x%p reqp = 0x%p",
(void *)ph, (void *)hdl, (void *)intr_reqp);
if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
tw_len = (intr_reqp->intr_len) ? intr_reqp->intr_len :
ph->p_ep.wMaxPacketSize;
/* duplicate client's intr request */
curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
(usb_intr_req_t *)intr_reqp, tw_len, usb_flags);
if (curr_intr_reqp == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_intr_wrapper: fail to create reqp");
return (NULL);
}
} else { /* OUT */
tw_len = intr_reqp->intr_len;
curr_intr_reqp = intr_reqp;
}
wr = wusb_wa_alloc_tw(wa_data, hdl, ph, tw_len, usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_bulk_wrapper: fail to create tw for %p",
(void *)intr_reqp);
return (NULL);
}
/* no locking needed */
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wr));
if ((epdt->bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
wr->wr_dir = WA_DIR_IN;
} else {
wr->wr_dir = WA_DIR_OUT;
}
wr->wr_type = WA_XFER_REQ_TYPE_BULK_INTR;
wr->wr_reqp = (usb_opaque_t)curr_intr_reqp;
wr->wr_timeout = (intr_reqp->intr_timeout == 0) ?
WA_RPIPE_DEFAULT_TIMEOUT : intr_reqp->intr_timeout;
wr->wr_cb = wusb_wa_handle_intr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_create_intr_wrapper: wr = 0x%p nseg = %d", (void *)wr,
wr->wr_nsegs);
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*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(wusb_wa_trans_wrapper_t *wr, wusb_wa_seg_t *seg,
uint8_t len)
{
mblk_t *data = seg->seg_trans_reqp->bulk_data;
uint8_t *trans_req = data->b_wptr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_trans_req: wr = 0x%p len = %d segnum = 0x%x",
(void*)wr, len, seg->seg_num);
bzero(trans_req, len);
trans_req[0] = len;
trans_req[1] = wr->wr_type;
trans_req[2] = wr->wr_rp->rp_descr.wRPipeIndex;
trans_req[3] = wr->wr_rp->rp_descr.wRPipeIndex >> 8;
trans_req[4] = seg->seg_id; /* dwTransferID */
trans_req[5] = seg->seg_id >> 8;
trans_req[6] = seg->seg_id >> 16;
trans_req[7] = seg->seg_id >> 24;
trans_req[8] = seg->seg_len;
trans_req[9] = seg->seg_len >> 8;
trans_req[10] = seg->seg_len >> 16;
trans_req[11] = seg->seg_len >> 24;
trans_req[12] = seg->seg_num;
/*
* 8-byte setupdata only for the first segment of a ctrl
* transfer request
*/
if (wr->wr_type == WA_XFER_REQ_TYPE_CTRL) {
usb_ctrl_req_t *ctrl_req = (usb_ctrl_req_t *)wr->wr_reqp;
/* what is the unsecured flag for ? */
trans_req[13] = wr->wr_dir | WA_CTRL_SECRT_REGULAR;
if ((seg->seg_num & 0x7f) == 0) {
/* only send baSetupDate on the first segment */
trans_req[16] = ctrl_req->ctrl_bmRequestType;
trans_req[17] = ctrl_req->ctrl_bRequest;
trans_req[18] = ctrl_req->ctrl_wValue;
trans_req[19] = ctrl_req->ctrl_wValue >> 8;
trans_req[20] = ctrl_req->ctrl_wIndex;
trans_req[21] = ctrl_req->ctrl_wIndex >> 8;
trans_req[22] = ctrl_req->ctrl_wLength;
trans_req[23] = ctrl_req->ctrl_wLength >> 8;
}
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_trans_req: Ctrl segment = %02x",
seg->seg_num);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_trans_req: Ctrl Setup Data: "
"%02x %02x %02x %02x %02x %02x %02x %02x",
trans_req[16], trans_req[17], trans_req[18],
trans_req[19], trans_req[20], trans_req[21],
trans_req[22], trans_req[23]);
}
data->b_wptr += len;
}
/*
* 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(usb_pipe_handle_t ph, struct usb_bulk_req *req)
{
wusb_wa_seg_t *seg = (wusb_wa_seg_t *)req->bulk_client_private;
wusb_wa_trans_wrapper_t *wr = (wusb_wa_trans_wrapper_t *)seg->seg_wr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_trans_bulk_cb: ph=%p req=0x%p cr=%d", (void*)ph,
(void*)req, req->bulk_completion_reason);
mutex_enter(&wr->wr_rp->rp_mutex);
/* callback returned, this seg can be freed */
seg->seg_trans_req_state = 0;
cv_signal(&seg->seg_trans_cv);
mutex_exit(&wr->wr_rp->rp_mutex);
}
void
wusb_wa_trans_bulk_exc_cb(usb_pipe_handle_t ph, struct usb_bulk_req *req)
{
wusb_wa_seg_t *seg = (wusb_wa_seg_t *)req->bulk_client_private;
wusb_wa_trans_wrapper_t *wr = (wusb_wa_trans_wrapper_t *)seg->seg_wr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_trans_bulk_exc_cb: ph=%p req=0x%p cr=%d", (void *)ph,
(void *)req, req->bulk_completion_reason);
mutex_enter(&wr->wr_rp->rp_mutex);
/* callback returned, this seg can be freed */
seg->seg_trans_req_state = 0;
cv_signal(&seg->seg_trans_cv);
mutex_exit(&wr->wr_rp->rp_mutex);
}
void
wusb_wa_data_bulk_cb(usb_pipe_handle_t ph, struct usb_bulk_req *req)
{
wusb_wa_seg_t *seg = (wusb_wa_seg_t *)req->bulk_client_private;
wusb_wa_trans_wrapper_t *wr = (wusb_wa_trans_wrapper_t *)seg->seg_wr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_data_bulk_cb: ph=%p req=0x%p cr=%d", (void *)ph,
(void *)req, req->bulk_completion_reason);
mutex_enter(&wr->wr_rp->rp_mutex);
/* callback returned, this seg can be freed */
seg->seg_data_req_state = 0;
cv_signal(&seg->seg_data_cv);
mutex_exit(&wr->wr_rp->rp_mutex);
}
void
wusb_wa_data_bulk_exc_cb(usb_pipe_handle_t ph, struct usb_bulk_req *req)
{
wusb_wa_seg_t *seg = (wusb_wa_seg_t *)req->bulk_client_private;
wusb_wa_trans_wrapper_t *wr = (wusb_wa_trans_wrapper_t *)seg->seg_wr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_data_bulk_exc_cb: ph=%p req=0x%p cr=%d", (void *)ph,
(void *)req, req->bulk_completion_reason);
mutex_enter(&wr->wr_rp->rp_mutex);
/* callback returned, this seg can be freed */
seg->seg_data_req_state = 0;
cv_signal(&seg->seg_data_cv);
mutex_exit(&wr->wr_rp->rp_mutex);
}
/*
* 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
wusb_wa_setup_segs(wusb_wa_data_t *wa_data, wusb_wa_trans_wrapper_t *wr,
uint32_t len, mblk_t *data)
{
int i, rval;
wusb_wa_seg_t *seg;
usb_bulk_req_t *trans_req, *data_req;
uint8_t trans_req_len;
uint8_t *p;
wusb_wa_rpipe_hdl_t *hdl = NULL;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_segs: wr = 0x%p len = %d data = 0x%p", (void *)wr,
len, (void *)data);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wr));
if (wr == NULL) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_segs: invalid wr");
return (USB_INVALID_ARGS);
}
if ((len != 0) && (data != NULL)) {
p = data->b_rptr;
}
for (i = 0; i < wr->wr_nsegs; i++) {
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*seg));
seg = &wr->wr_seg_array[i];
cv_init(&seg->seg_trans_cv, NULL, CV_DRIVER, NULL);
cv_init(&seg->seg_data_cv, NULL, CV_DRIVER, NULL);
seg->seg_wr = wr;
seg->seg_num = (uint8_t)i; /* 0-based */
seg->seg_len = wr->wr_max_seglen;
if (i == (wr->wr_nsegs - 1)) {
seg->seg_num |= 0x80; /* last segment */
seg->seg_len = len;
} else {
len -= seg->seg_len;
}
/*
* set seg_id, all segs are the same or unique ??
* now make all segs share the same id
*/
seg->seg_id = wr->wr_id;
/* alloc transfer request and set values */
switch (wr->wr_type) {
case WA_XFER_REQ_TYPE_CTRL:
trans_req_len = WA_CTRL_REQ_LEN;
break;
case WA_XFER_REQ_TYPE_BULK_INTR:
trans_req_len = WA_BULK_INTR_REQ_LEN;
break;
default:
trans_req_len = 0;
break;
}
if (trans_req_len == 0) {
rval = USB_NOT_SUPPORTED;
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_segs: trans len error");
goto error;
}
/* alloc transfer request for the ith seg */
trans_req = usb_alloc_bulk_req(wa_data->wa_dip,
trans_req_len, USB_FLAGS_NOSLEEP);
if (trans_req == NULL) {
rval = USB_NO_RESOURCES;
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_segs: can't alloc_bulk_req");
goto error;
}
/* setup the ith transfer request */
trans_req->bulk_len = trans_req_len;
trans_req->bulk_timeout = WA_RPIPE_DEFAULT_TIMEOUT;
trans_req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
trans_req->bulk_cb = wusb_wa_trans_bulk_cb;
trans_req->bulk_exc_cb = wusb_wa_trans_bulk_exc_cb;
trans_req->bulk_client_private = (usb_opaque_t)seg;
seg->seg_trans_reqp = trans_req;
wusb_wa_setup_trans_req(wr, seg, trans_req_len);
if (seg->seg_len != 0) {
/* alloc request for data stage */
data_req = usb_alloc_bulk_req(wa_data->wa_dip,
seg->seg_len, USB_FLAGS_NOSLEEP);
if (data_req == NULL) {
rval = USB_NO_RESOURCES;
USB_DPRINTF_L2(DPRINT_MASK_WHCDI,
whcdi_log_handle,
"wusb_wa_setup_segs: can't alloc_bulk_req"
" for data");
goto error;
}
/* setup the ith data transfer */
data_req->bulk_len = seg->seg_len;
data_req->bulk_timeout = WA_RPIPE_DEFAULT_TIMEOUT;
data_req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
data_req->bulk_cb = wusb_wa_data_bulk_cb;
data_req->bulk_exc_cb = wusb_wa_data_bulk_exc_cb;
data_req->bulk_client_private = (usb_opaque_t)seg;
seg->seg_data_reqp = data_req;
/*
* Copy data from client driver to bulk request for
* an OUT endpoint.
*/
if (wr->wr_dir == WA_DIR_OUT) {
ASSERT(data != NULL);
/*
* cannot increase data->b_rptr,
* or scsa2usb panic at bulk out
*/
ASSERT((intptr_t)((uintptr_t)data->b_wptr -
(uintptr_t)p) >= seg->seg_len);
bcopy(p,
data_req->bulk_data->b_wptr,
seg->seg_len);
p += seg->seg_len;
data_req->bulk_data->b_wptr += seg->seg_len;
}
}
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*seg));
}
/* 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) {
hdl = wr->wr_rp;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_segs: timeout=%d", wr->wr_timeout);
mutex_enter(&hdl->rp_mutex);
/* Add this new wrapper to the head of RPipe's timeout list */
if (hdl->rp_timeout_list) {
wr->wr_timeout_next = hdl->rp_timeout_list;
}
hdl->rp_timeout_list = wr;
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*wr));
mutex_exit(&hdl->rp_mutex);
}
return (USB_SUCCESS);
error:
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_setup_segs: fail, rval = %d", rval);
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*wr));
mutex_enter(&hdl->rp_mutex);
wusb_wa_free_segs(wr);
mutex_exit(&hdl->rp_mutex);
return (rval);
}
/* allocate transfer wrapper and setup all transfer segments */
wusb_wa_trans_wrapper_t *
wusb_wa_alloc_ctrl_resources(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags)
{
wusb_wa_trans_wrapper_t *wr;
wr = wusb_wa_create_ctrl_wrapper(wa_data, hdl, ph, ctrl_reqp,
usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_ctrl_resources failed");
return (NULL);
}
if (wusb_wa_setup_segs(wa_data, wr, ctrl_reqp->ctrl_wLength,
ctrl_reqp->ctrl_data) != USB_SUCCESS) {
wusb_wa_free_trans_wrapper(wr);
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_ctrl_resources failed to setup segs");
return (NULL);
}
return (wr);
}
/* allocate transfer wrapper and setup all transfer segments */
wusb_wa_trans_wrapper_t *
wusb_wa_alloc_bulk_resources(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags)
{
wusb_wa_trans_wrapper_t *wr;
wr = wusb_wa_create_bulk_wrapper(wa_data, hdl, ph, bulk_reqp,
usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_bulk_resources: failed to create wr");
return (NULL);
}
if (wusb_wa_setup_segs(wa_data, wr, bulk_reqp->bulk_len,
bulk_reqp->bulk_data) != USB_SUCCESS) {
wusb_wa_free_trans_wrapper(wr);
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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_trans_wrapper_t *
wusb_wa_alloc_intr_resources(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_intr_req_t *intr_reqp,
usb_flags_t usb_flags)
{
wusb_wa_trans_wrapper_t *wr;
wr = wusb_wa_create_intr_wrapper(wa_data, hdl, ph, intr_reqp,
usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_intr_resources: failed to create wr");
return (NULL);
}
if (wusb_wa_setup_segs(wa_data, wr, intr_reqp->intr_len,
intr_reqp->intr_data) != USB_SUCCESS) {
wusb_wa_free_trans_wrapper(wr);
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_alloc_intr_resources: failed to setup segs");
return (NULL);
}
return (wr);
}
/* free the bulk request structures for all segments */
void
wusb_wa_free_segs(wusb_wa_trans_wrapper_t *wr)
{
int i;
wusb_wa_seg_t *seg;
ASSERT(mutex_owned(&wr->wr_rp->rp_mutex));
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_free_segs: wr = 0x%p, segs=%p", (void *)wr,
(void *)wr->wr_seg_array);
if (wr->wr_seg_array == NULL) {
return;
}
for (i = 0; i < wr->wr_nsegs; i++) {
seg = &wr->wr_seg_array[i];
if (seg->seg_trans_reqp != NULL) {
while (seg->seg_trans_req_state == 1) {
cv_wait(&seg->seg_trans_cv,
&wr->wr_rp->rp_mutex);
}
/* free the bulk req for transfer request */
usb_free_bulk_req(seg->seg_trans_reqp);
seg->seg_trans_reqp = NULL;
}
if (seg->seg_data_reqp != NULL) {
while (seg->seg_data_req_state == 1) {
cv_wait(&seg->seg_data_cv,
&wr->wr_rp->rp_mutex);
}
/* free the bulk req for data transfer */
usb_free_bulk_req(seg->seg_data_reqp);
seg->seg_data_reqp = NULL;
}
cv_destroy(&seg->seg_trans_cv);
cv_destroy(&seg->seg_data_cv);
}
kmem_free(wr->wr_seg_array, sizeof (wusb_wa_seg_t) * wr->wr_nsegs);
wr->wr_seg_array = NULL;
wr->wr_nsegs = 0;
}
/* free transfer wrapper */
void
wusb_wa_free_trans_wrapper(wusb_wa_trans_wrapper_t *wr)
{
wusb_wa_rpipe_hdl_t *hdl = NULL;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_free_trans_wrapper: wr = 0x%p", (void *)wr);
if (wr == NULL) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_free_trans_wrapper: NULL wrapper");
return;
}
hdl = wr->wr_rp;
mutex_enter(&hdl->rp_mutex);
wusb_wa_remove_wr_from_timeout_list(hdl, wr);
if (wr->wr_seg_array != NULL) {
wusb_wa_free_segs(wr);
kmem_free(wr->wr_seg_array,
sizeof (wusb_wa_seg_t) * wr->wr_nsegs);
}
if (wr->wr_id != 0) {
WA_FREE_ID(wr->wr_id);
}
cv_destroy(&wr->wr_cv);
kmem_free(wr, sizeof (wusb_wa_trans_wrapper_t));
mutex_exit(&hdl->rp_mutex);
}
/* abort a transfer, refer to WUSB 1.0/8.3.3.5 */
void
wusb_wa_abort_req(wusb_wa_data_t *wa_data, wusb_wa_trans_wrapper_t *wr,
uint32_t id)
{
usb_bulk_req_t *req;
uint8_t *p;
int rval;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_abort_req: wr = 0x%p", (void *)wr);
req = usb_alloc_bulk_req(wa_data->wa_dip, WA_ABORT_REQ_LEN,
USB_FLAGS_NOSLEEP);
if (req == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_abort_req: alloc bulk req failed");
return;
}
req->bulk_len = WA_ABORT_REQ_LEN;
req->bulk_timeout = WA_RPIPE_DEFAULT_TIMEOUT;
req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
p = req->bulk_data->b_wptr;
p[0] = WA_ABORT_REQ_LEN;
p[1] = WA_XFER_REQ_TYPE_ABORT;
p[2] = wr->wr_rp->rp_descr.wRPipeIndex;
p[3] = wr->wr_rp->rp_descr.wRPipeIndex >> 8;
p[4] = (uint8_t)id;
p[5] = (uint8_t)(id >> 8);
p[6] = (uint8_t)(id >> 16);
p[7] = (uint8_t)(id >> 24);
req->bulk_data->b_wptr += WA_ABORT_REQ_LEN;
mutex_exit(&wr->wr_rp->rp_mutex);
rval = usb_pipe_bulk_xfer(wa_data->wa_bulkout_ph, req,
USB_FLAGS_SLEEP);
mutex_enter(&wr->wr_rp->rp_mutex);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_abort_req: send abort req failed, rval = %d",
rval);
}
usb_free_bulk_req(req);
}
static void
wusb_wa_remove_wr_from_timeout_list(wusb_wa_rpipe_hdl_t *hdl,
wusb_wa_trans_wrapper_t *tw)
{
wusb_wa_trans_wrapper_t *prev, *next;
int ret = 0; /* debug only */
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"remove_wr_from_timeout_list: %p", (void *)tw);
if (hdl->rp_timeout_list) {
if (hdl->rp_timeout_list == tw) {
hdl->rp_timeout_list = tw->wr_timeout_next;
tw->wr_timeout_next = NULL;
ret = 1;
} else {
prev = hdl->rp_timeout_list;
next = prev->wr_timeout_next;
while (next && (next != tw)) {
prev = next;
next = next->wr_timeout_next;
}
if (next == tw) {
prev->wr_timeout_next = next->wr_timeout_next;
tw->wr_timeout_next = NULL;
ret = 1;
}
}
}
/* debug only */
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"remove_wr_from_timeout_list: %p, on the list:%d",
(void *)tw, ret);
}
/* start timer on a rpipe */
void
wusb_wa_start_xfer_timer(wusb_wa_rpipe_hdl_t *hdl)
{
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_start_xfer_timer: rpipe hdl = 0x%p", (void *)hdl);
ASSERT(mutex_owned(&hdl->rp_mutex));
/*
* 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.
*/
if ((!hdl->rp_timer_id) && (hdl->rp_timeout_list)) {
hdl->rp_timer_id = timeout(wusb_wa_xfer_timeout_handler,
(void *)hdl, drv_usectohz(1000000));
}
}
/* transfer timeout handler */
void
wusb_wa_xfer_timeout_handler(void *arg)
{
wusb_wa_rpipe_hdl_t *hdl = (wusb_wa_rpipe_hdl_t *)arg;
wusb_wa_trans_wrapper_t *wr = NULL;
wusb_wa_trans_wrapper_t *next = NULL;
wusb_wa_data_t *wa_data = NULL;
int rval;
uint8_t rp_status;
wusb_wa_trans_wrapper_t *expire_list = NULL;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_xfer_timeout_handler: rphdl = 0x%p ", (void *)hdl);
mutex_enter(&hdl->rp_mutex);
/*
* 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 {
mutex_exit(&hdl->rp_mutex);
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.
*/
wr = hdl->rp_timeout_list;
while (wr) {
next = wr->wr_timeout_next;
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_xfer_timeout_handler: rhdl=0x%p"
" wr=0x%p(to=%d) nxt=0x%p", (void *)hdl, (void *)wr,
wr->wr_timeout, (void *)next);
/*
* 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) {
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_xfer_timeout_handler: 0x%p time out",
(void *)wr);
/* remove it from the rpipe's timeout list */
wusb_wa_remove_wr_from_timeout_list(hdl, wr);
/* put it on the expired list */
wr->wr_timeout_next = expire_list;
expire_list = wr;
}
wr = next;
}
/* Restart this RPipe's timer */
wusb_wa_start_xfer_timer(hdl);
/* timeout handling */
wr = expire_list;
while (wr) {
next = wr->wr_timeout_next;
/* other thread shouldn't continue processing it */
wr->wr_state = WR_TIMEOUT;
wa_data = wr->wr_wa_data;
mutex_exit(&hdl->rp_mutex);
rval = wusb_wa_get_rpipe_status(wa_data->wa_dip,
wa_data->wa_default_pipe, hdl->rp_descr.wRPipeIndex,
&rp_status);
mutex_enter(&hdl->rp_mutex);
if (rval != USB_SUCCESS) {
/* reset WA perhaps? */
hdl->rp_state = WA_RPIPE_STATE_ERROR;
hdl->rp_curr_wr = NULL;
mutex_exit(&hdl->rp_mutex);
wr->wr_cb(wa_data, wr, USB_CR_TIMEOUT, 1);
mutex_enter(&hdl->rp_mutex);
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_xfer_timeout_handler: fail to get"
" rpipe status, rval = %d", rval);
goto continuing;
}
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_xfer_timeout_handler: rpstat=0x%02x, wr=0x%p,"
" wr_state=%d", rp_status, (void *)wr, wr->wr_state);
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.
*/
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_xfer_timeout_handler: rp not idle");
mutex_exit(&hdl->rp_mutex);
rval = wusb_wa_rpipe_abort(wa_data->wa_dip,
wa_data->wa_default_pipe, hdl);
mutex_enter(&hdl->rp_mutex);
USB_DPRINTF_L3(DPRINT_MASK_WHCDI,
whcdi_log_handle,
"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) &&
(cv_reltimedwait(&wr->wr_cv, &hdl->rp_mutex,
drv_usectohz(100 * 1000), TR_CLOCK_TICK)
>= 0)) {
/* 100ms, random number, long enough? */
/* the result thread has processed it */
goto continuing;
}
USB_DPRINTF_L3(DPRINT_MASK_WHCDI,
whcdi_log_handle,
"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.
*/
hdl->rp_state = WA_RPIPE_STATE_IDLE;
hdl->rp_curr_wr = NULL;
/* release this WR's occupied req */
hdl->rp_avail_reqs += (wr->wr_curr_seg - wr->wr_seg_done);
cv_signal(&hdl->rp_cv);
mutex_exit(&hdl->rp_mutex);
wr->wr_cb(wa_data, wr, USB_CR_TIMEOUT, 0);
mutex_enter(&hdl->rp_mutex);
continuing:
wr = next;
}
mutex_exit(&hdl->rp_mutex);
}
/* stop timer */
void
wusb_wa_stop_xfer_timer(wusb_wa_trans_wrapper_t *wr)
{
wusb_wa_rpipe_hdl_t *hdl = wr->wr_rp;
timeout_id_t timer_id;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_stop_xfer_timer: wr = 0x%p", (void *)wr);
ASSERT(mutex_owned(&hdl->rp_mutex));
if (hdl->rp_timer_id == 0) {
return;
}
timer_id = hdl->rp_timer_id;
hdl->rp_timer_id = 0;
mutex_exit(&hdl->rp_mutex);
(void) untimeout(timer_id);
mutex_enter(&hdl->rp_mutex);
}
/*
* send transfer request and data to the bulk out pipe
*
* General transfer function for WA transfer, see Section 8.3.3.
*/
/* ARGSUSED */
int
wusb_wa_wr_xfer(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
wusb_wa_trans_wrapper_t *wr, usb_flags_t usb_flags)
{
int i, rval;
uint8_t curr_seg;
usb_bulk_req_t *req;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_wr_xfer: wr = 0x%p", (void *)wr);
ASSERT(wr->wr_seg_array != NULL);
ASSERT(mutex_owned(&hdl->rp_mutex));
if (hdl->rp_state == WA_RPIPE_STATE_IDLE) {
hdl->rp_state = WA_RPIPE_STATE_ACTIVE;
hdl->rp_curr_wr = wr;
}
curr_seg = wr->wr_curr_seg;
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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.
*/
for (i = curr_seg; i < wr->wr_nsegs; i++) {
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_wr_xfer: wr=%p curr_seg = %d, avail_req = %d,"
" dir=%s", (void *)wr, curr_seg, hdl->rp_avail_reqs,
(wr->wr_dir == WA_DIR_IN)?"IN":"OUT");
/* waiting for available requests if wr is still good */
while ((hdl->rp_avail_reqs == 0) && (wr->wr_state == 0)) {
rval = cv_wait_sig(&hdl->rp_cv, &hdl->rp_mutex);
}
if ((wr->wr_curr_seg - wr->wr_seg_done) >= 1) {
/* send only one segment */
break;
}
if (wr->wr_state != 0) {
/* wr transfer error, don't continue */
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_wr_xfer: wr_state!=0(%d)", wr->wr_state);
break;
}
req = wr->wr_seg_array[i].seg_trans_reqp;
ASSERT(req != NULL);
mutex_exit(&hdl->rp_mutex);
/* send ith transfer request */
rval = usb_pipe_bulk_xfer(wa_data->wa_bulkout_ph, req, 0);
mutex_enter(&hdl->rp_mutex);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_wr_xfer: send transfer request %d failed,"
"rv=%d", i, rval);
wr->wr_seg_array[i].seg_trans_req_state = 0; /* clear */
if (i == 0) {
/* no xfer in processing */
hdl->rp_state = WA_RPIPE_STATE_IDLE;
hdl->rp_curr_wr = NULL;
return (rval);
}
wusb_wa_abort_req(wa_data, wr, wr->wr_id);
wr->wr_state = WR_SEG_REQ_ERR; /* sending tr error */
break;
}
wr->wr_seg_array[i].seg_trans_req_state = 1; /* submitted */
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_wr_xfer: seg(%d) request(0x%p) sent,"
" avail_req = %d", i, (void*)req, hdl->rp_avail_reqs);
hdl->rp_avail_reqs--;
/* Get data in the GetResult thread for IN eps */
if (wr->wr_dir == WA_DIR_IN) {
wr->wr_curr_seg++;
/* only send data for out request */
continue;
}
req = wr->wr_seg_array[i].seg_data_reqp;
if (req == NULL) {
/* no data stage */
wr->wr_curr_seg++;
continue;
}
wr->wr_seg_array[i].seg_data_req_state = 1; /* submitted */
mutex_exit(&hdl->rp_mutex);
/* send ith data asynchronously */
rval = usb_pipe_bulk_xfer(wa_data->wa_bulkout_ph, req, 0);
mutex_enter(&hdl->rp_mutex);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_wr_xfer: send transfer data %d failed",
i);
wr->wr_seg_array[i].seg_data_req_state = 0; /* clear */
wusb_wa_abort_req(wa_data, wr, wr->wr_id);
wr->wr_state = WR_SEG_DAT_ERR; /* sending data error */
/* not inc rp_avail_reqs until callback */
break;
}
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_wr_xfer: seg(%d) data(0x%p) sent, avail_req = %d",
i, (void*)req, hdl->rp_avail_reqs);
wr->wr_curr_seg++;
}
/* start timer */
wusb_wa_start_xfer_timer(hdl);
/*
* 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
wusb_wa_submit_ctrl_wr(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
wusb_wa_trans_wrapper_t *wr, usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags)
{
int rval;
mutex_enter(&hdl->rp_mutex);
switch (hdl->rp_state) {
case WA_RPIPE_STATE_IDLE:
rval = wusb_wa_wr_xfer(wa_data, hdl, wr, usb_flags);
break;
case WA_RPIPE_STATE_ACTIVE:
/* only allow one req at a time, this should not happen */
default:
rval = USB_PIPE_ERROR;
break;
}
mutex_exit(&hdl->rp_mutex);
if (rval != USB_SUCCESS) {
if (ctrl_reqp->ctrl_completion_reason == USB_CR_OK) {
ctrl_reqp->ctrl_completion_reason = usba_rval2cr(rval);
}
mutex_enter(&hdl->rp_mutex);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_submit_ctrl_wr:fail, reqp=0x%p, rpstat=%d, rv=%d",
(void*)ctrl_reqp, hdl->rp_state, rval);
mutex_exit(&hdl->rp_mutex);
wusb_wa_free_trans_wrapper(wr);
}
/* 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
wusb_wa_ctrl_xfer(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_ctrl_req_t *ctrl_reqp,
usb_flags_t usb_flags)
{
int rval;
wusb_wa_trans_wrapper_t *wr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_ctrl_xfer: ph = 0x%p reqp = 0x%p",
(void*)ph, (void*)ctrl_reqp);
wr = wusb_wa_alloc_ctrl_resources(wa_data, hdl, ph, ctrl_reqp,
usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_ctrl_req: alloc ctrl resource failed");
return (USB_NO_RESOURCES);
}
rval = wusb_wa_submit_ctrl_wr(wa_data, hdl, wr, ctrl_reqp, usb_flags);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_submit_bulk_wr(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
wusb_wa_trans_wrapper_t *wr, usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags)
{
int rval;
mutex_enter(&hdl->rp_mutex);
switch (hdl->rp_state) {
case WA_RPIPE_STATE_IDLE:
rval = wusb_wa_wr_xfer(wa_data, hdl, wr, usb_flags);
break;
case WA_RPIPE_STATE_ACTIVE:
/* only allow one req at a time, this should not happen */
default:
rval = USB_PIPE_ERROR;
break;
}
mutex_exit(&hdl->rp_mutex);
if (rval != USB_SUCCESS) {
if (bulk_reqp->bulk_completion_reason == USB_CR_OK) {
bulk_reqp->bulk_completion_reason = usba_rval2cr(rval);
}
wusb_wa_free_trans_wrapper(wr);
}
/* In other cases, wr will be freed in callback */
return (rval);
}
/*
* WA general bulk transfer
* - allocate bulk resources
* - submit the bulk request
*/
int
wusb_wa_bulk_xfer(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_bulk_req_t *bulk_reqp,
usb_flags_t usb_flags)
{
int rval;
wusb_wa_trans_wrapper_t *wr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_bulk_xfer: ph = 0x%p reqp = 0x%p",
(void *)ph, (void *)bulk_reqp);
wr = wusb_wa_alloc_bulk_resources(wa_data, hdl, ph, bulk_reqp,
usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_bulk_xfer: alloc bulk resource failed");
return (USB_NO_RESOURCES);
}
rval = wusb_wa_submit_bulk_wr(wa_data, hdl, wr, bulk_reqp,
usb_flags);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_submit_intr_wr(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
wusb_wa_trans_wrapper_t *wr, usb_intr_req_t *intr_reqp,
usb_flags_t usb_flags)
{
int rval;
mutex_enter(&hdl->rp_mutex);
switch (hdl->rp_state) {
case WA_RPIPE_STATE_IDLE:
rval = wusb_wa_wr_xfer(wa_data, hdl, wr, usb_flags);
break;
case WA_RPIPE_STATE_ACTIVE:
/* only allow one req at a time, this should not happen */
default:
rval = USB_PIPE_ERROR;
break;
}
mutex_exit(&hdl->rp_mutex);
if (rval != USB_SUCCESS) {
if (intr_reqp->intr_completion_reason == USB_CR_OK) {
intr_reqp->intr_completion_reason = usba_rval2cr(rval);
}
wusb_wa_free_trans_wrapper(wr);
}
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_intr_xfer(wusb_wa_data_t *wa_data, wusb_wa_rpipe_hdl_t *hdl,
usba_pipe_handle_data_t *ph, usb_intr_req_t *intr_reqp,
usb_flags_t usb_flags)
{
int rval;
wusb_wa_trans_wrapper_t *wr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_intr_xfer: ph = 0x%p reqp = 0x%p",
(void *)ph, (void *)intr_reqp);
wr = wusb_wa_alloc_intr_resources(wa_data, hdl, ph, intr_reqp,
usb_flags);
if (wr == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_intr_req: alloc intr resource failed");
return (USB_NO_RESOURCES);
}
rval = wusb_wa_submit_intr_wr(wa_data, hdl, wr, intr_reqp,
usb_flags);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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.
*/
if ((ph->p_ep.bEndpointAddress & USB_EP_DIR_MASK) == USB_EP_DIR_IN) {
mutex_enter(&ph->p_mutex);
/*
* 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++;
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_intr_req: p_req_cnt = %d", ph->p_req_count);
mutex_exit(&ph->p_mutex);
}
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
wusb_wa_get_data(wusb_wa_data_t *wa_data, wusb_wa_seg_t *seg, uint32_t len)
{
usb_bulk_req_t *req;
int rval;
if (len == 0) {
return (USB_SUCCESS);
}
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_data: get data for wr: 0x%p", (void *)seg->seg_wr);
req = seg->seg_data_reqp;
ASSERT(req != NULL);
/* adjust bulk in length to actual length */
req->bulk_len = len;
rval = usb_pipe_bulk_xfer(wa_data->wa_bulkin_ph, req,
USB_FLAGS_SLEEP);
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.
*/
wusb_wa_trans_wrapper_t *
wusb_wa_retrieve_wr(wusb_wa_data_t *wa_data, uint32_t id)
{
wusb_wa_rpipe_hdl_t *rph;
uint16_t i;
wusb_wa_trans_wrapper_t *tw;
for (i = 0; i < wa_data->wa_num_rpipes; i++) {
rph = &wa_data->wa_rpipe_hdl[i];
mutex_enter(&rph->rp_mutex);
/* all outstanding TWs are put on the timeout list */
tw = rph->rp_timeout_list;
while (tw) {
if (tw->wr_id == id) {
mutex_exit(&rph->rp_mutex);
return (tw);
}
tw = tw->wr_timeout_next;
}
mutex_exit(&rph->rp_mutex);
}
return (NULL);
}
/* endlessly wait for transfer result on bulk-in ept and handle the result */
int
wusb_wa_get_xfer_result(wusb_wa_data_t *wa_data)
{
usb_bulk_req_t *req;
int rval;
mblk_t *data;
uint8_t *p;
wa_xfer_result_t result;
wusb_wa_trans_wrapper_t *wr;
wusb_wa_seg_t *seg;
uint8_t status;
uint_t len;
uint8_t lastseg = 0;
usb_cr_t cr;
uint32_t act_len;
wusb_wa_rpipe_hdl_t *hdl;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: started, wa=0x%p", (void*)wa_data);
/* grab lock before accessing wa_data */
mutex_enter(&wa_data->wa_mutex);
len = wa_data->wa_bulkin_ept.wMaxPacketSize;
req = usb_alloc_bulk_req(wa_data->wa_dip, len,
USB_FLAGS_NOSLEEP);
if (req == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: alloc bulk req failed");
mutex_exit(&wa_data->wa_mutex);
return (USB_NO_RESOURCES);
}
req->bulk_len = len;
req->bulk_timeout = 0;
req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK |
USB_ATTRS_AUTOCLEARING;
mutex_exit(&wa_data->wa_mutex);
/* Get the Transfer Result head, see Table 8-14 */
rval = usb_pipe_bulk_xfer(wa_data->wa_bulkin_ph, req,
USB_FLAGS_SLEEP);
if ((rval != USB_SUCCESS) || (req->bulk_data == NULL)) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: bulk xfer failed or "
"null data returned, rval=%d, req->bulk_data = %p",
rval, (void*)req->bulk_data);
usb_free_bulk_req(req);
return (rval);
}
data = req->bulk_data;
p = data->b_rptr;
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: received data len = %d",
(int)MBLKL(data));
if ((MBLKL(data) != WA_XFER_RESULT_LEN) ||
(p[1] != WA_RESULT_TYPE_TRANSFER)) {
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: invalid xfer result, "
"len = %d, p0 = 0x%x, p1 = 0x%x, p6 = 0x%x",
(int)MBLKL(data), p[0], p[1], p[6]);
usb_free_bulk_req(req);
return (USB_SUCCESS); /* don't stop this thread */
}
/* Transfer result. Section 8.3.3.4 */
(void) usb_parse_data("ccllccl", p, WA_XFER_RESULT_LEN, &result,
sizeof (wa_xfer_result_t));
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: id = 0x%x len = 0x%x nseg = 0x%02x"
" status = 0x%02x(0x%02x)", result.dwTransferID,
result.dwTransferLength, result.bTransferSegment,
result.bTransferStatus, p[11]&0x0f);
req->bulk_data = NULL; /* don't free it. we still need it */
usb_free_bulk_req(req);
status = result.bTransferStatus;
if ((status & 0x3f) == WA_STS_NOT_FOUND) {
freemsg(data);
/*
* The result is just ignored since the transfer request
* has completed
*/
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: TransferID not found");
return (USB_SUCCESS);
}
mutex_enter(&wa_data->wa_mutex);
wr = wusb_wa_retrieve_wr(wa_data, result.dwTransferID);
if ((wr == NULL)) {
/* this id's corresponding WR may have been freed by timeout handler */
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: wr == deadbeef or NULL");
mutex_exit(&wa_data->wa_mutex);
freemsg(data);
return (USB_SUCCESS);
}
/* bit 7 is last segment flag */
if ((result.bTransferSegment & 0x7f) >= wr->wr_nsegs) {
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: error - "
" bTransferSegment(%d) > segment coutnts(%d)",
(result.bTransferSegment & 0x7f), wr->wr_nsegs);
goto err;
}
lastseg = result.bTransferSegment & 0x80;
hdl = wr->wr_rp;
mutex_enter(&hdl->rp_mutex);
seg = &wr->wr_seg_array[result.bTransferSegment & 0x7f];
seg->seg_status = result.bTransferStatus;
act_len = seg->seg_actual_len = result.dwTransferLength;
/*
* if this is the last segment, we should not continue.
* IMPT: we expect the WA deliver result sequentially.
*/
seg->seg_done = (result.bTransferSegment) & 0x80;
wr->wr_seg_done++;
hdl->rp_avail_reqs++;
USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: wr = %p, rp=%p, avail_req=%d", (void*)wr,
(void*)wr->wr_rp, hdl->rp_avail_reqs);
cv_broadcast(&hdl->rp_cv);
if (status & 0x40) {
status = 0; /* ignore warning, see Tab8-15 */
}
seg->seg_status = status;
/* Error bit set */
if (status & 0x80) {
/* don't change timeout error */
if (wr->wr_state != WR_TIMEOUT) {
wr->wr_state = WR_XFER_ERR;
}
/*
* The timeout handler is waiting, but the result thread will
* process this wr.
*/
if ((wr->wr_state == WR_TIMEOUT) &&
(status & 0x3F) == WA_STS_ABORTED) {
wr->wr_has_aborted = 1;
cv_signal(&wr->wr_cv); /* to inform timeout hdler */
}
mutex_exit(&hdl->rp_mutex);
/* seg error, don't proceed with this WR */
goto err;
}
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: status = 0x%02x dir=%s",
status, (wr->wr_dir == WA_DIR_IN)?"IN":"OUT");
/*
* 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.
*/
if ((wr->wr_dir == WA_DIR_IN) &&
(act_len > 0) &&
((status & 0x3F) == 0)) { /* if segment error, don't read */
/* receive data */
mutex_exit(&hdl->rp_mutex);
mutex_exit(&wa_data->wa_mutex);
rval = wusb_wa_get_data(wa_data, seg, act_len);
mutex_enter(&wa_data->wa_mutex);
mutex_enter(&hdl->rp_mutex);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: can't get seg data:%d",
rval);
mutex_exit(&hdl->rp_mutex);
goto err;
}
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: get (%dB) data for IN ep",
act_len);
}
mutex_exit(&hdl->rp_mutex);
mutex_exit(&wa_data->wa_mutex);
/* check if the whole transfer has completed */
wusb_wa_check_req_done(wa_data, wr, lastseg);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: ended");
freemsg(data);
return (USB_SUCCESS);
err:
mutex_exit(&wa_data->wa_mutex);
mutex_enter(&hdl->rp_mutex);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: segment(%02x) error, abort wr 0x%p,"
"wr_state=%d", result.bTransferSegment, (void*)wr, wr->wr_state);
/* if it's timeout, just return the TIMEOUT error */
if (wr->wr_state == WR_TIMEOUT) {
cr = USB_CR_TIMEOUT;
} else {
cr = wusb_wa_sts2cr(status);
}
mutex_exit(&hdl->rp_mutex);
wusb_wa_handle_error(wa_data, wr, cr);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_get_xfer_result: error end, cr=%d",
cr);
freemsg(data);
return (USB_SUCCESS);
}
static void
wusb_wa_handle_error(wusb_wa_data_t *wa_data, wusb_wa_trans_wrapper_t *wr,
usb_cr_t cr)
{
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_handle_error: start");
mutex_enter(&wr->wr_rp->rp_mutex);
if (wr->wr_seg_done != wr->wr_curr_seg) {
/* still segments pending, abort them */
USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_handle_error: segment err, abort other segs");
wusb_wa_abort_req(wa_data, wr, wr->wr_id);
}
wusb_wa_stop_xfer_timer(wr);
wr->wr_rp->rp_state = WA_RPIPE_STATE_IDLE;
wr->wr_rp->rp_curr_wr = NULL;
mutex_exit(&wr->wr_rp->rp_mutex);
wr->wr_cb(wa_data, wr, cr, 1);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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
wusb_wa_check_req_done(wusb_wa_data_t *wa_data,
wusb_wa_trans_wrapper_t *wr, uint8_t lastseg)
{
wusb_wa_rpipe_hdl_t *hdl = wr->wr_rp;
wusb_wa_seg_t *seg;
int i, rval;
usb_cr_t cr;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_check_req_done: wr = 0x%p, lastseg=%02x",
(void*)wr, lastseg);
mutex_enter(&hdl->rp_mutex);
/* not done: submitted segs not finished and lastseg not set */
if ((wr->wr_seg_done != wr->wr_curr_seg) && (!lastseg)) {
mutex_exit(&hdl->rp_mutex);
return;
}
if (wr->wr_state != 0) { /* abort somewhere */
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_check_req_done: tw(%p) aborted somewhere",
(void*)wr);
cr = USB_CR_UNSPECIFIED_ERR;
goto reset;
}
/* check if there is any error */
for (i = 0; i < wr->wr_curr_seg; i++) {
seg = &wr->wr_seg_array[i];
if (seg->seg_status != WA_STS_SUCCESS) {
/* what about short xfer? need to fix */
cr = wusb_wa_sts2cr(seg->seg_status);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_check_req_done: seg fail, status=%02x",
seg->seg_status);
goto reset;
}
if (seg->seg_done == 0x80) {
/* device has told this is the last segment, we're done */
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_check_req_done: last seg");
goto done;
}
}
/* check if current request has completed */
/*
* Transfer another segment.
*
*/
if (wr->wr_curr_seg < wr->wr_nsegs) {
/* send the remained segments */
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_check_req_done: req not completed, restart");
rval = wusb_wa_wr_xfer(wa_data, hdl, wr, wr->wr_flags);
if (rval != USB_SUCCESS) {
cr = usba_rval2cr(rval);
goto reset;
}
mutex_exit(&hdl->rp_mutex);
return;
}
done:
wusb_wa_stop_xfer_timer(wr);
/* release the occupied requests */
hdl->rp_avail_reqs += (wr->wr_curr_seg - wr->wr_seg_done);
cv_signal(&hdl->rp_cv);
hdl->rp_state = WA_RPIPE_STATE_IDLE;
hdl->rp_curr_wr = NULL;
wr->wr_state = WR_FINISHED;
mutex_exit(&hdl->rp_mutex);
wr->wr_cb(wa_data, wr, USB_CR_OK, 0);
/* Need to move on to next request? usba will do this */
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_check_req_done: ended");
return;
reset:
wusb_wa_stop_xfer_timer(wr);
/* not necessary to reset the RPipe */
hdl->rp_state = WA_RPIPE_STATE_IDLE;
hdl->rp_curr_wr = NULL;
hdl->rp_avail_reqs += (wr->wr_curr_seg - wr->wr_seg_done);
cv_signal(&hdl->rp_cv);
/* if it's timeout, just return the TIMEOUT error */
if (wr->wr_state == WR_TIMEOUT)
cr = USB_CR_TIMEOUT;
mutex_exit(&hdl->rp_mutex);
wr->wr_cb(wa_data, wr, cr, 1);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_check_req_done: reset end");
}
/*
* callback for ctrl transfer
*
* reset_flag: not support yet
*/
void
wusb_wa_handle_ctrl(wusb_wa_data_t *wa_data, wusb_wa_trans_wrapper_t *wr,
usb_cr_t cr, uint_t reset_flag)
{
usb_ctrl_req_t *req;
usb_bulk_req_t *bulk_req;
mblk_t *data, *bulk_data;
int i;
size_t len;
wusb_wa_seg_t *seg;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_handle_ctrl: wr = 0x%p, cr = 0x%x, flag=%d",
(void*)wr, cr, reset_flag);
req = (usb_ctrl_req_t *)wr->wr_reqp;
if ((wr->wr_dir == WA_DIR_OUT) || (cr != USB_CR_OK)) {
/* do callback */
wusb_wa_callback(wa_data, wr->wr_ph, wr, cr);
return;
}
mutex_enter(&wr->wr_rp->rp_mutex);
data = req->ctrl_data;
for (i = 0; i < wr->wr_nsegs; i++) {
seg = &wr->wr_seg_array[i];
/* copy received data to original req buffer */
bulk_req = (usb_bulk_req_t *)
wr->wr_seg_array[i].seg_data_reqp;
bulk_data = bulk_req->bulk_data;
len = MBLKL(bulk_data);
bcopy(bulk_data->b_rptr, data->b_wptr, len);
data->b_wptr += len;
if (len < wr->wr_seg_array[i].seg_len) {
/* short xfer */
break;
}
if (seg->seg_done == 0x80) {
/* last segment, finish */
break;
}
}
mutex_exit(&wr->wr_rp->rp_mutex);
/* do callback */
wusb_wa_callback(wa_data, wr->wr_ph, wr, cr);
}
/*
* callback for bulk transfer
*
* reset_flag: not support yet
*/
void
wusb_wa_handle_bulk(wusb_wa_data_t *wa_data, wusb_wa_trans_wrapper_t *wr,
usb_cr_t cr, uint_t reset_flag)
{
usb_bulk_req_t *req;
usb_bulk_req_t *bulk_req;
mblk_t *data, *bulk_data;
int i;
size_t len;
wusb_wa_seg_t *seg;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_handle_bulk: wr = 0x%p, cr = 0x%x, flag=%d",
(void*)wr, cr, reset_flag);
req = (usb_bulk_req_t *)wr->wr_reqp;
if ((wr->wr_dir == WA_DIR_OUT) || (cr != USB_CR_OK)) {
/* do callback */
wusb_wa_callback(wa_data, wr->wr_ph, wr, cr);
return;
}
mutex_enter(&wr->wr_rp->rp_mutex);
data = req->bulk_data;
for (i = 0; i < wr->wr_nsegs; i++) {
seg = &wr->wr_seg_array[i];
/* copy received data to original req buffer */
bulk_req = (usb_bulk_req_t *)
wr->wr_seg_array[i].seg_data_reqp;
bulk_data = bulk_req->bulk_data;
len = MBLKL(bulk_data);
bcopy(bulk_data->b_rptr, data->b_wptr, len);
data->b_wptr += len;
if (len < wr->wr_seg_array[i].seg_len) {
/* short xfer */
break;
}
if (seg->seg_done == 0x80) {
/* last segment, finish */
break;
}
}
mutex_exit(&wr->wr_rp->rp_mutex);
/* do callback */
wusb_wa_callback(wa_data, wr->wr_ph, wr, cr);
}
int
wa_submit_periodic_req(wusb_wa_data_t *wa_data, usba_pipe_handle_data_t *ph)
{
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wa_submit_periodic_req: wa_data=0x%p, ph=0x%p",
(void*)wa_data, (void*)ph);
return (wa_data->pipe_periodic_req(wa_data, ph));
}
/*
* callback for intr transfer
*
* reset_flag: not support yet
*/
void
wusb_wa_handle_intr(wusb_wa_data_t *wa_data, wusb_wa_trans_wrapper_t *wr,
usb_cr_t cr, uint_t reset_flag)
{
usb_intr_req_t *req;
usb_req_attrs_t attrs;
usba_pipe_handle_data_t *ph = wr->wr_ph;
usb_bulk_req_t *bulk_req;
mblk_t *data, *bulk_data;
int i;
size_t len;
int rval;
wusb_wa_seg_t *seg;
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_handle_intr: wr = 0x%p, cr = 0x%x, flag=%d",
(void*)wr, cr, reset_flag);
req = (usb_intr_req_t *)wr->wr_reqp;
attrs = req->intr_attributes;
if ((wr->wr_dir == WA_DIR_OUT) || (cr != USB_CR_OK)) {
/* do callback */
wusb_wa_callback(wa_data, wr->wr_ph, wr, cr);
return;
}
mutex_enter(&wr->wr_rp->rp_mutex);
/* copy data to client's buffer */
data = req->intr_data;
for (i = 0; i < wr->wr_nsegs; i++) {
seg = &wr->wr_seg_array[i];
/* copy received data to original req buffer */
bulk_req = (usb_bulk_req_t *)
wr->wr_seg_array[i].seg_data_reqp;
bulk_data = bulk_req->bulk_data;
len = MBLKL(bulk_data);
bcopy(bulk_data->b_rptr, data->b_wptr, len);
data->b_wptr += len;
if (len < wr->wr_seg_array[i].seg_len) {
/* short xfer */
break;
}
if (seg->seg_done & 0x80) {
break;
}
}
if (attrs & USB_ATTRS_ONE_XFER) {
/* client requires ONE_XFER request, return */
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_handle_intr: ONE_XFER set");
mutex_exit(&wr->wr_rp->rp_mutex);
goto finish;
}
/* polling mode */
mutex_exit(&wr->wr_rp->rp_mutex);
rval = wa_submit_periodic_req(wa_data, ph);
if (rval != USB_SUCCESS) {
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_handle_intr: polling, fail to resubmit req");
goto finish;
}
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_handle_intr: polling, resubmit request, rv=%d", rval);
finish:
/* do callback */
wusb_wa_callback(wa_data, wr->wr_ph, wr, cr);
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"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(wusb_wa_data_t *wa_data, usba_pipe_handle_data_t *ph,
wusb_wa_trans_wrapper_t *wr, usb_cr_t cr)
{
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_callback: wr=0x%p, cr=0x%x, ph=0x%p, req = 0x%p",
(void*)wr, cr, (void*)ph, (void*)((wr == NULL)?0:wr->wr_reqp));
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.
*/
mutex_enter(&wr->wr_rp->rp_mutex);
wr->wr_rp->rp_state = WA_RPIPE_STATE_ERROR;
mutex_exit(&wr->wr_rp->rp_mutex);
}
wa_data->rpipe_xfer_cb(wa_data->wa_dip, ph, wr, cr);
/*
* need to consider carefully when to free wrapper
* if the rpipe is reset, what to do with current wr in processing?
*/
USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
"wusb_wa_callback: hwahc callback finish for wr= 0x%p, free it",
(void*)wr);
wusb_wa_free_trans_wrapper(wr);
}
static struct {
uint8_t status;
usb_cr_t cr;
} sts2cr[] = {
{WA_STS_SUCCESS, USB_CR_OK},
{WA_STS_HALTED, USB_CR_STALL},
{WA_STS_DATA_BUFFER_ERROR, USB_CR_DATA_OVERRUN},
{WA_STS_BABBLE, USB_CR_DATA_UNDERRUN},
{WA_STS_NOT_FOUND, USB_CR_NOT_ACCESSED},
{WA_STS_INSUFFICIENT_RESOURCE, USB_CR_NO_RESOURCES},
{0x80 | WA_STS_TRANSACTION_ERROR, USB_CR_STALL},
{0x40 | WA_STS_TRANSACTION_ERROR, USB_CR_OK},
{WA_STS_ABORTED, USB_CR_FLUSHED},
{WA_STS_RPIPE_NOT_READY, USB_CR_DEV_NOT_RESP},
{WA_STS_INVALID_REQ_FORMAT, USB_CR_CRC},
{WA_STS_UNEXPECTED_SEGMENT_NUM, USB_CR_UNEXP_PID},
{WA_STS_RPIPE_TYPE_MISMATCH, USB_CR_NOT_SUPPORTED},
{WA_STS_PACKET_DISCARDED, USB_CR_PID_CHECKFAILURE},
{0xff, 0} /* end */
};
/* translate transfer status to USB completion reason */
usb_cr_t
wusb_wa_sts2cr(uint8_t rawstatus)
{
int i;
uint8_t status;
/* cares about bits5:0 in WUSB 1.0 */
if ((rawstatus & 0x1f) == WA_STS_TRANSACTION_ERROR) {
status = rawstatus;
} else {
status = rawstatus & 0x1f;
}
for (i = 0; sts2cr[i].status != 0xff; i++) {
if (sts2cr[i].status == status) {
return (sts2cr[i].cr);
}
}
return (USB_CR_UNSPECIFIED_ERR);
}