#if defined(lint) && !defined(DEBUG)
#define DEBUG 1
#endif
#include <sys/usb/usba/usbai_version.h>
#include <sys/scsi/scsi.h>
#include <sys/cdio.h>
#include <sys/sunndi.h>
#include <sys/esunddi.h>
#include <sys/callb.h>
#include <sys/kobj.h>
#include <sys/kobj_lex.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/sysmacros.h>
#include <sys/usb/usba.h>
#include <sys/usb/clients/ugen/usb_ugen.h>
#include <sys/usb/usba/usba_ugen.h>
#include <sys/usb/usba/usba_private.h>
#include <sys/usb/usba/usba_ugend.h>
#include <sys/usb/clients/mass_storage/usb_bulkonly.h>
#include <sys/usb/scsa2usb/scsa2usb.h>
static int scsa2usb_attach(dev_info_t *, ddi_attach_cmd_t);
static int scsa2usb_info(dev_info_t *, ddi_info_cmd_t, void *,
void **);
static int scsa2usb_detach(dev_info_t *, ddi_detach_cmd_t);
static int scsa2usb_cleanup(dev_info_t *, scsa2usb_state_t *);
static void scsa2usb_detect_quirks(scsa2usb_state_t *);
static void scsa2usb_create_luns(scsa2usb_state_t *);
static int scsa2usb_is_usb(dev_info_t *);
static void scsa2usb_fake_inquiry(scsa2usb_state_t *,
struct scsi_inquiry *);
static void scsa2usb_do_inquiry(scsa2usb_state_t *,
uint_t, uint_t);
static int scsa2usb_do_tur(scsa2usb_state_t *, struct scsi_address *);
static void scsa2usb_override(scsa2usb_state_t *);
static int scsa2usb_parse_input_str(char *, scsa2usb_ov_t *,
scsa2usb_state_t *);
static void scsa2usb_override_error(char *, scsa2usb_state_t *);
static void scsa2usb_panic_callb_init(scsa2usb_state_t *);
static void scsa2usb_panic_callb_fini(scsa2usb_state_t *);
static boolean_t scsa2usb_panic_callb(void *, int);
static int scsa2usb_scsi_tgt_probe(struct scsi_device *, int (*)(void));
static int scsa2usb_scsi_tgt_init(dev_info_t *, dev_info_t *,
scsi_hba_tran_t *, struct scsi_device *);
static void scsa2usb_scsi_tgt_free(dev_info_t *, dev_info_t *,
scsi_hba_tran_t *, struct scsi_device *);
static struct scsi_pkt *scsa2usb_scsi_init_pkt(struct scsi_address *,
struct scsi_pkt *, struct buf *, int, int,
int, int, int (*)(), caddr_t);
static void scsa2usb_scsi_destroy_pkt(struct scsi_address *,
struct scsi_pkt *);
static int scsa2usb_scsi_start(struct scsi_address *, struct scsi_pkt *);
static int scsa2usb_scsi_abort(struct scsi_address *, struct scsi_pkt *);
static int scsa2usb_scsi_reset(struct scsi_address *, int);
static int scsa2usb_scsi_getcap(struct scsi_address *, char *, int);
static int scsa2usb_scsi_setcap(struct scsi_address *, char *, int, int);
static int scsa2usb_scsi_bus_config(dev_info_t *, uint_t,
ddi_bus_config_op_t, void *, dev_info_t **);
static int scsa2usb_scsi_bus_unconfig(dev_info_t *, uint_t,
ddi_bus_config_op_t, void *);
static void scsa2usb_prepare_pkt(scsa2usb_state_t *, struct scsi_pkt *);
static int scsa2usb_cmd_transport(scsa2usb_state_t *, scsa2usb_cmd_t *);
static int scsa2usb_check_bulkonly_quirks(scsa2usb_state_t *,
scsa2usb_cmd_t *);
static int scsa2usb_check_ufi_quirks(scsa2usb_state_t *,
scsa2usb_cmd_t *);
static int scsa2usb_handle_scsi_cmd_sub_class(scsa2usb_state_t *,
scsa2usb_cmd_t *, struct scsi_pkt *);
static int scsa2usb_handle_ufi_subclass_cmd(scsa2usb_state_t *,
scsa2usb_cmd_t *, struct scsi_pkt *);
static void scsa2usb_work_thread(void *);
static void scsa2usb_transport_request(scsa2usb_state_t *, uint_t);
static void scsa2usb_flush_waitQ(scsa2usb_state_t *, uint_t, uchar_t);
static int scsa2usb_all_waitQs_empty(scsa2usb_state_t *);
static int scsa2usb_create_arq_pkt(scsa2usb_state_t *,
struct scsi_address *);
static void scsa2usb_delete_arq_pkt(scsa2usb_state_t *);
static void scsa2usb_complete_arq_pkt(scsa2usb_state_t *, struct scsi_pkt *,
scsa2usb_cmd_t *, struct buf *);
static int scsa2usb_open_usb_pipes(scsa2usb_state_t *);
void scsa2usb_close_usb_pipes(scsa2usb_state_t *);
static void scsa2usb_fill_up_cdb_len(scsa2usb_cmd_t *, int);
static void scsa2usb_fill_up_cdb_lba(scsa2usb_cmd_t *, uint64_t);
static void scsa2usb_fill_up_g4_cdb_lba(scsa2usb_cmd_t *, uint64_t);
static void scsa2usb_fill_up_ReadCD_cdb_len(scsa2usb_cmd_t *, int, int);
static void scsa2usb_fill_up_12byte_cdb_len(scsa2usb_cmd_t *, int, int);
static void scsa2usb_fill_up_16byte_cdb_len(scsa2usb_cmd_t *, int, int);
static int scsa2usb_read_cd_blk_size(uchar_t);
int scsa2usb_rw_transport(scsa2usb_state_t *, struct scsi_pkt *);
void scsa2usb_setup_next_xfer(scsa2usb_state_t *, scsa2usb_cmd_t *);
static mblk_t *scsa2usb_bp_to_mblk(scsa2usb_state_t *);
int scsa2usb_handle_data_start(scsa2usb_state_t *,
scsa2usb_cmd_t *, usb_bulk_req_t *);
void scsa2usb_handle_data_done(scsa2usb_state_t *,
scsa2usb_cmd_t *cmd, usb_bulk_req_t *);
usb_bulk_req_t *scsa2usb_init_bulk_req(scsa2usb_state_t *,
size_t, uint_t, usb_req_attrs_t, usb_flags_t);
int scsa2usb_bulk_timeout(int);
int scsa2usb_clear_ept_stall(scsa2usb_state_t *, uint_t,
usb_pipe_handle_t, char *);
static void scsa2usb_pkt_completion(scsa2usb_state_t *, struct scsi_pkt *);
static int scsa2usb_reconnect_event_cb(dev_info_t *);
static int scsa2usb_disconnect_event_cb(dev_info_t *);
static int scsa2usb_cpr_suspend(dev_info_t *);
static void scsa2usb_cpr_resume(dev_info_t *);
static void scsa2usb_restore_device_state(dev_info_t *, scsa2usb_state_t *);
static void scsa2usb_create_pm_components(dev_info_t *, scsa2usb_state_t *);
static void scsa2usb_raise_power(scsa2usb_state_t *);
static int scsa2usb_pwrlvl0(scsa2usb_state_t *);
static int scsa2usb_pwrlvl1(scsa2usb_state_t *);
static int scsa2usb_pwrlvl2(scsa2usb_state_t *);
static int scsa2usb_pwrlvl3(scsa2usb_state_t *);
static int scsa2usb_power(dev_info_t *, int comp, int level);
static void scsa2usb_pm_busy_component(scsa2usb_state_t *);
static void scsa2usb_pm_idle_component(scsa2usb_state_t *);
extern int scsa2usb_bulk_only_transport(scsa2usb_state_t *,
scsa2usb_cmd_t *);
extern int scsa2usb_bulk_only_get_max_lun(scsa2usb_state_t *);
extern int scsa2usb_cbi_transport(scsa2usb_state_t *, scsa2usb_cmd_t *);
extern void scsa2usb_cbi_stop_intr_polling(scsa2usb_state_t *);
static char *scsa2usb_cmds[] = {
"\000tur",
"\001rezero",
"\003rqsense",
"\004format",
"\014cartprot",
"\022inquiry",
"\026tranlba",
"\030fmtverify",
"\032modesense",
"\033start",
"\035snddiag",
"\036doorlock",
"\043formatcap",
"\045readcap",
"\050read10",
"\052write10",
"\053seek10",
"\056writeverify",
"\057verify",
"\065synchcache",
"\076readlong",
"\077writelong",
"\102readsubchan",
"\103readtoc",
"\104readhdr",
"\105playaudio10",
"\107playaudio_msf",
"\110playaudio_ti",
"\111playtrk_r10",
"\112geteventnotify",
"\113pause_resume",
"\116stop/play_scan",
"\121readdiscinfo",
"\122readtrkinfo",
"\123reservedtrk",
"\124sendopcinfo",
"\125modeselect",
"\132modesense",
"\133closetrksession",
"\135sendcuesheet",
"\136prin",
"\137prout",
"\210read16",
"\212write16",
"\241blankcd",
"\245playaudio12",
"\250read12",
"\251playtrk12",
"\252write12",
"\254getperf",
"\271readcdmsf",
"\273setcdspeed",
"\275mechanism_sts",
"\276readcd",
NULL
};
#define X UINT16_MAX
static struct quirk {
uint16_t q_vid;
uint16_t q_pid;
uint16_t q_rev;
uint16_t q_attr;
} scsa2usb_quirks[] = {
{MS_IOMEGA_VID, MS_IOMEGA_PID1_ZIP100, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_PM},
{MS_IOMEGA_VID, MS_IOMEGA_PID2_ZIP100, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_PM},
{MS_IOMEGA_VID, MS_IOMEGA_PID3_ZIP100, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_PM},
{MS_IOMEGA_VID, MS_IOMEGA_PID_ZIP250, X, SCSA2USB_ATTRS_GET_LUN},
{MS_IOMEGA_VID, MS_IOMEGA_PID_CLIK, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_START_STOP},
{MS_TOSHIBA_VID, MS_TOSHIBA_PID0, X,
SCSA2USB_ATTRS_GET_LUN},
{MS_PNY_VID, MS_PNY_PID0, X,
SCSA2USB_ATTRS_GET_LUN},
{MS_SMSC_VID, X, X, SCSA2USB_ATTRS_START_STOP},
{MS_HAGIWARA_SYS_COM_VID, MS_HAGIWARA_SYSCOM_PID1, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_START_STOP},
{MS_HAGIWARA_SYS_COM_VID, MS_HAGIWARA_SYSCOM_PID2, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_START_STOP},
{MS_HAGIWARA_SYS_COM_VID, MS_HAGIWARA_SYSCOM_PID3, X,
SCSA2USB_ATTRS_START_STOP},
{MS_HAGIWARA_SYS_COM_VID, MS_HAGIWARA_SYSCOM_PID4, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_START_STOP},
{MS_HAGIWARA_SYS_COM_VID, MS_HAGIWARA_SYSCOM_PID5, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_START_STOP},
{MS_MITSUMI_VID, X, X, SCSA2USB_ATTRS_BIG_TIMEOUT |
SCSA2USB_ATTRS_GET_CONF | SCSA2USB_ATTRS_GET_PERF},
{MS_NEODIO_VID, MS_NEODIO_DEVICE_3050, X,
SCSA2USB_ATTRS_MODE_SENSE },
{MS_SONY_FLASH_VID, MS_SONY_FLASH_PID, X,
SCSA2USB_ATTRS_REDUCED_CMD},
{MS_TREK_FLASH_VID, MS_TREK_FLASH_PID, X,
SCSA2USB_ATTRS_REDUCED_CMD},
{MS_PENN_FLASH_VID, MS_PENN_FLASH_PID, X,
SCSA2USB_ATTRS_REDUCED_CMD},
{MS_SIMPLETECH_VID, MS_SIMPLETECH_PID1, X,
SCSA2USB_ATTRS_REDUCED_CMD},
{MS_ADDONICS_CARD_READER_VID, MS_ADDONICS_CARD_READER_PID,
X, SCSA2USB_ATTRS_REDUCED_CMD},
{MS_ACOMDATA_VID, MS_ACOMDATA_PID1, X,
SCSA2USB_ATTRS_USE_CSW_RESIDUE},
{MS_OTI_VID, MS_OTI_DEVICE_6828, X,
SCSA2USB_ATTRS_USE_CSW_RESIDUE},
{MS_AMI_VID, MS_AMI_VIRTUAL_FLOPPY, X,
SCSA2USB_ATTRS_NO_MEDIA_CHECK},
{MS_SCANLOGIC_VID, MS_SCANLOGIC_PID1, X,
SCSA2USB_ATTRS_NO_CAP_ADJUST},
{MS_SUPERTOP_VID, MS_SUPERTOP_DEVICE_6600, X,
SCSA2USB_ATTRS_USE_CSW_RESIDUE},
{MS_AIGO_VID, MS_AIGO_DEVICE_6981, X,
SCSA2USB_ATTRS_USE_CSW_RESIDUE},
{MS_ALCOR_VID, MS_ALCOR_PID0, X,
SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_USE_CSW_RESIDUE},
{MS_WD_VID, MS_WD_PID, X,
SCSA2USB_ATTRS_INQUIRY_EVPD},
{MS_INSYDE_VID, MS_INSYDE_PID_CDROM, X,
SCSA2USB_ATTRS_MODE_SENSE},
};
struct scsa2usb_subclass_protocol_override {
char *name;
int value;
};
static struct scsa2usb_subclass_protocol_override scsa2usb_protocol[] = {
{"CB", SCSA2USB_CB_PROTOCOL},
{"CBI", SCSA2USB_CBI_PROTOCOL},
{"BO", SCSA2USB_BULK_ONLY_PROTOCOL}
};
static struct scsa2usb_subclass_protocol_override scsa2usb_subclass[] = {
{"SCSI", SCSA2USB_SCSI_CMDSET},
{"ATAPI", SCSA2USB_ATAPI_CMDSET},
{"UFI", SCSA2USB_UFI_CMDSET}
};
#define N_SCSA2USB_SUBC_OVERRIDE (sizeof (scsa2usb_subclass))/ \
sizeof (struct scsa2usb_subclass_protocol_override)
#define N_SCSA2USB_PROT_OVERRIDE (sizeof (scsa2usb_protocol))/ \
sizeof (struct scsa2usb_subclass_protocol_override)
static void *scsa2usb_statep;
static boolean_t scsa2usb_sync_message = B_TRUE;
uint_t scsa2usb_errmask = (uint_t)DPRINT_MASK_ALL;
uint_t scsa2usb_errlevel = USB_LOG_L4;
uint_t scsa2usb_instance_debug = (uint_t)-1;
uint_t scsa2usb_scsi_bus_config_debug = 0;
uint_t scsa2usb_long_timeout = 50 * SCSA2USB_BULK_PIPE_TIMEOUT;
uint_t scsa2usb_max_bulk_xfer_size = SCSA2USB_MAX_BULK_XFER_SIZE;
#ifdef SCSA2USB_BULK_ONLY_TEST
static int scsa2usb_test_case_5 = 0;
int scsa2usb_test_case_8 = 0;
int scsa2usb_test_case_10 = 0;
static int scsa2usb_test_case_11 = 0;
static void scsa2usb_test_mblk(scsa2usb_state_t *, boolean_t);
#endif
static int scsa2usb_ugen_open(dev_t *, int, int, cred_t *);
static int scsa2usb_ugen_close(dev_t, int, int, cred_t *);
static int scsa2usb_ugen_read(dev_t, struct uio *, cred_t *);
static int scsa2usb_ugen_write(dev_t, struct uio *, cred_t *);
static int scsa2usb_ugen_poll(dev_t, short, int, short *,
struct pollhead **);
static struct cb_ops scsa2usb_cbops = {
scsa2usb_ugen_open,
scsa2usb_ugen_close,
nodev,
nodev,
nodev,
scsa2usb_ugen_read,
scsa2usb_ugen_write,
nodev,
nodev,
nodev,
nodev,
scsa2usb_ugen_poll,
ddi_prop_op,
NULL,
D_MP,
CB_REV,
nodev,
nodev
};
static struct dev_ops scsa2usb_ops = {
DEVO_REV,
0,
scsa2usb_info,
nulldev,
nulldev,
scsa2usb_attach,
scsa2usb_detach,
nodev,
&scsa2usb_cbops,
NULL,
scsa2usb_power,
ddi_quiesce_not_needed,
};
static struct modldrv modldrv = {
&mod_driverops,
"SCSA to USB Driver",
&scsa2usb_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
static usb_event_t scsa2usb_events = {
scsa2usb_disconnect_event_cb,
scsa2usb_reconnect_event_cb,
NULL, NULL
};
int
_init(void)
{
int rval;
if (((rval = ddi_soft_state_init(&scsa2usb_statep,
sizeof (scsa2usb_state_t), SCSA2USB_INITIAL_ALLOC)) != 0)) {
return (rval);
}
if ((rval = scsi_hba_init(&modlinkage)) != 0) {
ddi_soft_state_fini(&scsa2usb_statep);
return (rval);
}
if ((rval = mod_install(&modlinkage)) != 0) {
scsi_hba_fini(&modlinkage);
ddi_soft_state_fini(&scsa2usb_statep);
return (rval);
}
return (rval);
}
int
_fini(void)
{
int rval;
if ((rval = mod_remove(&modlinkage)) == 0) {
scsi_hba_fini(&modlinkage);
ddi_soft_state_fini(&scsa2usb_statep);
}
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
scsa2usb_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
void *arg, void **result)
{
scsa2usb_state_t *scsa2usbp = NULL;
int error = DDI_FAILURE;
int instance = SCSA2USB_MINOR_TO_INSTANCE(getminor((dev_t)arg));
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (((scsa2usbp = ddi_get_soft_state(scsa2usb_statep,
instance)) != NULL) &&
scsa2usbp->scsa2usb_dip) {
*result = scsa2usbp->scsa2usb_dip;
error = DDI_SUCCESS;
} else {
*result = NULL;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(uintptr_t)instance;
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
static int
scsa2usb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance = ddi_get_instance(dip);
int interface;
uint_t lun;
boolean_t ept_check = B_TRUE;
scsi_hba_tran_t *tran;
scsa2usb_state_t *scsa2usbp;
usb_log_handle_t log_handle;
usb_ep_data_t *ep_data;
usb_client_dev_data_t *dev_data;
usb_alt_if_data_t *altif_data;
usb_ugen_info_t usb_ugen_info;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, NULL,
"scsa2usb_attach: dip = 0x%p", (void *)dip);
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
scsa2usb_cpr_resume(dip);
return (DDI_SUCCESS);
default:
USB_DPRINTF_L2(DPRINT_MASK_SCSA, NULL,
"scsa2usb_attach: failed");
return (DDI_FAILURE);
}
if (ddi_soft_state_zalloc(scsa2usb_statep, instance) != DDI_SUCCESS) {
ddi_prop_remove_all(dip);
return (DDI_FAILURE);
}
if ((scsa2usbp = ddi_get_soft_state(scsa2usb_statep,
instance)) == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, NULL,
"scsa2usb%d: bad soft state", instance);
ddi_prop_remove_all(dip);
return (DDI_FAILURE);
}
scsa2usbp->scsa2usb_dip = dip;
scsa2usbp->scsa2usb_instance = instance;
scsa2usbp->scsa2usb_log_handle = log_handle =
usb_alloc_log_hdl(dip, "s2u",
&scsa2usb_errlevel,
&scsa2usb_errmask, &scsa2usb_instance_debug,
0);
if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"usb_client_attach failed");
goto fail;
}
if (usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0) !=
USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"usb_get_dev_data failed");
goto fail;
}
mutex_init(&scsa2usbp->scsa2usb_mutex, NULL, MUTEX_DRIVER,
dev_data->dev_iblock_cookie);
cv_init(&scsa2usbp->scsa2usb_transport_busy_cv, NULL, CV_DRIVER, NULL);
for (lun = 0; lun < SCSA2USB_MAX_LUNS; lun++) {
usba_init_list(&scsa2usbp->scsa2usb_waitQ[lun], NULL,
dev_data->dev_iblock_cookie);
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_dip = dip;
scsa2usbp->scsa2usb_instance = instance;
scsa2usbp->scsa2usb_attrs = SCSA2USB_ALL_ATTRS;
scsa2usbp->scsa2usb_dev_data = dev_data;
scsa2usbp->scsa2usb_default_pipe = dev_data->dev_default_ph;
scsa2usbp->scsa2usb_flags |= SCSA2USB_FLAGS_LOCKS_INIT;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, log_handle,
"curr_cfg=%ld, curr_if=%d",
(long)(dev_data->dev_curr_cfg - &dev_data->dev_cfg[0]),
dev_data->dev_curr_if);
interface = dev_data->dev_curr_if;
scsa2usbp->scsa2usb_intfc_num = dev_data->dev_curr_if;
altif_data = &dev_data->dev_curr_cfg->cfg_if[interface].if_alt[0];
if (altif_data->altif_n_ep == 0) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"invalid alt 0 for interface %d", interface);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
if (altif_data->altif_descr.bInterfaceClass !=
USB_CLASS_MASS_STORAGE) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"invalid interface class (0x%x)",
altif_data->altif_descr.bInterfaceClass);
}
scsa2usbp->scsa2usb_intfc_descr = altif_data->altif_descr;
if ((ep_data = usb_lookup_ep_data(dip, dev_data, interface, 0, 0,
USB_EP_ATTR_BULK, USB_EP_DIR_OUT)) != NULL) {
if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
dip, ep_data, &scsa2usbp->scsa2usb_bulkout_xept) !=
USB_SUCCESS) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
}
if ((ep_data = usb_lookup_ep_data(dip, dev_data, interface, 0, 0,
USB_EP_ATTR_BULK, USB_EP_DIR_IN)) != NULL) {
if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
dip, ep_data, &scsa2usbp->scsa2usb_bulkin_xept) !=
USB_SUCCESS) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
}
if ((ep_data = usb_lookup_ep_data(dip, dev_data, interface, 0, 0,
USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) {
if (usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
dip, ep_data, &scsa2usbp->scsa2usb_intr_xept) !=
USB_SUCCESS) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
}
scsa2usb_override(scsa2usbp);
USB_DPRINTF_L3(DPRINT_MASK_SCSA, log_handle,
"protocol=0x%x override=0x%x subclass=0x%x override=0x%x",
scsa2usbp->scsa2usb_intfc_descr.bInterfaceProtocol,
scsa2usbp->scsa2usb_protocol_override,
scsa2usbp->scsa2usb_intfc_descr.bInterfaceSubClass,
scsa2usbp->scsa2usb_subclass_override);
switch (scsa2usbp->scsa2usb_intfc_descr.bInterfaceProtocol) {
case USB_PROTO_MS_CBI:
scsa2usbp->scsa2usb_cmd_protocol |= SCSA2USB_CB_PROTOCOL;
break;
case USB_PROTO_MS_CBI_WC:
scsa2usbp->scsa2usb_cmd_protocol |= SCSA2USB_CBI_PROTOCOL;
break;
case USB_PROTO_MS_ISD_1999_SILICN:
case USB_PROTO_MS_BULK_ONLY:
scsa2usbp->scsa2usb_cmd_protocol |= SCSA2USB_BULK_ONLY_PROTOCOL;
break;
default:
if (scsa2usbp->scsa2usb_protocol_override) {
scsa2usbp->scsa2usb_cmd_protocol |=
scsa2usbp->scsa2usb_protocol_override;
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"overriding protocol %x",
scsa2usbp->scsa2usb_intfc_descr.bInterfaceProtocol);
break;
}
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"unsupported protocol = %x",
scsa2usbp->scsa2usb_intfc_descr.bInterfaceProtocol);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
switch (scsa2usbp->scsa2usb_intfc_descr.bInterfaceSubClass) {
case USB_SUBCLS_MS_SCSI:
scsa2usbp->scsa2usb_cmd_protocol |= SCSA2USB_SCSI_CMDSET;
break;
case USB_SUBCLS_MS_SFF8020I:
case USB_SUBCLS_MS_SFF8070I:
scsa2usbp->scsa2usb_cmd_protocol |= SCSA2USB_ATAPI_CMDSET;
break;
case USB_SUBCLS_MS_UFI:
scsa2usbp->scsa2usb_cmd_protocol |= SCSA2USB_UFI_CMDSET;
break;
default:
if (scsa2usbp->scsa2usb_subclass_override) {
scsa2usbp->scsa2usb_cmd_protocol |=
scsa2usbp->scsa2usb_subclass_override;
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"overriding subclass %x",
scsa2usbp->scsa2usb_intfc_descr.bInterfaceSubClass);
break;
}
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"unsupported subclass = %x",
scsa2usbp->scsa2usb_intfc_descr.bInterfaceSubClass);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
if (SCSA2USB_IS_BULK_ONLY(scsa2usbp) || SCSA2USB_IS_CB(scsa2usbp)) {
if ((scsa2usbp->scsa2usb_bulkout_ept.bLength == 0) ||
(scsa2usbp->scsa2usb_bulkin_ept.bLength == 0)) {
ept_check = B_FALSE;
}
} else if (SCSA2USB_IS_CBI(scsa2usbp)) {
if ((scsa2usbp->scsa2usb_bulkout_ept.bLength == 0) ||
(scsa2usbp->scsa2usb_bulkin_ept.bLength == 0) ||
(scsa2usbp->scsa2usb_intr_ept.bLength == 0)) {
ept_check = B_FALSE;
}
}
if (ept_check == B_FALSE) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"scsa2usb%d doesn't support minimum required endpoints",
instance);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
scsa2usb_detect_quirks(scsa2usbp);
if (scsa2usbp->scsa2usb_dev_data->dev_serial) {
USB_DPRINTF_L4(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle, "Serial Number = %s",
scsa2usbp->scsa2usb_dev_data->dev_serial);
}
tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
scsa2usbp->scsa2usb_tran = tran;
tran->tran_hba_private = scsa2usbp;
tran->tran_tgt_private = NULL;
tran->tran_tgt_init = scsa2usb_scsi_tgt_init;
tran->tran_tgt_probe = scsa2usb_scsi_tgt_probe;
tran->tran_tgt_free = scsa2usb_scsi_tgt_free;
tran->tran_start = scsa2usb_scsi_start;
tran->tran_abort = scsa2usb_scsi_abort;
tran->tran_reset = scsa2usb_scsi_reset;
tran->tran_getcap = scsa2usb_scsi_getcap;
tran->tran_setcap = scsa2usb_scsi_setcap;
tran->tran_init_pkt = scsa2usb_scsi_init_pkt;
tran->tran_destroy_pkt = scsa2usb_scsi_destroy_pkt;
tran->tran_dmafree = NULL;
tran->tran_sync_pkt = NULL;
tran->tran_reset_notify = NULL;
tran->tran_get_bus_addr = NULL;
tran->tran_get_name = NULL;
tran->tran_quiesce = NULL;
tran->tran_unquiesce = NULL;
tran->tran_bus_reset = NULL;
tran->tran_add_eventcall = NULL;
tran->tran_get_eventcookie = NULL;
tran->tran_post_event = NULL;
tran->tran_remove_eventcall = NULL;
tran->tran_bus_config = scsa2usb_scsi_bus_config;
tran->tran_bus_unconfig = scsa2usb_scsi_bus_unconfig;
if (scsi_hba_attach_setup(dip, usba_get_hc_dma_attr(dip), tran, 0)) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"scsi_hba_attach_setup failed");
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
scsa2usbp->scsa2usb_flags |= SCSA2USB_FLAGS_HBA_ATTACH_SETUP;
if (ddi_create_minor_node(dip, "scsa2usb", S_IFCHR,
instance << SCSA2USB_MINOR_INSTANCE_SHIFT,
DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsi_attach: ddi_create_minor_node failed");
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
if (scsa2usb_open_usb_pipes(scsa2usbp) == USB_FAILURE) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"error opening pipes");
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto fail;
}
for (lun = 0; lun < SCSA2USB_MAX_LUNS; lun++) {
scsa2usbp->scsa2usb_lbasize[lun] = DEV_BSIZE;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsa2usb_panic_callb_init(scsa2usbp);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_dev_state = USB_DEV_ONLINE;
scsa2usb_create_pm_components(dip, scsa2usbp);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if (usb_register_event_cbs(scsa2usbp->scsa2usb_dip, &scsa2usb_events,
0) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, log_handle,
"error cb registering");
goto fail;
}
usb_free_descr_tree(dip, dev_data);
scsa2usb_pm_idle_component(scsa2usbp);
if (scsa2usbp->scsa2usb_override_str) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb.conf override: %s",
scsa2usbp->scsa2usb_override_str);
}
if (usb_owns_device(dip)) {
bzero(&usb_ugen_info, sizeof (usb_ugen_info));
usb_ugen_info.usb_ugen_flags = 0;
usb_ugen_info.usb_ugen_minor_node_ugen_bits_mask =
(dev_t)SCSA2USB_MINOR_UGEN_BITS_MASK;
usb_ugen_info.usb_ugen_minor_node_instance_mask =
(dev_t)~SCSA2USB_MINOR_UGEN_BITS_MASK;
scsa2usbp->scsa2usb_ugen_hdl =
usb_ugen_get_hdl(dip, &usb_ugen_info);
if (usb_ugen_attach(scsa2usbp->scsa2usb_ugen_hdl, cmd) !=
USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"usb_ugen_attach failed");
usb_ugen_release_hdl(scsa2usbp->scsa2usb_ugen_hdl);
scsa2usbp->scsa2usb_ugen_hdl = NULL;
}
}
ddi_report_dev(dip);
return (DDI_SUCCESS);
fail:
if (scsa2usbp) {
(void) scsa2usb_cleanup(dip, scsa2usbp);
}
return (DDI_FAILURE);
}
static int
scsa2usb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
scsi_hba_tran_t *tran;
scsa2usb_state_t *scsa2usbp;
int rval;
tran = ddi_get_driver_private(dip);
ASSERT(tran != NULL);
scsa2usbp = (scsa2usb_state_t *)tran->tran_hba_private;
ASSERT(scsa2usbp);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_detach: dip = 0x%p, cmd = %d", (void *)dip, cmd);
switch (cmd) {
case DDI_DETACH:
if (scsa2usb_cleanup(dip, scsa2usbp) != USB_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_SUSPEND:
rval = scsa2usb_cpr_suspend(dip);
return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
default:
return (DDI_FAILURE);
}
}
static int
scsa2usb_ugen_open(dev_t *devp, int flag, int sflag, cred_t *cr)
{
scsa2usb_state_t *scsa2usbp;
int rval;
if ((scsa2usbp = ddi_get_soft_state(scsa2usb_statep,
SCSA2USB_MINOR_TO_INSTANCE(getminor(*devp)))) == NULL) {
return (ENXIO);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_ugen_open: dev_t=0x%lx", *devp);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (scsa2usbp->scsa2usb_busy_proc != curproc) {
while (scsa2usbp->scsa2usb_transport_busy ||
(scsa2usb_all_waitQs_empty(scsa2usbp) !=
USB_SUCCESS)) {
rval = cv_wait_sig(
&scsa2usbp->scsa2usb_transport_busy_cv,
&scsa2usbp->scsa2usb_mutex);
if (rval == 0) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (EINTR);
}
}
scsa2usbp->scsa2usb_transport_busy++;
scsa2usbp->scsa2usb_busy_proc = curproc;
}
scsa2usbp->scsa2usb_ugen_open_count++;
scsa2usb_raise_power(scsa2usbp);
scsa2usb_close_usb_pipes(scsa2usbp);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
rval = usb_ugen_open(scsa2usbp->scsa2usb_ugen_hdl, devp, flag,
sflag, cr);
if (!rval) {
usb_ugen_hdl_impl_t *usb_ugen_hdl_impl;
ugen_state_t *ugenp;
int ugen_minor, clone;
mutex_enter(&scsa2usbp->scsa2usb_mutex);
usb_ugen_hdl_impl =
(usb_ugen_hdl_impl_t *)scsa2usbp->scsa2usb_ugen_hdl;
ugenp = usb_ugen_hdl_impl->hdl_ugenp;
for (clone = ugenp->ug_minor_node_table_index + 1;
clone < SCSA2USB_MAX_CLONE; clone++) {
if (!scsa2usbp->scsa2usb_clones[clone])
break;
}
if (clone >= SCSA2USB_MAX_CLONE) {
cmn_err(CE_WARN, "scsa2usb_ugen_open: too many clones");
rval = EBUSY;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
goto open_done;
}
ugen_minor = getminor(*devp) & SCSA2USB_MINOR_UGEN_BITS_MASK;
*devp = makedevice(getmajor(*devp),
(scsa2usbp->scsa2usb_instance
<< SCSA2USB_MINOR_INSTANCE_SHIFT)
+ clone);
scsa2usbp->scsa2usb_clones[clone] = (uint8_t)ugen_minor;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_ugen_open: new dev=%lx, old minor=%x",
*devp, ugen_minor);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
open_done:
if (rval) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (--scsa2usbp->scsa2usb_ugen_open_count == 0) {
scsa2usbp->scsa2usb_transport_busy--;
scsa2usbp->scsa2usb_busy_proc = NULL;
cv_signal(&scsa2usbp->scsa2usb_transport_busy_cv);
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsa2usb_pm_idle_component(scsa2usbp);
}
return (rval);
}
static int
scsa2usb_ugen_close(dev_t dev, int flag, int otype, cred_t *cr)
{
int rval;
int ugen_minor, clone;
scsa2usb_state_t *scsa2usbp = ddi_get_soft_state(scsa2usb_statep,
SCSA2USB_MINOR_TO_INSTANCE(getminor(dev)));
if (scsa2usbp == NULL) {
return (ENXIO);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_ugen_close: dev_t=0x%lx", dev);
clone = getminor(dev) & SCSA2USB_MINOR_UGEN_BITS_MASK;
ugen_minor = scsa2usbp->scsa2usb_clones[clone];
dev = makedevice(getmajor(dev),
(scsa2usbp->scsa2usb_instance << SCSA2USB_MINOR_INSTANCE_SHIFT)
+ ugen_minor);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_ugen_close: old dev=%lx", dev);
rval = usb_ugen_close(scsa2usbp->scsa2usb_ugen_hdl, dev, flag,
otype, cr);
if (rval == 0) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_clones[clone] = 0;
if (--scsa2usbp->scsa2usb_ugen_open_count == 0) {
scsa2usbp->scsa2usb_transport_busy--;
scsa2usbp->scsa2usb_busy_proc = NULL;
cv_signal(&scsa2usbp->scsa2usb_transport_busy_cv);
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsa2usb_pm_idle_component(scsa2usbp);
}
return (rval);
}
static int
scsa2usb_ugen_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
int clone, ugen_minor;
scsa2usb_state_t *scsa2usbp = ddi_get_soft_state(scsa2usb_statep,
SCSA2USB_MINOR_TO_INSTANCE(getminor(dev)));
if (scsa2usbp == NULL) {
return (ENXIO);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_ugen_read: dev_t=0x%lx", dev);
clone = getminor(dev) & SCSA2USB_MINOR_UGEN_BITS_MASK;
ugen_minor = scsa2usbp->scsa2usb_clones[clone];
dev = makedevice(getmajor(dev),
(scsa2usbp->scsa2usb_instance << SCSA2USB_MINOR_INSTANCE_SHIFT)
+ ugen_minor);
return (usb_ugen_read(scsa2usbp->scsa2usb_ugen_hdl, dev,
uiop, credp));
}
static int
scsa2usb_ugen_write(dev_t dev, struct uio *uiop, cred_t *credp)
{
int clone, ugen_minor;
scsa2usb_state_t *scsa2usbp = ddi_get_soft_state(scsa2usb_statep,
SCSA2USB_MINOR_TO_INSTANCE(getminor(dev)));
if (scsa2usbp == NULL) {
return (ENXIO);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_ugen_write: dev_t=0x%lx", dev);
clone = getminor(dev) & SCSA2USB_MINOR_UGEN_BITS_MASK;
ugen_minor = scsa2usbp->scsa2usb_clones[clone];
dev = makedevice(getmajor(dev),
(scsa2usbp->scsa2usb_instance << SCSA2USB_MINOR_INSTANCE_SHIFT)
+ ugen_minor);
return (usb_ugen_write(scsa2usbp->scsa2usb_ugen_hdl,
dev, uiop, credp));
}
static int
scsa2usb_ugen_poll(dev_t dev, short events,
int anyyet, short *reventsp, struct pollhead **phpp)
{
int clone, ugen_minor;
scsa2usb_state_t *scsa2usbp = ddi_get_soft_state(scsa2usb_statep,
SCSA2USB_MINOR_TO_INSTANCE(getminor(dev)));
if (scsa2usbp == NULL) {
return (ENXIO);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_ugen_poll: dev_t=0x%lx", dev);
clone = getminor(dev) & SCSA2USB_MINOR_UGEN_BITS_MASK;
ugen_minor = scsa2usbp->scsa2usb_clones[clone];
dev = makedevice(getmajor(dev),
(scsa2usbp->scsa2usb_instance << SCSA2USB_MINOR_INSTANCE_SHIFT)
+ ugen_minor);
return (usb_ugen_poll(scsa2usbp->scsa2usb_ugen_hdl, dev, events,
anyyet, reventsp, phpp));
}
static int
scsa2usb_cleanup(dev_info_t *dip, scsa2usb_state_t *scsa2usbp)
{
int rval, i;
scsa2usb_power_t *pm;
uint_t lun;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_cleanup:");
mutex_enter(&scsa2usbp->scsa2usb_mutex);
for (i = 0; i < SCSA2USB_DRAIN_TIMEOUT; i++) {
if (scsa2usbp->scsa2usb_work_thread_id == NULL) {
break;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
delay(drv_usectohz(1000000));
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if (i >= SCSA2USB_DRAIN_TIMEOUT) {
return (USB_FAILURE);
}
usb_unregister_event_cbs(scsa2usbp->scsa2usb_dip, &scsa2usb_events);
if (scsa2usbp->scsa2usb_flags & SCSA2USB_FLAGS_LOCKS_INIT) {
for (lun = 0; lun < SCSA2USB_MAX_LUNS; lun++) {
scsa2usb_flush_waitQ(scsa2usbp, lun, CMD_TRAN_ERR);
usba_destroy_list(&scsa2usbp->scsa2usb_waitQ[lun]);
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (scsa2usbp->scsa2usb_flags &
SCSA2USB_FLAGS_HBA_ATTACH_SETUP) {
(void) scsi_hba_detach(dip);
scsi_hba_tran_free(scsa2usbp->scsa2usb_tran);
}
if (scsa2usbp->scsa2usb_flags &
SCSA2USB_FLAGS_PIPES_OPENED) {
scsa2usb_close_usb_pipes(scsa2usbp);
}
pm = scsa2usbp->scsa2usb_pm;
if (pm && (scsa2usbp->scsa2usb_dev_state !=
USB_DEV_DISCONNECTED)) {
if (pm->scsa2usb_wakeup_enabled) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
(void) pm_raise_power(dip, 0,
USB_DEV_OS_FULL_PWR);
if ((rval = usb_handle_remote_wakeup(dip,
USB_REMOTE_WAKEUP_DISABLE)) !=
USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"disable remote wakeup failed "
"(%d)", rval);
}
} else {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
if (pm) {
kmem_free(pm, sizeof (scsa2usb_power_t));
}
if (scsa2usbp->scsa2usb_override_str) {
kmem_free(scsa2usbp->scsa2usb_override_str,
strlen(scsa2usbp->scsa2usb_override_str) + 1);
scsa2usbp->scsa2usb_override_str = NULL;
}
ddi_remove_minor_node(dip, NULL);
scsa2usb_panic_callb_fini(scsa2usbp);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
mutex_destroy(&scsa2usbp->scsa2usb_mutex);
cv_destroy(&scsa2usbp->scsa2usb_transport_busy_cv);
}
usb_client_detach(scsa2usbp->scsa2usb_dip,
scsa2usbp->scsa2usb_dev_data);
if (scsa2usbp->scsa2usb_ugen_hdl) {
(void) usb_ugen_detach(scsa2usbp->scsa2usb_ugen_hdl,
DDI_DETACH);
usb_ugen_release_hdl(scsa2usbp->scsa2usb_ugen_hdl);
}
usb_free_log_hdl(scsa2usbp->scsa2usb_log_handle);
ddi_prop_remove_all(dip);
ddi_soft_state_free(scsa2usb_statep, ddi_get_instance(dip));
return (USB_SUCCESS);
}
static void
scsa2usb_override(scsa2usb_state_t *scsa2usbp)
{
scsa2usb_ov_t ov;
char **override_str = NULL;
char *override_str_cpy;
uint_t override_str_len, override_str_cpy_len;
uint_t i;
usb_dev_descr_t *descr = scsa2usbp->scsa2usb_dev_data->dev_descr;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
scsa2usbp->scsa2usb_subclass_override =
scsa2usbp->scsa2usb_protocol_override = 0;
if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, scsa2usbp->scsa2usb_dip,
DDI_PROP_DONTPASS, "attribute-override-list",
&override_str, &override_str_len) != DDI_PROP_SUCCESS) {
return;
}
for (i = 0; i < override_str_len; i++) {
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"override_str[%d] = %s", i, override_str[i]);
override_str_cpy_len = strlen(override_str[i]) + 1;
override_str_cpy = kmem_zalloc(override_str_cpy_len, KM_SLEEP);
(void) strcpy(override_str_cpy, override_str[i]);
bzero(&ov, sizeof (scsa2usb_ov_t));
if (scsa2usb_parse_input_str(override_str[i], &ov,
scsa2usbp) == USB_FAILURE) {
kmem_free(override_str_cpy, override_str_cpy_len);
continue;
}
if (((descr->idVendor == (uint16_t)ov.vid) || (ov.vid == 0)) &&
((descr->idProduct == (uint16_t)ov.pid) || (ov.pid == 0)) &&
((descr->bcdDevice == (uint16_t)ov.rev) || (ov.rev == 0))) {
scsa2usbp->scsa2usb_subclass_override = ov.subclass;
scsa2usbp->scsa2usb_protocol_override = ov.protocol;
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"vid=0x%x pid=0x%x rev=0x%x subclass=0x%x "
"protocol=0x%x "
"pmoff=%d fake_removable=%d modesense=%d "
"reduced-cmd-support=%d",
ov.vid, ov.pid, ov.rev, ov.subclass, ov.protocol,
ov.pmoff, ov.fake_removable, ov.no_modesense,
ov.reduced_cmd_support);
if (ov.pmoff) {
scsa2usbp->scsa2usb_attrs &= ~SCSA2USB_ATTRS_PM;
}
if (ov.fake_removable) {
scsa2usbp->scsa2usb_attrs &=
~SCSA2USB_ATTRS_RMB;
}
if (ov.no_modesense) {
scsa2usbp->scsa2usb_attrs &=
~SCSA2USB_ATTRS_MODE_SENSE;
}
if (ov.reduced_cmd_support) {
scsa2usbp->scsa2usb_attrs &=
~SCSA2USB_ATTRS_REDUCED_CMD;
}
scsa2usbp->scsa2usb_override_str = override_str_cpy;
break;
} else {
kmem_free(override_str_cpy, override_str_cpy_len);
}
}
ddi_prop_free(override_str);
}
static int
scsa2usb_parse_input_str(char *str, scsa2usb_ov_t *ovp,
scsa2usb_state_t *scsa2usbp)
{
char *input_field, *input_value;
char *lasts;
uint_t i;
u_longlong_t value;
for (input_field = strtok_r(str, "=", &lasts);
input_field != NULL;
input_field = strtok_r(lasts, "=", &lasts)) {
if ((input_value = strtok_r(lasts, " ", &lasts)) ==
NULL) {
scsa2usb_override_error("format", scsa2usbp);
return (USB_FAILURE);
}
if (strcmp(input_value, "*") == 0) {
continue;
}
if (strcasecmp(input_field, "vid") == 0) {
if (kobj_getvalue(input_value, &value) == -1) {
scsa2usb_override_error("vendor id", scsa2usbp);
return (USB_FAILURE);
}
ovp->vid = (int)value;
} else if (strcasecmp(input_field, "pid") == 0) {
if (kobj_getvalue(input_value, &value) == -1) {
scsa2usb_override_error("product id",
scsa2usbp);
return (USB_FAILURE);
}
ovp->pid = (int)value;
} else if (strcasecmp(input_field, "rev") == 0) {
if (kobj_getvalue(input_value, &value) == -1) {
scsa2usb_override_error("revision id",
scsa2usbp);
return (USB_FAILURE);
}
ovp->rev = (int)value;
} else if (strcasecmp(input_field, "subclass") == 0) {
for (i = 0; i < N_SCSA2USB_SUBC_OVERRIDE; i++) {
if (strcasecmp(input_value,
scsa2usb_subclass[i].name) == 0) {
ovp->subclass =
scsa2usb_subclass[i].value;
break;
}
}
if (ovp->subclass == 0) {
scsa2usb_override_error("subclass", scsa2usbp);
return (USB_FAILURE);
}
} else if (strcasecmp(input_field, "protocol") == 0) {
for (i = 0; i < N_SCSA2USB_PROT_OVERRIDE; i++) {
if (strcasecmp(input_value,
scsa2usb_protocol[i].name) == 0) {
ovp->protocol =
scsa2usb_protocol[i].value;
break;
}
}
if (ovp->protocol == 0) {
scsa2usb_override_error("protocol", scsa2usbp);
return (USB_FAILURE);
}
} else if (strcasecmp(input_field, "pm") == 0) {
if (strcasecmp(input_value, "off") == 0) {
ovp->pmoff = 1;
break;
} else {
scsa2usb_override_error("pm", scsa2usbp);
return (USB_FAILURE);
}
} else if (strcasecmp(input_field, "removable") == 0) {
if (strcasecmp(input_value, "true") == 0) {
ovp->fake_removable = 1;
break;
} else {
scsa2usb_override_error("removable", scsa2usbp);
return (USB_FAILURE);
}
} else if (strcasecmp(input_field, "modesense") == 0) {
if (strcasecmp(input_value, "false") == 0) {
ovp->no_modesense = 1;
break;
} else {
scsa2usb_override_error("modesense",
scsa2usbp);
return (USB_FAILURE);
}
} else if (strcasecmp(input_field,
"reduced-cmd-support") == 0) {
if (strcasecmp(input_value, "true") == 0) {
ovp->reduced_cmd_support = 1;
break;
} else {
scsa2usb_override_error(
"reduced-cmd-support", scsa2usbp);
return (USB_FAILURE);
}
} else {
scsa2usb_override_error(input_field, scsa2usbp);
return (USB_FAILURE);
}
}
return (USB_SUCCESS);
}
static void
scsa2usb_override_error(char *input_field, scsa2usb_state_t *scsa2usbp)
{
USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"invalid %s in scsa2usb.conf file entry", input_field);
}
static void
scsa2usb_detect_quirks(scsa2usb_state_t *scsa2usbp)
{
int mask;
usb_dev_descr_t *desc = scsa2usbp->scsa2usb_dev_data->dev_descr;
if (!SCSA2USB_IS_BULK_ONLY(scsa2usbp)) {
scsa2usbp->scsa2usb_attrs &= ~SCSA2USB_ATTRS_GET_LUN;
}
for (uint_t i = 0; i < ARRAY_SIZE(scsa2usb_quirks); i++) {
struct quirk *q = &scsa2usb_quirks[i];
if (q->q_vid == desc->idVendor &&
(q->q_pid == desc->idProduct || q->q_pid == X) &&
(q->q_rev == desc->bcdDevice || q->q_rev == X)) {
scsa2usbp->scsa2usb_attrs &= ~(q->q_attr);
break;
}
}
if (desc->idVendor == MS_MITSUMI_VID) {
mask = scsa2usbp->scsa2usb_cmd_protocol & SCSA2USB_CMDSET_MASK;
if (mask) {
scsa2usbp->scsa2usb_cmd_protocol &= ~mask;
}
scsa2usbp->scsa2usb_cmd_protocol |= SCSA2USB_UFI_CMDSET;
}
if (scsa2usbp->scsa2usb_attrs != SCSA2USB_ALL_ATTRS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb attributes modified: 0x%x",
scsa2usbp->scsa2usb_attrs);
}
}
static void
scsa2usb_create_luns(scsa2usb_state_t *scsa2usbp)
{
int lun, rval;
char *compatible[MAX_COMPAT_NAMES];
dev_info_t *cdip;
uchar_t dtype;
char *node_name;
char *driver_name = NULL;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_create_luns:");
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_n_luns = 1;
if ((scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_GET_LUN) == 0) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"get_max_lun cmd not supported");
} else {
if (SCSA2USB_IS_BULK_ONLY(scsa2usbp)) {
scsa2usbp->scsa2usb_n_luns =
scsa2usb_bulk_only_get_max_lun(scsa2usbp);
}
}
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_create_luns: %d luns found", scsa2usbp->scsa2usb_n_luns);
for (lun = 0; lun < scsa2usbp->scsa2usb_n_luns; lun++) {
ASSERT(scsa2usbp->scsa2usb_lun_dip[lun] == NULL);
scsa2usb_do_inquiry(scsa2usbp, 0, lun);
dtype = scsa2usbp->scsa2usb_lun_inquiry[lun].
inq_dtype & DTYPE_MASK;
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"dtype[%d]=0x%x", lun, dtype);
driver_name = NULL;
switch (dtype) {
case DTYPE_DIRECT:
case DTYPE_RODIRECT:
case DTYPE_OPTICAL:
node_name = "disk";
driver_name = "sd";
break;
case DTYPE_SEQUENTIAL:
node_name = "tape";
driver_name = "st";
break;
case DTYPE_PRINTER:
node_name = "printer";
break;
case DTYPE_PROCESSOR:
node_name = "processor";
break;
case DTYPE_WORM:
node_name = "worm";
break;
case DTYPE_SCANNER:
node_name = "scanner";
break;
case DTYPE_CHANGER:
node_name = "changer";
break;
case DTYPE_COMM:
node_name = "comm";
break;
case DTYPE_ARRAY_CTRL:
node_name = "array_ctrl";
break;
case DTYPE_ESI:
node_name = "esi";
driver_name = "ses";
break;
default:
node_name = "generic";
break;
}
if (driver_name) {
compatible[0] = driver_name;
}
ndi_devi_alloc_sleep(scsa2usbp->scsa2usb_dip, node_name,
(pnode_t)DEVI_SID_NODEID, &cdip);
rval = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "target", 0);
if (rval != DDI_PROP_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"ndi_prop_update_int target failed %d", rval);
(void) ndi_devi_free(cdip);
continue;
}
rval = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
"hotpluggable");
if (rval != DDI_PROP_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"ndi_prop_create_boolean hotpluggable failed %d",
rval);
ddi_prop_remove_all(cdip);
(void) ndi_devi_free(cdip);
continue;
}
rval = ndi_prop_update_int(DDI_DEV_T_NONE, cdip,
"pm-capable", 1);
if (rval != DDI_PROP_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"ndi_prop_update_int pm-capable failed %d", rval);
ddi_prop_remove_all(cdip);
(void) ndi_devi_free(cdip);
continue;
}
rval = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "lun", lun);
if (rval != DDI_PROP_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"ndi_prop_update_int lun failed %d", rval);
ddi_prop_remove_all(cdip);
(void) ndi_devi_free(cdip);
continue;
}
if (driver_name) {
rval = ndi_prop_update_string_array(DDI_DEV_T_NONE,
cdip, "compatible", (char **)compatible,
MAX_COMPAT_NAMES);
if (rval != DDI_PROP_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"ndi_prop_update_string_array failed %d",
rval);
ddi_prop_remove_all(cdip);
(void) ndi_devi_free(cdip);
continue;
}
}
rval = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip, "usb");
if (rval != DDI_PROP_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"ndi_prop_create_boolean failed %d", rval);
ddi_prop_remove_all(cdip);
(void) ndi_devi_free(cdip);
continue;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
(void) ddi_initchild(scsa2usbp->scsa2usb_dip, cdip);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
usba_set_usba_device(cdip,
usba_get_usba_device(scsa2usbp->scsa2usb_dip));
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
static int
scsa2usb_is_usb(dev_info_t *dip)
{
if (dip) {
return (ddi_prop_exists(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "usb"));
}
return (0);
}
static void
scsa2usb_panic_callb_init(scsa2usb_state_t *scsa2usbp)
{
scsa2usbp->scsa2usb_panic_info =
kmem_zalloc(sizeof (scsa2usb_cpr_t), KM_SLEEP);
mutex_init(&scsa2usbp->scsa2usb_panic_info->lockp,
NULL, MUTEX_DRIVER,
scsa2usbp->scsa2usb_dev_data->dev_iblock_cookie);
scsa2usbp->scsa2usb_panic_info->statep = scsa2usbp;
scsa2usbp->scsa2usb_panic_info->cpr.cc_lockp =
&scsa2usbp->scsa2usb_panic_info->lockp;
scsa2usbp->scsa2usb_panic_info->cpr.cc_id =
callb_add(scsa2usb_panic_callb,
(void *)scsa2usbp->scsa2usb_panic_info,
CB_CL_PANIC, "scsa2usb");
}
static void
scsa2usb_panic_callb_fini(scsa2usb_state_t *scsa2usbp)
{
if (scsa2usbp->scsa2usb_panic_info) {
SCSA2USB_CANCEL_CB(scsa2usbp->scsa2usb_panic_info->cpr.cc_id);
mutex_destroy(&scsa2usbp->scsa2usb_panic_info->lockp);
scsa2usbp->scsa2usb_panic_info->statep = NULL;
kmem_free(scsa2usbp->scsa2usb_panic_info,
sizeof (scsa2usb_cpr_t));
scsa2usbp->scsa2usb_panic_info = NULL;
}
}
static boolean_t
scsa2usb_panic_callb(void *arg, int code)
{
scsa2usb_cpr_t *cpr_infop;
scsa2usb_state_t *scsa2usbp;
uint_t lun;
_NOTE(NO_COMPETING_THREADS_NOW);
cpr_infop = (scsa2usb_cpr_t *)arg;
scsa2usbp = (scsa2usb_state_t *)cpr_infop->statep;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_panic_callb: code=%d", code);
if (scsa2usbp->scsa2usb_cur_pkt) {
scsa2usbp->scsa2usb_cur_pkt->pkt_reason = CMD_CMPLT;
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usb_pkt_completion(scsa2usbp, scsa2usbp->scsa2usb_cur_pkt);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
for (lun = 0; lun < SCSA2USB_MAX_LUNS; lun++) {
scsa2usb_flush_waitQ(scsa2usbp, lun, CMD_CMPLT);
}
#ifndef lint
_NOTE(COMPETING_THREADS_NOW);
#endif
return (B_TRUE);
}
static int
scsa2usb_cpr_suspend(dev_info_t *dip)
{
scsa2usb_state_t *scsa2usbp;
int prev_state;
int rval = USB_FAILURE;
scsa2usbp = ddi_get_soft_state(scsa2usb_statep, ddi_get_instance(dip));
ASSERT(scsa2usbp != NULL);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_cpr_suspend:");
mutex_enter(&scsa2usbp->scsa2usb_mutex);
switch (scsa2usbp->scsa2usb_dev_state) {
case USB_DEV_ONLINE:
case USB_DEV_PWRED_DOWN:
case USB_DEV_DISCONNECTED:
prev_state = scsa2usbp->scsa2usb_dev_state;
scsa2usbp->scsa2usb_dev_state = USB_DEV_SUSPENDED;
if (SCSA2USB_BUSY(scsa2usbp)) {
USB_DPRINTF_L3(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_cpr_suspend: I/O active");
scsa2usbp->scsa2usb_dev_state = prev_state;
} else {
rval = USB_SUCCESS;
}
break;
case USB_DEV_SUSPENDED:
default:
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_cpr_suspend: Illegal dev state: %d",
scsa2usbp->scsa2usb_dev_state);
break;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if ((rval == USB_SUCCESS) && scsa2usbp->scsa2usb_ugen_hdl) {
rval = usb_ugen_detach(scsa2usbp->scsa2usb_ugen_hdl,
DDI_SUSPEND);
}
return (rval);
}
static void
scsa2usb_cpr_resume(dev_info_t *dip)
{
scsa2usb_state_t *scsa2usbp =
ddi_get_soft_state(scsa2usb_statep, ddi_get_instance(dip));
ASSERT(scsa2usbp != NULL);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_cpr_resume: dip = 0x%p", (void *)dip);
scsa2usb_restore_device_state(dip, scsa2usbp);
if (scsa2usbp->scsa2usb_ugen_hdl) {
(void) usb_ugen_attach(scsa2usbp->scsa2usb_ugen_hdl,
DDI_RESUME);
}
}
static void
scsa2usb_restore_device_state(dev_info_t *dip, scsa2usb_state_t *scsa2usbp)
{
uint_t prev_state;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_restore_device_state:");
mutex_enter(&scsa2usbp->scsa2usb_mutex);
prev_state = scsa2usbp->scsa2usb_dev_state;
scsa2usb_raise_power(scsa2usbp);
ASSERT((prev_state == USB_DEV_DISCONNECTED) ||
(prev_state == USB_DEV_SUSPENDED));
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if (usb_check_same_device(dip, scsa2usbp->scsa2usb_log_handle,
USB_LOG_L0, DPRINT_MASK_ALL, USB_CHK_ALL, NULL) != USB_SUCCESS) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_dev_state = USB_DEV_DISCONNECTED;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsa2usb_pm_idle_component(scsa2usbp);
return;
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (scsa2usbp->scsa2usb_pm &&
scsa2usbp->scsa2usb_pm->scsa2usb_wakeup_enabled) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
(void) usb_handle_remote_wakeup(scsa2usbp->scsa2usb_dip,
USB_REMOTE_WAKEUP_ENABLE);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
scsa2usbp->scsa2usb_dev_state = USB_DEV_ONLINE;
scsa2usbp->scsa2usb_pkt_state = SCSA2USB_PKT_NONE;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsa2usb_pm_idle_component(scsa2usbp);
}
static int
scsa2usb_scsi_tgt_probe(struct scsi_device *sd, int (*waitfunc)(void))
{
scsi_hba_tran_t *tran;
scsa2usb_state_t *scsa2usbp;
dev_info_t *dip = ddi_get_parent(sd->sd_dev);
int rval;
ASSERT(dip);
tran = ddi_get_driver_private(dip);
ASSERT(tran != NULL);
scsa2usbp = (scsa2usb_state_t *)tran->tran_hba_private;
ASSERT(scsa2usbp);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_tgt_probe:");
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (SCSIPROBE_FAILURE);
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_tgt_probe: scsi_device = 0x%p", (void *)sd);
if ((rval = scsi_hba_probe(sd, waitfunc)) == SCSIPROBE_EXISTS) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_RMB)) {
_NOTE(SCHEME_PROTECTS_DATA("unshared", scsi_inquiry))
sd->sd_inq->inq_rmb = 1;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
return (rval);
}
static int
scsa2usb_scsi_tgt_init(dev_info_t *dip, dev_info_t *cdip,
scsi_hba_tran_t *tran, struct scsi_device *sd)
{
scsa2usb_state_t *scsa2usbp = (scsa2usb_state_t *)
tran->tran_hba_private;
int lun;
int t_len = sizeof (lun);
if (ddi_prop_op(DDI_DEV_T_ANY, cdip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS|DDI_PROP_CANSLEEP, "lun", (caddr_t)&lun,
&t_len) != DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_tgt_init: %s, lun%d", ddi_driver_name(cdip), lun);
if (scsa2usb_is_usb(cdip) == 0) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_tgt_init: new child %s%d",
ddi_driver_name(cdip), ddi_get_instance(cdip));
if (ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip, "usb") !=
DDI_PROP_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"ndi_prop_create_boolean failed");
return (DDI_FAILURE);
}
usba_set_usba_device(cdip,
usba_get_usba_device(scsa2usbp->scsa2usb_dip));
return (DDI_SUCCESS);
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if ((lun >= scsa2usbp->scsa2usb_n_luns) ||
(scsa2usbp->scsa2usb_lun_dip[lun] != NULL)) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (DDI_FAILURE);
}
scsa2usbp->scsa2usb_lun_dip[lun] = cdip;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (DDI_SUCCESS);
}
static void
scsa2usb_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *cdip,
scsi_hba_tran_t *tran, struct scsi_device *sd)
{
scsa2usb_state_t *scsa2usbp = (scsa2usb_state_t *)
tran->tran_hba_private;
int lun;
int t_len = sizeof (lun);
if (scsa2usb_is_usb(cdip) == 0) {
return;
}
if (ddi_prop_op(DDI_DEV_T_ANY, cdip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS|DDI_PROP_CANSLEEP, "lun", (caddr_t)&lun,
&t_len) != DDI_PROP_SUCCESS) {
return;
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_tgt_free: %s lun%d", ddi_driver_name(cdip), lun);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (lun < scsa2usbp->scsa2usb_n_luns) {
if (scsa2usbp->scsa2usb_lun_dip[lun] == cdip) {
scsa2usbp->scsa2usb_lun_dip[lun] = NULL;
}
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
static int
scsa2usb_scsi_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
void *arg, dev_info_t **child)
{
int rval;
scsa2usb_state_t *scsa2usbp =
ddi_get_soft_state(scsa2usb_statep, ddi_get_instance(dip));
ASSERT(scsa2usbp != NULL);
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_bus_config: op=%d", op);
if (scsa2usb_scsi_bus_config_debug) {
flag |= NDI_DEVI_DEBUG;
}
ndi_devi_enter(dip);
if (DEVI(dip)->devi_child == NULL) {
scsa2usb_create_luns(scsa2usbp);
}
rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
ndi_devi_exit(dip);
return (rval);
}
static int
scsa2usb_scsi_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
void *arg)
{
scsa2usb_state_t *scsa2usbp =
ddi_get_soft_state(scsa2usb_statep, ddi_get_instance(dip));
int rval = NDI_SUCCESS;
uint_t save_flag = flag;
ASSERT(scsa2usbp != NULL);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_bus_unconfig: op=%d", op);
if (scsa2usb_scsi_bus_config_debug) {
flag |= NDI_DEVI_DEBUG;
}
if (op == BUS_UNCONFIG_ALL) {
flag &= ~(NDI_DEVI_REMOVE | NDI_UNCONFIG);
}
ndi_devi_enter(dip);
rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
if (op == BUS_UNCONFIG_ALL && rval == NDI_SUCCESS &&
(flag & NDI_AUTODETACH) == 0) {
flag |= NDI_DEVI_REMOVE;
rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
}
ndi_devi_exit(dip);
if ((rval != NDI_SUCCESS) && (op == BUS_UNCONFIG_ALL) &&
(save_flag & NDI_DEVI_REMOVE)) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (scsa2usbp->scsa2usb_warning_given != B_TRUE) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"Disconnected device was busy, "
"please reconnect.");
scsa2usbp->scsa2usb_warning_given = B_TRUE;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_bus_unconfig: rval=%d", rval);
return (rval);
}
static struct scsi_pkt *
scsa2usb_scsi_init_pkt(struct scsi_address *ap,
struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen,
int tgtlen, int flags, int (*callback)(), caddr_t arg)
{
scsa2usb_cmd_t *cmd;
scsa2usb_state_t *scsa2usbp;
struct scsi_pkt *in_pkt = pkt;
ASSERT(callback == NULL_FUNC || callback == SLEEP_FUNC);
scsa2usbp = (scsa2usb_state_t *)ADDR2SCSA2USB(ap);
if (ddi_in_panic()) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
SCSA2USB_PRINT_SYNC_MSG(scsa2usb_sync_message, scsa2usbp);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
if (pkt == NULL) {
if (statuslen < sizeof (struct scsi_arq_status)) {
statuslen = sizeof (struct scsi_arq_status);
}
pkt = scsi_hba_pkt_alloc(scsa2usbp->scsa2usb_dip, ap, cmdlen,
statuslen, tgtlen, sizeof (scsa2usb_cmd_t),
callback, arg);
if (pkt == NULL) {
return (NULL);
}
cmd = PKT2CMD(pkt);
cmd->cmd_pkt = pkt;
cmd->cmd_scblen = statuslen;
cmd->cmd_cdblen = (uchar_t)cmdlen;
mutex_enter(&scsa2usbp->scsa2usb_mutex);
cmd->cmd_tag = scsa2usbp->scsa2usb_tag++;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
cmd->cmd_bp = bp;
if (cmd->cmd_scblen == sizeof (struct scsi_arq_status)) {
pkt->pkt_scbp = (opaque_t)&cmd->cmd_scb;
}
usba_init_list(&cmd->cmd_waitQ, (usb_opaque_t)cmd,
scsa2usbp->scsa2usb_dev_data->dev_iblock_cookie);
} else {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb: pkt != NULL");
}
if (bp && (bp->b_bcount != 0)) {
if ((bp_mapin_common(bp, (callback == SLEEP_FUNC) ?
VM_SLEEP : VM_NOSLEEP)) == NULL) {
if (pkt != in_pkt) {
scsi_hba_pkt_free(ap, pkt);
}
return (NULL);
}
USB_DPRINTF_L3(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_init_pkt: mapped in 0x%p, addr=0x%p",
(void *)bp, (void *)bp->b_un.b_addr);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_init_pkt: ap = 0x%p pkt: 0x%p\n\t"
"bp = 0x%p cmdlen = %x stlen = 0x%x tlen = 0x%x flags = 0x%x",
(void *)ap, (void *)pkt, (void *)bp, cmdlen, statuslen,
tgtlen, flags);
return (pkt);
}
static void
scsa2usb_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
{
scsa2usb_cmd_t *cmd = PKT2CMD(pkt);
scsa2usb_state_t *scsa2usbp = ADDR2SCSA2USB(ap);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_destroy_pkt: pkt=0x%p", (void *)pkt);
usba_destroy_list(&cmd->cmd_waitQ);
scsi_hba_pkt_free(ap, pkt);
}
static int
scsa2usb_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
{
scsa2usb_cmd_t *cmd;
scsa2usb_state_t *scsa2usbp = ADDR2SCSA2USB(ap);
uint_t lun = ap->a_lun;
mutex_enter(&scsa2usbp->scsa2usb_mutex);
cmd = PKT2CMD(pkt);
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_start:\n\t"
"bp: 0x%p ap: 0x%p pkt: 0x%p flag: 0x%x time: 0x%x\n\tcdb0: 0x%x "
"dev_state: 0x%x pkt_state: 0x%x flags: 0x%x pipe_state: 0x%x",
(void *)cmd->cmd_bp, (void *)ap, (void *)pkt, pkt->pkt_flags,
pkt->pkt_time, pkt->pkt_cdbp[0], scsa2usbp->scsa2usb_dev_state,
scsa2usbp->scsa2usb_pkt_state, scsa2usbp->scsa2usb_flags,
scsa2usbp->scsa2usb_pipe_state);
if (pkt->pkt_time == 0) {
USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"pkt submitted with 0 timeout which may cause indefinite "
"hangs");
}
if (ddi_in_panic()) {
extern int do_polled_io;
ASSERT(do_polled_io);
scsa2usb_prepare_pkt(scsa2usbp, pkt);
SCSA2USB_PRINT_SYNC_MSG(scsa2usb_sync_message, scsa2usbp);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (TRAN_ACCEPT);
}
if (pkt->pkt_flags & FLAG_NOINTR) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"NOINTR packet: opcode = 0%x", pkt->pkt_cdbp[0]);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (TRAN_BADPKT);
}
scsa2usb_prepare_pkt(scsa2usbp, pkt);
if (usba_list_entry_count(&scsa2usbp->scsa2usb_waitQ[lun]) >
SCSA2USB_MAX_REQ_PER_LUN) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_start: limit (%d) exceeded",
SCSA2USB_MAX_REQ_PER_LUN);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (TRAN_BUSY);
}
usba_add_to_list(&scsa2usbp->scsa2usb_waitQ[lun], &cmd->cmd_waitQ);
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_work_thread_id=0x%p, count=%d, lun=%d",
(void *)scsa2usbp->scsa2usb_work_thread_id,
usba_list_entry_count(&scsa2usbp->scsa2usb_waitQ[lun]), lun);
if (scsa2usbp->scsa2usb_work_thread_id == 0) {
if ((usb_async_req(scsa2usbp->scsa2usb_dip,
scsa2usb_work_thread,
(void *)scsa2usbp, USB_FLAGS_SLEEP)) != USB_SUCCESS) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"no work thread started");
if (usba_rm_from_list(
&scsa2usbp->scsa2usb_waitQ[lun],
&cmd->cmd_waitQ) == USB_SUCCESS) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (TRAN_BUSY);
} else {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (TRAN_ACCEPT);
}
}
scsa2usbp->scsa2usb_work_thread_id = (kthread_t *)1;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (TRAN_ACCEPT);
}
static int
scsa2usb_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
{
scsa2usb_state_t *scsa2usbp = (scsa2usb_state_t *)ADDR2SCSA2USB(ap);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_abort: pkt = %p", (void *)pkt);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (0);
}
if ((ap->a_target == pkt->pkt_address.a_target) &&
(ap->a_lun == pkt->pkt_address.a_lun)) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsa2usb_flush_waitQ(scsa2usbp, ap->a_lun, CMD_ABORTED);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (0);
}
static int
scsa2usb_scsi_reset(struct scsi_address *ap, int level)
{
scsa2usb_state_t *scsa2usbp = (scsa2usb_state_t *)ADDR2SCSA2USB(ap);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_reset: ap = 0x%p, level = %d", (void *)ap, level);
scsa2usb_flush_waitQ(scsa2usbp, ap->a_lun, CMD_RESET);
return (1);
}
static int
scsa2usb_scsi_getcap(struct scsi_address *ap, char *cap, int whom)
{
int rval = -1;
uint_t cidx;
size_t dev_bsize_cap;
scsa2usb_state_t *scsa2usbp = (scsa2usb_state_t *)ADDR2SCSA2USB(ap);
ASSERT(scsa2usbp);
if (cap == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_getcap: invalid arg, "
"cap = 0x%p whom = %d", (void *)cap, whom);
return (rval);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_getcap: cap = %s", cap);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
cidx = scsi_hba_lookup_capstr(cap);
switch (cidx) {
case SCSI_CAP_GEOMETRY:
if (scsa2usbp->scsa2usb_secsz[ap->a_lun] == 0) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_getcap failed:"
"scsa2usbp->scsa2usb_secsz[ap->a_lun] == 0");
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
dev_bsize_cap = scsa2usbp->scsa2usb_totalsec[ap->a_lun];
if (scsa2usbp->scsa2usb_secsz[ap->a_lun] > DEV_BSIZE) {
dev_bsize_cap *=
scsa2usbp->scsa2usb_secsz[ap->a_lun] / DEV_BSIZE;
} else if (scsa2usbp->scsa2usb_secsz[ap->a_lun] <
DEV_BSIZE) {
dev_bsize_cap /=
DEV_BSIZE / scsa2usbp->scsa2usb_secsz[ap->a_lun];
}
if (dev_bsize_cap < 65536 * 2 * 18) {
rval = ((2 << 16) | 18);
} else if (dev_bsize_cap < 65536 * 64 * 32) {
rval = ((64 << 16) | 32);
} else if (dev_bsize_cap < 65536 * 255 * 63) {
rval = ((255 << 16) | 63);
} else {
rval = ((512 << 16) | 256);
}
break;
case SCSI_CAP_DMA_MAX:
rval = scsa2usbp->scsa2usb_max_bulk_xfer_size;
break;
case SCSI_CAP_SCSI_VERSION:
rval = SCSI_VERSION_2;
break;
case SCSI_CAP_INTERCONNECT_TYPE:
rval = INTERCONNECT_USB;
break;
case SCSI_CAP_ARQ:
case SCSI_CAP_UNTAGGED_QING:
rval = 1;
break;
default:
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_getcap: unsupported cap = %s", cap);
break;
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_getcap: cap = %s, returned = %d", cap, rval);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
static int
scsa2usb_scsi_setcap(struct scsi_address *ap, char *cap, int value, int whom)
{
int rval = -1;
uint_t cidx;
scsa2usb_state_t *scsa2usbp = (scsa2usb_state_t *)ADDR2SCSA2USB(ap);
ASSERT(scsa2usbp);
if (cap == NULL || whom == 0) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_setcap: invalid arg");
return (rval);
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
cidx = scsi_hba_lookup_capstr(cap);
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_setcap: ap = 0x%p value = 0x%x whom = 0x%x "
"cidx = 0x%x", (void *)ap, value, whom, cidx);
switch (cidx) {
case SCSI_CAP_SECTOR_SIZE:
if (value) {
scsa2usbp->scsa2usb_secsz[ap->a_lun] = value;
}
break;
case SCSI_CAP_TOTAL_SECTORS:
if (value) {
scsa2usbp->scsa2usb_totalsec[ap->a_lun] = value;
}
break;
case SCSI_CAP_ARQ:
rval = 1;
break;
case SCSI_CAP_DMA_MAX:
case SCSI_CAP_SCSI_VERSION:
case SCSI_CAP_INTERCONNECT_TYPE:
case SCSI_CAP_UNTAGGED_QING:
rval = 0;
break;
default:
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_scsi_setcap: unsupported cap = %s", cap);
break;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
static void
scsa2usb_prepare_pkt(scsa2usb_state_t *scsa2usbp, struct scsi_pkt *pkt)
{
scsa2usb_cmd_t *cmd = PKT2CMD(pkt);
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_prepare_pkt: pkt=0x%p cdb: 0x%x (%s)",
(void *)pkt, pkt->pkt_cdbp[0],
scsi_cname(pkt->pkt_cdbp[0], scsa2usb_cmds));
pkt->pkt_reason = CMD_CMPLT;
pkt->pkt_state = 0;
pkt->pkt_statistics = 0;
pkt->pkt_resid = 0;
bzero(pkt->pkt_scbp, cmd->cmd_scblen);
if (cmd) {
cmd->cmd_timeout = pkt->pkt_time;
cmd->cmd_xfercount = 0;
cmd->cmd_total_xfercount = 0;
cmd->cmd_lba = 0;
cmd->cmd_done = 0;
cmd->cmd_dir = 0;
cmd->cmd_offset = 0;
cmd->cmd_actual_len = cmd->cmd_cdblen;
}
}
static void
scsa2usb_force_invalid_request(scsa2usb_state_t *scsa2usbp,
scsa2usb_cmd_t *cmd)
{
struct scsi_arq_status *arqp;
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_force_invalid_request: pkt = 0x%p", (void *)cmd->cmd_pkt);
if (cmd->cmd_scblen >= sizeof (struct scsi_arq_status)) {
arqp = (struct scsi_arq_status *)cmd->cmd_pkt->pkt_scbp;
bzero(arqp, cmd->cmd_scblen);
arqp->sts_status.sts_chk = 1;
arqp->sts_rqpkt_reason = CMD_CMPLT;
arqp->sts_rqpkt_state = STATE_XFERRED_DATA |
STATE_GOT_BUS | STATE_GOT_STATUS;
arqp->sts_sensedata.es_valid = 1;
arqp->sts_sensedata.es_class = 7;
arqp->sts_sensedata.es_key = KEY_ILLEGAL_REQUEST;
cmd->cmd_pkt->pkt_state = STATE_ARQ_DONE |
STATE_GOT_BUS | STATE_GOT_BUS | STATE_GOT_BUS |
STATE_GOT_STATUS;
#ifdef DEBUG
{
uchar_t *p = (uchar_t *)(&arqp->sts_sensedata);
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"cdb: %x rqsense: "
"%x %x %x %x %x %x %x %x %x %x "
"%x %x %x %x %x %x %x %x %x %x",
cmd->cmd_pkt->pkt_cdbp[0],
p[0], p[1], p[2], p[3], p[4],
p[5], p[6], p[7], p[8], p[9],
p[10], p[11], p[12], p[13], p[14],
p[15], p[16], p[17], p[18], p[19]);
}
#endif
}
}
static int
scsa2usb_cmd_transport(scsa2usb_state_t *scsa2usbp, scsa2usb_cmd_t *cmd)
{
int rval, transport;
struct scsi_pkt *pkt;
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_cmd_transport: pkt: 0x%p, cur_pkt = 0x%p",
(void *)cmd->cmd_pkt, (void *)scsa2usbp->scsa2usb_cur_pkt);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
ASSERT(scsa2usbp->scsa2usb_cur_pkt == NULL);
pkt = scsa2usbp->scsa2usb_cur_pkt = cmd->cmd_pkt;
if (SCSA2USB_IS_BULK_ONLY(scsa2usbp)) {
transport = scsa2usb_check_bulkonly_quirks(scsa2usbp, cmd);
} else if (SCSA2USB_IS_CB(scsa2usbp) || SCSA2USB_IS_CBI(scsa2usbp)) {
transport = scsa2usb_check_ufi_quirks(scsa2usbp, cmd);
} else {
return (TRAN_FATAL_ERROR);
}
if (transport == SCSA2USB_JUST_ACCEPT) {
SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp);
return (TRAN_ACCEPT);
} else if (transport == SCSA2USB_REJECT) {
return (TRAN_FATAL_ERROR);
}
if (SCSA2USB_IS_SCSI_CMDSET(scsa2usbp) ||
SCSA2USB_IS_ATAPI_CMDSET(scsa2usbp)) {
transport =
scsa2usb_handle_scsi_cmd_sub_class(scsa2usbp, cmd, pkt);
} else if (SCSA2USB_IS_UFI_CMDSET(scsa2usbp)) {
transport =
scsa2usb_handle_ufi_subclass_cmd(scsa2usbp, cmd, pkt);
} else {
transport = SCSA2USB_REJECT;
}
switch (transport) {
case SCSA2USB_TRANSPORT:
if (SCSA2USB_IS_BULK_ONLY(scsa2usbp)) {
rval = scsa2usb_bulk_only_transport(scsa2usbp, cmd);
} else if (SCSA2USB_IS_CB(scsa2usbp) ||
SCSA2USB_IS_CBI(scsa2usbp)) {
rval = scsa2usb_cbi_transport(scsa2usbp, cmd);
} else {
rval = TRAN_FATAL_ERROR;
}
break;
case SCSA2USB_JUST_ACCEPT:
SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp);
rval = TRAN_ACCEPT;
break;
default:
rval = TRAN_FATAL_ERROR;
}
return (rval);
}
int
scsa2usb_check_bulkonly_quirks(scsa2usb_state_t *scsa2usbp, scsa2usb_cmd_t *cmd)
{
struct scsi_inquiry *inq =
&scsa2usbp->scsa2usb_lun_inquiry[cmd->cmd_pkt->pkt_address.a_lun];
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
switch (cmd->cmd_pkt->pkt_cdbp[0]) {
case SCMD_DOORLOCK:
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_DOORLOCK)) {
return (SCSA2USB_JUST_ACCEPT);
} else if ((inq->inq_dtype == DTYPE_RODIRECT) ||
(inq->inq_dtype == DTYPE_OPTICAL)) {
if (inq->inq_rmb) {
break;
}
}
return (SCSA2USB_JUST_ACCEPT);
case SCMD_START_STOP:
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_START_STOP)) {
return (SCSA2USB_JUST_ACCEPT);
} else if (inq->inq_dtype == DTYPE_SEQUENTIAL) {
break;
} else if (cmd->cmd_pkt->pkt_cdbp[4] & LOEJECT) {
if (inq->inq_rmb) {
break;
}
return (SCSA2USB_JUST_ACCEPT);
} else if (!scsa2usbp->scsa2usb_rcvd_not_ready) {
return (SCSA2USB_JUST_ACCEPT);
}
break;
case SCMD_INQUIRY:
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_INQUIRY)) {
uchar_t evpd = 0x01;
unsigned int bufsize;
int count;
if (cmd->cmd_pkt->pkt_cdbp[1] & evpd)
return (SCSA2USB_REJECT);
scsa2usb_fake_inquiry(scsa2usbp, inq);
count = MIN(cmd->cmd_bp->b_bcount,
sizeof (struct scsi_inquiry));
bufsize = cmd->cmd_pkt->pkt_cdbp[4];
count = MIN(count, bufsize);
bcopy(inq, cmd->cmd_bp->b_un.b_addr, count);
cmd->cmd_pkt->pkt_resid = bufsize - count;
cmd->cmd_pkt->pkt_state |= STATE_XFERRED_DATA;
return (SCSA2USB_JUST_ACCEPT);
} else if (!(scsa2usbp->scsa2usb_attrs &
SCSA2USB_ATTRS_INQUIRY_EVPD)) {
uchar_t evpd = 0x01;
if (!(cmd->cmd_pkt->pkt_cdbp[1] & evpd))
break;
if (cmd->cmd_bp) {
cmd->cmd_pkt->pkt_resid = cmd->cmd_bp->
b_bcount;
}
scsa2usb_force_invalid_request(scsa2usbp, cmd);
return (SCSA2USB_JUST_ACCEPT);
}
break;
case SCMD_RESERVE:
case SCMD_RELEASE:
case SCMD_PERSISTENT_RESERVE_IN:
case SCMD_PERSISTENT_RESERVE_OUT:
return (SCSA2USB_JUST_ACCEPT);
case SCMD_MODE_SENSE:
case SCMD_MODE_SELECT:
case SCMD_MODE_SENSE_G1:
case SCMD_MODE_SELECT_G1:
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_MODE_SENSE)) {
if (cmd->cmd_bp) {
cmd->cmd_pkt->pkt_resid = cmd->cmd_bp->
b_bcount;
}
scsa2usb_force_invalid_request(scsa2usbp, cmd);
return (SCSA2USB_JUST_ACCEPT);
}
break;
default:
break;
}
return (SCSA2USB_TRANSPORT);
}
int
scsa2usb_handle_scsi_cmd_sub_class(scsa2usb_state_t *scsa2usbp,
scsa2usb_cmd_t *cmd, struct scsi_pkt *pkt)
{
uchar_t evpd = 0x01;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_handle_scsi_cmd_sub_class: cmd = 0x%p pkt = 0x%p",
(void *)cmd, (void *)pkt);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
bzero(&cmd->cmd_cdb, SCSI_CDB_SIZE);
cmd->cmd_cdb[SCSA2USB_OPCODE] = pkt->pkt_cdbp[0];
cmd->cmd_cdb[SCSA2USB_LUN] = pkt->pkt_cdbp[1];
switch (pkt->pkt_cdbp[0]) {
case SCMD_FORMAT:
if ((pkt->pkt_cdbp[1] & 0x10) && cmd->cmd_bp) {
cmd->cmd_xfercount = cmd->cmd_bp->b_bcount;
} else {
cmd->cmd_xfercount = 4;
}
cmd->cmd_dir = CBW_DIR_OUT;
cmd->cmd_actual_len = CDB_GROUP0;
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
break;
case SCMD_INQUIRY:
cmd->cmd_dir = CBW_DIR_IN;
cmd->cmd_actual_len = CDB_GROUP0;
cmd->cmd_cdb[SCSA2USB_LBA_0] = pkt->pkt_cdbp[2];
if (pkt->pkt_cdbp[1] & evpd) {
cmd->cmd_cdb[SCSA2USB_LBA_2] = cmd->cmd_xfercount =
(cmd->cmd_bp ? cmd->cmd_bp->b_bcount : 0);
} else {
cmd->cmd_cdb[SCSA2USB_LBA_2] = cmd->cmd_xfercount =
min(SCSA2USB_MAX_INQ_LEN,
cmd->cmd_bp ? cmd->cmd_bp->b_bcount : 0);
}
break;
case SCMD_READ_CAPACITY:
cmd->cmd_dir = CBW_DIR_IN;
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
cmd->cmd_xfercount = sizeof (scsa2usb_read_cap_t);
break;
case SCMD_READ:
case SCMD_WRITE:
case SCMD_READ_G1:
case SCMD_WRITE_G1:
case SCMD_READ_G4:
case SCMD_WRITE_G4:
case SCMD_READ_G5:
case SCMD_WRITE_G5:
case SCMD_READ_LONG:
case SCMD_WRITE_LONG:
case SCMD_READ_CD:
switch (scsa2usbp->
scsa2usb_lun_inquiry[pkt->pkt_address.a_lun].
inq_dtype & DTYPE_MASK) {
case DTYPE_DIRECT:
case DTYPE_RODIRECT:
case DTYPE_OPTICAL:
return (scsa2usb_rw_transport(
scsa2usbp, pkt));
default:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
if (cmd->cmd_bp) {
cmd->cmd_dir =
(cmd->cmd_bp->b_flags & B_READ) ?
CBW_DIR_IN : CBW_DIR_OUT;
cmd->cmd_xfercount =
cmd->cmd_bp->b_bcount;
}
break;
}
break;
case SCMD_REQUEST_SENSE:
cmd->cmd_dir = CBW_DIR_IN;
cmd->cmd_xfercount = pkt->pkt_cdbp[4];
cmd->cmd_cdb[SCSA2USB_LBA_2] = pkt->pkt_cdbp[4];
cmd->cmd_actual_len = CDB_GROUP0;
break;
case SCMD_DOORLOCK:
case SCMD_START_STOP:
case SCMD_TEST_UNIT_READY:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
break;
case SCMD_SDIAG:
case SCMD_REZERO_UNIT:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
cmd->cmd_actual_len = CDB_GROUP1;
break;
case SCMD_WRITE_VERIFY:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
cmd->cmd_dir = CBW_DIR_OUT;
cmd->cmd_xfercount = (pkt->pkt_cdbp[7] << 8) | pkt->pkt_cdbp[8];
cmd->cmd_actual_len = CDB_GROUP1;
break;
case SCMD_READ_FORMAT_CAP:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
cmd->cmd_dir = CBW_DIR_IN;
cmd->cmd_xfercount = (pkt->pkt_cdbp[7] << 8) | pkt->pkt_cdbp[8];
cmd->cmd_actual_len = CDB_GROUP1;
break;
case IOMEGA_CMD_CARTRIDGE_PROTECT:
cmd->cmd_dir = CBW_DIR_OUT;
cmd->cmd_cdb[SCSA2USB_LBA_2] = pkt->pkt_cdbp[4];
cmd->cmd_cdb[SCSA2USB_LBA_2] &= ~1;
cmd->cmd_cdb[SCSA2USB_LUN] = pkt->pkt_cdbp[1];
cmd->cmd_actual_len = CDB_GROUP0;
cmd->cmd_xfercount = pkt->pkt_cdbp[4];
break;
case SCMD_MODE_SENSE:
case SCMD_MODE_SELECT:
if (((pkt->pkt_cdbp[2] & SD_MODE_SENSE_PAGE_MASK)
== SD_MODE_SENSE_PAGE3_CODE) ||
((pkt->pkt_cdbp[2] & SD_MODE_SENSE_PAGE_MASK)
== SD_MODE_SENSE_PAGE4_CODE)) {
if (cmd->cmd_bp) {
cmd->cmd_pkt->pkt_resid = cmd->cmd_bp->b_bcount;
}
scsa2usb_force_invalid_request(scsa2usbp, cmd);
return (SCSA2USB_JUST_ACCEPT);
}
default:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
if (cmd->cmd_bp) {
cmd->cmd_dir = (cmd->cmd_bp->b_flags & B_READ) ?
CBW_DIR_IN : CBW_DIR_OUT;
cmd->cmd_xfercount = cmd->cmd_bp->b_bcount;
}
break;
}
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_handle_scsi_cmd_sub_class: opcode = 0x%x count = 0x%lx",
pkt->pkt_cdbp[SCSA2USB_OPCODE], cmd->cmd_xfercount);
cmd->cmd_total_xfercount = cmd->cmd_xfercount;
return (SCSA2USB_TRANSPORT);
}
static int
scsa2usb_do_tur(scsa2usb_state_t *scsa2usbp, struct scsi_address *ap)
{
struct scsi_pkt *pkt;
scsa2usb_cmd_t *turcmd;
int rval = -1;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_do_tur:");
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if ((pkt = scsi_init_pkt(ap, NULL, NULL, CDB_GROUP0, 1,
PKT_PRIV_LEN, PKT_CONSISTENT, SLEEP_FUNC, NULL)) == NULL) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_do_tur: init pkt failed");
return (rval);
}
RQ_MAKECOM_G0(pkt, FLAG_HEAD | FLAG_NODISCON,
(char)SCMD_TEST_UNIT_READY, 0, 0);
pkt->pkt_comp = NULL;
pkt->pkt_time = PKT_DEFAULT_TIMEOUT;
turcmd = PKT2CMD(pkt);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usb_prepare_pkt(scsa2usbp, turcmd->cmd_pkt);
if (scsa2usb_cmd_transport(scsa2usbp, turcmd) != TRAN_ACCEPT) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_do_tur: cmd transport failed, "
"pkt_reason=0x%x", turcmd->cmd_pkt->pkt_reason);
} else if (*(turcmd->cmd_pkt->pkt_scbp) != STATUS_GOOD) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_do_tur: media not ready");
} else {
rval = 0;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsi_destroy_pkt(pkt);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
static int
scsa2usb_check_ufi_quirks(scsa2usb_state_t *scsa2usbp, scsa2usb_cmd_t *cmd)
{
int rval = SCSA2USB_TRANSPORT;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
switch (cmd->cmd_pkt->pkt_cdbp[0]) {
case SCMD_PRIN:
case SCMD_PROUT:
rval = SCSA2USB_JUST_ACCEPT;
break;
case SCMD_MODE_SENSE:
case SCMD_MODE_SELECT:
if (cmd->cmd_bp) {
cmd->cmd_pkt->pkt_resid = cmd->cmd_bp->b_bcount;
}
scsa2usb_force_invalid_request(scsa2usbp, cmd);
rval = SCSA2USB_JUST_ACCEPT;
break;
case SCMD_GET_CONFIGURATION:
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_GET_CONF)) {
rval = SCSA2USB_JUST_ACCEPT;
}
break;
case SCMD_GET_PERFORMANCE:
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_GET_PERF)) {
rval = SCSA2USB_JUST_ACCEPT;
}
break;
case SCMD_START_STOP:
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_START_STOP)) {
rval = SCSA2USB_JUST_ACCEPT;
}
break;
case SCMD_READ_CAPACITY:
if (!(scsa2usbp->scsa2usb_attrs &
SCSA2USB_ATTRS_NO_MEDIA_CHECK)) {
struct scsi_pkt *pkt = cmd->cmd_pkt;
ASSERT(scsa2usbp->scsa2usb_cur_pkt == pkt);
scsa2usbp->scsa2usb_cur_pkt = NULL;
if (scsa2usb_do_tur(scsa2usbp,
&pkt->pkt_address) != 0) {
if (cmd->cmd_bp) {
cmd->cmd_pkt->pkt_resid =
cmd->cmd_bp->b_bcount;
}
scsa2usb_force_invalid_request(scsa2usbp, cmd);
rval = SCSA2USB_JUST_ACCEPT;
}
scsa2usbp->scsa2usb_cur_pkt = pkt;
}
break;
default:
break;
}
return (rval);
}
int
scsa2usb_handle_ufi_subclass_cmd(scsa2usb_state_t *scsa2usbp,
scsa2usb_cmd_t *cmd, struct scsi_pkt *pkt)
{
uchar_t opcode = pkt->pkt_cdbp[0];
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_handle_ufi_subclass_cmd: cmd = 0x%p pkt = 0x%p",
(void *)cmd, (void *)pkt);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
bzero(&cmd->cmd_cdb, SCSI_CDB_SIZE);
cmd->cmd_cdb[SCSA2USB_OPCODE] = opcode;
cmd->cmd_cdb[SCSA2USB_LUN] = pkt->pkt_cdbp[1];
switch (opcode) {
case SCMD_FORMAT:
if (pkt->pkt_cdbp[1] & 0x10) {
cmd->cmd_xfercount =
(pkt->pkt_cdbp[7] << 8) | pkt->pkt_cdbp[8];
cmd->cmd_dir = USB_EP_DIR_OUT;
cmd->cmd_actual_len = CDB_GROUP5;
}
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
break;
case SCMD_INQUIRY:
cmd->cmd_dir = USB_EP_DIR_IN;
cmd->cmd_actual_len = CDB_GROUP0;
cmd->cmd_cdb[SCSA2USB_LBA_0] = pkt->pkt_cdbp[2];
cmd->cmd_cdb[SCSA2USB_LBA_2] = cmd->cmd_xfercount =
min(SCSA2USB_MAX_INQ_LEN,
cmd->cmd_bp ? cmd->cmd_bp->b_bcount : 0);
break;
case SCMD_READ_CAPACITY:
cmd->cmd_dir = USB_EP_DIR_IN;
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
cmd->cmd_xfercount = sizeof (scsa2usb_read_cap_t);
break;
case SCMD_REQUEST_SENSE:
cmd->cmd_dir = USB_EP_DIR_IN;
cmd->cmd_xfercount = pkt->pkt_cdbp[4];
cmd->cmd_cdb[SCSA2USB_LBA_2] = pkt->pkt_cdbp[4];
cmd->cmd_actual_len = CDB_GROUP0;
break;
case SCMD_READ:
case SCMD_WRITE:
case SCMD_READ_G1:
case SCMD_WRITE_G1:
case SCMD_READ_G4:
case SCMD_WRITE_G4:
case SCMD_READ_G5:
case SCMD_WRITE_G5:
case SCMD_READ_LONG:
case SCMD_WRITE_LONG:
case SCMD_READ_CD:
return (scsa2usb_rw_transport(scsa2usbp, pkt));
case SCMD_TEST_UNIT_READY:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
break;
case SCMD_READ_FORMAT_CAP:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
cmd->cmd_dir = USB_EP_DIR_IN;
cmd->cmd_actual_len = CDB_GROUP1;
cmd->cmd_xfercount = (pkt->pkt_cdbp[7] << 8) | pkt->pkt_cdbp[8];
break;
case SCMD_WRITE_VERIFY:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
cmd->cmd_dir = USB_EP_DIR_OUT;
cmd->cmd_actual_len = CDB_GROUP1;
cmd->cmd_xfercount = (pkt->pkt_cdbp[7] << 8) | pkt->pkt_cdbp[8];
break;
case SCMD_START_STOP:
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_BIG_TIMEOUT)) {
cmd->cmd_timeout = max(cmd->cmd_timeout,
20 * SCSA2USB_BULK_PIPE_TIMEOUT);
}
default:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
if (cmd->cmd_bp) {
cmd->cmd_dir = (cmd->cmd_bp->b_flags & B_READ) ?
CBW_DIR_IN : CBW_DIR_OUT;
cmd->cmd_xfercount = cmd->cmd_bp->b_bcount;
}
break;
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_handle_ufi_subclass_cmd: opcode = 0x%x count = 0x%lx",
opcode, cmd->cmd_xfercount);
cmd->cmd_total_xfercount = cmd->cmd_xfercount;
return (SCSA2USB_TRANSPORT);
}
int
scsa2usb_rw_transport(scsa2usb_state_t *scsa2usbp, struct scsi_pkt *pkt)
{
scsa2usb_cmd_t *cmd = PKT2CMD(pkt);
int dir, opcode;
uint64_t lba;
struct buf *bp = cmd->cmd_bp;
size_t len, xfer_count;
size_t blk_size;
int sz;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_rw_transport:");
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
opcode = pkt->pkt_cdbp[0];
blk_size = scsa2usbp->scsa2usb_lbasize[pkt->pkt_address.a_lun];
switch (opcode) {
case SCMD_READ:
lba = SCSA2USB_LBA_6BYTE(pkt);
len = SCSA2USB_LEN_6BYTE(pkt);
opcode = SCMD_READ_G1;
dir = USB_EP_DIR_IN;
break;
case SCMD_WRITE:
lba = SCSA2USB_LBA_6BYTE(pkt);
len = SCSA2USB_LEN_6BYTE(pkt);
opcode = SCMD_WRITE_G1;
dir = USB_EP_DIR_OUT;
break;
case SCMD_READ_G1:
case SCMD_READ_LONG:
lba = SCSA2USB_LBA_10BYTE(pkt);
len = SCSA2USB_LEN_10BYTE(pkt);
dir = USB_EP_DIR_IN;
break;
case SCMD_WRITE_G1:
case SCMD_WRITE_LONG:
lba = SCSA2USB_LBA_10BYTE(pkt);
len = SCSA2USB_LEN_10BYTE(pkt);
dir = USB_EP_DIR_OUT;
if (len > 0) {
sz = SCSA2USB_CDRW_BLKSZ(bp != NULL ?
bp->b_bcount : 0, len);
if (SCSA2USB_VALID_CDRW_BLKSZ(sz)) {
blk_size = sz;
}
}
break;
case SCMD_READ_CD:
lba = SCSA2USB_LBA_10BYTE(pkt);
len = SCSA2USB_LEN_READ_CD(pkt);
dir = USB_EP_DIR_IN;
blk_size = scsa2usb_read_cd_blk_size(pkt->pkt_cdbp[1] >> 2);
break;
case SCMD_READ_G4:
lba = SCSA2USB_LBA_16BYTE(pkt);
len = SCSA2USB_LEN_16BYTE(pkt);
dir = USB_EP_DIR_IN;
break;
case SCMD_WRITE_G4:
lba = SCSA2USB_LBA_16BYTE(pkt);
len = SCSA2USB_LEN_16BYTE(pkt);
dir = USB_EP_DIR_OUT;
break;
case SCMD_READ_G5:
lba = SCSA2USB_LBA_12BYTE(pkt);
len = SCSA2USB_LEN_12BYTE(pkt);
dir = USB_EP_DIR_IN;
break;
case SCMD_WRITE_G5:
lba = SCSA2USB_LBA_12BYTE(pkt);
len = SCSA2USB_LEN_12BYTE(pkt);
dir = USB_EP_DIR_OUT;
break;
}
cmd->cmd_total_xfercount = xfer_count = len * blk_size;
if (blk_size != 0 &&
xfer_count > scsa2usbp->scsa2usb_max_bulk_xfer_size) {
if (SCSA2USB_VALID_CDRW_BLKSZ(blk_size)) {
xfer_count = (scsa2usbp->scsa2usb_max_bulk_xfer_size /
blk_size) * blk_size;
len = xfer_count / blk_size;
xfer_count = blk_size * len;
} else {
xfer_count = scsa2usbp->scsa2usb_max_bulk_xfer_size;
len = xfer_count / blk_size;
}
}
cmd->cmd_xfercount = xfer_count;
cmd->cmd_dir = (uchar_t)dir;
cmd->cmd_blksize = (int)blk_size;
cmd->cmd_cdb[SCSA2USB_OPCODE] = (uchar_t)opcode;
switch (opcode) {
case SCMD_READ_CD:
bcopy(pkt->pkt_cdbp, &cmd->cmd_cdb, cmd->cmd_cdblen);
scsa2usb_fill_up_ReadCD_cdb_len(cmd, len, CDB_GROUP5);
scsa2usb_fill_up_cdb_lba(cmd, lba);
break;
case SCMD_WRITE_G4:
case SCMD_READ_G4:
scsa2usb_fill_up_16byte_cdb_len(cmd, len, CDB_GROUP4);
scsa2usb_fill_up_g4_cdb_lba(cmd, lba);
break;
case SCMD_WRITE_G5:
case SCMD_READ_G5:
scsa2usb_fill_up_12byte_cdb_len(cmd, len, CDB_GROUP5);
scsa2usb_fill_up_cdb_lba(cmd, lba);
break;
default:
scsa2usb_fill_up_cdb_len(cmd, len);
cmd->cmd_actual_len = CDB_GROUP1;
scsa2usb_fill_up_cdb_lba(cmd, lba);
break;
}
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"bcount=0x%lx lba=0x%x len=0x%lx xfercount=0x%lx total=0x%lx",
bp ? bp->b_bcount : 0, lba, len, cmd->cmd_xfercount,
cmd->cmd_total_xfercount);
if ((opcode == SCMD_WRITE_G1) && SCSA2USB_VALID_CDRW_BLKSZ(blk_size)) {
cmd->cmd_timeout *= 4;
USB_DPRINTF_L4(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"new timeout value = 0x%x", cmd->cmd_timeout);
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"lba 0x%x len 0x%lx xfercount 0x%lx total 0x%lx",
lba, len, cmd->cmd_xfercount, cmd->cmd_total_xfercount);
return (SCSA2USB_TRANSPORT);
}
void
scsa2usb_setup_next_xfer(scsa2usb_state_t *scsa2usbp, scsa2usb_cmd_t *cmd)
{
int xfer_len = min(scsa2usbp->scsa2usb_max_bulk_xfer_size,
cmd->cmd_total_xfercount);
int cdb_len;
size_t blk_size;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_setup_next_xfer: opcode = 0x%x lba = 0x%x "
"total count = 0x%lx", cmd->cmd_cdb[SCSA2USB_OPCODE],
cmd->cmd_lba, cmd->cmd_total_xfercount);
ASSERT(cmd->cmd_total_xfercount > 0);
cmd->cmd_xfercount = xfer_len;
blk_size = scsa2usbp->scsa2usb_lbasize[
cmd->cmd_pkt->pkt_address.a_lun];
switch (cmd->cmd_cdb[SCSA2USB_OPCODE]) {
case SCMD_READ_CD:
cmd->cmd_lba += (cmd->cmd_cdb[6] << 16) +
(cmd->cmd_cdb[7] << 8) + cmd->cmd_cdb[8];
cdb_len = xfer_len / cmd->cmd_blksize;
cmd->cmd_cdb[SCSA2USB_READ_CD_LEN_2] = (uchar_t)cdb_len;
cmd->cmd_xfercount = cdb_len * cmd->cmd_blksize;
scsa2usb_fill_up_cdb_lba(cmd, cmd->cmd_lba);
break;
case SCMD_WRITE_G4:
case SCMD_READ_G4:
cmd->cmd_lba += (cmd->cmd_cdb[10] << 24) +
(cmd->cmd_cdb[11] << 16) + (cmd->cmd_cdb[12] << 8) +
cmd->cmd_cdb[13];
if (blk_size != 0) {
xfer_len /= blk_size;
}
scsa2usb_fill_up_16byte_cdb_len(cmd, xfer_len, CDB_GROUP5);
scsa2usb_fill_up_g4_cdb_lba(cmd, cmd->cmd_lba);
break;
case SCMD_WRITE_G5:
case SCMD_READ_G5:
cmd->cmd_lba += (cmd->cmd_cdb[6] << 24) +
(cmd->cmd_cdb[7] << 16) + (cmd->cmd_cdb[8] << 8) +
cmd->cmd_cdb[9];
if (blk_size != 0) {
xfer_len /= blk_size;
}
scsa2usb_fill_up_12byte_cdb_len(cmd, xfer_len, CDB_GROUP5);
scsa2usb_fill_up_cdb_lba(cmd, cmd->cmd_lba);
break;
case SCMD_WRITE_G1:
case SCMD_WRITE_LONG:
cmd->cmd_lba += (cmd->cmd_cdb[7] << 8) + cmd->cmd_cdb[8];
if (SCSA2USB_VALID_CDRW_BLKSZ(cmd->cmd_blksize)) {
blk_size = cmd->cmd_blksize;
}
cdb_len = xfer_len / blk_size;
scsa2usb_fill_up_cdb_len(cmd, cdb_len);
cmd->cmd_xfercount = cdb_len * blk_size;
scsa2usb_fill_up_cdb_lba(cmd, cmd->cmd_lba);
break;
default:
if (blk_size != 0) {
xfer_len /= blk_size;
}
scsa2usb_fill_up_cdb_len(cmd, xfer_len);
cmd->cmd_lba += scsa2usbp->scsa2usb_max_bulk_xfer_size /
blk_size;
scsa2usb_fill_up_cdb_lba(cmd, cmd->cmd_lba);
break;
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_setup_next_xfer:\n\tlba = 0x%x xfer_len = 0x%x "
"xfercount = 0x%lx total = 0x%lx", cmd->cmd_lba, xfer_len,
cmd->cmd_xfercount, cmd->cmd_total_xfercount);
}
static void
scsa2usb_transport_request(scsa2usb_state_t *scsa2usbp, uint_t lun)
{
int rval;
struct scsi_pkt *pkt;
struct scsa2usb_cmd *cmd, *arqcmd;
if ((cmd = (scsa2usb_cmd_t *)
usba_rm_first_pvt_from_list(
&scsa2usbp->scsa2usb_waitQ[lun])) == NULL) {
return;
}
pkt = cmd->cmd_pkt;
if (scsa2usbp->scsa2usb_dev_state == USB_DEV_DISCONNECTED) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"device not accessible");
pkt->pkt_reason = CMD_DEV_GONE;
SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp);
scsa2usb_pkt_completion(scsa2usbp, pkt);
return;
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_transport_request: cmd=0x%p bp=0x%p addr=0x%p",
(void *)cmd, (void *)cmd->cmd_bp,
(void *)(cmd->cmd_bp ? cmd->cmd_bp->b_un.b_addr : NULL));
rval = scsa2usb_cmd_transport(scsa2usbp, cmd);
USB_DPRINTF_L3(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_transport_request: transport rval = %d",
rval);
if (scsa2usbp->scsa2usb_cur_pkt == NULL) {
return;
}
ASSERT(pkt == scsa2usbp->scsa2usb_cur_pkt);
if (ddi_in_panic()) {
pkt->pkt_reason = CMD_CMPLT;
scsa2usb_pkt_completion(scsa2usbp, pkt);
return;
}
if ((*(pkt->pkt_scbp) == STATUS_CHECK) &&
(cmd->cmd_scblen >= sizeof (struct scsi_arq_status)) &&
((pkt->pkt_state & STATE_ARQ_DONE) == 0) &&
(scsa2usb_create_arq_pkt(scsa2usbp,
&pkt->pkt_address) == USB_SUCCESS)) {
arqcmd = scsa2usbp->scsa2usb_arq_cmd;
arqcmd->cmd_pkt->pkt_time = pkt->pkt_time;
scsa2usb_prepare_pkt(scsa2usbp,
arqcmd->cmd_pkt);
scsa2usbp->scsa2usb_cur_pkt = NULL;
if (scsa2usb_cmd_transport(
scsa2usbp, arqcmd) == TRAN_ACCEPT) {
scsa2usb_complete_arq_pkt(
scsa2usbp, arqcmd->cmd_pkt, cmd,
scsa2usbp->scsa2usb_arq_bp);
pkt->pkt_reason = CMD_CMPLT;
}
scsa2usbp->scsa2usb_cur_pkt = pkt;
scsa2usb_delete_arq_pkt(scsa2usbp);
}
if ((rval != TRAN_ACCEPT) &&
(pkt->pkt_reason == CMD_CMPLT)) {
pkt->pkt_reason = CMD_TRAN_ERR;
}
SCSA2USB_SET_PKT_DO_COMP_STATE(scsa2usbp);
scsa2usb_pkt_completion(scsa2usbp, pkt);
ASSERT(scsa2usbp->scsa2usb_cur_pkt == NULL);
}
static void
scsa2usb_work_thread(void *arg)
{
scsa2usb_state_t *scsa2usbp = (scsa2usb_state_t *)arg;
uint_t lun;
uint_t count;
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_work_thread start: thread_id=0x%p",
(void *)scsa2usbp->scsa2usb_work_thread_id);
ASSERT(scsa2usbp->scsa2usb_work_thread_id == (kthread_t *)1);
scsa2usbp->scsa2usb_work_thread_id = curthread;
while (scsa2usbp->scsa2usb_transport_busy) {
cv_wait(&scsa2usbp->scsa2usb_transport_busy_cv,
&scsa2usbp->scsa2usb_mutex);
}
ASSERT(scsa2usbp->scsa2usb_ugen_open_count == 0);
scsa2usbp->scsa2usb_transport_busy++;
scsa2usbp->scsa2usb_busy_proc = curproc;
scsa2usb_raise_power(scsa2usbp);
(void) scsa2usb_open_usb_pipes(scsa2usbp);
for (;;) {
ASSERT(scsa2usbp->scsa2usb_ugen_open_count == 0);
for (lun = 0; lun < scsa2usbp->scsa2usb_n_luns; lun++) {
scsa2usb_transport_request(scsa2usbp, lun);
}
count = 0;
for (lun = 0; lun < SCSA2USB_MAX_LUNS; lun++) {
count += usba_list_entry_count(
&scsa2usbp->scsa2usb_waitQ[lun]);
}
if (count == 0) {
break;
}
}
scsa2usbp->scsa2usb_work_thread_id = 0;
ASSERT(scsa2usbp->scsa2usb_ugen_open_count == 0);
scsa2usbp->scsa2usb_transport_busy--;
scsa2usbp->scsa2usb_busy_proc = NULL;
cv_signal(&scsa2usbp->scsa2usb_transport_busy_cv);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_work_thread: exit");
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsa2usb_pm_idle_component(scsa2usbp);
}
static void
scsa2usb_flush_waitQ(scsa2usb_state_t *scsa2usbp, uint_t lun,
uchar_t error)
{
struct scsi_pkt *pkt;
struct scsa2usb_cmd *cmd;
usba_list_entry_t head;
mutex_enter(&scsa2usbp->scsa2usb_mutex);
usba_move_list(&scsa2usbp->scsa2usb_waitQ[lun], &head,
scsa2usbp->scsa2usb_dev_data->dev_iblock_cookie);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
while ((cmd = (scsa2usb_cmd_t *)usba_rm_first_pvt_from_list(&head)) !=
NULL) {
pkt = cmd->cmd_pkt;
pkt->pkt_reason = error;
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_pkt_state = SCSA2USB_PKT_DO_COMP;
scsa2usb_pkt_completion(scsa2usbp, pkt);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
}
static void
scsa2usb_do_inquiry(scsa2usb_state_t *scsa2usbp, uint_t target, uint_t lun)
{
struct buf *bp;
struct scsi_pkt *pkt;
struct scsi_address ap;
int len = SCSA2USB_MAX_INQ_LEN;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_do_inquiry: %d bytes", len);
if (!(scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_INQUIRY)) {
scsa2usb_fake_inquiry(scsa2usbp,
&scsa2usbp->scsa2usb_lun_inquiry[lun]);
return;
}
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
bzero(&ap, sizeof (struct scsi_address));
ap.a_hba_tran = scsa2usbp->scsa2usb_tran;
ap.a_target = (ushort_t)target;
ap.a_lun = (uchar_t)lun;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if ((bp = scsi_alloc_consistent_buf(&ap, (struct buf *)NULL,
len, B_READ, SLEEP_FUNC, NULL)) == NULL) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_do_inquiry: failed");
return;
}
pkt = scsi_init_pkt(&ap, NULL, bp, CDB_GROUP0, 1,
PKT_PRIV_LEN, PKT_CONSISTENT, SLEEP_FUNC, NULL);
RQ_MAKECOM_G0(pkt, FLAG_NOINTR, (char)SCMD_INQUIRY, 0, (char)len);
pkt->pkt_comp = NULL;
pkt->pkt_time = 5;
bzero(bp->b_un.b_addr, len);
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_do_inquiry:INQUIRY");
(void) scsi_transport(pkt);
if (pkt->pkt_reason) {
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"INQUIRY failed, cannot determine device type, "
"pkt_reason=0x%x", pkt->pkt_reason);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_attrs &=
~SCSA2USB_ATTRS_REDUCED_CMD;
scsa2usb_fake_inquiry(scsa2usbp,
&scsa2usbp->scsa2usb_lun_inquiry[lun]);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
scsi_destroy_pkt(pkt);
scsi_free_consistent_buf(bp);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
static void
scsa2usb_fake_inquiry(scsa2usb_state_t *scsa2usbp, struct scsi_inquiry *inqp)
{
usb_client_dev_data_t *dev_data = scsa2usbp->scsa2usb_dev_data;
int len;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_fake_inquiry:");
bzero(inqp, sizeof (struct scsi_inquiry));
for (len = 0; len < sizeof (inqp->inq_vid); len++) {
*(inqp->inq_vid + len) = ' ';
}
for (len = 0; len < sizeof (inqp->inq_pid); len++) {
*(inqp->inq_pid + len) = ' ';
}
inqp->inq_dtype = DTYPE_DIRECT;
inqp->inq_rmb = 1;
inqp->inq_ansi = 2;
inqp->inq_rdf = RDF_SCSI2;
inqp->inq_len = sizeof (struct scsi_inquiry)-4;
if (dev_data->dev_mfg) {
if ((len = strlen(dev_data->dev_mfg)) >
sizeof (inqp->inq_vid)) {
len = sizeof (inqp->inq_vid);
}
bcopy(dev_data->dev_mfg, inqp->inq_vid, len);
}
if (dev_data->dev_product) {
if ((len = strlen(dev_data->dev_product)) >
sizeof (inqp->inq_pid)) {
len = sizeof (inqp->inq_pid);
}
bcopy(dev_data->dev_product, inqp->inq_pid, len);
}
inqp->inq_revision[0] = 0x30 +
((dev_data->dev_descr->bcdDevice>>12) & 0xF);
inqp->inq_revision[1] = 0x30 +
((dev_data->dev_descr->bcdDevice>>8) & 0xF);
inqp->inq_revision[2] = 0x30 +
((dev_data->dev_descr->bcdDevice>>4) & 0xF);
inqp->inq_revision[3] = 0x30 +
((dev_data->dev_descr->bcdDevice) & 0xF);
}
static int
scsa2usb_create_arq_pkt(scsa2usb_state_t *scsa2usbp, struct scsi_address *ap)
{
struct buf *bp;
scsa2usb_cmd_t *arq_cmd;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_create_arq_pkt: scsa2usbp: %p, ap: %p",
(void *)scsa2usbp, (void *)ap);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if ((bp = scsi_alloc_consistent_buf(ap, (struct buf *)NULL,
SENSE_LENGTH, B_READ, SLEEP_FUNC, NULL)) == NULL) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
return (USB_FAILURE);
}
arq_cmd = PKT2CMD(scsi_init_pkt(ap, NULL, bp, CDB_GROUP0, 1,
PKT_PRIV_LEN, PKT_CONSISTENT, SLEEP_FUNC, NULL));
mutex_enter(&scsa2usbp->scsa2usb_mutex);
RQ_MAKECOM_G0(arq_cmd->cmd_pkt,
FLAG_SENSING | FLAG_HEAD | FLAG_NODISCON,
(char)SCMD_REQUEST_SENSE, 0, (char)SENSE_LENGTH);
arq_cmd->cmd_pkt->pkt_ha_private = arq_cmd;
scsa2usbp->scsa2usb_arq_cmd = arq_cmd;
scsa2usbp->scsa2usb_arq_bp = bp;
arq_cmd->cmd_pkt->pkt_comp = NULL;
bzero(bp->b_un.b_addr, SENSE_LENGTH);
return (USB_SUCCESS);
}
static void
scsa2usb_delete_arq_pkt(scsa2usb_state_t *scsa2usbp)
{
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_delete_arq_pkt: cmd: 0x%p",
(void *)scsa2usbp->scsa2usb_arq_cmd);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
if (scsa2usbp->scsa2usb_arq_cmd != NULL) {
scsi_destroy_pkt(scsa2usbp->scsa2usb_arq_cmd->cmd_pkt);
scsi_free_consistent_buf(scsa2usbp->scsa2usb_arq_bp);
}
scsa2usbp->scsa2usb_arq_cmd = NULL;
scsa2usbp->scsa2usb_arq_bp = NULL;
}
static void
scsa2usb_complete_arq_pkt(scsa2usb_state_t *scsa2usbp,
struct scsi_pkt *pkt, scsa2usb_cmd_t *ssp, struct buf *bp)
{
scsa2usb_cmd_t *sp = pkt->pkt_ha_private;
struct scsi_arq_status *arqp;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
arqp = (struct scsi_arq_status *)(ssp->cmd_pkt->pkt_scbp);
arqp->sts_rqpkt_status = *((struct scsi_status *)
(sp->cmd_pkt->pkt_scbp));
arqp->sts_rqpkt_reason = CMD_CMPLT;
arqp->sts_rqpkt_state |= STATE_XFERRED_DATA;
arqp->sts_rqpkt_statistics = arqp->sts_rqpkt_resid = 0;
if (*(bp->b_un.b_addr) != 0) {
bcopy(bp->b_un.b_addr, &arqp->sts_sensedata, SENSE_LENGTH);
ssp->cmd_pkt->pkt_state |= STATE_ARQ_DONE;
}
if (arqp->sts_sensedata.es_key == KEY_NOT_READY) {
scsa2usbp->scsa2usb_rcvd_not_ready = B_TRUE;
}
}
static int
scsa2usb_open_usb_pipes(scsa2usb_state_t *scsa2usbp)
{
int rval;
usb_pipe_policy_t policy;
size_t sz;
ASSERT(scsa2usbp);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_open_usb_pipes: dip = 0x%p flag = 0x%x",
(void *)scsa2usbp->scsa2usb_dip, scsa2usbp->scsa2usb_flags);
if (!(scsa2usbp->scsa2usb_flags & SCSA2USB_FLAGS_PIPES_OPENED)) {
bzero(&policy, sizeof (usb_pipe_policy_t));
policy.pp_max_async_reqs = 1;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_open_usb_pipes: opening bulk pipes");
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if ((rval = usb_pipe_xopen(scsa2usbp->scsa2usb_dip,
&scsa2usbp->scsa2usb_bulkin_xept, &policy, USB_FLAGS_SLEEP,
&scsa2usbp->scsa2usb_bulkin_pipe)) != USB_SUCCESS) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_open_usb_pipes: bulk/in pipe open "
" failed rval = %d", rval);
return (USB_FAILURE);
}
if ((rval = usb_pipe_xopen(scsa2usbp->scsa2usb_dip,
&scsa2usbp->scsa2usb_bulkout_xept, &policy, USB_FLAGS_SLEEP,
&scsa2usbp->scsa2usb_bulkout_pipe)) != USB_SUCCESS) {
usb_pipe_close(scsa2usbp->scsa2usb_dip,
scsa2usbp->scsa2usb_bulkin_pipe,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_bulkin_pipe = NULL;
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_open_usb_pipes: bulk/out pipe open"
" failed rval = %d", rval);
return (USB_FAILURE);
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (SCSA2USB_IS_CBI(scsa2usbp)) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if ((rval = usb_pipe_xopen(scsa2usbp->scsa2usb_dip,
&scsa2usbp->scsa2usb_intr_xept, &policy,
USB_FLAGS_SLEEP, &scsa2usbp->scsa2usb_intr_pipe)) !=
USB_SUCCESS) {
usb_pipe_close(scsa2usbp->scsa2usb_dip,
scsa2usbp->scsa2usb_bulkin_pipe,
USB_FLAGS_SLEEP, NULL, NULL);
usb_pipe_close(scsa2usbp->scsa2usb_dip,
scsa2usbp->scsa2usb_bulkout_pipe,
USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_bulkin_pipe = NULL;
scsa2usbp->scsa2usb_bulkout_pipe = NULL;
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_open_usb_pipes: intr pipe open"
" failed rval = %d", rval);
return (USB_FAILURE);
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if (usb_pipe_get_max_bulk_transfer_size(scsa2usbp->scsa2usb_dip,
&sz) == USB_SUCCESS) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_max_bulk_xfer_size = sz;
} else {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_max_bulk_xfer_size = DEV_BSIZE;
}
scsa2usbp->scsa2usb_max_bulk_xfer_size = min(
scsa2usbp->scsa2usb_max_bulk_xfer_size,
scsa2usb_max_bulk_xfer_size);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_open_usb_pipes: max bulk transfer size = %lx",
scsa2usbp->scsa2usb_max_bulk_xfer_size);
scsa2usbp->scsa2usb_flags |= SCSA2USB_FLAGS_PIPES_OPENED;
scsa2usbp->scsa2usb_pipe_state = SCSA2USB_PIPE_NORMAL;
scsa2usbp->scsa2usb_pkt_state = SCSA2USB_PKT_NONE;
}
return (USB_SUCCESS);
}
void
scsa2usb_close_usb_pipes(scsa2usb_state_t *scsa2usbp)
{
usb_flags_t flags = USB_FLAGS_SLEEP;
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_close_usb_pipes: scsa2usb_state = 0x%p",
(void *)scsa2usbp);
ASSERT(scsa2usbp);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
if ((scsa2usbp->scsa2usb_flags & SCSA2USB_FLAGS_PIPES_OPENED) == 0) {
return;
}
scsa2usbp->scsa2usb_pipe_state = SCSA2USB_PIPE_CLOSING;
scsa2usbp->scsa2usb_flags &= ~SCSA2USB_FLAGS_PIPES_OPENED;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
usb_pipe_close(scsa2usbp->scsa2usb_dip,
scsa2usbp->scsa2usb_bulkout_pipe, flags, NULL, NULL);
usb_pipe_close(scsa2usbp->scsa2usb_dip,
scsa2usbp->scsa2usb_bulkin_pipe, flags, NULL, NULL);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (SCSA2USB_IS_CBI(scsa2usbp)) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
usb_pipe_close(scsa2usbp->scsa2usb_dip,
scsa2usbp->scsa2usb_intr_pipe, flags, NULL, NULL);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
scsa2usbp->scsa2usb_bulkout_pipe = NULL;
scsa2usbp->scsa2usb_bulkin_pipe = NULL;
scsa2usbp->scsa2usb_intr_pipe = NULL;
scsa2usbp->scsa2usb_pipe_state = SCSA2USB_PIPE_NORMAL;
}
static void
scsa2usb_fill_up_cdb_lba(scsa2usb_cmd_t *cmd, uint64_t lba)
{
cmd->cmd_cdb[SCSA2USB_LUN] &= 0xE0;
cmd->cmd_cdb[SCSA2USB_LBA_0] = lba >> 24;
cmd->cmd_cdb[SCSA2USB_LBA_1] = lba >> 16;
cmd->cmd_cdb[SCSA2USB_LBA_2] = lba >> 8;
cmd->cmd_cdb[SCSA2USB_LBA_3] = (uchar_t)lba;
cmd->cmd_lba = lba;
}
static void
scsa2usb_fill_up_g4_cdb_lba(scsa2usb_cmd_t *cmd, uint64_t lba)
{
cmd->cmd_cdb[SCSA2USB_LUN] &= 0xE0;
cmd->cmd_cdb[2] = lba >> 56;
cmd->cmd_cdb[3] = lba >> 48;
cmd->cmd_cdb[4] = lba >> 40;
cmd->cmd_cdb[5] = lba >> 32;
cmd->cmd_cdb[6] = lba >> 24;
cmd->cmd_cdb[7] = lba >> 16;
cmd->cmd_cdb[8] = lba >> 8;
cmd->cmd_cdb[9] = lba;
cmd->cmd_lba = lba;
}
static void
scsa2usb_fill_up_ReadCD_cdb_len(scsa2usb_cmd_t *cmd, int len, int actual_len)
{
cmd->cmd_cdb[SCSA2USB_READ_CD_LEN_0] = len >> 16;
cmd->cmd_cdb[SCSA2USB_READ_CD_LEN_1] = len >> 8;
cmd->cmd_cdb[SCSA2USB_READ_CD_LEN_2] = (uchar_t)len;
cmd->cmd_actual_len = (uchar_t)actual_len;
}
static void
scsa2usb_fill_up_16byte_cdb_len(scsa2usb_cmd_t *cmd, int len, int actual_len)
{
cmd->cmd_cdb[10] = len >> 24;
cmd->cmd_cdb[11] = len >> 16;
cmd->cmd_cdb[12] = len >> 8;
cmd->cmd_cdb[13] = len;
cmd->cmd_actual_len = actual_len;
}
static void
scsa2usb_fill_up_12byte_cdb_len(scsa2usb_cmd_t *cmd, int len, int actual_len)
{
cmd->cmd_cdb[6] = len >> 24;
cmd->cmd_cdb[7] = len >> 16;
cmd->cmd_cdb[8] = len >> 8;
cmd->cmd_cdb[9] = (uchar_t)len;
cmd->cmd_actual_len = (uchar_t)actual_len;
}
static void
scsa2usb_fill_up_cdb_len(scsa2usb_cmd_t *cmd, int len)
{
cmd->cmd_cdb[SCSA2USB_LEN_0] = len >> 8;
cmd->cmd_cdb[SCSA2USB_LEN_1] = (uchar_t)len;
}
static int
scsa2usb_read_cd_blk_size(uchar_t expected_sector_type)
{
int blk_size;
switch (expected_sector_type) {
case READ_CD_EST_CDDA:
blk_size = CDROM_BLK_2352;
break;
case READ_CD_EST_MODE2:
blk_size = CDROM_BLK_2336;
break;
case READ_CD_EST_MODE2FORM2:
blk_size = CDROM_BLK_2324;
break;
case READ_CD_EST_MODE2FORM1:
case READ_CD_EST_ALLTYPE:
case READ_CD_EST_MODE1:
default:
blk_size = CDROM_BLK_2048;
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, NULL, "scsa2usb_read_cd_blk_size: "
"est = 0x%x blk_size = %d", expected_sector_type, blk_size);
return (blk_size);
}
static mblk_t *
scsa2usb_bp_to_mblk(scsa2usb_state_t *scsa2usbp)
{
size_t size;
mblk_t *mp;
struct buf *bp;
scsa2usb_cmd_t *cmd = PKT2CMD(scsa2usbp->scsa2usb_cur_pkt);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_bp_to_mblk: ");
ASSERT(scsa2usbp->scsa2usb_cur_pkt);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
bp = cmd->cmd_bp;
if (bp && (bp->b_bcount > 0)) {
size = ((bp->b_bcount > cmd->cmd_xfercount) ?
cmd->cmd_xfercount : bp->b_bcount);
} else {
return (NULL);
}
mp = esballoc_wait((uchar_t *)bp->b_un.b_addr + cmd->cmd_offset,
size, BPRI_LO, &frnop);
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_bp_to_mblk: "
"mp=0x%p bp=0x%p pkt=0x%p off=0x%lx sz=%lu add=0x%p",
(void *)mp, (void *)bp, (void *)scsa2usbp->scsa2usb_cur_pkt,
cmd->cmd_offset, bp->b_bcount - cmd->cmd_offset,
(void *)bp->b_un.b_addr);
mp->b_wptr += size;
cmd->cmd_offset += size;
return (mp);
}
int
scsa2usb_handle_data_start(scsa2usb_state_t *scsa2usbp,
scsa2usb_cmd_t *cmd, usb_bulk_req_t *req)
{
int rval = USB_SUCCESS;
uint_t ept_addr;
usb_flags_t flags = USB_FLAGS_SLEEP;
#ifdef SCSA2USB_BULK_ONLY_TEST
usb_req_attrs_t attrs = 0;
#else
usb_req_attrs_t attrs = USB_ATTRS_SHORT_XFER_OK;
#endif
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_handle_data_start: BEGIN cmd = %p, req = %p",
(void *)cmd, (void *)req);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
switch (cmd->cmd_dir) {
case USB_EP_DIR_IN:
#ifdef SCSA2USB_BULK_ONLY_TEST
if (scsa2usb_test_case_5) {
usb_bulk_req_t *req2;
req->bulk_len = cmd->cmd_xfercount - 1;
req->bulk_attributes = 0;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
SCSA2USB_FREE_MSG(req->bulk_data);
req->bulk_data = allocb_wait(req->bulk_len, BPRI_LO,
STR_NOSIG, NULL);
ASSERT(req->bulk_timeout);
rval = usb_pipe_bulk_xfer(
scsa2usbp->scsa2usb_bulkin_pipe, req, flags);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L1(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle, "rval = %x", rval);
req2 = scsa2usb_init_bulk_req(scsa2usbp,
cmd->cmd_xfercount + 2,
cmd->cmd_timeout, 0, flags);
req2->bulk_len = cmd->cmd_xfercount + 2;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
ASSERT(req2->bulk_timeout);
rval = usb_pipe_bulk_xfer(
scsa2usbp->scsa2usb_bulkin_pipe, req2, flags);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L1(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"TEST 5: Hi > Di: rval = 0x%x", rval);
scsa2usb_test_case_5 = 0;
usb_free_bulk_req(req2);
return (rval);
}
if (scsa2usb_test_case_8 && (cmd->cmd_cdb[0] == SCMD_READ_G1)) {
USB_DPRINTF_L1(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"TEST 8: Hi <> Do: Step 2");
scsa2usb_test_mblk(scsa2usbp, B_TRUE);
scsa2usb_test_case_8 = 0;
return (rval);
}
#endif
ept_addr = scsa2usbp->scsa2usb_bulkin_ept.bEndpointAddress;
req->bulk_len = cmd->cmd_xfercount;
req->bulk_attributes = attrs;
SCSA2USB_FREE_MSG(req->bulk_data);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
req->bulk_data = esballoc_wait(
(uchar_t *)cmd->cmd_bp->b_un.b_addr +
cmd->cmd_offset,
req->bulk_len, BPRI_LO, &frnop);
ASSERT(req->bulk_timeout);
rval = usb_pipe_bulk_xfer(scsa2usbp->scsa2usb_bulkin_pipe,
req, flags);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
break;
case USB_EP_DIR_OUT:
#ifdef SCSA2USB_BULK_ONLY_TEST
if (scsa2usb_test_case_10 &&
(cmd->cmd_cdb[0] == SCMD_WRITE_G1)) {
req->bulk_len = CSW_LEN;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
ASSERT(req->bulk_timeout);
rval = usb_pipe_bulk_xfer(
scsa2usbp->scsa2usb_bulkin_pipe, req, flags);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L1(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"TEST 10: Ho <> Di: done rval = 0x%x", rval);
scsa2usb_test_case_10 = 0;
return (rval);
}
#endif
req->bulk_data = scsa2usb_bp_to_mblk(scsa2usbp);
if (req->bulk_data == NULL) {
return (USB_FAILURE);
}
#ifdef SCSA2USB_BULK_ONLY_TEST
if (scsa2usb_test_case_11) {
USB_DPRINTF_L1(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle, "TEST 11: Ho > Do");
scsa2usb_test_mblk(scsa2usbp, B_FALSE);
scsa2usb_test_case_11 = 0;
}
#endif
ept_addr = scsa2usbp->scsa2usb_bulkout_ept.bEndpointAddress;
req->bulk_len = MBLKL(req->bulk_data);
req->bulk_timeout = scsa2usb_bulk_timeout(cmd->cmd_timeout);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
ASSERT(req->bulk_timeout);
rval = usb_pipe_bulk_xfer(scsa2usbp->scsa2usb_bulkout_pipe,
req, flags);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
break;
}
USB_DPRINTF_L3(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_handle_data_start: rval=%d cr=%d", rval,
req->bulk_completion_reason);
if (rval != USB_SUCCESS) {
if (req->bulk_completion_reason == USB_CR_STALL) {
if (cmd->cmd_dir == USB_EP_DIR_IN) {
(void) scsa2usb_clear_ept_stall(
scsa2usbp, ept_addr,
scsa2usbp-> scsa2usb_bulkin_pipe,
"bulk-in");
} else {
(void) scsa2usb_clear_ept_stall(
scsa2usbp, ept_addr,
scsa2usbp-> scsa2usb_bulkout_pipe,
"bulk-out");
}
}
cmd->cmd_done = 1;
}
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_handle_data_start: END %s data rval = %d",
(cmd->cmd_dir == USB_EP_DIR_IN) ? "bulk-in" : "bulk-out", rval);
return (rval);
}
void
scsa2usb_handle_data_done(scsa2usb_state_t *scsa2usbp,
scsa2usb_cmd_t *cmd, usb_bulk_req_t *req)
{
struct buf *bp = cmd->cmd_bp;
struct scsi_pkt *pkt = scsa2usbp->scsa2usb_cur_pkt;
mblk_t *data = req->bulk_data;
int len = data ? MBLKL(data) : 0;
uint32_t max_lba;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_handle_data_done:\n\tcmd = 0x%p data = 0x%p len = 0x%x",
(void *)cmd, (void *)data, len);
cmd->cmd_resid_xfercount = cmd->cmd_xfercount - len;
if (len) {
uchar_t *p;
uchar_t dtype;
scsa2usb_read_cap_t *cap;
struct scsi_inquiry *inq;
switch (cmd->cmd_cdb[SCSA2USB_OPCODE]) {
case SCMD_INQUIRY:
if (((cmd->cmd_cdb[SCSA2USB_LUN] & 0x1f) == 0) &&
(len >= SCSA2USB_MAX_INQ_LEN)) {
inq = (struct scsi_inquiry *)data->b_rptr;
dtype = inq->inq_dtype & DTYPE_MASK;
if (((dtype == DTYPE_DIRECT) ||
(dtype == DTYPE_RBC)) &&
(inq->inq_ansi > 2)) {
inq->inq_ansi = 0;
}
bzero(&scsa2usbp->scsa2usb_lun_inquiry
[pkt->pkt_address.a_lun],
sizeof (struct scsi_inquiry));
bcopy(data->b_rptr,
&scsa2usbp->scsa2usb_lun_inquiry
[pkt->pkt_address.a_lun], len);
}
USB_DPRINTF_L3(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsi inquiry type = 0x%x",
scsa2usbp->scsa2usb_lun_inquiry
[pkt->pkt_address.a_lun].inq_dtype);
cmd->cmd_done = 1;
goto handle_data;
case SCMD_READ_CAPACITY:
cap = (scsa2usb_read_cap_t *)data->b_rptr;
if ((len >= sizeof (struct scsa2usb_read_cap)) &&
(req->bulk_completion_reason == USB_CR_OK)) {
scsa2usbp->
scsa2usb_lbasize[pkt->pkt_address.a_lun] =
SCSA2USB_MK_32BIT(
cap->scsa2usb_read_cap_blen3,
cap->scsa2usb_read_cap_blen2,
cap->scsa2usb_read_cap_blen1,
cap->scsa2usb_read_cap_blen0);
max_lba = SCSA2USB_MK_32BIT(
cap->scsa2usb_read_cap_lba3,
cap->scsa2usb_read_cap_lba2,
cap->scsa2usb_read_cap_lba1,
cap->scsa2usb_read_cap_lba0);
if (max_lba > 0 && (scsa2usbp->scsa2usb_attrs &
SCSA2USB_ATTRS_NO_CAP_ADJUST) == 0) {
max_lba -= 1;
cap->scsa2usb_read_cap_lba0 =
(uchar_t)(max_lba & 0xFF);
cap->scsa2usb_read_cap_lba1 =
(uchar_t)(max_lba >> 8 & 0xFF);
cap->scsa2usb_read_cap_lba2 =
(uchar_t)(max_lba >> 16 & 0xFF);
cap->scsa2usb_read_cap_lba3 =
(uchar_t)(max_lba >> 24 & 0xFF);
}
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"bytes in each logical block=0x%lx,"
"number of total logical blocks=0x%x",
scsa2usbp->
scsa2usb_lbasize[pkt->pkt_address.a_lun],
max_lba + 1);
}
cmd->cmd_done = 1;
goto handle_data;
case SCMD_REQUEST_SENSE:
p = data->b_rptr;
USB_DPRINTF_L2(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"cdb: %x rqsense: "
"%x %x %x %x %x %x %x %x %x %x\n\t"
"%x %x %x %x %x %x %x %x %x %x",
cmd->cmd_cdb[0],
p[0], p[1], p[2], p[3], p[4],
p[5], p[6], p[7], p[8], p[9],
p[10], p[11], p[12], p[13], p[14],
p[15], p[16], p[17], p[18], p[19]);
scsa2usbp->scsa2usb_last_cmd.status = p[2];
cmd->cmd_done = 1;
default:
handle_data:
if (bp && len && (cmd->cmd_dir == USB_EP_DIR_IN)) {
cmd->cmd_offset += len;
}
USB_DPRINTF_L3(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"len = 0x%x total = 0x%lx offset = 0x%lx",
len, cmd->cmd_total_xfercount, cmd->cmd_offset);
cmd->cmd_total_xfercount -= len;
if ((req->bulk_completion_reason != USB_CR_OK) ||
(cmd->cmd_resid_xfercount != 0) ||
(cmd->cmd_total_xfercount == 0)) {
pkt->pkt_resid = cmd->cmd_total_xfercount;
cmd->cmd_done = 1;
}
break;
}
} else {
if (cmd->cmd_dir == USB_EP_DIR_OUT) {
if (cmd->cmd_total_xfercount == 0) {
cmd->cmd_done = 1;
}
}
}
}
usb_bulk_req_t *
scsa2usb_init_bulk_req(scsa2usb_state_t *scsa2usbp, size_t length,
uint_t timeout, usb_req_attrs_t attrs, usb_flags_t flags)
{
usb_bulk_req_t *req;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
req = usb_alloc_bulk_req(scsa2usbp->scsa2usb_dip, length,
flags | USB_FLAGS_SLEEP);
req->bulk_len = (uint_t)length;
req->bulk_timeout = scsa2usb_bulk_timeout(timeout);
req->bulk_attributes = attrs;
req->bulk_client_private = (usb_opaque_t)scsa2usbp;
return (req);
}
int
scsa2usb_bulk_timeout(int timeout)
{
return ((timeout == 0) ? scsa2usb_long_timeout : timeout);
}
int
scsa2usb_clear_ept_stall(scsa2usb_state_t *scsa2usbp, uint_t ept_addr,
usb_pipe_handle_t ph, char *what)
{
int rval;
dev_info_t *dip = scsa2usbp->scsa2usb_dip;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
if (!(SCSA2USB_DEVICE_ACCESS_OK(scsa2usbp))) {
return (USB_FAILURE);
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
rval = usb_clr_feature(dip, USB_DEV_REQ_RCPT_EP, 0, ept_addr,
USB_FLAGS_SLEEP, NULL, NULL);
usb_pipe_reset(dip, ph, USB_FLAGS_SLEEP, NULL, NULL);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_clear_ept_stall: on %s: ept = 0x%x rval = %d",
what, ept_addr, rval);
return (rval);
}
static void
scsa2usb_pkt_completion(scsa2usb_state_t *scsa2usbp, struct scsi_pkt *pkt)
{
scsa2usb_cmd_t *cmd = PKT2CMD(pkt);
size_t len;
ASSERT(pkt);
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_pkt_completion:\n\tscsa2usbp = 0x%p "
"reason=%d, status=%d state=0x%x stats=0x%x resid=0x%lx",
(void *)scsa2usbp, pkt->pkt_reason, *(pkt->pkt_scbp),
pkt->pkt_state, pkt->pkt_statistics, pkt->pkt_resid);
if (pkt->pkt_reason == CMD_CMPLT) {
pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
if (cmd->cmd_xfercount) {
pkt->pkt_state |= STATE_XFERRED_DATA;
}
} else {
pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD;
}
if ((scsa2usbp->scsa2usb_cur_pkt == pkt) && !ddi_in_panic()) {
SCSA2USB_RESET_CUR_PKT(scsa2usbp);
len = sizeof (scsa2usbp->scsa2usb_last_cmd.cdb);
bzero(scsa2usbp->scsa2usb_last_cmd.cdb, len);
len = (len < cmd->cmd_cdblen) ? len : cmd->cmd_cdblen;
USB_DPRINTF_L3(DPRINT_MASK_SCSA,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_pkt_completion: save last cmd, len=%ld", len);
bcopy(pkt->pkt_cdbp, scsa2usbp->scsa2usb_last_cmd.cdb, len);
if ((pkt->pkt_cdbp[0] != SCMD_REQUEST_SENSE) &&
(pkt->pkt_cdbp[0] != SCMD_INQUIRY)) {
scsa2usbp->scsa2usb_last_cmd.status = 0;
}
scsa2usbp->scsa2usb_pkt_state = SCSA2USB_PKT_NONE;
}
if (pkt->pkt_comp) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
scsi_hba_pkt_comp(pkt);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
}
static int
scsa2usb_reconnect_event_cb(dev_info_t *dip)
{
scsa2usb_state_t *scsa2usbp =
ddi_get_soft_state(scsa2usb_statep, ddi_get_instance(dip));
dev_info_t *cdip;
int rval = USB_SUCCESS;
ASSERT(scsa2usbp != NULL);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_reconnect_event_cb: dip = 0x%p", (void *)dip);
scsa2usb_restore_device_state(dip, scsa2usbp);
USB_DPRINTF_L0(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"Reinserted device is accessible again.");
ndi_devi_enter(dip);
for (cdip = ddi_get_child(dip); cdip; ) {
dev_info_t *next = ddi_get_next_sibling(cdip);
mutex_enter(&DEVI(cdip)->devi_lock);
DEVI_SET_DEVICE_REINSERTED(cdip);
mutex_exit(&DEVI(cdip)->devi_lock);
cdip = next;
}
ndi_devi_exit(dip);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_warning_given = B_FALSE;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if (scsa2usbp->scsa2usb_ugen_hdl) {
rval = usb_ugen_reconnect_ev_cb(
scsa2usbp->scsa2usb_ugen_hdl);
}
return (rval);
}
static int
scsa2usb_all_waitQs_empty(scsa2usb_state_t *scsa2usbp)
{
uint_t lun;
for (lun = 0; lun < SCSA2USB_MAX_LUNS; lun++) {
if (usba_list_entry_count(
&scsa2usbp->scsa2usb_waitQ[lun])) {
return (USB_FAILURE);
}
}
return (USB_SUCCESS);
}
static int
scsa2usb_disconnect_event_cb(dev_info_t *dip)
{
scsa2usb_state_t *scsa2usbp =
ddi_get_soft_state(scsa2usb_statep, ddi_get_instance(dip));
dev_info_t *cdip;
int i;
int rval = USB_SUCCESS;
ASSERT(scsa2usbp != NULL);
USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_disconnect_event_cb: dip = 0x%p", (void *)dip);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
scsa2usbp->scsa2usb_dev_state = USB_DEV_DISCONNECTED;
for (i = 0; i < SCSA2USB_DRAIN_TIMEOUT; i++) {
if ((scsa2usbp->scsa2usb_work_thread_id == NULL) &&
(scsa2usbp->scsa2usb_cur_pkt == NULL) &&
(scsa2usb_all_waitQs_empty(scsa2usbp) ==
USB_SUCCESS)) {
break;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
delay(drv_usectohz(1000000));
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
ndi_devi_enter(dip);
for (cdip = ddi_get_child(dip); cdip; ) {
dev_info_t *next = ddi_get_next_sibling(cdip);
mutex_enter(&DEVI(cdip)->devi_lock);
DEVI_SET_DEVICE_REMOVED(cdip);
mutex_exit(&DEVI(cdip)->devi_lock);
cdip = next;
}
ndi_devi_exit(dip);
if (scsa2usbp->scsa2usb_ugen_hdl) {
rval = usb_ugen_disconnect_ev_cb(
scsa2usbp->scsa2usb_ugen_hdl);
}
return (rval);
}
static void
scsa2usb_create_pm_components(dev_info_t *dip, scsa2usb_state_t *scsa2usbp)
{
scsa2usb_power_t *pm;
uint_t pwr_states;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
USB_DPRINTF_L4(DPRINT_MASK_PM, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_create_pm_components: dip = 0x%p, scsa2usbp = 0x%p",
(void *)dip, (void *)scsa2usbp);
if ((scsa2usbp->scsa2usb_attrs & SCSA2USB_ATTRS_PM) == 0) {
USB_DPRINTF_L2(DPRINT_MASK_PM, scsa2usbp->scsa2usb_log_handle,
"device cannot be power managed");
return;
}
pm = kmem_zalloc(sizeof (scsa2usb_power_t), KM_SLEEP);
scsa2usbp->scsa2usb_pm = pm;
pm->scsa2usb_current_power = USB_DEV_OS_FULL_PWR;
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if (usb_create_pm_components(dip, &pwr_states) ==
USB_SUCCESS) {
if (usb_handle_remote_wakeup(dip,
USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
pm->scsa2usb_wakeup_enabled = 1;
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
pm->scsa2usb_pwr_states = (uint8_t)pwr_states;
scsa2usb_raise_power(scsa2usbp);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
static void
scsa2usb_raise_power(scsa2usb_state_t *scsa2usbp)
{
USB_DPRINTF_L4(DPRINT_MASK_PM, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_raise_power:");
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
if (scsa2usbp->scsa2usb_pm) {
scsa2usb_pm_busy_component(scsa2usbp);
if (scsa2usbp->scsa2usb_pm->scsa2usb_current_power !=
USB_DEV_OS_FULL_PWR) {
mutex_exit(&scsa2usbp->scsa2usb_mutex);
(void) pm_raise_power(scsa2usbp->scsa2usb_dip,
0, USB_DEV_OS_FULL_PWR);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
}
}
static int
scsa2usb_pwrlvl0(scsa2usb_state_t *scsa2usbp)
{
int rval;
switch (scsa2usbp->scsa2usb_dev_state) {
case USB_DEV_ONLINE:
if (scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy != 0) {
return (USB_FAILURE);
}
scsa2usb_cbi_stop_intr_polling(scsa2usbp);
rval = usb_set_device_pwrlvl3(scsa2usbp->scsa2usb_dip);
ASSERT(rval == USB_SUCCESS);
scsa2usbp->scsa2usb_dev_state = USB_DEV_PWRED_DOWN;
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
case USB_DEV_PWRED_DOWN:
default:
scsa2usbp->scsa2usb_pm->scsa2usb_current_power =
USB_DEV_OS_PWR_OFF;
return (USB_SUCCESS);
}
}
static int
scsa2usb_pwrlvl1(scsa2usb_state_t *scsa2usbp)
{
int rval;
rval = usb_set_device_pwrlvl2(scsa2usbp->scsa2usb_dip);
ASSERT(rval == USB_SUCCESS);
return (DDI_FAILURE);
}
static int
scsa2usb_pwrlvl2(scsa2usb_state_t *scsa2usbp)
{
int rval;
rval = usb_set_device_pwrlvl1(scsa2usbp->scsa2usb_dip);
ASSERT(rval == USB_SUCCESS);
return (DDI_FAILURE);
}
static int
scsa2usb_pwrlvl3(scsa2usb_state_t *scsa2usbp)
{
int rval;
if (scsa2usbp->scsa2usb_dev_state != USB_DEV_DISCONNECTED) {
rval = usb_set_device_pwrlvl0(scsa2usbp->scsa2usb_dip);
ASSERT(rval == USB_SUCCESS);
scsa2usbp->scsa2usb_dev_state = USB_DEV_ONLINE;
}
scsa2usbp->scsa2usb_pm->scsa2usb_current_power = USB_DEV_OS_FULL_PWR;
return (DDI_SUCCESS);
}
static int
scsa2usb_power(dev_info_t *dip, int comp, int level)
{
scsa2usb_state_t *scsa2usbp;
scsa2usb_power_t *pm;
int rval = DDI_FAILURE;
scsa2usbp = ddi_get_soft_state(scsa2usb_statep, ddi_get_instance(dip));
USB_DPRINTF_L3(DPRINT_MASK_PM, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_power: Begin scsa2usbp (%p): level = %d",
(void *)scsa2usbp, level);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
if (SCSA2USB_BUSY(scsa2usbp)) {
USB_DPRINTF_L2(DPRINT_MASK_PM, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_power: busy");
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
pm = scsa2usbp->scsa2usb_pm;
if (pm == NULL) {
USB_DPRINTF_L2(DPRINT_MASK_PM, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_power: pm NULL");
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
if (USB_DEV_PWRSTATE_OK(pm->scsa2usb_pwr_states, level)) {
USB_DPRINTF_L2(DPRINT_MASK_PM, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_power: illegal power level = %d "
"pwr_states: %x", level, pm->scsa2usb_pwr_states);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return (rval);
}
switch (level) {
case USB_DEV_OS_PWR_OFF :
rval = scsa2usb_pwrlvl0(scsa2usbp);
break;
case USB_DEV_OS_PWR_1 :
rval = scsa2usb_pwrlvl1(scsa2usbp);
break;
case USB_DEV_OS_PWR_2 :
rval = scsa2usb_pwrlvl2(scsa2usbp);
break;
case USB_DEV_OS_FULL_PWR :
rval = scsa2usb_pwrlvl3(scsa2usbp);
break;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
}
static void
scsa2usb_pm_busy_component(scsa2usb_state_t *scsa2usbp)
{
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
if (scsa2usbp->scsa2usb_pm) {
scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy++;
USB_DPRINTF_L4(DPRINT_MASK_PM,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_pm_busy_component: %d",
scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
if (pm_busy_component(scsa2usbp->scsa2usb_dip, 0) !=
DDI_SUCCESS) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
ASSERT(scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy > 0);
scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy--;
USB_DPRINTF_L2(DPRINT_MASK_PM,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_pm_busy_component failed: %d",
scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy);
return;
}
mutex_enter(&scsa2usbp->scsa2usb_mutex);
}
}
static void
scsa2usb_pm_idle_component(scsa2usb_state_t *scsa2usbp)
{
ASSERT(!mutex_owned(&scsa2usbp->scsa2usb_mutex));
if (scsa2usbp->scsa2usb_pm) {
if (pm_idle_component(scsa2usbp->scsa2usb_dip, 0) ==
DDI_SUCCESS) {
mutex_enter(&scsa2usbp->scsa2usb_mutex);
ASSERT(scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy > 0);
scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy--;
USB_DPRINTF_L4(DPRINT_MASK_PM,
scsa2usbp->scsa2usb_log_handle,
"scsa2usb_pm_idle_component: %d",
scsa2usbp->scsa2usb_pm->scsa2usb_pm_busy);
mutex_exit(&scsa2usbp->scsa2usb_mutex);
}
}
}
#ifdef DEBUG
void
scsa2usb_print_cdb(scsa2usb_state_t *scsa2usbp, scsa2usb_cmd_t *cmd)
{
uchar_t *c = (uchar_t *)&cmd->cmd_cdb;
USB_DPRINTF_L3(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"cmd = 0x%p opcode=%s "
"cdb: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
(void *)cmd,
scsi_cname(cmd->cmd_cdb[SCSA2USB_OPCODE], scsa2usb_cmds),
c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8],
c[9], c[10], c[11], c[12], c[13], c[14], c[15]);
}
#endif
#ifdef SCSA2USB_BULK_ONLY_TEST
static void
scsa2usb_test_mblk(scsa2usb_state_t *scsa2usbp, boolean_t large)
{
int i, rval;
size_t len;
usb_flags_t flags = USB_FLAGS_SLEEP;
usb_bulk_req_t *req;
ASSERT(mutex_owned(&scsa2usbp->scsa2usb_mutex));
len = (large == B_TRUE) ? DEV_BSIZE : USB_BULK_CBWCMD_LEN;
req = scsa2usb_init_bulk_req(scsa2usbp, len,
SCSA2USB_BULK_PIPE_TIMEOUT, 0, flags);
for (i = 0; i < len; i++) {
*req->bulk_data->b_wptr++ = (uchar_t)i;
}
mutex_exit(&scsa2usbp->scsa2usb_mutex);
ASSERT(req->bulk_timeout);
rval = usb_pipe_bulk_xfer(scsa2usbp->scsa2usb_bulkout_pipe, req, flags);
mutex_enter(&scsa2usbp->scsa2usb_mutex);
USB_DPRINTF_L1(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
"scsa2usb_test_mblk: Sent Data Out rval = 0x%x", rval);
usb_free_bulk_req(req);
}
#endif