/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* This file implements USB Mass Storage Class
*/
/*
* Function Prototypes
*/
static int scsa2usb_handle_cbi_status(usb_intr_req_t *);
static void scsa2usb_cbi_reset_recovery(scsa2usb_state_t *);
/* extern functions */
extern int scsa2usb_handle_data_start(scsa2usb_state_t *,
scsa2usb_cmd_t *, usb_bulk_req_t *);
usb_bulk_req_t *);
usb_pipe_handle_t, char *);
extern void scsa2usb_close_usb_pipes(scsa2usb_state_t *);
#ifdef DEBUG /* debugging information */
#endif /* DEBUG */
/*
* scsa2usb_cbi_transport:
* a) Issues command to the device over control pipe.
* b) Start Data Phase if applicable
* c) Start Status Phase
*
* returns TRAN_* values and not USB_SUCCESS/FAILURE
*
* scsa2usb_cbi_transport() handles the normal transitions or
* continuation after clearing stalls or error recovery.
*
* Command Phase:
* prepare a valid command and transport it on default pipe
* if error on default-pipe:
* set pkt_reason to CMD_TRAN_ERR
* new pkt state is SCSA2USB_PKT_DO_COMP
* do reset recovery synchronously
* else
* proceed to data phase
*
* Data Phase:
* if data in:
* setup data in on bulkin
* else if data out:
* setup data out on bulkout
*
* data: (in)
* copy data transferred so far, no more data to transfer
*
* if stall on bulkin pipe
* terminate data transfers, set cmd_done
* clear stall on bulkin syncrhonously
* else if other exception
* set pkt_reason to CMD_TRAN_ERR
* new pkt state is SCSA2USB_PKT_DO_COMP
* do reset recovery synchronously
* else (no error)
* receive status
*
* data: (out)
* if stall on bulkout pipe
* terminate data transfers
* clear stall on bulkout synchronously USBA
* else if other exception
* set pkt_reason to CMD_TRAN_ERR
* new pkt state is SCSA2USB_PKT_DO_COMP
* do reset recovery synchronously
* else (no error)
* receive status
*
* Status Phase: (on Interrupt pipe for CBI devices only)
* if error
* if stall
* new pkt state is SCSA2USB_PKT_DO_COMP
* clear stall on interrupt pipe
* else
* set pkt_reason to CMD_TRAN_ERR
* new pkt state is SCSA2USB_PKT_DO_COMP
* do reset recovery synchronously
* else (no error)
* goto read status
*
* read status:
* if not OK or phase error
* new pkt state is SCSA2USB_PKT_DO_COMP
* set pkt reason CMD_TRAN_ERR
* reset recovery synchronously
* else (status ok)
* goto SCSA2USB_PKT_DO_COMP
*
* The reset recovery walks sequentially thru device reset, clearing
* stalls and pipe resets. When the reset recovery completes we return
* to the taskq thread.
*
* Clearing stalls clears the stall condition, resets the pipe, and
* then returns to the transport.
*/
int
{
"scsa2usb_cbi_transport: cmd = 0x%p", (void *)cmd);
if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) {
return (TRAN_FATAL_ERROR);
}
/*
* Start command phase (C - in CBI)
*/
/* Initialize the data */
for (i = 0; i < CBI_CLASS_CMD_LEN; i++) {
}
/* Send the Command to the device */
CBI_REQUEST_TYPE, /* bmRequestType */
0, /* bRequest */
CBI_WVALUE, /* wValue */
CBI_CLASS_CMD_LEN, /* wLength */
&data, /* data */
USB_ATTRS_PIPE_RESET, /* attributes */
"scsa2usb_cbi_transport: sent cmd = 0x%x rval = %d",
if (rval != USB_SUCCESS) {
return (TRAN_FATAL_ERROR);
}
/*
* Xferred command to the device.
* Start data phase (B - in CBI)
*/
/*
* we've not transferred any data yet; updated in
* scsa2usb_handle_data_done
*/
cmd->cmd_resid_xfercount = 0;
/* if data to be xferred ? */
if (cmd->cmd_xfercount) {
/* Initialize a bulk_req_t */
data_req);
/* handle data returned */
data_req);
if (rval != USB_SUCCESS) {
/*
* we ran into an error and it wasn't a STALL
*/
if (scsa2usbp->scsa2usb_cur_pkt) {
}
} else {
/* get rid of req */
return (TRAN_FATAL_ERROR);
}
}
}
/* CB devices don't do status over interrupt pipe */
if (SCSA2USB_IS_CB(scsa2usbp)) {
"scsa2usb_cbi_transport: CB done rval = %d", rval);
goto end_it;
}
/*
* Start status phase (I - in CBI)
*/
/* Get Status over interrupt pipe */
return (TRAN_FATAL_ERROR); /* lack of better return code */
}
/* stop interrupt pipe polling (CBI only) */
if (SCSA2USB_IS_CBI(scsa2usbp)) {
}
goto Cmd_Phase;
} else {
if (SCSA2USB_IS_CB(scsa2usbp)) {
}
}
}
/*
* scsa2usb_cbi_handle_error:
* handle errors from transport that are not STALL conditions
*/
static void
{
/* do reset error recovery */
switch (cr) {
case USB_CR_STALL:
if (pkt) {
}
break;
case USB_CR_TIMEOUT:
if (pkt) {
}
break;
case USB_CR_DEV_NOT_RESP:
if (pkt) {
/* scsi_poll relies on this */
}
break;
default:
if (pkt) {
}
}
}
/*
* scsa2usb_cbi_start_intr_polling:
* start polling asynchronously without notification
*/
static usb_intr_req_t *
{
int rval;
"scsa2usb_cbi_start_intr_polling:");
if (!SCSA2USB_IS_CBI(scsa2usbp)) {
return (NULL);
}
USB_FLAGS_SLEEP)) != USB_SUCCESS) {
"polling failed rval: %d", rval);
/* clear stall */
(void) scsa2usb_clear_ept_stall(scsa2usbp,
}
/* handle other errors here */
}
if (pipe_state != USB_PIPE_STATE_ACTIVE) {
}
return (req);
}
/*
* scsa2usb_cbi_stop_intr_polling:
* Stop polling on interrupt pipe (for status)
*/
void
{
"scsa2usb_cbi_stop_intr_polling:");
if (!SCSA2USB_IS_CBI(scsa2usbp)) {
return;
}
if (scsa2usbp->scsa2usb_intr_pipe) {
}
}
/*
* scsa2usb_handle_cbi_status:
* Handle CBI status results
*/
static int
{
int status;
char *msg;
"scsa2usb_handle_cbi_status: req: 0x%p", (void *)req);
/*
* CBI status contains ASC and ASCQ.
* SCMD_REQUEST_SENSE and SCMD_INQUIRY don't affect the sense data
* on CBI devices. So, we can ignore that info for these 2 commands.
*
* (See details in UFI spec section 3.5 - that says that INQUIRY,
* SEND_DIAG, and REQUEST_SENSE ought to be supported by any deivce
* irrespective).
*/
"scsa2usb_handle_cbi_status: CBI STATUS = (0x%x, 0x%x)",
return (USB_SUCCESS);
}
switch (status) {
case CBI_STATUS_PASS:
msg = "PASSED";
/* non-zero command completion interrupt */
}
break;
case CBI_STATUS_FAILED:
"PERSISTENT_FAILURE" : "FAILED";
break;
case CBI_STATUS_PHASE_ERR:
msg = "PHASE_ERR";
rval = USB_FAILURE;
break;
}
/* we are done and ready to callback */
return (rval);
}
/*
* scsa2usb_cbi_reset_recovery:
* Reset the USB device in case of errors.
*/
static void
{
int i, rval;
"scsa2usb_cbi_reset_recovery: (0x%p)", (void *)scsa2usbp);
if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) {
return;
}
if (scsa2usbp->scsa2usb_cur_pkt) {
}
/* Allocate an mblk for CBR */
for (i = 2; i < CBI_CLASS_CMD_LEN; i++) {
}
/*
* Send a Reset request to the device
*/
CBI_REQUEST_TYPE, /* bmRequestType */
0, /* bRequest */
CBI_WVALUE, /* wValue */
CBI_CLASS_CMD_LEN, /* wLength */
&cdb, /* data to be sent */
0, &completion_reason, &cb_flags, 0);
if (rval != USB_SUCCESS) {
goto exc_exit;
}
/* reset and clear STALL on bulk-in pipe */
"\tclear stall on bulk-in pipe: %d", rval);
if (rval != USB_SUCCESS) {
goto exc_exit;
}
/* reset and clear STALL on bulk-out pipe */
"\tclear stall on bulk-out pipe: %d", rval);
if (rval != USB_SUCCESS) {
goto exc_exit;
}
/* reset and clear STALL on interrupt pipe */
if (SCSA2USB_IS_CBI(scsa2usbp)) {
"\tclear stall on intr pipe: %d", rval);
}
}