4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
21 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
29 * This file implements USB Mass Storage Class
30 * Control Bulk Interrupt (CB/CBI) transport v1.0
31 * http://www.usb.org/developers/data/devclass/usbmass-cbi10.pdf
33 #include <sys/usb/usba/usbai_version.h>
34 #include <sys/scsi/scsi.h>
35 #include <sys/callb.h> /* needed by scsa2usb.h */
36 #include <sys/strsubr.h>
38 #include <sys/usb/usba.h>
39 #include <sys/usb/usba/usba_private.h>
40 #include <sys/usb/usba/usba_ugen.h>
42 #include <sys/usb/clients/mass_storage/usb_cbi.h>
43 #include <sys/usb/scsa2usb/scsa2usb.h>
48 int scsa2usb_cbi_transport(scsa2usb_state_t
*, scsa2usb_cmd_t
*);
49 static int scsa2usb_handle_cbi_status(usb_intr_req_t
*);
50 static void scsa2usb_cbi_reset_recovery(scsa2usb_state_t
*);
51 static void scsa2usb_cbi_handle_error(scsa2usb_state_t
*, int, usb_cr_t
);
52 static usb_intr_req_t
*scsa2usb_cbi_start_intr_polling(scsa2usb_state_t
*);
53 void scsa2usb_cbi_stop_intr_polling(scsa2usb_state_t
*);
55 /* extern functions */
56 extern void scsa2usb_setup_next_xfer(scsa2usb_state_t
*, scsa2usb_cmd_t
*);
57 extern int scsa2usb_handle_data_start(scsa2usb_state_t
*,
58 scsa2usb_cmd_t
*, usb_bulk_req_t
*);
59 extern void scsa2usb_handle_data_done(scsa2usb_state_t
*, scsa2usb_cmd_t
*,
61 extern usb_bulk_req_t
*scsa2usb_init_bulk_req(scsa2usb_state_t
*,
62 size_t, uint_t
, usb_req_attrs_t
, usb_flags_t
);
63 extern int scsa2usb_clear_ept_stall(scsa2usb_state_t
*, uint_t
,
64 usb_pipe_handle_t
, char *);
65 extern void scsa2usb_close_usb_pipes(scsa2usb_state_t
*);
67 #ifdef DEBUG /* debugging information */
68 extern void scsa2usb_print_cdb(scsa2usb_state_t
*, scsa2usb_cmd_t
*);
73 * scsa2usb_cbi_transport:
74 * Implements the CB/CBI state machine by these steps:
75 * a) Issues command to the device over control pipe.
76 * b) Start Data Phase if applicable
77 * c) Start Status Phase
79 * returns TRAN_* values and not USB_SUCCESS/FAILURE
81 * scsa2usb_cbi_transport() handles the normal transitions or
82 * continuation after clearing stalls or error recovery.
85 * prepare a valid command and transport it on default pipe
86 * if error on default-pipe:
87 * set pkt_reason to CMD_TRAN_ERR
88 * new pkt state is SCSA2USB_PKT_DO_COMP
89 * do reset recovery synchronously
91 * proceed to data phase
95 * setup data in on bulkin
97 * setup data out on bulkout
100 * copy data transferred so far, no more data to transfer
102 * if stall on bulkin pipe
103 * terminate data transfers, set cmd_done
104 * clear stall on bulkin syncrhonously
105 * else if other exception
106 * set pkt_reason to CMD_TRAN_ERR
107 * new pkt state is SCSA2USB_PKT_DO_COMP
108 * do reset recovery synchronously
113 * if stall on bulkout pipe
114 * terminate data transfers
115 * clear stall on bulkout synchronously USBA
116 * else if other exception
117 * set pkt_reason to CMD_TRAN_ERR
118 * new pkt state is SCSA2USB_PKT_DO_COMP
119 * do reset recovery synchronously
123 * Status Phase: (on Interrupt pipe for CBI devices only)
126 * new pkt state is SCSA2USB_PKT_DO_COMP
127 * clear stall on interrupt pipe
129 * set pkt_reason to CMD_TRAN_ERR
130 * new pkt state is SCSA2USB_PKT_DO_COMP
131 * do reset recovery synchronously
136 * if not OK or phase error
137 * new pkt state is SCSA2USB_PKT_DO_COMP
138 * set pkt reason CMD_TRAN_ERR
139 * reset recovery synchronously
141 * goto SCSA2USB_PKT_DO_COMP
143 * The reset recovery walks sequentially thru device reset, clearing
144 * stalls and pipe resets. When the reset recovery completes we return
145 * to the taskq thread.
147 * Clearing stalls clears the stall condition, resets the pipe, and
148 * then returns to the transport.
151 scsa2usb_cbi_transport(scsa2usb_state_t
*scsa2usbp
, scsa2usb_cmd_t
*cmd
)
153 int i
, rval
= TRAN_ACCEPT
;
155 usb_cr_t completion_reason
;
156 usb_cb_flags_t cb_flags
;
157 usb_bulk_req_t
*data_req
;
158 usb_intr_req_t
*status_req
;
160 USB_DPRINTF_L4(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
161 "scsa2usb_cbi_transport: cmd = 0x%p", (void *)cmd
);
162 ASSERT(mutex_owned(&scsa2usbp
->scsa2usb_mutex
));
165 if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp
))) {
167 return (TRAN_FATAL_ERROR
);
171 * Start command phase (C - in CBI)
173 data
= allocb_wait(CBI_CLASS_CMD_LEN
, BPRI_LO
, STR_NOSIG
, NULL
);
175 /* Initialize the data */
176 for (i
= 0; i
< CBI_CLASS_CMD_LEN
; i
++) {
177 *data
->b_wptr
++ = cmd
->cmd_cdb
[i
];
180 SCSA2USB_PRINT_CDB(scsa2usbp
, cmd
); /* print the CDB */
182 /* Send the Command to the device */
183 mutex_exit(&scsa2usbp
->scsa2usb_mutex
);
184 rval
= usb_pipe_sync_ctrl_xfer(scsa2usbp
->scsa2usb_dip
,
185 scsa2usbp
->scsa2usb_default_pipe
,
186 CBI_REQUEST_TYPE
, /* bmRequestType */
188 CBI_WVALUE
, /* wValue */
189 scsa2usbp
->scsa2usb_intfc_num
, /* wIndex */
190 CBI_CLASS_CMD_LEN
, /* wLength */
192 USB_ATTRS_PIPE_RESET
, /* attributes */
193 &completion_reason
, &cb_flags
, USB_FLAGS_SLEEP
);
194 mutex_enter(&scsa2usbp
->scsa2usb_mutex
);
196 USB_DPRINTF_L2(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
197 "scsa2usb_cbi_transport: sent cmd = 0x%x rval = %d",
198 cmd
->cmd_cdb
[SCSA2USB_OPCODE
], rval
);
200 SCSA2USB_FREE_MSG(data
); /* get rid of the data */
201 if (rval
!= USB_SUCCESS
) {
202 scsa2usb_cbi_handle_error(scsa2usbp
, rval
, completion_reason
);
204 return (TRAN_FATAL_ERROR
);
208 * Xferred command to the device.
209 * Start data phase (B - in CBI)
213 * we've not transferred any data yet; updated in
214 * scsa2usb_handle_data_done
216 cmd
->cmd_resid_xfercount
= 0;
218 /* if data to be xferred ? */
219 if (cmd
->cmd_xfercount
) {
221 /* Initialize a bulk_req_t */
222 data_req
= scsa2usb_init_bulk_req(scsa2usbp
, 0,
223 cmd
->cmd_timeout
, USB_ATTRS_PIPE_RESET
, USB_FLAGS_SLEEP
);
225 /* start I/O to/from the device */
226 rval
= scsa2usb_handle_data_start(scsa2usbp
, cmd
,
228 /* handle data returned */
229 scsa2usb_handle_data_done(scsa2usbp
, cmd
,
231 if (rval
!= USB_SUCCESS
) {
233 * we ran into an error and it wasn't a STALL
235 if (data_req
->bulk_completion_reason
== USB_CR_STALL
) {
236 if (scsa2usbp
->scsa2usb_cur_pkt
) {
237 scsa2usbp
->scsa2usb_cur_pkt
->
238 pkt_reason
= CMD_TRAN_ERR
;
241 scsa2usb_cbi_handle_error(scsa2usbp
,
242 rval
, data_req
->bulk_completion_reason
);
245 SCSA2USB_FREE_BULK_REQ(data_req
);
247 return (TRAN_FATAL_ERROR
);
251 SCSA2USB_FREE_BULK_REQ(data_req
); /* get rid of bulk_req */
254 /* CB devices don't do status over interrupt pipe */
255 if (SCSA2USB_IS_CB(scsa2usbp
)) {
256 USB_DPRINTF_L2(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
257 "scsa2usb_cbi_transport: CB done rval = %d", rval
);
262 * Start status phase (I - in CBI)
265 /* Get Status over interrupt pipe */
266 if ((status_req
= scsa2usb_cbi_start_intr_polling(scsa2usbp
)) == NULL
) {
268 return (TRAN_FATAL_ERROR
); /* lack of better return code */
271 rval
= scsa2usb_handle_cbi_status(status_req
);
273 usb_free_intr_req(status_req
);
275 /* stop interrupt pipe polling (CBI only) */
276 if (SCSA2USB_IS_CBI(scsa2usbp
)) {
277 scsa2usb_cbi_stop_intr_polling(scsa2usbp
);
281 if ((rval
== USB_SUCCESS
) && /* CSW was ok */
282 (scsa2usbp
->scsa2usb_cur_pkt
->pkt_reason
== CMD_CMPLT
) &&
283 (cmd
->cmd_xfercount
!= 0) && /* more data to xfer */
284 !cmd
->cmd_done
) { /* we aren't done yet */
285 scsa2usb_setup_next_xfer(scsa2usbp
, cmd
);
288 if (SCSA2USB_IS_CB(scsa2usbp
)) {
290 SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp
);
294 return (rval
== USB_SUCCESS
? TRAN_ACCEPT
: TRAN_FATAL_ERROR
);
299 * scsa2usb_cbi_handle_error:
300 * handle errors from transport that are not STALL conditions
303 scsa2usb_cbi_handle_error(scsa2usb_state_t
*scsa2usbp
, int rval
, usb_cr_t cr
)
305 struct scsi_pkt
*pkt
= scsa2usbp
->scsa2usb_cur_pkt
;
307 USB_DPRINTF_L4(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
308 "scsa2usb_cbi_handle_error: data error %d cr = %d", rval
, cr
);
310 SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp
);
312 /* do reset error recovery */
316 pkt
->pkt_reason
= CMD_TRAN_ERR
;
317 *(pkt
->pkt_scbp
) = STATUS_CHECK
;
322 pkt
->pkt_reason
= CMD_TIMEOUT
;
323 pkt
->pkt_statistics
|= STAT_TIMEOUT
;
326 case USB_CR_DEV_NOT_RESP
:
327 scsa2usb_cbi_stop_intr_polling(scsa2usbp
);
329 pkt
->pkt_reason
= CMD_DEV_GONE
;
330 /* scsi_poll relies on this */
331 pkt
->pkt_state
= STATE_GOT_BUS
;
336 pkt
->pkt_reason
= CMD_TRAN_ERR
;
338 scsa2usb_cbi_reset_recovery(scsa2usbp
);
344 * scsa2usb_cbi_start_intr_polling:
345 * start polling asynchronously without notification
347 static usb_intr_req_t
*
348 scsa2usb_cbi_start_intr_polling(scsa2usb_state_t
*scsa2usbp
)
351 usb_pipe_state_t pipe_state
;
352 usb_intr_req_t
*req
= NULL
;
354 USB_DPRINTF_L4(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
355 "scsa2usb_cbi_start_intr_polling:");
357 if (!SCSA2USB_IS_CBI(scsa2usbp
)) {
362 req
= usb_alloc_intr_req(scsa2usbp
->scsa2usb_dip
, 0, USB_FLAGS_SLEEP
);
363 req
->intr_client_private
= (usb_opaque_t
)scsa2usbp
;
364 req
->intr_attributes
= USB_ATTRS_ONE_XFER
| USB_ATTRS_PIPE_RESET
|
365 USB_ATTRS_SHORT_XFER_OK
;
366 req
->intr_len
= scsa2usbp
->scsa2usb_intr_ept
.wMaxPacketSize
;
367 req
->intr_timeout
= 20; /* arbitrary large for now */
368 mutex_exit(&scsa2usbp
->scsa2usb_mutex
);
370 if ((rval
= usb_pipe_intr_xfer(scsa2usbp
->scsa2usb_intr_pipe
, req
,
371 USB_FLAGS_SLEEP
)) != USB_SUCCESS
) {
372 mutex_enter(&scsa2usbp
->scsa2usb_mutex
);
373 USB_DPRINTF_L2(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
374 "polling failed rval: %d", rval
);
377 if (req
->intr_completion_reason
== USB_CR_STALL
) {
378 (void) scsa2usb_clear_ept_stall(scsa2usbp
,
379 scsa2usbp
->scsa2usb_intr_ept
.bEndpointAddress
,
380 scsa2usbp
->scsa2usb_intr_pipe
, "intr");
383 /* handle other errors here */
384 scsa2usb_cbi_handle_error(scsa2usbp
, rval
,
385 req
->intr_completion_reason
);
386 mutex_exit(&scsa2usbp
->scsa2usb_mutex
);
388 usb_free_intr_req(req
);
392 rval
= usb_pipe_get_state(scsa2usbp
->scsa2usb_intr_pipe
,
393 &pipe_state
, USB_FLAGS_SLEEP
);
394 if (pipe_state
!= USB_PIPE_STATE_ACTIVE
) {
395 USB_DPRINTF_L2(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
396 "intr pipes state: %d, rval: %d", pipe_state
, rval
);
398 mutex_enter(&scsa2usbp
->scsa2usb_mutex
);
405 * scsa2usb_cbi_stop_intr_polling:
406 * Stop polling on interrupt pipe (for status)
409 scsa2usb_cbi_stop_intr_polling(scsa2usb_state_t
*scsa2usbp
)
411 USB_DPRINTF_L4(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
412 "scsa2usb_cbi_stop_intr_polling:");
414 ASSERT(mutex_owned(&scsa2usbp
->scsa2usb_mutex
));
416 if (!SCSA2USB_IS_CBI(scsa2usbp
)) {
421 if (scsa2usbp
->scsa2usb_intr_pipe
) {
422 mutex_exit(&scsa2usbp
->scsa2usb_mutex
);
423 usb_pipe_stop_intr_polling(scsa2usbp
->scsa2usb_intr_pipe
,
425 mutex_enter(&scsa2usbp
->scsa2usb_mutex
);
431 * scsa2usb_handle_cbi_status:
432 * Handle CBI status results
435 scsa2usb_handle_cbi_status(usb_intr_req_t
*req
)
437 int rval
= USB_SUCCESS
;
441 scsa2usb_state_t
*scsa2usbp
= (scsa2usb_state_t
*)
442 req
->intr_client_private
;
444 USB_DPRINTF_L4(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
445 "scsa2usb_handle_cbi_status: req: 0x%p", (void *)req
);
447 ASSERT(mutex_owned(&scsa2usbp
->scsa2usb_mutex
));
448 ASSERT(req
->intr_data
!= NULL
);
450 cmd
= PKT2CMD(scsa2usbp
->scsa2usb_cur_pkt
);
451 status
= *(req
->intr_data
->b_rptr
+ 1) & CBI_STATUS_MASK
;
454 * CBI status contains ASC and ASCQ.
455 * SCMD_REQUEST_SENSE and SCMD_INQUIRY don't affect the sense data
456 * on CBI devices. So, we can ignore that info for these 2 commands.
458 * (See details in UFI spec section 3.5 - that says that INQUIRY,
459 * SEND_DIAG, and REQUEST_SENSE ought to be supported by any deivce
462 if ((cmd
->cmd_cdb
[SCSA2USB_OPCODE
] == SCMD_REQUEST_SENSE
) ||
463 (cmd
->cmd_cdb
[SCSA2USB_OPCODE
] == SCMD_INQUIRY
)) {
464 USB_DPRINTF_L4(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
465 "scsa2usb_handle_cbi_status: CBI STATUS = (0x%x, 0x%x)",
466 *(req
->intr_data
->b_rptr
), *(req
->intr_data
->b_rptr
+1));
468 SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp
);
470 return (USB_SUCCESS
);
474 case CBI_STATUS_PASS
:
476 /* non-zero command completion interrupt */
477 if (*(req
->intr_data
->b_rptr
)) {
478 *(scsa2usbp
->scsa2usb_cur_pkt
->pkt_scbp
) = STATUS_CHECK
;
482 case CBI_STATUS_FAILED
:
483 case CBI_STATUS_PERSISTENT_FAIL
:
484 msg
= (status
== CBI_STATUS_PERSISTENT_FAIL
) ?
485 "PERSISTENT_FAILURE" : "FAILED";
486 *(scsa2usbp
->scsa2usb_cur_pkt
->pkt_scbp
) = STATUS_CHECK
;
489 case CBI_STATUS_PHASE_ERR
:
491 scsa2usb_cbi_reset_recovery(scsa2usbp
);
496 USB_DPRINTF_L4(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
497 "CBI STATUS = 0x%x %s (0x%x, 0x%x)", status
, msg
,
498 *(req
->intr_data
->b_rptr
), *(req
->intr_data
->b_rptr
+1));
500 /* we are done and ready to callback */
501 SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp
);
508 * scsa2usb_cbi_reset_recovery:
509 * Reset the USB device in case of errors.
512 scsa2usb_cbi_reset_recovery(scsa2usb_state_t
*scsa2usbp
)
516 usb_cr_t completion_reason
;
517 usb_cb_flags_t cb_flags
;
519 USB_DPRINTF_L4(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
520 "scsa2usb_cbi_reset_recovery: (0x%p)", (void *)scsa2usbp
);
522 ASSERT(mutex_owned(&scsa2usbp
->scsa2usb_mutex
));
524 if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp
))) {
529 if (scsa2usbp
->scsa2usb_cur_pkt
) {
530 scsa2usbp
->scsa2usb_cur_pkt
->pkt_statistics
|= STAT_DEV_RESET
;
533 /* Allocate an mblk for CBR */
534 cdb
= allocb_wait(CBI_CLASS_CMD_LEN
, BPRI_LO
, STR_NOSIG
, NULL
);
536 *cdb
->b_wptr
++ = SCMD_SDIAG
; /* Set it to DIAG */
537 *cdb
->b_wptr
++ = CBI_SELF_TEST
; /* Set it to reset */
538 for (i
= 2; i
< CBI_CLASS_CMD_LEN
; i
++) {
539 *cdb
->b_wptr
++ = CBI_CBR_VALUE
; /* Set it to 0xff */
542 scsa2usbp
->scsa2usb_pipe_state
= SCSA2USB_PIPE_DEV_RESET
;
545 * Send a Reset request to the device
547 mutex_exit(&scsa2usbp
->scsa2usb_mutex
);
548 rval
= usb_pipe_sync_ctrl_xfer(scsa2usbp
->scsa2usb_dip
,
549 scsa2usbp
->scsa2usb_default_pipe
,
550 CBI_REQUEST_TYPE
, /* bmRequestType */
552 CBI_WVALUE
, /* wValue */
553 scsa2usbp
->scsa2usb_intfc_num
, /* wIndex address */
554 CBI_CLASS_CMD_LEN
, /* wLength */
555 &cdb
, /* data to be sent */
556 0, &completion_reason
, &cb_flags
, 0);
557 mutex_enter(&scsa2usbp
->scsa2usb_mutex
);
559 USB_DPRINTF_L3(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
560 "\tCBI RESET: rval = %x cr = %x", rval
, completion_reason
);
561 if (rval
!= USB_SUCCESS
) {
565 /* reset and clear STALL on bulk-in pipe */
566 rval
= scsa2usb_clear_ept_stall(scsa2usbp
,
567 scsa2usbp
->scsa2usb_bulkin_ept
.bEndpointAddress
,
568 scsa2usbp
->scsa2usb_bulkin_pipe
, "bulk-in");
569 USB_DPRINTF_L3(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
570 "\tclear stall on bulk-in pipe: %d", rval
);
571 if (rval
!= USB_SUCCESS
) {
575 /* reset and clear STALL on bulk-out pipe */
576 rval
= scsa2usb_clear_ept_stall(scsa2usbp
,
577 scsa2usbp
->scsa2usb_bulkout_ept
.bEndpointAddress
,
578 scsa2usbp
->scsa2usb_bulkout_pipe
, "bulk-out");
579 USB_DPRINTF_L3(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
580 "\tclear stall on bulk-out pipe: %d", rval
);
581 if (rval
!= USB_SUCCESS
) {
585 /* reset and clear STALL on interrupt pipe */
586 if (SCSA2USB_IS_CBI(scsa2usbp
)) {
587 rval
= scsa2usb_clear_ept_stall(scsa2usbp
,
588 scsa2usbp
->scsa2usb_intr_ept
.bEndpointAddress
,
589 scsa2usbp
->scsa2usb_intr_pipe
, "intr");
591 USB_DPRINTF_L3(DPRINT_MASK_SCSA
, scsa2usbp
->scsa2usb_log_handle
,
592 "\tclear stall on intr pipe: %d", rval
);
596 SCSA2USB_FREE_MSG(cdb
); /* Free the data */
597 scsa2usbp
->scsa2usb_pipe_state
&= ~SCSA2USB_PIPE_DEV_RESET
;