17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
563602c90Sfb * Common Development and Distribution License (the "License").
663602c90Sfb * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate *
21*b5a2ac7bSfb * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
227c478bd9Sstevel@tonic-gate * Use is subject to license terms.
237c478bd9Sstevel@tonic-gate */
247c478bd9Sstevel@tonic-gate
257c478bd9Sstevel@tonic-gate
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate * scsa2usb_ms_bulkonly.c:
287c478bd9Sstevel@tonic-gate *
297c478bd9Sstevel@tonic-gate * This file implements USB Mass Storage Class
307c478bd9Sstevel@tonic-gate * Bulk Only (BO) transport v1.0
317c478bd9Sstevel@tonic-gate * http://www.usb.org/developers/data/devclass/usbmassbulk_10.pdf
327c478bd9Sstevel@tonic-gate */
337c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
347c478bd9Sstevel@tonic-gate #include <sys/scsi/scsi.h>
357c478bd9Sstevel@tonic-gate #include <sys/callb.h> /* needed by scsa2usb.h */
367c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
37d29f5a71Szhigang lu - Sun Microsystems - Beijing China #include <sys/strsun.h>
387c478bd9Sstevel@tonic-gate
397c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
407c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_private.h>
417c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_ugen.h>
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate #include <sys/usb/clients/mass_storage/usb_bulkonly.h>
447c478bd9Sstevel@tonic-gate #include <sys/usb/scsa2usb/scsa2usb.h>
457c478bd9Sstevel@tonic-gate
467c478bd9Sstevel@tonic-gate /*
477c478bd9Sstevel@tonic-gate * Function Prototypes
487c478bd9Sstevel@tonic-gate */
497c478bd9Sstevel@tonic-gate int scsa2usb_bulk_only_transport(scsa2usb_state_t *,
507c478bd9Sstevel@tonic-gate scsa2usb_cmd_t *);
517c478bd9Sstevel@tonic-gate static void scsa2usb_fill_in_cbw(scsa2usb_state_t *, scsa2usb_cmd_t *,
527c478bd9Sstevel@tonic-gate mblk_t *);
537c478bd9Sstevel@tonic-gate static void scsa2usb_bulk_only_reset_recovery(scsa2usb_state_t *);
547c478bd9Sstevel@tonic-gate static void scsa2usb_bulk_only_handle_error(scsa2usb_state_t *,
557c478bd9Sstevel@tonic-gate usb_bulk_req_t *);
567c478bd9Sstevel@tonic-gate int scsa2usb_bulk_only_get_max_lun(scsa2usb_state_t *);
577c478bd9Sstevel@tonic-gate static int scsa2usb_handle_status_start(scsa2usb_state_t *,
587c478bd9Sstevel@tonic-gate usb_bulk_req_t *);
597c478bd9Sstevel@tonic-gate static int scsa2usb_handle_csw_result(scsa2usb_state_t *, mblk_t *);
607c478bd9Sstevel@tonic-gate
617c478bd9Sstevel@tonic-gate
627c478bd9Sstevel@tonic-gate /* extern functions */
637c478bd9Sstevel@tonic-gate extern void scsa2usb_setup_next_xfer(scsa2usb_state_t *, scsa2usb_cmd_t *);
647c478bd9Sstevel@tonic-gate extern int scsa2usb_handle_data_start(scsa2usb_state_t *,
657c478bd9Sstevel@tonic-gate scsa2usb_cmd_t *, usb_bulk_req_t *);
667c478bd9Sstevel@tonic-gate extern void scsa2usb_handle_data_done(scsa2usb_state_t *, scsa2usb_cmd_t *,
677c478bd9Sstevel@tonic-gate usb_bulk_req_t *);
687c478bd9Sstevel@tonic-gate extern usb_bulk_req_t *scsa2usb_init_bulk_req(scsa2usb_state_t *,
697c478bd9Sstevel@tonic-gate size_t, uint_t, usb_req_attrs_t, usb_flags_t);
707c478bd9Sstevel@tonic-gate extern int scsa2usb_bulk_timeout(int);
7163602c90Sfb extern int scsa2usb_clear_ept_stall(scsa2usb_state_t *, uint_t,
727c478bd9Sstevel@tonic-gate usb_pipe_handle_t, char *);
737c478bd9Sstevel@tonic-gate extern void scsa2usb_close_usb_pipes(scsa2usb_state_t *);
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gate #ifdef DEBUG /* debugging information */
767c478bd9Sstevel@tonic-gate extern void scsa2usb_print_cdb(scsa2usb_state_t *, scsa2usb_cmd_t *);
777c478bd9Sstevel@tonic-gate #endif /* DEBUG */
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate
807c478bd9Sstevel@tonic-gate #ifdef SCSA2USB_BULK_ONLY_TEST
817c478bd9Sstevel@tonic-gate /*
827c478bd9Sstevel@tonic-gate * Test 13 cases. (See USB Mass Storage Class - Bulk Only Transport).
837c478bd9Sstevel@tonic-gate * We are not covering test cases 1, 6, and 12 as these are the "good"
847c478bd9Sstevel@tonic-gate * test cases and are tested as part of the normal drive access operations.
857c478bd9Sstevel@tonic-gate *
867c478bd9Sstevel@tonic-gate * NOTE: This is for testing only. It will be replaced by a uscsi test.
877c478bd9Sstevel@tonic-gate */
884610e4a0Sfrits int scsa2usb_test_case_2 = 0;
894610e4a0Sfrits int scsa2usb_test_case_3 = 0;
904610e4a0Sfrits int scsa2usb_test_case_4 = 0;
914610e4a0Sfrits int scsa2usb_test_case_7 = 0;
927c478bd9Sstevel@tonic-gate extern int scsa2usb_test_case_8;
934610e4a0Sfrits int scsa2usb_test_case_9 = 0;
947c478bd9Sstevel@tonic-gate extern int scsa2usb_test_case_10;
954610e4a0Sfrits int scsa2usb_test_case_13 = 0;
967c478bd9Sstevel@tonic-gate #endif /* SCSA2USB_BULK_ONLY_TEST */
977c478bd9Sstevel@tonic-gate
987c478bd9Sstevel@tonic-gate
997c478bd9Sstevel@tonic-gate /*
1007c478bd9Sstevel@tonic-gate * scsa2usb_bulk_only_transport:
1017c478bd9Sstevel@tonic-gate * Implements the BO state machine by these steps:
1027c478bd9Sstevel@tonic-gate * a) Issues CBW to a Bulk Only device.
1037c478bd9Sstevel@tonic-gate * b) Start Data Phase if applicable
1047c478bd9Sstevel@tonic-gate * c) Start Status Phase
1057c478bd9Sstevel@tonic-gate *
1067c478bd9Sstevel@tonic-gate * returns TRAN_* values
1077c478bd9Sstevel@tonic-gate *
1087c478bd9Sstevel@tonic-gate * scsa2usb_bulk_only_state_machine:
1097c478bd9Sstevel@tonic-gate *
1107c478bd9Sstevel@tonic-gate * scsa2usb_bulk_only_transport() handles the normal transitions or
1117c478bd9Sstevel@tonic-gate * continuation after clearing stalls or error recovery.
1127c478bd9Sstevel@tonic-gate *
1137c478bd9Sstevel@tonic-gate * Command Phase:
1147c478bd9Sstevel@tonic-gate * prepare a valid CBW and transport it on bulk-out pipe
1157c478bd9Sstevel@tonic-gate * if error on bulkout:
1167c478bd9Sstevel@tonic-gate * set pkt_reason to CMD_TRAN_ERR
1177c478bd9Sstevel@tonic-gate * new pkt state is SCSA2USB_PKT_DO_COMP
1187c478bd9Sstevel@tonic-gate * reset recovery synchronously
1197c478bd9Sstevel@tonic-gate * else
1207c478bd9Sstevel@tonic-gate * proceed to data phase
1217c478bd9Sstevel@tonic-gate *
1227c478bd9Sstevel@tonic-gate * Data Phase:
1237c478bd9Sstevel@tonic-gate * if data in:
1247c478bd9Sstevel@tonic-gate * setup data in on bulkin
1257c478bd9Sstevel@tonic-gate * else if data out:
1267c478bd9Sstevel@tonic-gate * setup data out on bulkout
1277c478bd9Sstevel@tonic-gate *
1287c478bd9Sstevel@tonic-gate * data: (in)
1297c478bd9Sstevel@tonic-gate * copy data transferred so far, no more data to transfer
1307c478bd9Sstevel@tonic-gate *
1317c478bd9Sstevel@tonic-gate * if stall on bulkin pipe
1327c478bd9Sstevel@tonic-gate * terminate data transfers, set cmd_done
1337c478bd9Sstevel@tonic-gate * clear stall on bulkin syncrhonously
1347c478bd9Sstevel@tonic-gate * else if other exception
1357c478bd9Sstevel@tonic-gate * set pkt_reason to CMD_TRAN_ERR
1367c478bd9Sstevel@tonic-gate * new pkt state is SCSA2USB_PKT_DO_COMP
1377c478bd9Sstevel@tonic-gate * reset recovery syncrhonously
1387c478bd9Sstevel@tonic-gate * else (no error)
1397c478bd9Sstevel@tonic-gate * receive status
1407c478bd9Sstevel@tonic-gate *
1417c478bd9Sstevel@tonic-gate * data: (out)
1427c478bd9Sstevel@tonic-gate * if stall on bulkout pipe
1437c478bd9Sstevel@tonic-gate * terminate data transfers, set cmd_done
1447c478bd9Sstevel@tonic-gate * clear stall on bulkout synchronously USBA
1457c478bd9Sstevel@tonic-gate * else if other exception
1467c478bd9Sstevel@tonic-gate * set pkt_reason to CMD_TRAN_ERR
1477c478bd9Sstevel@tonic-gate * new pkt state is SCSA2USB_PKT_DO_COMP
1487c478bd9Sstevel@tonic-gate * reset recovery synchronously
1497c478bd9Sstevel@tonic-gate * else (no error)
1507c478bd9Sstevel@tonic-gate * receive status
1517c478bd9Sstevel@tonic-gate *
1527c478bd9Sstevel@tonic-gate * Status Phase:
1537c478bd9Sstevel@tonic-gate *
1547c478bd9Sstevel@tonic-gate * if stall (first attempt)
1557c478bd9Sstevel@tonic-gate * new pkt state is SCSA2USB_PKT_PROCESS_CSW
1567c478bd9Sstevel@tonic-gate * setup receiving status on bulkin
1577c478bd9Sstevel@tonic-gate * if stall (second attempt)
1587c478bd9Sstevel@tonic-gate * new pkt state is SCSA2USB_PKT_DO_COMP
1597c478bd9Sstevel@tonic-gate * reset recovery synchronously, we are hosed.
1607c478bd9Sstevel@tonic-gate * else
1617c478bd9Sstevel@tonic-gate * goto check CSW
1627c478bd9Sstevel@tonic-gate * else
1637c478bd9Sstevel@tonic-gate * goto check CSW
1647c478bd9Sstevel@tonic-gate *
1657c478bd9Sstevel@tonic-gate * check CSW:
1667c478bd9Sstevel@tonic-gate * - check length equals 13, signature, and matching tag
1677c478bd9Sstevel@tonic-gate * - check status is less than or equal to 2
1687c478bd9Sstevel@tonic-gate * - check residue is less than or equal to data length
1697c478bd9Sstevel@tonic-gate * adjust residue based on if we got valid data
1707c478bd9Sstevel@tonic-gate *
1717c478bd9Sstevel@tonic-gate * if not OK
1727c478bd9Sstevel@tonic-gate * new pkt state is SCSA2USB_PKT_DO_COMP
1737c478bd9Sstevel@tonic-gate * set pkt reason CMD_TRAN_ERR
1747c478bd9Sstevel@tonic-gate * reset recovery synchronously, we are hosed
1757c478bd9Sstevel@tonic-gate * else if phase error
1767c478bd9Sstevel@tonic-gate * new pkt state is SCSA2USB_PKT_DO_COMP
1777c478bd9Sstevel@tonic-gate * set pkt reason CMD_TRAN_ERR
1787c478bd9Sstevel@tonic-gate * reset recovery synchronously
1797c478bd9Sstevel@tonic-gate * else if (status < 2)
1807c478bd9Sstevel@tonic-gate * if status is equal to 1
1817c478bd9Sstevel@tonic-gate * set check condition
1827c478bd9Sstevel@tonic-gate * if residue
1837c478bd9Sstevel@tonic-gate * calculate residue from data xferred and DataResidue
1847c478bd9Sstevel@tonic-gate *
1857c478bd9Sstevel@tonic-gate * set pkt_residue
1867c478bd9Sstevel@tonic-gate * goto SCSA2USB_PKT_DO_COMP
1877c478bd9Sstevel@tonic-gate *
1887c478bd9Sstevel@tonic-gate * The reset recovery walks sequentially thru device reset, clearing
1897c478bd9Sstevel@tonic-gate * stalls and pipe resets. When the reset recovery completes we return
1907c478bd9Sstevel@tonic-gate * to the taskq thread.
1917c478bd9Sstevel@tonic-gate *
1927c478bd9Sstevel@tonic-gate * Clearing stalls clears the stall condition, resets the pipe, and
1937c478bd9Sstevel@tonic-gate * then returns to the transport.
1947c478bd9Sstevel@tonic-gate */
1957c478bd9Sstevel@tonic-gate int
scsa2usb_bulk_only_transport(scsa2usb_state_t * scsa2usbp,scsa2usb_cmd_t * cmd)1967c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_transport(scsa2usb_state_t *scsa2usbp, scsa2usb_cmd_t *cmd)
1977c478bd9Sstevel@tonic-gate {
1987c478bd9Sstevel@tonic-gate int rval;
1996d9a41ffSqz int nretry;
2007c478bd9Sstevel@tonic-gate usb_bulk_req_t *req;
2017c478bd9Sstevel@tonic-gate
2027c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
2037c478bd9Sstevel@tonic-gate
2047c478bd9Sstevel@tonic-gate Cmd_Phase:
2057c478bd9Sstevel@tonic-gate /*
2067c478bd9Sstevel@tonic-gate * Start Command Phase
2077c478bd9Sstevel@tonic-gate * Initialize a bulk_req_t
2087c478bd9Sstevel@tonic-gate */
2097c478bd9Sstevel@tonic-gate req = scsa2usb_init_bulk_req(scsa2usbp, USB_BULK_CBWCMD_LEN,
2107c478bd9Sstevel@tonic-gate SCSA2USB_BULK_PIPE_TIMEOUT, USB_ATTRS_PIPE_RESET, USB_FLAGS_SLEEP);
2117c478bd9Sstevel@tonic-gate
2127c478bd9Sstevel@tonic-gate scsa2usb_fill_in_cbw(scsa2usbp, cmd, req->bulk_data); /* Fill CBW */
2137c478bd9Sstevel@tonic-gate SCSA2USB_PRINT_CDB(scsa2usbp, cmd); /* Print CDB */
2147c478bd9Sstevel@tonic-gate
2157c478bd9Sstevel@tonic-gate /* Send a Bulk Command Block Wrapper (CBW) to the device */
2167c478bd9Sstevel@tonic-gate mutex_exit(&scsa2usbp->scsa2usb_mutex);
2177c478bd9Sstevel@tonic-gate
2187c478bd9Sstevel@tonic-gate ASSERT(req->bulk_timeout);
2197c478bd9Sstevel@tonic-gate rval = usb_pipe_bulk_xfer(scsa2usbp->scsa2usb_bulkout_pipe, req,
2207c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP);
2217c478bd9Sstevel@tonic-gate mutex_enter(&scsa2usbp->scsa2usb_mutex);
2227c478bd9Sstevel@tonic-gate
2237c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
2247c478bd9Sstevel@tonic-gate "scsa2usb_bulk_only_transport: "
2257c478bd9Sstevel@tonic-gate "sent cmd = 0x%x Tag = 0x%x DataXferLen = 0x%lx rval = %d",
2267c478bd9Sstevel@tonic-gate cmd->cmd_cdb[SCSA2USB_OPCODE], cmd->cmd_tag, cmd->cmd_xfercount,
2277c478bd9Sstevel@tonic-gate rval);
2287c478bd9Sstevel@tonic-gate
2297c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
2307c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_handle_error(scsa2usbp, req);
2317c478bd9Sstevel@tonic-gate
2327c478bd9Sstevel@tonic-gate return (TRAN_FATAL_ERROR);
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate
2357c478bd9Sstevel@tonic-gate /* free the data */
2367c478bd9Sstevel@tonic-gate SCSA2USB_FREE_MSG(req->bulk_data);
2377c478bd9Sstevel@tonic-gate req->bulk_data = NULL;
2387c478bd9Sstevel@tonic-gate
2397c478bd9Sstevel@tonic-gate Data_Phase:
2407c478bd9Sstevel@tonic-gate /*
2417c478bd9Sstevel@tonic-gate * Start Data Phase
2427c478bd9Sstevel@tonic-gate * re-set timeout
2437c478bd9Sstevel@tonic-gate */
2447c478bd9Sstevel@tonic-gate req->bulk_timeout = scsa2usb_bulk_timeout(cmd->cmd_timeout);
2457c478bd9Sstevel@tonic-gate
2467c478bd9Sstevel@tonic-gate /*
2477c478bd9Sstevel@tonic-gate * we've not transferred any data yet; updated in
2487c478bd9Sstevel@tonic-gate * scsa2usb_handle_data_done
2497c478bd9Sstevel@tonic-gate */
2507c478bd9Sstevel@tonic-gate cmd->cmd_resid_xfercount = cmd->cmd_xfercount;
2517c478bd9Sstevel@tonic-gate
2527c478bd9Sstevel@tonic-gate if (cmd->cmd_xfercount) {
2537c478bd9Sstevel@tonic-gate /* start I/O to/from the device */
2547c478bd9Sstevel@tonic-gate rval = scsa2usb_handle_data_start(scsa2usbp, cmd, req);
2557c478bd9Sstevel@tonic-gate
2567c478bd9Sstevel@tonic-gate /* handle data returned, if any */
2577c478bd9Sstevel@tonic-gate scsa2usb_handle_data_done(scsa2usbp, cmd, req);
2587c478bd9Sstevel@tonic-gate
2597c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
2607c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA,
2617c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_log_handle,
2627c478bd9Sstevel@tonic-gate "data xfer phase, error = %d, cr = %d",
2637c478bd9Sstevel@tonic-gate rval, req->bulk_completion_reason);
2647c478bd9Sstevel@tonic-gate /*
2657c478bd9Sstevel@tonic-gate * we ran into an error
2667c478bd9Sstevel@tonic-gate */
2677c478bd9Sstevel@tonic-gate if (req->bulk_completion_reason == USB_CR_STALL) {
2687c478bd9Sstevel@tonic-gate if (scsa2usbp->scsa2usb_cur_pkt) {
2697c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_cur_pkt->
27063602c90Sfb pkt_reason = CMD_TRAN_ERR;
2717c478bd9Sstevel@tonic-gate }
2727c478bd9Sstevel@tonic-gate } else {
2737c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_handle_error(scsa2usbp, req);
2747c478bd9Sstevel@tonic-gate
2757c478bd9Sstevel@tonic-gate return (TRAN_FATAL_ERROR);
2767c478bd9Sstevel@tonic-gate }
2777c478bd9Sstevel@tonic-gate } /* end of else */
2787c478bd9Sstevel@tonic-gate
2797c478bd9Sstevel@tonic-gate /* free the data */
2807c478bd9Sstevel@tonic-gate SCSA2USB_FREE_MSG(req->bulk_data);
2817c478bd9Sstevel@tonic-gate req->bulk_data = NULL;
2827c478bd9Sstevel@tonic-gate }
2837c478bd9Sstevel@tonic-gate
2847c478bd9Sstevel@tonic-gate Status_Phase:
2857c478bd9Sstevel@tonic-gate /*
2867c478bd9Sstevel@tonic-gate * Start status phase
2877c478bd9Sstevel@tonic-gate * read in CSW
2887c478bd9Sstevel@tonic-gate */
2896d9a41ffSqz for (nretry = 0; nretry < SCSA2USB_STATUS_RETRIES; nretry++) {
2906d9a41ffSqz rval = scsa2usb_handle_status_start(scsa2usbp, req);
2917c478bd9Sstevel@tonic-gate
2926d9a41ffSqz if ((rval != USB_SUCCESS) &&
2936d9a41ffSqz (req->bulk_completion_reason == USB_CR_STALL)) {
2946d9a41ffSqz /*
2956d9a41ffSqz * We ran into STALL condition here.
2966d9a41ffSqz * If the condition cannot be cleared
2976d9a41ffSqz * successfully, retry for limited times.
2986d9a41ffSqz */
2996d9a41ffSqz scsa2usbp->scsa2usb_pkt_state =
30063602c90Sfb SCSA2USB_PKT_PROCESS_CSW;
3017c478bd9Sstevel@tonic-gate } else {
3027c478bd9Sstevel@tonic-gate
3036d9a41ffSqz break;
3047c478bd9Sstevel@tonic-gate }
3057c478bd9Sstevel@tonic-gate }
3067c478bd9Sstevel@tonic-gate
3076d9a41ffSqz if (rval == USB_SUCCESS) {
3086d9a41ffSqz /* process CSW */
3096d9a41ffSqz rval = scsa2usb_handle_csw_result(scsa2usbp, req->bulk_data);
3106d9a41ffSqz } else {
3116d9a41ffSqz scsa2usb_bulk_only_handle_error(scsa2usbp, req);
3126d9a41ffSqz
3136d9a41ffSqz return (TRAN_FATAL_ERROR);
3146d9a41ffSqz }
3156d9a41ffSqz
3167c478bd9Sstevel@tonic-gate SCSA2USB_FREE_BULK_REQ(req); /* free request */
3177c478bd9Sstevel@tonic-gate
3187c478bd9Sstevel@tonic-gate if ((rval == USB_SUCCESS) && /* CSW was ok */
3197c478bd9Sstevel@tonic-gate (scsa2usbp->scsa2usb_cur_pkt->pkt_reason == CMD_CMPLT) &&
3207c478bd9Sstevel@tonic-gate (cmd->cmd_xfercount != 0) && /* more data to xfer */
3217c478bd9Sstevel@tonic-gate !cmd->cmd_done) { /* we aren't done yet */
3227c478bd9Sstevel@tonic-gate scsa2usb_setup_next_xfer(scsa2usbp, cmd);
3237c478bd9Sstevel@tonic-gate goto Cmd_Phase;
3247c478bd9Sstevel@tonic-gate }
3257c478bd9Sstevel@tonic-gate
3267c478bd9Sstevel@tonic-gate return (rval == USB_SUCCESS ? TRAN_ACCEPT : TRAN_FATAL_ERROR);
3277c478bd9Sstevel@tonic-gate }
3287c478bd9Sstevel@tonic-gate
3297c478bd9Sstevel@tonic-gate
3307c478bd9Sstevel@tonic-gate /*
3317c478bd9Sstevel@tonic-gate * scsa2usb_fill_in_cbw:
3327c478bd9Sstevel@tonic-gate * Fill in a CBW request packet. This
3337c478bd9Sstevel@tonic-gate * packet is transported to the device
3347c478bd9Sstevel@tonic-gate */
3357c478bd9Sstevel@tonic-gate static void
scsa2usb_fill_in_cbw(scsa2usb_state_t * scsa2usbp,scsa2usb_cmd_t * cmd,mblk_t * mp)3367c478bd9Sstevel@tonic-gate scsa2usb_fill_in_cbw(scsa2usb_state_t *scsa2usbp,
3377c478bd9Sstevel@tonic-gate scsa2usb_cmd_t *cmd, mblk_t *mp)
3387c478bd9Sstevel@tonic-gate {
3397c478bd9Sstevel@tonic-gate int i;
3407c478bd9Sstevel@tonic-gate int len;
3417c478bd9Sstevel@tonic-gate uchar_t dir, *cdb = (uchar_t *)(&cmd->cmd_cdb);
3427c478bd9Sstevel@tonic-gate
3437c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
3447c478bd9Sstevel@tonic-gate
3457c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MSB(CBW_SIGNATURE); /* CBW Signature */;
3467c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MID1(CBW_SIGNATURE);
3477c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MID2(CBW_SIGNATURE);
3487c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_LSB(CBW_SIGNATURE);
3497c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_LSB(cmd->cmd_tag); /* CBW Tag */
3507c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MID2(cmd->cmd_tag);
3517c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MID1(cmd->cmd_tag);
3527c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MSB(cmd->cmd_tag);
3537c478bd9Sstevel@tonic-gate
3547c478bd9Sstevel@tonic-gate dir = cmd->cmd_dir;
3557c478bd9Sstevel@tonic-gate len = cmd->cmd_xfercount;
3567c478bd9Sstevel@tonic-gate #ifdef SCSA2USB_BULK_ONLY_TEST
3577c478bd9Sstevel@tonic-gate if (scsa2usb_test_case_2 && (cdb[0] == SCMD_READ_CAPACITY)) {
3587c478bd9Sstevel@tonic-gate /* Host expects no data. The device wants data. Hn < Di */
3597c478bd9Sstevel@tonic-gate scsa2usb_test_case_2 = len = 0;
3607c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
3617c478bd9Sstevel@tonic-gate "TEST 2: Hn < Di cdb: 0x%x len: 0x%x", cdb[0], len);
3627c478bd9Sstevel@tonic-gate }
3637c478bd9Sstevel@tonic-gate
3647c478bd9Sstevel@tonic-gate if (scsa2usb_test_case_3 && (cmd->cmd_dir == CBW_DIR_OUT)) {
3657c478bd9Sstevel@tonic-gate /* Host expects no data. The device wants data. Hn < Do */
3667c478bd9Sstevel@tonic-gate if (cdb[0] == SCMD_WRITE_G1) {
3677c478bd9Sstevel@tonic-gate scsa2usb_test_case_3 = len = 0;
3687c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_SCSA,
3697c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_log_handle,
3707c478bd9Sstevel@tonic-gate "TEST 3: Hn < Do cdb: 0x%x len:%x", cdb[0], len);
3717c478bd9Sstevel@tonic-gate }
3727c478bd9Sstevel@tonic-gate }
3737c478bd9Sstevel@tonic-gate
3747c478bd9Sstevel@tonic-gate if (scsa2usb_test_case_4 && (cdb[0] == SCMD_READ_G1)) {
3757c478bd9Sstevel@tonic-gate cdb[0] = 0x5e;
3767c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
3777c478bd9Sstevel@tonic-gate "TEST 4: Hi > Dn: changed cdb to 0x%x", cdb[0]);
3787c478bd9Sstevel@tonic-gate scsa2usb_test_case_4 = 0;
3797c478bd9Sstevel@tonic-gate }
3807c478bd9Sstevel@tonic-gate
3817c478bd9Sstevel@tonic-gate if (scsa2usb_test_case_7 && (cmd->cmd_cdb[0] == SCMD_READ_G1)) {
3827c478bd9Sstevel@tonic-gate len -= 0x10;
3837c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
3847c478bd9Sstevel@tonic-gate "TEST 7: Hi < Di cdb: 0x%x len: 0x%x", cdb[0], len);
3857c478bd9Sstevel@tonic-gate scsa2usb_test_case_7 = 0;
3867c478bd9Sstevel@tonic-gate }
3877c478bd9Sstevel@tonic-gate
3887c478bd9Sstevel@tonic-gate if (scsa2usb_test_case_8 && (cdb[0] == SCMD_READ_G1)) {
3897c478bd9Sstevel@tonic-gate dir = (dir == CBW_DIR_IN) ? CBW_DIR_OUT : dir;
3907c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
3917c478bd9Sstevel@tonic-gate "TEST 8: Hi <> Do cdb: 0x%x dir: 0x%x", cdb[0], dir);
3927c478bd9Sstevel@tonic-gate }
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gate if (scsa2usb_test_case_9 && (cdb[0] == SCMD_WRITE_G1)) {
3957c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
3967c478bd9Sstevel@tonic-gate "TEST 9: Ho <> Di (%x)", cdb[0]);
3977c478bd9Sstevel@tonic-gate cdb[SCSA2USB_LEN_0] = cdb[SCSA2USB_LEN_1] = 0;
3987c478bd9Sstevel@tonic-gate scsa2usb_test_case_9 = 0;
3997c478bd9Sstevel@tonic-gate }
4007c478bd9Sstevel@tonic-gate
4017c478bd9Sstevel@tonic-gate if (scsa2usb_test_case_10 && (cdb[0] == SCMD_WRITE_G1)) {
4027c478bd9Sstevel@tonic-gate dir = (dir == CBW_DIR_OUT) ? CBW_DIR_IN : dir;
4037c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
4047c478bd9Sstevel@tonic-gate "TEST 10: Ho <> Di cdb: 0x%x dir: 0x%x", cdb[0], dir);
4057c478bd9Sstevel@tonic-gate }
4067c478bd9Sstevel@tonic-gate
4077c478bd9Sstevel@tonic-gate /*
4087c478bd9Sstevel@tonic-gate * This case occurs when the device intends to receive
4097c478bd9Sstevel@tonic-gate * more data from the host than the host sends.
4107c478bd9Sstevel@tonic-gate */
4117c478bd9Sstevel@tonic-gate if (scsa2usb_test_case_13) {
4127c478bd9Sstevel@tonic-gate if ((cdb[0] == SCMD_WRITE_G1) || (cdb[0] == SCMD_READ_G1)) {
4137c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(DPRINT_MASK_SCSA,
4147c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_log_handle, "TEST 13: Ho < Do");
4157c478bd9Sstevel@tonic-gate
4167c478bd9Sstevel@tonic-gate len -= 30;
4177c478bd9Sstevel@tonic-gate scsa2usb_test_case_13 = 0;
4187c478bd9Sstevel@tonic-gate }
4197c478bd9Sstevel@tonic-gate }
4207c478bd9Sstevel@tonic-gate #endif /* SCSA2USB_BULK_ONLY_TEST */
4217c478bd9Sstevel@tonic-gate
4227c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MSB(len); /* Transfer Length */
4237c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MID1(len);
4247c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_MID2(len);
4257c478bd9Sstevel@tonic-gate *mp->b_wptr++ = CBW_LSB(len);
4267c478bd9Sstevel@tonic-gate
4277c478bd9Sstevel@tonic-gate *mp->b_wptr++ = dir; /* Transfer Direction */
4287c478bd9Sstevel@tonic-gate *mp->b_wptr++ = cmd->cmd_pkt->pkt_address.a_lun; /* Lun # */
4297c478bd9Sstevel@tonic-gate *mp->b_wptr++ = cmd->cmd_actual_len; /* CDB Len */
4307c478bd9Sstevel@tonic-gate
4317c478bd9Sstevel@tonic-gate /* Copy the CDB out */
4327c478bd9Sstevel@tonic-gate for (i = 0; i < CBW_CDB_LEN; i++) {
4337c478bd9Sstevel@tonic-gate *mp->b_wptr++ = *cdb++;
4347c478bd9Sstevel@tonic-gate }
4357c478bd9Sstevel@tonic-gate #ifdef DUMP_CWB
4367c478bd9Sstevel@tonic-gate {
4377c478bd9Sstevel@tonic-gate int len = mp->b_wptr - mp->b_rptr;
4387c478bd9Sstevel@tonic-gate char *buf;
4397c478bd9Sstevel@tonic-gate
4407c478bd9Sstevel@tonic-gate int i;
4417c478bd9Sstevel@tonic-gate
4427c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "CWB: len=%d\n", len);
4437c478bd9Sstevel@tonic-gate buf = kmem_zalloc(512, KM_SLEEP);
4447c478bd9Sstevel@tonic-gate for (i = 0; i < len; i++) {
4457c478bd9Sstevel@tonic-gate sprintf(&buf[strlen(buf)], "%02x ", mp->b_rptr[i]);
4467c478bd9Sstevel@tonic-gate }
4477c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "%s\n", buf);
4487c478bd9Sstevel@tonic-gate kmem_free(buf, 512);
4497c478bd9Sstevel@tonic-gate }
4507c478bd9Sstevel@tonic-gate #endif
4517c478bd9Sstevel@tonic-gate
4527c478bd9Sstevel@tonic-gate }
4537c478bd9Sstevel@tonic-gate
4547c478bd9Sstevel@tonic-gate
4557c478bd9Sstevel@tonic-gate /*
4567c478bd9Sstevel@tonic-gate * scsa2usb_bulk_only_handle_error:
4577c478bd9Sstevel@tonic-gate * handle transport errors and start recovery
4587c478bd9Sstevel@tonic-gate */
4597c478bd9Sstevel@tonic-gate static void
scsa2usb_bulk_only_handle_error(scsa2usb_state_t * scsa2usbp,usb_bulk_req_t * req)4607c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_handle_error(scsa2usb_state_t *scsa2usbp,
4617c478bd9Sstevel@tonic-gate usb_bulk_req_t *req)
4627c478bd9Sstevel@tonic-gate {
4637c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt = scsa2usbp->scsa2usb_cur_pkt;
4647c478bd9Sstevel@tonic-gate
4657c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
4667c478bd9Sstevel@tonic-gate "scsa2usb_bulk_only_handle_error: req = 0x%p, cr = 0x%x",
467112116d8Sfb (void *)req, (req ? req->bulk_completion_reason : 0));
4687c478bd9Sstevel@tonic-gate
4697c478bd9Sstevel@tonic-gate if (req) {
4707c478bd9Sstevel@tonic-gate SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp);
4717c478bd9Sstevel@tonic-gate
4727c478bd9Sstevel@tonic-gate /* invoke reset recovery */
4737c478bd9Sstevel@tonic-gate switch (req->bulk_completion_reason) {
4747c478bd9Sstevel@tonic-gate case USB_CR_STALL:
4757c478bd9Sstevel@tonic-gate if (pkt) {
4767c478bd9Sstevel@tonic-gate pkt->pkt_reason = CMD_TRAN_ERR;
4777c478bd9Sstevel@tonic-gate }
4787c478bd9Sstevel@tonic-gate break;
4797c478bd9Sstevel@tonic-gate case USB_CR_TIMEOUT:
4807c478bd9Sstevel@tonic-gate if (pkt) {
4817c478bd9Sstevel@tonic-gate pkt->pkt_reason = CMD_TIMEOUT;
4827c478bd9Sstevel@tonic-gate pkt->pkt_statistics |= STAT_TIMEOUT;
4837c478bd9Sstevel@tonic-gate }
4847c478bd9Sstevel@tonic-gate break;
4857c478bd9Sstevel@tonic-gate case USB_CR_DEV_NOT_RESP:
4867c478bd9Sstevel@tonic-gate if (pkt) {
4877c478bd9Sstevel@tonic-gate pkt->pkt_reason = CMD_DEV_GONE;
4887c478bd9Sstevel@tonic-gate /* scsi_poll relies on this */
4897c478bd9Sstevel@tonic-gate pkt->pkt_state = STATE_GOT_BUS;
4907c478bd9Sstevel@tonic-gate }
4917c478bd9Sstevel@tonic-gate break;
4927c478bd9Sstevel@tonic-gate default:
4937c478bd9Sstevel@tonic-gate if (pkt) {
4947c478bd9Sstevel@tonic-gate pkt->pkt_reason = CMD_TRAN_ERR;
4957c478bd9Sstevel@tonic-gate }
4967c478bd9Sstevel@tonic-gate }
4977c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_reset_recovery(scsa2usbp);
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate
5007c478bd9Sstevel@tonic-gate SCSA2USB_FREE_BULK_REQ(req);
5017c478bd9Sstevel@tonic-gate }
5027c478bd9Sstevel@tonic-gate
5037c478bd9Sstevel@tonic-gate
5047c478bd9Sstevel@tonic-gate /*
5057c478bd9Sstevel@tonic-gate * scsa2usb_handle_status_start:
5067c478bd9Sstevel@tonic-gate * Receive status data
5077c478bd9Sstevel@tonic-gate */
5087c478bd9Sstevel@tonic-gate static int
scsa2usb_handle_status_start(scsa2usb_state_t * scsa2usbp,usb_bulk_req_t * req)5097c478bd9Sstevel@tonic-gate scsa2usb_handle_status_start(scsa2usb_state_t *scsa2usbp,
5107c478bd9Sstevel@tonic-gate usb_bulk_req_t *req)
5117c478bd9Sstevel@tonic-gate {
5127c478bd9Sstevel@tonic-gate int rval;
5137c478bd9Sstevel@tonic-gate
5147c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
515112116d8Sfb "scsa2usb_handle_status_start: req = 0x%p", (void *)req);
5167c478bd9Sstevel@tonic-gate
5177c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
5187c478bd9Sstevel@tonic-gate
5197c478bd9Sstevel@tonic-gate /* setup up for receiving CSW */
5207c478bd9Sstevel@tonic-gate #ifdef SCSA2USB_BULK_ONLY_TEST
5217c478bd9Sstevel@tonic-gate req->bulk_attributes = 0;
5227c478bd9Sstevel@tonic-gate #else
5237c478bd9Sstevel@tonic-gate req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK;
5247c478bd9Sstevel@tonic-gate #endif /* SCSA2USB_BULK_ONLY_TEST */
5257c478bd9Sstevel@tonic-gate req->bulk_len = CSW_LEN;
5267c478bd9Sstevel@tonic-gate
5277c478bd9Sstevel@tonic-gate SCSA2USB_FREE_MSG(req->bulk_data);
5287c478bd9Sstevel@tonic-gate req->bulk_data = allocb_wait(req->bulk_len,
5297c478bd9Sstevel@tonic-gate BPRI_LO, STR_NOSIG, NULL);
5307c478bd9Sstevel@tonic-gate
5317c478bd9Sstevel@tonic-gate /* Issue the request */
5327c478bd9Sstevel@tonic-gate mutex_exit(&scsa2usbp->scsa2usb_mutex);
5337c478bd9Sstevel@tonic-gate
5347c478bd9Sstevel@tonic-gate ASSERT(req->bulk_timeout);
5357c478bd9Sstevel@tonic-gate rval = usb_pipe_bulk_xfer(scsa2usbp->scsa2usb_bulkin_pipe, req,
5367c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP);
5377c478bd9Sstevel@tonic-gate mutex_enter(&scsa2usbp->scsa2usb_mutex);
5387c478bd9Sstevel@tonic-gate
5397c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
5407c478bd9Sstevel@tonic-gate "scsa2usb_handle_status_start: END rval = 0x%x", rval);
5417c478bd9Sstevel@tonic-gate
5427c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
5437c478bd9Sstevel@tonic-gate if (scsa2usbp->scsa2usb_pkt_state == SCSA2USB_PKT_PROCESS_CSW) {
5447c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_reset_recovery(scsa2usbp);
5457c478bd9Sstevel@tonic-gate
5467c478bd9Sstevel@tonic-gate return (rval);
5477c478bd9Sstevel@tonic-gate }
5487c478bd9Sstevel@tonic-gate
5497c478bd9Sstevel@tonic-gate if (req->bulk_completion_reason == USB_CR_STALL) {
5507c478bd9Sstevel@tonic-gate (void) scsa2usb_clear_ept_stall(scsa2usbp,
5517c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_bulkin_ept.bEndpointAddress,
5527c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_bulkin_pipe, "bulk-in");
5537c478bd9Sstevel@tonic-gate }
5547c478bd9Sstevel@tonic-gate }
5557c478bd9Sstevel@tonic-gate
5567c478bd9Sstevel@tonic-gate return (rval);
5577c478bd9Sstevel@tonic-gate }
5587c478bd9Sstevel@tonic-gate
5597c478bd9Sstevel@tonic-gate
5607c478bd9Sstevel@tonic-gate /*
5617c478bd9Sstevel@tonic-gate * scsa2usb_handle_csw_result:
5627c478bd9Sstevel@tonic-gate * Handle status results
5637c478bd9Sstevel@tonic-gate */
5647c478bd9Sstevel@tonic-gate static int
scsa2usb_handle_csw_result(scsa2usb_state_t * scsa2usbp,mblk_t * data)5657c478bd9Sstevel@tonic-gate scsa2usb_handle_csw_result(scsa2usb_state_t *scsa2usbp, mblk_t *data)
5667c478bd9Sstevel@tonic-gate {
5677c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS;
5687c478bd9Sstevel@tonic-gate int residue;
5697c478bd9Sstevel@tonic-gate char *msg = "CSW FAILED";
5707c478bd9Sstevel@tonic-gate uint_t signature, tag, status;
5717c478bd9Sstevel@tonic-gate usb_bulk_csw_t csw;
5727c478bd9Sstevel@tonic-gate struct scsi_pkt *pkt = scsa2usbp->scsa2usb_cur_pkt;
5737c478bd9Sstevel@tonic-gate scsa2usb_cmd_t *cmd = PKT2CMD(pkt);
5747c478bd9Sstevel@tonic-gate
5757c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
5767c478bd9Sstevel@tonic-gate
5777c478bd9Sstevel@tonic-gate /*
5787c478bd9Sstevel@tonic-gate * This shouldn't happen. It implies the device's
5797c478bd9Sstevel@tonic-gate * firmware is bad and has returned NULL CSW.
5807c478bd9Sstevel@tonic-gate * return failure back.
5817c478bd9Sstevel@tonic-gate */
5827c478bd9Sstevel@tonic-gate if (data == NULL) {
5837c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
5847c478bd9Sstevel@tonic-gate "scsa2usb_handle_csw_result: data == NULL");
5857c478bd9Sstevel@tonic-gate
5867c478bd9Sstevel@tonic-gate return (USB_FAILURE);
5877c478bd9Sstevel@tonic-gate }
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate /* check if we got back CSW_LEN or not */
590d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(data) != CSW_LEN) {
5917c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
592112116d8Sfb "scsa2usb_handle_csw_result: no enough data (%ld)",
593d29f5a71Szhigang lu - Sun Microsystems - Beijing China (long)(MBLKL(data)));
5947c478bd9Sstevel@tonic-gate
5957c478bd9Sstevel@tonic-gate return (USB_FAILURE);
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate
5987c478bd9Sstevel@tonic-gate /* Read into csw */
5997c478bd9Sstevel@tonic-gate bcopy(data->b_rptr, &csw, CSW_LEN);
6007c478bd9Sstevel@tonic-gate
6017c478bd9Sstevel@tonic-gate status = csw.csw_bCSWStatus;
6027c478bd9Sstevel@tonic-gate signature = SCSA2USB_MK_32BIT(csw.csw_dCSWSignature3,
6037c478bd9Sstevel@tonic-gate csw.csw_dCSWSignature2, csw.csw_dCSWSignature1,
6047c478bd9Sstevel@tonic-gate csw.csw_dCSWSignature0);
6057c478bd9Sstevel@tonic-gate residue = SCSA2USB_MK_32BIT(csw.csw_dCSWDataResidue3,
6067c478bd9Sstevel@tonic-gate csw.csw_dCSWDataResidue2, csw.csw_dCSWDataResidue1,
6077c478bd9Sstevel@tonic-gate csw.csw_dCSWDataResidue0);
6087c478bd9Sstevel@tonic-gate tag = SCSA2USB_MK_32BIT(csw.csw_dCSWTag3, csw.csw_dCSWTag2,
6097c478bd9Sstevel@tonic-gate csw.csw_dCSWTag1, csw.csw_dCSWTag0);
6107c478bd9Sstevel@tonic-gate
6117c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
6127c478bd9Sstevel@tonic-gate "CSW: Signature = 0x%x Status = 0%x Tag = 0x%x Residue = 0x%x",
6137c478bd9Sstevel@tonic-gate signature, status, tag, residue);
6147c478bd9Sstevel@tonic-gate
6157c478bd9Sstevel@tonic-gate /* Check for abnormal errors */
6167c478bd9Sstevel@tonic-gate if ((signature != CSW_SIGNATURE) || (tag != cmd->cmd_tag) ||
617d29f5a71Szhigang lu - Sun Microsystems - Beijing China (status > CSW_STATUS_PHASE_ERROR)) {
6187c478bd9Sstevel@tonic-gate
6197c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
6207c478bd9Sstevel@tonic-gate "CSW_ERR: Status = 0x%x, Tag = 0x%x xfercount = 0x%lx",
6217c478bd9Sstevel@tonic-gate status, cmd->cmd_tag, cmd->cmd_total_xfercount);
6227c478bd9Sstevel@tonic-gate
6237c478bd9Sstevel@tonic-gate return (USB_FAILURE);
6247c478bd9Sstevel@tonic-gate }
6257c478bd9Sstevel@tonic-gate
6267c478bd9Sstevel@tonic-gate switch (status) {
6277c478bd9Sstevel@tonic-gate case CSW_STATUS_GOOD:
6287c478bd9Sstevel@tonic-gate /*
6297c478bd9Sstevel@tonic-gate * Fail the command if the device misbehaves and
6307c478bd9Sstevel@tonic-gate * gives a good status but doesn't transfer any data.
6317c478bd9Sstevel@tonic-gate * Otherwise we'll get into an infinite retry loop.
6327c478bd9Sstevel@tonic-gate *
6337c478bd9Sstevel@tonic-gate * We test only against cmd_total_xfercount here and
6347c478bd9Sstevel@tonic-gate * assume that this will not happen on a command that
6357c478bd9Sstevel@tonic-gate * transfers a large amount of data and therefore may
6367c478bd9Sstevel@tonic-gate * be split into separate transfers. For a large data
6377c478bd9Sstevel@tonic-gate * transfer it is assumed that the device will return
6387c478bd9Sstevel@tonic-gate * an error status if the transfer does not occur.
6397c478bd9Sstevel@tonic-gate * this isn't quite correct because a subsequent request
6407c478bd9Sstevel@tonic-gate * sense may not give a valid sense key.
6417c478bd9Sstevel@tonic-gate */
6427c478bd9Sstevel@tonic-gate if (!cmd->cmd_done && residue &&
6437c478bd9Sstevel@tonic-gate (residue == cmd->cmd_total_xfercount)) {
6447c478bd9Sstevel@tonic-gate *(pkt->pkt_scbp) = STATUS_CHECK;
6457c478bd9Sstevel@tonic-gate cmd->cmd_xfercount = 0;
6467c478bd9Sstevel@tonic-gate cmd->cmd_done = 1;
6477c478bd9Sstevel@tonic-gate } else {
6487c478bd9Sstevel@tonic-gate msg = "CSW GOOD";
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate break;
6517c478bd9Sstevel@tonic-gate case CSW_STATUS_FAILED:
6527c478bd9Sstevel@tonic-gate *(pkt->pkt_scbp) = STATUS_CHECK; /* Set check condition */
6537c478bd9Sstevel@tonic-gate cmd->cmd_done = 1;
6547c478bd9Sstevel@tonic-gate break;
6557c478bd9Sstevel@tonic-gate case CSW_STATUS_PHASE_ERROR:
6567c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
6577c478bd9Sstevel@tonic-gate "scsa2usb_handle_csw_result: Phase Error");
6587c478bd9Sstevel@tonic-gate
6597c478bd9Sstevel@tonic-gate /* invoke reset recovery */
6607c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_handle_error(scsa2usbp, NULL);
6617c478bd9Sstevel@tonic-gate
6627c478bd9Sstevel@tonic-gate return (USB_FAILURE);
6637c478bd9Sstevel@tonic-gate default: /* shouldn't happen anymore */
6647c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
6657c478bd9Sstevel@tonic-gate "scsa2usb_handle_csw_result: Invalid CSW");
6667c478bd9Sstevel@tonic-gate
6677c478bd9Sstevel@tonic-gate /* invoke reset recovery */
6687c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_handle_error(scsa2usbp, NULL);
6697c478bd9Sstevel@tonic-gate
6707c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
6717c478bd9Sstevel@tonic-gate } /* end of switch */
6727c478bd9Sstevel@tonic-gate
6737c478bd9Sstevel@tonic-gate /* Set resid */
6747c478bd9Sstevel@tonic-gate if (residue || cmd->cmd_resid_xfercount) {
6757c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA,
6767c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_log_handle,
6773fbbb872Ssl "total=0x%lx cmd_xfercount=0x%lx residue=0x%x "
6783fbbb872Ssl "cmd_offset=0x%lx",
6797c478bd9Sstevel@tonic-gate cmd->cmd_total_xfercount, cmd->cmd_xfercount,
6803fbbb872Ssl residue, cmd->cmd_offset);
6817c478bd9Sstevel@tonic-gate
6827c478bd9Sstevel@tonic-gate /*
6837c478bd9Sstevel@tonic-gate * we need to adjust using the residue and
6847c478bd9Sstevel@tonic-gate * assume worst case. Some devices lie about
6857c478bd9Sstevel@tonic-gate * residue. some report a residue greater than
6867c478bd9Sstevel@tonic-gate * the residue we have calculated.
6877c478bd9Sstevel@tonic-gate * first adjust back the total_xfercount
6887c478bd9Sstevel@tonic-gate */
6897c478bd9Sstevel@tonic-gate cmd->cmd_total_xfercount += cmd->cmd_xfercount -
69063602c90Sfb cmd->cmd_resid_xfercount;
6913fbbb872Ssl /*
6923fbbb872Ssl * we need to adjust cmd_offset as well, or the data
6933fbbb872Ssl * buffer for subsequent transfer may exceed the buffer
6943fbbb872Ssl * boundary
6953fbbb872Ssl */
6963fbbb872Ssl cmd->cmd_offset -= cmd->cmd_xfercount -
6973fbbb872Ssl cmd->cmd_resid_xfercount;
6987c478bd9Sstevel@tonic-gate
6997c478bd9Sstevel@tonic-gate /*
7007c478bd9Sstevel@tonic-gate * now take the min of the reported residue by
7017c478bd9Sstevel@tonic-gate * the device and the requested xfer count
7027c478bd9Sstevel@tonic-gate * (just in case the device reported a residue greater
7037c478bd9Sstevel@tonic-gate * than our request count).
7047c478bd9Sstevel@tonic-gate * then take the max of this residue and the residue
7057c478bd9Sstevel@tonic-gate * that the HCD reported and subtract this from
7067c478bd9Sstevel@tonic-gate * the request count. This is the actual number
7077c478bd9Sstevel@tonic-gate * of valid bytes transferred during the last transfer
7087c478bd9Sstevel@tonic-gate * which we now subtract from the total_xfercount
7097c478bd9Sstevel@tonic-gate */
7102115f0c6Ssl if ((!(scsa2usbp->scsa2usb_attrs &
7112115f0c6Ssl SCSA2USB_ATTRS_USE_CSW_RESIDUE)) ||
7122115f0c6Ssl (residue < 0) ||
7137c478bd9Sstevel@tonic-gate (residue > cmd->cmd_total_xfercount)) {
7142115f0c6Ssl /* some devices lie about the resid, ignore */
7152115f0c6Ssl cmd->cmd_total_xfercount -=
7162115f0c6Ssl cmd->cmd_xfercount - cmd->cmd_resid_xfercount;
7173fbbb872Ssl cmd->cmd_offset +=
7183fbbb872Ssl cmd->cmd_xfercount - cmd->cmd_resid_xfercount;
7192115f0c6Ssl } else {
7202115f0c6Ssl cmd->cmd_total_xfercount -=
7212115f0c6Ssl cmd->cmd_xfercount -
7222115f0c6Ssl max(min(residue, cmd->cmd_xfercount),
7232115f0c6Ssl cmd->cmd_resid_xfercount);
7243fbbb872Ssl cmd->cmd_offset +=
7253fbbb872Ssl cmd->cmd_xfercount -
7263fbbb872Ssl max(min(residue, cmd->cmd_xfercount),
7273fbbb872Ssl cmd->cmd_resid_xfercount);
7283fbbb872Ssl /*
7293fbbb872Ssl * if HCD does not report residue while the device
7303fbbb872Ssl * reports a residue equivalent to the xfercount,
7313fbbb872Ssl * it is very likely the device lies about the
7323fbbb872Ssl * residue. we need to stop the command, or we'll
7333fbbb872Ssl * get into an infinite retry loop.
7343fbbb872Ssl */
7353fbbb872Ssl if ((cmd->cmd_resid_xfercount == 0) &&
7363fbbb872Ssl (residue == cmd->cmd_xfercount)) {
7373fbbb872Ssl cmd->cmd_xfercount = 0;
7383fbbb872Ssl cmd->cmd_done = 1;
7393fbbb872Ssl }
7407c478bd9Sstevel@tonic-gate }
7417c478bd9Sstevel@tonic-gate
7427c478bd9Sstevel@tonic-gate pkt->pkt_resid = cmd->cmd_total_xfercount;
7437c478bd9Sstevel@tonic-gate }
7447c478bd9Sstevel@tonic-gate
7457c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
7467c478bd9Sstevel@tonic-gate "scsa2usb_handle_csw_result: %s, resid: 0x%lx",
7477c478bd9Sstevel@tonic-gate msg, pkt->pkt_resid);
7487c478bd9Sstevel@tonic-gate
7497c478bd9Sstevel@tonic-gate /* we are done and ready to callback */
7507c478bd9Sstevel@tonic-gate SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp);
7517c478bd9Sstevel@tonic-gate
7527c478bd9Sstevel@tonic-gate return (rval);
7537c478bd9Sstevel@tonic-gate }
7547c478bd9Sstevel@tonic-gate
7557c478bd9Sstevel@tonic-gate
7567c478bd9Sstevel@tonic-gate /*
7577c478bd9Sstevel@tonic-gate * scsa2usb_bulk_only_reset_recovery:
7587c478bd9Sstevel@tonic-gate * Reset the USB device step-wise in case of errors.
7597c478bd9Sstevel@tonic-gate * NOTE that the order of reset is very important.
7607c478bd9Sstevel@tonic-gate */
7617c478bd9Sstevel@tonic-gate static void
scsa2usb_bulk_only_reset_recovery(scsa2usb_state_t * scsa2usbp)7627c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_reset_recovery(scsa2usb_state_t *scsa2usbp)
7637c478bd9Sstevel@tonic-gate {
7647c478bd9Sstevel@tonic-gate int rval;
7657c478bd9Sstevel@tonic-gate usb_cr_t completion_reason;
7667c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags;
7677c478bd9Sstevel@tonic-gate
7687c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
769112116d8Sfb "scsa2usb_bulk_only_reset_recovery: scsa2usbp = 0x%p",
770112116d8Sfb (void *)scsa2usbp);
7717c478bd9Sstevel@tonic-gate
7727c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
7737c478bd9Sstevel@tonic-gate
7747c478bd9Sstevel@tonic-gate if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) {
7757c478bd9Sstevel@tonic-gate
7767c478bd9Sstevel@tonic-gate return;
7777c478bd9Sstevel@tonic-gate }
7787c478bd9Sstevel@tonic-gate
7797c478bd9Sstevel@tonic-gate /*
7807c478bd9Sstevel@tonic-gate * assume that the reset will be successful. if it isn't, retrying
7817c478bd9Sstevel@tonic-gate * from target driver won't help much
7827c478bd9Sstevel@tonic-gate */
7837c478bd9Sstevel@tonic-gate if (scsa2usbp->scsa2usb_cur_pkt) {
7847c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_cur_pkt->pkt_statistics |= STAT_DEV_RESET;
7857c478bd9Sstevel@tonic-gate }
7867c478bd9Sstevel@tonic-gate
7877c478bd9Sstevel@tonic-gate /* set the reset condition */
7887c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_pipe_state = SCSA2USB_PIPE_DEV_RESET;
7897c478bd9Sstevel@tonic-gate
7907c478bd9Sstevel@tonic-gate /* Send a sync DEVICE-RESET request to the device */
7917c478bd9Sstevel@tonic-gate mutex_exit(&scsa2usbp->scsa2usb_mutex);
7927c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(scsa2usbp->scsa2usb_dip,
7937c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_default_pipe,
7947c478bd9Sstevel@tonic-gate USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF,
7957c478bd9Sstevel@tonic-gate (uint8_t)BULK_ONLY_RESET, /* bRequest */
7967c478bd9Sstevel@tonic-gate 0, /* wValue */
7977c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_intfc_num, /* wIndex */
7987c478bd9Sstevel@tonic-gate 0, /* wLength */
7997c478bd9Sstevel@tonic-gate NULL, 0, &completion_reason, &cb_flags, 0);
8007c478bd9Sstevel@tonic-gate mutex_enter(&scsa2usbp->scsa2usb_mutex);
8017c478bd9Sstevel@tonic-gate
8027c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
8037c478bd9Sstevel@tonic-gate "\tbulk-only device-reset rval: %d", rval);
8047c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
8057c478bd9Sstevel@tonic-gate goto exc_exit;
8067c478bd9Sstevel@tonic-gate }
8077c478bd9Sstevel@tonic-gate
8087c478bd9Sstevel@tonic-gate /* reset and clear STALL on bulk-in pipe */
8097c478bd9Sstevel@tonic-gate rval = scsa2usb_clear_ept_stall(scsa2usbp,
8107c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_bulkin_ept.bEndpointAddress,
8117c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_bulkin_pipe, "bulk-in");
8127c478bd9Sstevel@tonic-gate
8137c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
8147c478bd9Sstevel@tonic-gate "\tbulk-in pipe clear stall: %d", rval);
8157c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
8167c478bd9Sstevel@tonic-gate goto exc_exit;
8177c478bd9Sstevel@tonic-gate }
8187c478bd9Sstevel@tonic-gate
8197c478bd9Sstevel@tonic-gate /* reset and clear STALL on bulk-out pipe */
8207c478bd9Sstevel@tonic-gate rval = scsa2usb_clear_ept_stall(scsa2usbp,
8217c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_bulkout_ept.bEndpointAddress,
8227c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_bulkout_pipe, "bulk-out");
8237c478bd9Sstevel@tonic-gate
8247c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
8257c478bd9Sstevel@tonic-gate "\tbulk-out pipe clear stall: %d", rval);
8267c478bd9Sstevel@tonic-gate
8277c478bd9Sstevel@tonic-gate exc_exit:
8287c478bd9Sstevel@tonic-gate /* clear the reset condition */
8297c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_pipe_state &= ~SCSA2USB_PIPE_DEV_RESET;
8307c478bd9Sstevel@tonic-gate }
8317c478bd9Sstevel@tonic-gate
8327c478bd9Sstevel@tonic-gate
8337c478bd9Sstevel@tonic-gate /*
8347c478bd9Sstevel@tonic-gate * scsa2usb_bulk_only_get_max_lun:
8357c478bd9Sstevel@tonic-gate * this function returns the number of LUNs supported by the device
8367c478bd9Sstevel@tonic-gate */
8377c478bd9Sstevel@tonic-gate int
scsa2usb_bulk_only_get_max_lun(scsa2usb_state_t * scsa2usbp)8387c478bd9Sstevel@tonic-gate scsa2usb_bulk_only_get_max_lun(scsa2usb_state_t *scsa2usbp)
8397c478bd9Sstevel@tonic-gate {
8407c478bd9Sstevel@tonic-gate int luns = 1, rval;
8417c478bd9Sstevel@tonic-gate mblk_t *data = NULL;
8427c478bd9Sstevel@tonic-gate usb_cr_t completion_reason;
8437c478bd9Sstevel@tonic-gate usb_cb_flags_t cb_flags;
8447c478bd9Sstevel@tonic-gate
8457c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
8467c478bd9Sstevel@tonic-gate "scsa2usb_bulk_only_get_max_lun:");
8477c478bd9Sstevel@tonic-gate
8487c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
8497c478bd9Sstevel@tonic-gate
8507c478bd9Sstevel@tonic-gate mutex_exit(&scsa2usbp->scsa2usb_mutex);
8517c478bd9Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(scsa2usbp->scsa2usb_dip,
8527c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_default_pipe,
8537c478bd9Sstevel@tonic-gate BULK_ONLY_GET_MAXLUN_BMREQ, /* bmRequestType */
8547c478bd9Sstevel@tonic-gate BULK_ONLY_GET_MAXLUN_REQ, /* bRequest */
8557c478bd9Sstevel@tonic-gate 0, /* wValue */
8567c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_intfc_num, /* wIndex */
8577c478bd9Sstevel@tonic-gate 1, /* wLength */
8587c478bd9Sstevel@tonic-gate &data, 0,
8597c478bd9Sstevel@tonic-gate &completion_reason, &cb_flags, 0);
8607c478bd9Sstevel@tonic-gate mutex_enter(&scsa2usbp->scsa2usb_mutex);
8617c478bd9Sstevel@tonic-gate
8627c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
8637c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
8647c478bd9Sstevel@tonic-gate "get max lun failed, rval=%d cr=%d cb=0x%x data=0x%p",
865112116d8Sfb rval, completion_reason, cb_flags, (void *)data);
8667c478bd9Sstevel@tonic-gate } else {
8677c478bd9Sstevel@tonic-gate /*
8687c478bd9Sstevel@tonic-gate * This check ensures that we have valid data returned back.
8697c478bd9Sstevel@tonic-gate * Otherwise we assume that device supports only one LUN.
8707c478bd9Sstevel@tonic-gate */
871d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(data) != 1) {
872d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_SCSA,
8737c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_log_handle,
8747c478bd9Sstevel@tonic-gate "device reported incorrect luns (adjusting to 1)");
8757c478bd9Sstevel@tonic-gate } else {
8767c478bd9Sstevel@tonic-gate /*
8777c478bd9Sstevel@tonic-gate * Set scsa2usb_n_luns to value returned by the device
8787c478bd9Sstevel@tonic-gate * plus 1. (See Section 3.2)
8797c478bd9Sstevel@tonic-gate */
8807c478bd9Sstevel@tonic-gate luns = *data->b_rptr + 1;
8817c478bd9Sstevel@tonic-gate
8827c478bd9Sstevel@tonic-gate /*
8837c478bd9Sstevel@tonic-gate * In case a device returns incorrect LUNs
8847c478bd9Sstevel@tonic-gate * which are more than 15 or negative or 0;
8857c478bd9Sstevel@tonic-gate * we assume 1.
8867c478bd9Sstevel@tonic-gate */
8877c478bd9Sstevel@tonic-gate if ((luns >= SCSA2USB_MAX_LUNS) || (luns <= 0)) {
888d291d9f2Sfrits USB_DPRINTF_L2(DPRINT_MASK_SCSA,
8897c478bd9Sstevel@tonic-gate scsa2usbp->scsa2usb_log_handle,
8907c478bd9Sstevel@tonic-gate "device reported %d luns "
8917c478bd9Sstevel@tonic-gate "(adjusting to 1)", luns);
8927c478bd9Sstevel@tonic-gate luns = 1;
8937c478bd9Sstevel@tonic-gate }
8947c478bd9Sstevel@tonic-gate }
8957c478bd9Sstevel@tonic-gate }
8967c478bd9Sstevel@tonic-gate
8977c478bd9Sstevel@tonic-gate SCSA2USB_FREE_MSG(data); /* Free data */
8987c478bd9Sstevel@tonic-gate
8997c478bd9Sstevel@tonic-gate return (luns);
9007c478bd9Sstevel@tonic-gate }
901