#include "usb_scsi.h"
#include <KernelExport.h>
#include <module.h>
#include <malloc.h>
#include <strings.h>
#include <stdio.h>
#include <device_manager.h>
#include <bus/SCSI.h>
#include "device_info.h"
#include "settings.h"
#include "transform_procs.h"
#include "tracing.h"
#include "scsi_commands.h"
#include "proto_common.h"
#include "proto_bulk.h"
#include "proto_cbi.h"
#include "usb_defs.h"
#include "fake_device.h"
#include "sg_buffer.h"
#if 0
status_t device_added(const usb_device device, void **cookie);
status_t device_removed(void *cookie);
static long sim_action(CCB_HEADER *ccbh);
static long sim_init();
#define SIM_VERSION 1
#define HBA_VERSION 1
#define INQ_VENDOR_LEN 0x08
#define INQ_PRODUCT_LEN 0x10
#define INQ_REVISION_LEN 0x04
#define TRANS_TIMEOUT 7500000
static long path_id = -1;
static int32 load_count = 0;
static char sim_vendor_name[] = "Haiku";
static char hba_vendor_name[] = "USB";
static char controller_family[] = "USB SCSI";
struct usb_support_descriptor supported_devices[] = {
{0, 0, 0, 0, 0}
};
usb_device_info *usb_devices[MAX_DEVICES_COUNT];
sem_id usb_serial_lock = -1;
usb_module_info *usb;
static cam_for_sim_module_info *cam;
struct usb_notify_hooks notify_hooks = {
device_added,
device_removed
};
static status_t get_interface_properties(usb_interface_info *uii, uint32 *pproperties);
static status_t load_vendor_module(char **path, const char *path_mask, const char *prop, module_info **mi);
static status_t setup_transport_modules(usb_device_info *udi, usb_device_settings *uds);
static status_t setup_endpoints(usb_interface_info *uii, usb_device_info *udi);
static status_t allocate_resources(usb_device_info *udi);
static void release_resources(usb_device_info *udi);
static status_t xpt_scsi_io(CCB_SCSIIO *ccbio);
static status_t xpt_path_inquiry(CCB_PATHINQ *ccbp);
static status_t xpt_extended_path_inquiry(CCB_EXTENDED_PATHINQ *ccbep);
status_t get_interface_properties(usb_interface_info *uii, uint32 *pproperties)
{
status_t status = B_BAD_TYPE;
if(uii->descr->interface_class == USB_DEV_CLASS_MASS){
status = B_OK;
switch(uii->descr->interface_subclass){
case USB_DEV_SUBCLASS_RBC: *pproperties |= CMDSET_RBC; break;
case USB_DEV_SUBCLASS_UFI: *pproperties |= CMDSET_UFI; break;
case USB_DEV_SUBCLASS_SFF8020I:
case USB_DEV_SUBCLASS_SFF8070I: *pproperties |= CMDSET_ATAPI; break;
case USB_DEV_SUBCLASS_SCSI: *pproperties |= CMDSET_SCSI; break;
case USB_DEV_SUBCLASS_QIC157: *pproperties |= CMDSET_QIC157; break;
default:
TRACE_ALWAYS("get_interface_properties:unknown USB subclass:%02x\n",
uii->descr->interface_subclass);
break;
}
switch(uii->descr->interface_protocol){
case USB_DEV_PROTOCOL_CBI: *pproperties |= PROTO_CBI; break;
case USB_DEV_PROTOCOL_CB: *pproperties |= PROTO_CB; break;
case USB_DEV_PROTOCOL_BULK: *pproperties |= PROTO_BULK_ONLY; break;
default:
TRACE_ALWAYS("get_interface_properties:unknown USB protocol:%02x\n",
uii->descr->interface_protocol);
break;
}
if(status == B_OK){
TRACE("get_interface_properties: standard properties:%08x\n", *pproperties);
}
}
return status;
}
status_t load_vendor_module(char **path, const char *path_mask,
const char *prop, module_info **mi)
{
status_t status = B_NO_MEMORY;
int path_len = strlen(path_mask) + strlen(prop);
*path = malloc(path_len);
if(*path){
sprintf(*path, path_mask, prop);
status = get_module(*path, mi);
if(status != B_OK)
TRACE_ALWAYS("load_vendor_module:get_module(%s) failed:%08x\n", *path, status);
} else {
TRACE_ALWAYS("load_vendor_module:couldn't allocate %d bytes\n", path_len);
}
return status;
}
status_t setup_transport_modules(usb_device_info *udi,
usb_device_settings *uds)
{
status_t status = B_OK;
switch(PROTO(udi->properties)){
case PROTO_BULK_ONLY:
udi->protocol_m = &bulk_only_protocol_m;
break;
case PROTO_CB:
case PROTO_CBI:
udi->protocol_m = &cbi_protocol_m;
break;
case PROTO_VENDOR:{
status = load_vendor_module(&udi->protocol_m_path,
PROTOCOL_MODULE_MASK,
uds->vendor_protocol,
(module_info**)&udi->protocol_m);
}break;
default:
TRACE_ALWAYS("setup_transport_modules: "
"transport %02x is not supported\n", PROTO(udi->properties));
status = B_ENTRY_NOT_FOUND;
}
if(status == B_OK){
switch(CMDSET(udi->properties)){
case CMDSET_SCSI:
udi->transform_m = &scsi_transform_m;
break;
case CMDSET_UFI:
udi->transform_m = &ufi_transform_m;
udi->properties |= FIX_FORCE_MS_TO_10;
break;
case CMDSET_ATAPI:
udi->transform_m = &atapi_transform_m;
udi->properties |= FIX_FORCE_MS_TO_10;
break;
case CMDSET_RBC:
udi->transform_m = &rbc_transform_m;
break;
case CMDSET_QIC157:
udi->transform_m = &qic157_transform_m;
udi->properties |= FIX_FORCE_MS_TO_10;
break;
case CMDSET_VENDOR:{
status = load_vendor_module(&udi->transform_m_path,
TRANSFORM_MODULE_MASK,
uds->vendor_commandset,
(module_info**)&udi->transform_m);
}break;
default:
TRACE_ALWAYS("setup_transport_modules: "
"protocol %02x is not supported\n", CMDSET(udi->properties));
status = B_ENTRY_NOT_FOUND;
}
}
return status;
}
static void
release_transport_modules(usb_device_info *udi)
{
if(PROTO(udi->properties) == PROTO_VENDOR && 0 != udi->protocol_m_path){
put_module(udi->protocol_m_path);
udi->protocol_m = 0;
free(udi->protocol_m_path);
}
if(CMDSET(udi->properties) == CMDSET_VENDOR && 0 != udi->transform_m_path){
put_module(udi->transform_m_path);
udi->transform_m = 0;
free(udi->transform_m_path);
}
}
status_t setup_endpoints(usb_interface_info *uii, usb_device_info *udi)
{
status_t status = B_OK;
int16 idx = 0;
enum{ epIn = 0, epOut, epIntr, epCount };
size_t epts[epCount] = { -1, -1, -1 };
char *epnames[epCount] = {"input", "output", "interrupt"};
size_t ep = 0;
for(; ep < uii->endpoint_count; ep++){
usb_endpoint_descriptor *ed = uii->endpoint[ep].descr;
TRACE("try endpoint:%d %x %x %x\n", ep, (int32)ed->attributes, (int32)ed->endpoint_address, uii->endpoint[ep].handle);
if((ed->attributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_BULK){
if((ed->endpoint_address & USB_EP_ADDR_DIR_IN) == USB_EP_ADDR_DIR_IN){
epts[epIn] = ep;
}else{
epts[epOut] = ep;
}
}else{
if((ed->attributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTERRUPT)
epts[epIntr] = ep;
}
}
switch(PROTO(udi->properties)){
case PROTO_CB:
case PROTO_BULK_ONLY:
if(epts[epIntr] == -1)
epts[epIntr] = 0;
break;
case PROTO_CBI:
default:
}
for(idx = 0; idx < epCount; idx++){
if(epts[idx] == -1 && PROTO(udi->properties) != PROTO_VENDOR){
TRACE_ALWAYS("setup_endpoints: required %s endpoint not found. "
"ignore this interface\n", epnames[idx]);
status = B_ERROR;
}
}
if(status == B_OK){
udi->pipe_in = uii->endpoint[epts[epIn]].handle;
udi->pipe_out = uii->endpoint[epts[epOut]].handle;
udi->pipe_intr = uii->endpoint[epts[epIntr]].handle;
TRACE("endpoint:%x %x %x\n", udi->pipe_in, udi->pipe_out, udi->pipe_intr);
}
return status;
}
status_t allocate_resources(usb_device_info *udi)
{
char name[32];
status_t status = B_NO_MEMORY;
uint16 dev = 0;
acquire_sem(usb_serial_lock);
for(; dev < MAX_DEVICES_COUNT; dev++){
if(!usb_devices[dev])
break;
}
if(dev < MAX_DEVICES_COUNT){
usb_devices[dev] = udi;
udi->lock_sem =
udi->trans_sem = -1;
sprintf(name, "usb_scsi lock_sem:%d", dev);
if((udi->lock_sem = create_sem(0, name)) >= 0){
sprintf(name, "usb_scsi trans_sem:%d", dev);
if((udi->trans_sem = create_sem(0, name))>=0){
udi->dev_num = dev;
release_sem(udi->lock_sem);
status = B_OK;
}else
status = udi->trans_sem;
} else
status = udi->lock_sem;
if(status != B_OK){
TRACE_ALWAYS("allocate_resources:error:%s", strerror(status));
if(udi->lock_sem >= 0)
delete_sem(udi->lock_sem);
if(udi->trans_sem >= 0)
delete_sem(udi->trans_sem);
usb_devices[dev] = NULL;
free(udi);
}
}else{
TRACE_ALWAYS("allocate_resources:reserved devices space exhausted."
"Unplug unnesesary devices or reconfigure this driver.\n");
}
release_sem(usb_serial_lock);
return status;
}
void release_resources(usb_device_info *udi)
{
release_transport_modules(udi);
udi->usb_m = 0;
(*usb->cancel_queued_transfers)(udi->pipe_in);
(*usb->cancel_queued_transfers)(udi->pipe_out);
delete_sem(udi->lock_sem);
delete_sem(udi->trans_sem);
acquire_sem(usb_serial_lock);
usb_devices[udi->dev_num] = NULL;
release_sem(usb_serial_lock);
}
status_t device_added(const usb_device device, void **cookie){
status_t status = B_NO_MEMORY;
const usb_configuration_info *uci = NULL;
uint16 cfg = 0;
bool b_found = false;
bool b_has_extra_settings = false;
const usb_device_descriptor *udd = (*usb->get_device_descriptor)(device);
usb_device_info *udi = (usb_device_info *)malloc(sizeof(usb_device_info));
TRACE("device_added: probing canidate: "
"%04x/%04x %02d/%02d/%02d\n",
udd->vendor_id, udd->product_id,
udd->device_class,
udd->device_subclass,
udd->device_protocol);
if(udi){
status = B_NO_INIT;
while(!b_found && (uci = (*usb->get_nth_configuration)(device, cfg++))){
uint16 itf = 0;
for(; itf < uci->interface_count && !b_found; itf++){
usb_interface_list *ifl = &uci->interface[itf];
uint16 alt = 0;
for(; alt < ifl->alt_count; alt++){
usb_interface_info *uii = &ifl->alt[alt];
usb_device_settings ud_settings;
memset(udi, 0, sizeof(usb_device_info));
memset(&ud_settings, 0, sizeof(usb_device_settings));
udi->device = device;
udi->interface = itf;
b_has_extra_settings = lookup_device_settings(udd, &ud_settings);
switch(get_interface_properties(uii, &udi->properties)){
case B_BAD_TYPE:
if(!b_has_extra_settings){
continue;
}
case B_OK:
if(b_has_extra_settings){
if(PROTO(ud_settings.properties) != PROTO_NONE){
udi->properties &= ~PROTO_MASK;
udi->properties |= PROTO(ud_settings.properties);
}
if(CMDSET(ud_settings.properties) != CMDSET_NONE){
udi->properties &= ~CMDSET_MASK;
udi->properties |= CMDSET(ud_settings.properties);
}
udi->properties |= ud_settings.properties & FIX_MASK;
TRACE("device_added: properties merged:%08x\n", udi->properties);
}
if( B_OK == setup_transport_modules(udi, &ud_settings)){
break;
}
default:
continue;
}
if(alt != 0){
if((status = (*usb->set_alt_interface)(device, uii)) != B_OK){
TRACE_ALWAYS("device_added:setting alt interface failed:%s",
strerror(status));
goto Failed;
}
}
if((*usb->get_configuration)(device) != uci){
if((status = (*usb->set_configuration)(device, uci)) != B_OK){
TRACE_ALWAYS("device_added:setting configuration failed:%08x uci: %08x\n",
(*usb->get_configuration)(device), uci);
TRACE_ALWAYS("device_added:setting configuration failed:%s\n",
strerror(status));
goto Failed;
}
}
if(B_OK != setup_endpoints(uii, udi)){
continue;
}
if((status = allocate_resources(udi)) == B_OK){
udi->b_trace = b_log_protocol;
udi->trace = usb_scsi_trace;
udi->trace_bytes = usb_scsi_trace_bytes;
udi->trans_timeout = TRANS_TIMEOUT;
udi->usb_m = usb;
udi->not_ready_luns = 0xff;
if((status = (*udi->protocol_m->init)(udi)) == B_OK){
TRACE("device_added[%d]: SUCCESS! Enjoy using!\n", udi->dev_num);
*cookie = udi;
b_found = true;
break;
} else {
release_resources(udi);
}
}
}
}
}
if(status == B_OK){
(*cam->minfo.rescan)();
} else {
free(udi);
}
}
if(status != B_OK){
TRACE("device_added: probing failed (%s) for: %04x/%04x\n",
strerror(status), udd->vendor_id, udd->product_id);
}
Failed:
return status;
}
status_t device_removed(void *cookie)
{
status_t status = B_OK;
usb_device_info *udi = (usb_device_info *)cookie;
acquire_sem(udi->lock_sem);
release_resources(udi);
TRACE_ALWAYS("device_removed[%d]:All The Best !!!\n", udi->dev_num);
free(udi);
(*cam->minfo.rescan)();
return status;
}
static long sim_init(void)
{
status_t status = B_OK;
TRACE("sim_init\n");
return status;
}
static bool
pre_check_scsi_io_request(usb_device_info *udi, CCB_SCSIIO *ccbio,
status_t *ret_status)
{
int target_id = ccbio->cam_ch.cam_target_id;
uint8 target_lun = ccbio->cam_ch.cam_target_lun;
*ret_status = B_OK;
if(b_reservation_on && udi == NULL &&
target_id < reserved_devices &&
target_lun < reserved_luns)
{
*ret_status = fake_scsi_io(ccbio);
return false;
}
if(udi == NULL || target_lun > udi->max_lun){
ccbio->cam_ch.cam_status = CAM_DEV_NOT_THERE;
*ret_status = B_DEV_BAD_DRIVE_NUM;
return false;
}
if(ccbio->cam_cdb_len != 6 &&
ccbio->cam_cdb_len != 10 &&
ccbio->cam_cdb_len != 12)
{
TRACE("Bad SCSI command length:%d.Ignore\n", ccbio->cam_cdb_len);
ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
*ret_status = B_BAD_VALUE;
return false;
}
if(NULL == ccbio->cam_sense_ptr){
memset(&udi->autosense_data, 0, sizeof(udi->autosense_data));
} else {
memset(ccbio->cam_sense_ptr, 0, ccbio->cam_sense_len);
}
return true;
}
static bool
pre_handle_features(usb_device_info *udi, CCB_SCSIIO *ccbio,
scsi_cmd_generic *command, sg_buffer *sgb,
status_t *ret_status)
{
uint8 target_lun = ccbio->cam_ch.cam_target_lun;
*ret_status = B_OK;
udi->trans_timeout = TRANS_TIMEOUT;
switch(command->opcode){
case MODE_SELECT_6:
case MODE_SENSE_6:{
bool b_select = (MODE_SELECT_6 == command->opcode);
const char*cmd_name = b_select ? "MODE_SELECT" : "MODE_SENSE";
if(udi->not_ready_luns & (1 << target_lun)){
TRACE("pre_handle_features:%s_6 bypassed for LUN:%d\n", cmd_name, target_lun);
goto set_REQ_INVALID_and_return;
}
if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
if( B_OK != realloc_sg_buffer(sgb, ccbio->cam_dxfer_len + 4)){
TRACE_ALWAYS("pre_handle_features:error allocating %d bytes for %s_10\n",
ccbio->cam_dxfer_len + 4, cmd_name);
goto set_REQ_INVALID_and_return;
}
if(b_select){
sg_buffer sgb_sense_6;
if(B_OK != init_sg_buffer(&sgb_sense_6, ccbio)){
goto set_REQ_INVALID_and_return;
}
TRACE_MODE_SENSE_SGB("MODE SELECT 6:", &sgb_sense_6);
if(B_OK != sg_memcpy(sgb, 1, &sgb_sense_6, 0, 3) ||
B_OK != sg_memcpy(sgb, 7, &sgb_sense_6, 3, 1) ||
B_OK != sg_memcpy(sgb, 8, &sgb_sense_6, 4, ccbio->cam_dxfer_len - sizeof(scsi_mode_param_header_6)))
{
goto set_REQ_INVALID_and_return;
}
TRACE_MODE_SENSE_SGB("MODE SELECT 10:", sgb);
}
}
}break;
case INQUIRY:
if(HAS_FIXES(udi->properties, FIX_NO_INQUIRY)){
fake_inquiry_request(udi, ccbio);
goto set_REQ_CMP_and_return;
} break;
case TEST_UNIT_READY:
if(HAS_FIXES(udi->properties, FIX_NO_TEST_UNIT)){
goto set_REQ_CMP_and_return;
} break;
case PREVENT_ALLOW_MEDIA_REMOVAL:
if(HAS_FIXES(udi->properties, FIX_NO_PREVENT_MEDIA)){
goto set_REQ_CMP_and_return;
} break;
case FORMAT_UNIT:
udi->trans_timeout = B_INFINITE_TIMEOUT;
break;
default: break;
}
return true;
set_REQ_CMP_and_return:
ccbio->cam_ch.cam_status = CAM_REQ_CMP;
return false;
set_REQ_INVALID_and_return:
ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
*ret_status = B_BAD_VALUE;
return false;
}
static bool
post_handle_features(usb_device_info *udi, CCB_SCSIIO *ccbio,
scsi_cmd_generic *command, sg_buffer *sgb,
status_t *ret_status)
{
bool b_cmd_ok = (ccbio->cam_ch.cam_status == CAM_REQ_CMP);
uint8 target_lun = ccbio->cam_ch.cam_target_lun;
switch(command->opcode){
case READ_6:
case WRITE_6:
#if 0
if(!b_cmd_ok && !HAS_FEATURES(udi->descr.properties, PROP_FORCE_RW_TO_6)){
TRACE("post_handle_features:READ(10)/WRITE(10) failed - retry 6-byte one\n");
udi->descr.properties |= PROP_FORCE_RW_TO_6;
ccbio->cam_scsi_status = SCSI_STATUS_OK;
ccbio->cam_ch.cam_status = CAM_REQ_INPROG;
b_retry = true;
}
#endif
break;
case MODE_SENSE_6:
if(!b_cmd_ok && !HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
TRACE("post_handle_features:MODE SENSE(6) failed - retry 10-byte one\n");
udi->properties |= FIX_FORCE_MS_TO_10;
ccbio->cam_scsi_status = SCSI_STATUS_OK;
ccbio->cam_ch.cam_status = CAM_REQ_INPROG;
return true;
}
case MODE_SELECT_6:{
if(MODE_SENSE_6 == command->opcode){
sg_buffer sgb_sense_6;
if(B_OK != init_sg_buffer(&sgb_sense_6, ccbio)){
TRACE_ALWAYS("post_hanlde_features: initialize sgb failed\n");
goto set_REQ_INVALID_and_return;
}
if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
uchar mode_data_len_msb = 0, block_descr_len_msb = 0;
if( B_OK != sg_access_byte(sgb, 0, &mode_data_len_msb, false) ||
B_OK != sg_access_byte(sgb, 6, &block_descr_len_msb, false) ||
0 != mode_data_len_msb || 0 != block_descr_len_msb)
{
TRACE_ALWAYS("MODE_SENSE 10->6 conversion overflow: %d, %d\n",
mode_data_len_msb, block_descr_len_msb);
goto set_REQ_INVALID_and_return;
}
TRACE_MODE_SENSE_SGB("MODE SENSE 10:", sgb);
if( B_OK != sg_memcpy(&sgb_sense_6, 0, sgb, 1, 3) ||
B_OK != sg_memcpy(&sgb_sense_6, 3, sgb, 7, 1) ||
B_OK != sg_memcpy(&sgb_sense_6, 4, sgb, 8, ccbio->cam_dxfer_len - sizeof(scsi_mode_param_header_6)))
{
TRACE_ALWAYS("MODE_SENSE 10->6 conversion failed\n");
goto set_REQ_INVALID_and_return;
}
}
if(HAS_FIXES(udi->properties, FIX_FORCE_READ_ONLY)){
status_t status = B_OK;
uchar device_spec_params = 0;
if(B_OK == (status = sg_access_byte(sgb, 2, &device_spec_params, false))){
device_spec_params |= 0x80;
status = sg_access_byte(sgb, 2, &device_spec_params, true);
}
if(B_OK != status){
TRACE_ALWAYS("MODE_SENSE set READ-ONLY mode failed. Writing ALLOWED!\n");
}
}
TRACE_MODE_SENSE_SGB("MODE SENSE 6:", &sgb_sense_6);
}
if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){
free_sg_buffer(sgb);
}
} break;
case TEST_UNIT_READY: {
scsi_sense_data *sense_data = (ccbio->cam_sense_ptr == NULL) ?
&udi->autosense_data : (scsi_sense_data *)ccbio->cam_sense_ptr;
if((sense_data->flags & SSD_KEY) == SSD_KEY_NOT_READY){
udi->not_ready_luns |= (1 << target_lun);
} else {
udi->not_ready_luns &= ~(1 << target_lun);
}
usb_scsi_trace_bytes("NOT_READY_LUNS:", &udi->not_ready_luns, 1);
} break;
case READ_CAPACITY:{
TRACE_CAPACITY("READ_CAPACITY:", sgb);
} break;
default: break;
}
return false;
set_REQ_INVALID_and_return:
ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
*ret_status = B_BAD_VALUE;
return false;
}
status_t xpt_scsi_io(CCB_SCSIIO *ccbio)
{
status_t status = B_OK;
usb_device_info *udi = usb_devices[ccbio->cam_ch.cam_target_id];
uint8 *cmd;
sg_buffer sgb;
ccbio->cam_scsi_status = SCSI_STATUS_OK;
if(false == pre_check_scsi_io_request(udi, ccbio, &status)){
return status;
}
#if 0
usb_scsi_trace_CCB_SCSIIO(ccbio);
#endif
if((status = acquire_sem(udi->lock_sem)) != B_OK){
TRACE("xpt_scsi_io:acquire_sem_etc() failed:%08x\n", status);
ccbio->cam_ch.cam_status = CAM_DEV_NOT_THERE;
return B_DEV_BAD_DRIVE_NUM;
}
if(ccbio->cam_ch.cam_flags & CAM_CDB_POINTER){
cmd = ccbio->cam_cdb_io.cam_cdb_ptr;
}else{
cmd = ccbio->cam_cdb_io.cam_cdb_bytes;
}
init_sg_buffer(&sgb, ccbio);
do{
uint8 *rcmd;
uint8 rcmdlen;
uint32 transfer_len = 0;
EDirection dir = eDirNone;
if(!pre_handle_features(udi, ccbio, (scsi_cmd_generic *)cmd, &sgb, &status)){
release_sem(udi->lock_sem);
return status;
}
rcmd = udi->scsi_command_buf;
rcmdlen = sizeof(udi->scsi_command_buf);
if((status = (*udi->transform_m->transform)(udi, cmd, ccbio->cam_cdb_len & 0x1f,
&rcmd, &rcmdlen)) != B_OK)
{
TRACE_ALWAYS("xpt_scsi_io: transform failed: %08x\n", status);
ccbio->cam_ch.cam_status = CAM_REQ_INVALID;
release_sem(udi->lock_sem);
return B_BAD_VALUE;
}
switch(CAM_DIR_MASK & ccbio->cam_ch.cam_flags){
case CAM_DIR_IN: dir = eDirIn; break;
case CAM_DIR_OUT: dir = eDirOut; break;
default: dir = eDirNone; break;
}
TRACE_DATA_IO_SG(sgb.piov, sgb.count);
sg_buffer_len(&sgb, &transfer_len);
(*udi->protocol_m->transfer)(udi, rcmd, rcmdlen, sgb.piov, sgb.count,
transfer_len,
dir, ccbio, transfer_callback);
} while(post_handle_features(udi, ccbio, (scsi_cmd_generic *)cmd, &sgb, &status));
release_sem(udi->lock_sem);
return status;
}
status_t xpt_path_inquiry(CCB_PATHINQ *ccbp)
{
status_t status = B_OK;
ccbp->cam_version_num = SIM_VERSION;
ccbp->cam_target_sprt = 0;
ccbp->cam_hba_eng_cnt = 0;
memset (ccbp->cam_vuhba_flags, 0, VUHBA);
ccbp->cam_sim_priv = SIM_PRIV;
ccbp->cam_async_flags = 0;
ccbp->cam_initiator_id = CONTROLLER_SCSI_ID;
ccbp->cam_hba_inquiry = CONTROLLER_SCSI_BUS;
ccbp->cam_hba_misc = PIM_NOINQUIRY;
ccbp->cam_osd_usage = 0;
strncpy(ccbp->cam_sim_vid, sim_vendor_name, SIM_ID);
strncpy(ccbp->cam_hba_vid, hba_vendor_name, HBA_ID);
ccbp->cam_ch.cam_status = CAM_REQ_CMP;
return status;
}
status_t xpt_extended_path_inquiry(CCB_EXTENDED_PATHINQ *ccbep)
{
status_t status = B_OK;
xpt_path_inquiry((CCB_PATHINQ *)ccbep);
sprintf(ccbep->cam_sim_version, "%d.0", SIM_VERSION);
sprintf(ccbep->cam_hba_version, "%d.0", HBA_VERSION);
strncpy(ccbep->cam_controller_family, controller_family, FAM_ID);
strncpy(ccbep->cam_controller_type, "USB-SCSI", TYPE_ID);
return status;
}
static long sim_action(CCB_HEADER *ccbh)
{
status_t status = B_ERROR;
if(path_id != ccbh->cam_path_id){
TRACE_ALWAYS("sim_action:path_id mismatch of func:%d:our:%d,requested:%d\n",
ccbh->cam_func_code, path_id, ccbh->cam_path_id);
ccbh->cam_status = CAM_PATH_INVALID;
} else {
ccbh->cam_status = CAM_REQ_INPROG;
switch(ccbh->cam_func_code){
case XPT_SCSI_IO:
status = xpt_scsi_io((CCB_SCSIIO *)ccbh);
break;
case XPT_PATH_INQ:
status = xpt_path_inquiry((CCB_PATHINQ *)ccbh);
break;
case XPT_EXTENDED_PATH_INQ:
status = xpt_extended_path_inquiry((CCB_EXTENDED_PATHINQ *)ccbh);
break;
default:
TRACE_ALWAYS("sim_action: unsupported function: %x\n", ccbh->cam_func_code);
ccbh->cam_status = CAM_REQ_INVALID;
break;
}
}
return status;
}
#endif
static status_t std_ops(int32 op, ...)
{
status_t status = B_OK;
switch(op) {
case B_MODULE_INIT:
TRACE_ALWAYS("std_ops: B_MODULE_INIT called!\n");
load_module_settings();
break;
case B_MODULE_UNINIT:
TRACE_ALWAYS("std_ops: B_MODULE_UNINIT called!\n");
break;
}
return status;
}
static float
supports_device(device_node_handle parent, bool *_noConnection)
{
TRACE_ALWAYS("supports_device\n");
return 0.f;
}
static status_t
register_device(device_node_handle parent)
{
TRACE_ALWAYS("register_device\n");
return B_OK;
}
static status_t
init_module(device_node_handle node, void *user_cookie, void **_cookie)
{
TRACE_ALWAYS("inti_driver\n");
return B_OK;
}
static status_t
uninit_module(void *cookie)
{
TRACE_ALWAYS("uninit_driver\n");
return B_OK;
}
static void
device_removed(device_node_handle node, void *cookie)
{
TRACE_ALWAYS("device_removed\n");
}
static void
device_cleanup(device_node_handle node)
{
TRACE_ALWAYS("device_cleanup\n");
}
static void
get_supported_paths(const char ***_busses, const char ***_devices)
{
TRACE_ALWAYS("get_supported_path\n");
}
static void
scsi_io( scsi_sim_cookie cookie, scsi_ccb *ccb )
{
TRACE_ALWAYS("scsi_io\n");
}
static uchar
abort( scsi_sim_cookie cookie, scsi_ccb *ccb_to_abort )
{
TRACE_ALWAYS("scsi_sim\n");
return 0;
}
static uchar
reset_device( scsi_sim_cookie cookie, uchar target_id, uchar target_lun )
{
TRACE_ALWAYS("supports_device\n");
return 0;
}
static uchar
term_io( scsi_sim_cookie cookie, scsi_ccb *ccb_to_terminate )
{
TRACE_ALWAYS("term_io\n");
return 0;
}
static uchar
path_inquiry( scsi_sim_cookie cookie, scsi_path_inquiry *inquiry_data )
{
TRACE_ALWAYS("path_inquiry\n");
return 0;
}
static uchar
scan_bus( scsi_sim_cookie cookie )
{
TRACE_ALWAYS("scan_bus\n");
return 0;
}
static uchar
reset_bus( scsi_sim_cookie cookie )
{
TRACE_ALWAYS("reset_bus\n");
return 0;
}
static void
get_restrictions(scsi_sim_cookie cookie, uchar target_id, bool *is_atapi, bool *no_autosense, uint32 *max_blocks )
{
TRACE_ALWAYS("get_restrictions\n");
}
static status_t
module_ioctl(scsi_sim_cookie cookie, uint8 targetID, uint32 op, void *buffer, size_t length)
{
TRACE_ALWAYS("ioctl\n");
return B_DEV_INVALID_IOCTL;
}
static scsi_sim_interface usb_scsi_sim = {
{
{
"busses/scsi/usb/device_v1",
0,
&std_ops
},
supports_device,
register_device,
init_module,
uninit_module,
device_removed,
device_cleanup,
get_supported_paths,
},
scsi_io,
abort,
reset_device,
term_io,
path_inquiry,
scan_bus,
reset_bus,
get_restrictions,
module_ioctl
};
_EXPORT module_info *modules[] = {
(module_info *) &usb_scsi_sim,
NULL
};