1 /** 2 * 3 * TODO: description 4 * 5 * This file is a part of USB SCSI CAM for Haiku OS. 6 * May be used under terms of the MIT License 7 * 8 * Author(s): 9 * Siarzhuk Zharski <imker@gmx.li> 10 * 11 * 12 */ 13 /** Main part of USB SIM implementation */ 14 15 #include "usb_scsi.h" 16 17 #include <KernelExport.h> 18 #include <module.h> 19 #include <malloc.h> 20 #include <strings.h> 21 #include <stdio.h> 22 #include "device_info.h" 23 #include "settings.h" 24 #include "transform_procs.h" 25 #include "tracing.h" 26 #include "scsi_commands.h" 27 #include "proto_common.h" 28 #include "proto_bulk.h" 29 #include "proto_cbi.h" 30 #include "usb_defs.h" 31 #include "fake_device.h" 32 #include "sg_buffer.h" 33 34 status_t device_added(const usb_device device, void **cookie); 35 36 status_t device_removed(void *cookie); 37 38 static long sim_action(CCB_HEADER *ccbh); 39 static long sim_init(); 40 41 #define SIM_VERSION 1 42 #define HBA_VERSION 1 43 44 //#define ROUNDUP(size, seg) (((size) + (seg) - 1) & ~((seg) - 1)) 45 46 #define INQ_VENDOR_LEN 0x08 47 #define INQ_PRODUCT_LEN 0x10 48 #define INQ_REVISION_LEN 0x04 49 50 #define TRANS_TIMEOUT 7500000 51 52 static long path_id = -1; 53 static int32 load_count = 0; 54 55 static char sim_vendor_name[] = "S.Zharski"; /* who wrote this driver */ 56 static char hba_vendor_name[] = "USB"; /* who made the hardware */ 57 static char controller_family[] = "USB SCSI"; /* what family of products */ 58 59 struct usb_support_descriptor supported_devices[] = { 60 {0, 0, 0, 0, 0} 61 }; 62 63 #define SIZEOF(array) (sizeof(array)/sizeof(array[0])) //????? 64 65 usb_device_info *usb_devices[MAX_DEVICES_COUNT]; 66 /* main devices table locking semaphore */ 67 sem_id usb_serial_lock = -1; 68 69 usb_module_info *usb; 70 static cam_for_sim_module_info *cam; 71 72 struct usb_notify_hooks notify_hooks = { 73 device_added, 74 device_removed 75 }; 76 77 /* function prototupes */ 78 static status_t get_interface_properties(usb_interface_info *uii, uint32 *pproperties); 79 static status_t load_vendor_module(char **path, const char *path_mask, const char *prop, module_info **mi); 80 static status_t setup_transport_modules(usb_device_info *udi, usb_device_settings *uds); 81 static status_t setup_endpoints(usb_interface_info *uii, usb_device_info *udi); 82 static status_t allocate_resources(usb_device_info *udi); 83 static void release_resources(usb_device_info *udi); 84 static status_t xpt_scsi_io(CCB_SCSIIO *ccbio); 85 static status_t xpt_path_inquiry(CCB_PATHINQ *ccbp); 86 static status_t xpt_extended_path_inquiry(CCB_EXTENDED_PATHINQ *ccbep); 87 88 /** 89 \fn:match_device 90 \param device:??? 91 \param uii:??? 92 \param pproperties:??? 93 \return:B_BAD_TYPE , B_ENTRY_NOT_FOUND, B_OK 94 95 ?? 96 */ 97 status_t get_interface_properties(usb_interface_info *uii, uint32 *pproperties) 98 { 99 status_t status = B_BAD_TYPE; 100 if(uii->descr->interface_class == USB_DEV_CLASS_MASS){ 101 status = B_OK; 102 switch(uii->descr->interface_subclass){ 103 case USB_DEV_SUBCLASS_RBC: *pproperties |= CMDSET_RBC; break; 104 case USB_DEV_SUBCLASS_UFI: *pproperties |= CMDSET_UFI; break; 105 case USB_DEV_SUBCLASS_SFF8020I: 106 case USB_DEV_SUBCLASS_SFF8070I: *pproperties |= CMDSET_ATAPI; break; 107 case USB_DEV_SUBCLASS_SCSI: *pproperties |= CMDSET_SCSI; break; 108 case USB_DEV_SUBCLASS_QIC157: *pproperties |= CMDSET_QIC157; break; 109 default: 110 TRACE_ALWAYS("get_interface_properties:unknown USB subclass:%02x\n", 111 uii->descr->interface_subclass); 112 /*status = B_ENTRY_NOT_FOUND;*/ /*B_BAD_TYPE assumed*/ 113 break; 114 } 115 switch(uii->descr->interface_protocol){ 116 case USB_DEV_PROTOCOL_CBI: *pproperties |= PROTO_CBI; break; 117 case USB_DEV_PROTOCOL_CB: *pproperties |= PROTO_CB; break; 118 case USB_DEV_PROTOCOL_BULK: *pproperties |= PROTO_BULK_ONLY; break; 119 default: 120 TRACE_ALWAYS("get_interface_properties:unknown USB protocol:%02x\n", 121 uii->descr->interface_protocol); 122 /*status = B_ENTRY_NOT_FOUND;*/ /*B_BAD_TYPE assumed*/ 123 break; 124 } 125 if(status == B_OK){ 126 TRACE("get_interface_properties: standard properties:%08x\n", *pproperties); 127 } 128 } 129 return status; 130 } 131 /** 132 \fn: 133 134 */ 135 status_t load_vendor_module(char **path, const char *path_mask, 136 const char *prop, module_info **mi) 137 { 138 status_t status = B_NO_MEMORY; 139 int path_len = strlen(path_mask) + strlen(prop); 140 *path = malloc(path_len); 141 if(*path){ 142 sprintf(*path, path_mask, prop); 143 status = get_module(*path, mi); 144 if(status != B_OK) 145 TRACE_ALWAYS("load_vendor_module:get_module(%s) failed:%08x\n", *path, status); 146 } else { 147 TRACE_ALWAYS("load_vendor_module:couldn't allocate %d bytes\n", path_len); 148 } 149 return status; 150 } 151 /** 152 \fn: 153 154 */ 155 status_t setup_transport_modules(usb_device_info *udi, 156 usb_device_settings *uds) 157 { 158 status_t status = B_OK; 159 switch(PROTO(udi->properties)){ 160 case PROTO_BULK_ONLY: 161 udi->protocol_m = &bulk_only_protocol_m; 162 break; 163 case PROTO_CB: 164 case PROTO_CBI: 165 udi->protocol_m = &cbi_protocol_m; 166 break; 167 case PROTO_VENDOR:{ 168 status = load_vendor_module(&udi->protocol_m_path, 169 PROTOCOL_MODULE_MASK, 170 uds->vendor_protocol, 171 (module_info**)&udi->protocol_m); 172 }break; 173 default: 174 TRACE_ALWAYS("setup_transport_modules: " 175 "transport %02x is not supported\n", PROTO(udi->properties)); 176 status = B_ENTRY_NOT_FOUND; 177 } 178 if(status == B_OK){ 179 switch(CMDSET(udi->properties)){ 180 case CMDSET_SCSI: 181 udi->transform_m = &scsi_transform_m; 182 break; 183 case CMDSET_UFI: 184 udi->transform_m = &ufi_transform_m; 185 udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */ 186 break; 187 case CMDSET_ATAPI: 188 udi->transform_m = &atapi_transform_m; 189 udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */ 190 break; 191 case CMDSET_RBC: 192 udi->transform_m = &rbc_transform_m; 193 break; 194 case CMDSET_QIC157: 195 udi->transform_m = &qic157_transform_m; 196 udi->properties |= FIX_FORCE_MS_TO_10; /* always use 10-byte request */ 197 break; 198 case CMDSET_VENDOR:{ 199 status = load_vendor_module(&udi->transform_m_path, 200 TRANSFORM_MODULE_MASK, 201 uds->vendor_commandset, 202 (module_info**)&udi->transform_m); 203 }break; 204 default: 205 TRACE_ALWAYS("setup_transport_modules: " 206 "protocol %02x is not supported\n", CMDSET(udi->properties)); 207 status = B_ENTRY_NOT_FOUND; 208 } 209 } 210 return status; 211 } 212 213 static void 214 release_transport_modules(usb_device_info *udi) 215 { 216 if(PROTO(udi->properties) == PROTO_VENDOR && 0 != udi->protocol_m_path){ 217 put_module(udi->protocol_m_path); 218 udi->protocol_m = 0; 219 free(udi->protocol_m_path); 220 } 221 if(CMDSET(udi->properties) == CMDSET_VENDOR && 0 != udi->transform_m_path){ 222 put_module(udi->transform_m_path); 223 udi->transform_m = 0; 224 free(udi->transform_m_path); 225 } 226 } 227 228 /** 229 \fn: 230 */ 231 status_t setup_endpoints(usb_interface_info *uii, usb_device_info *udi) 232 { 233 status_t status = B_OK; 234 int16 idx = 0; 235 enum{ epIn = 0, epOut, epIntr, epCount }; 236 size_t epts[epCount] = { -1, -1, -1 }; 237 char *epnames[epCount] = {"input", "output", "interrupt"}; 238 size_t ep = 0; 239 for(; ep < uii->endpoint_count; ep++){ 240 usb_endpoint_descriptor *ed = uii->endpoint[ep].descr; 241 TRACE("try endpoint:%d %x %x %x\n", ep, (int32)ed->attributes, (int32)ed->endpoint_address, uii->endpoint[ep].handle); 242 if((ed->attributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_BULK){ 243 if((ed->endpoint_address & USB_EP_ADDR_DIR_IN) == USB_EP_ADDR_DIR_IN){ 244 epts[epIn] = ep; 245 }else{ 246 epts[epOut] = ep; 247 } 248 }else{ 249 if((ed->attributes & USB_EP_ATTR_MASK) == USB_EP_ATTR_INTERRUPT) 250 epts[epIntr] = ep; 251 } 252 } 253 switch(PROTO(udi->properties)){ 254 case PROTO_CB: 255 case PROTO_BULK_ONLY: 256 if(epts[epIntr] == -1) 257 epts[epIntr] = 0; /* not required for this transports - set it to default*/ 258 break; 259 case PROTO_CBI: 260 default: 261 } 262 for(idx = 0; idx < epCount; idx++){ 263 if(epts[idx] == -1 && PROTO(udi->properties) != PROTO_VENDOR){ 264 TRACE_ALWAYS("setup_endpoints: required %s endpoint not found. " 265 "ignore this interface\n", epnames[idx]); 266 // DEBUG!!!!!!!!!! 267 status = B_ERROR; 268 } 269 } 270 if(status == B_OK){ 271 udi->pipe_in = uii->endpoint[epts[epIn]].handle; 272 udi->pipe_out = uii->endpoint[epts[epOut]].handle; 273 udi->pipe_intr = uii->endpoint[epts[epIntr]].handle; 274 //TRACE("setup_endpoints: input:%d output:%d " 275 // "interrupt:%d\n", epts[epIn], epts[epOut], epts[epIntr]); 276 TRACE("endpoint:%x %x %x\n", udi->pipe_in, udi->pipe_out, udi->pipe_intr); 277 } 278 return status; 279 } 280 /** 281 \fn:create_device_info 282 \param device:??? 283 \param ppudi:??? 284 \return:??? 285 286 ??? 287 */ 288 status_t allocate_resources(usb_device_info *udi) 289 { 290 char name[32]; 291 status_t status = B_NO_MEMORY; 292 uint16 dev = 0; 293 acquire_sem(usb_serial_lock); 294 for(; dev < MAX_DEVICES_COUNT; dev++){ 295 if(!usb_devices[dev]) 296 break; 297 } 298 if(dev < MAX_DEVICES_COUNT){ 299 usb_devices[dev] = udi; 300 udi->lock_sem = 301 udi->trans_sem = -1; 302 sprintf(name, "usb_scsi lock_sem:%d", dev); 303 if((udi->lock_sem = create_sem(0, name)) >= 0){ 304 sprintf(name, "usb_scsi trans_sem:%d", dev); 305 if((udi->trans_sem = create_sem(0, name))>=0){ 306 udi->dev_num = dev; 307 release_sem(udi->lock_sem); 308 status = B_OK; 309 }else 310 status = udi->trans_sem; 311 } else 312 status = udi->lock_sem; 313 314 if(status != B_OK){ 315 TRACE_ALWAYS("allocate_resources:error:%s", strerror(status)); 316 if(udi->lock_sem >= 0) 317 delete_sem(udi->lock_sem); 318 if(udi->trans_sem >= 0) 319 delete_sem(udi->trans_sem); 320 usb_devices[dev] = NULL; 321 free(udi); 322 } 323 }else{ 324 TRACE_ALWAYS("allocate_resources:reserved devices space exhausted." 325 "Unplug unnesesary devices or reconfigure this driver.\n"); 326 } 327 release_sem(usb_serial_lock); 328 return status; 329 } 330 /** 331 \fn: 332 333 */ 334 void release_resources(usb_device_info *udi) 335 { 336 release_transport_modules(udi); 337 udi->usb_m = 0; 338 (*usb->cancel_queued_transfers)(udi->pipe_in); 339 (*usb->cancel_queued_transfers)(udi->pipe_out); 340 delete_sem(udi->lock_sem); 341 delete_sem(udi->trans_sem); 342 acquire_sem(usb_serial_lock); 343 usb_devices[udi->dev_num] = NULL; 344 release_sem(usb_serial_lock); 345 } 346 /** 347 \fn:device_added 348 \param device:?? 349 \param cookie:?? 350 \return:?? 351 352 ?? 353 */ 354 status_t device_added(const usb_device device, void **cookie){ 355 status_t status = B_NO_MEMORY; 356 const usb_configuration_info *uci = NULL; 357 uint16 cfg = 0; 358 bool b_found = false; 359 bool b_has_extra_settings = false; 360 const usb_device_descriptor *udd = (*usb->get_device_descriptor)(device); 361 usb_device_info *udi = (usb_device_info *)malloc(sizeof(usb_device_info)); 362 TRACE("device_added: probing canidate: " 363 "%04x/%04x %02d/%02d/%02d\n", 364 udd->vendor_id, udd->product_id, 365 udd->device_class, 366 udd->device_subclass, 367 udd->device_protocol); 368 if(udi){ 369 status = B_NO_INIT; 370 while(!b_found && (uci = (*usb->get_nth_configuration)(device, cfg++))){ 371 uint16 itf = 0; 372 for(; itf < uci->interface_count && !b_found; itf++){ 373 usb_interface_list *ifl = &uci->interface[itf]; 374 uint16 alt = 0; 375 for(; alt < ifl->alt_count; alt++){ 376 usb_interface_info *uii = &ifl->alt[alt]; 377 usb_device_settings ud_settings; 378 memset(udi, 0, sizeof(usb_device_info)); 379 memset(&ud_settings, 0, sizeof(usb_device_settings)); 380 udi->device = device; 381 udi->interface = itf; 382 b_has_extra_settings = lookup_device_settings(udd, &ud_settings); 383 switch(get_interface_properties(uii, &udi->properties)){ 384 case B_BAD_TYPE: /* non-standard USB class*/ 385 if(!b_has_extra_settings){ 386 continue; /* skip to next interface */ 387 } /* no break - fall through */ 388 case B_OK: 389 if(b_has_extra_settings){ 390 if(PROTO(ud_settings.properties) != PROTO_NONE){ 391 udi->properties &= ~PROTO_MASK; 392 udi->properties |= PROTO(ud_settings.properties); 393 } 394 if(CMDSET(ud_settings.properties) != CMDSET_NONE){ 395 udi->properties &= ~CMDSET_MASK; 396 udi->properties |= CMDSET(ud_settings.properties); 397 } 398 udi->properties |= ud_settings.properties & FIX_MASK; 399 TRACE("device_added: properties merged:%08x\n", udi->properties); 400 } 401 if( B_OK == setup_transport_modules(udi, &ud_settings)){ 402 break; 403 } /* else - no break - fall through */ 404 default: 405 continue; /* skip to next interface */ 406 } 407 if(alt != 0){ /*TODO: are we need this ???*/ 408 if((status = (*usb->set_alt_interface)(device, uii)) != B_OK){ 409 TRACE_ALWAYS("device_added:setting alt interface failed:%s", 410 strerror(status)); 411 goto Failed;/* Break - is it right?*/ 412 } 413 } 414 if((*usb->get_configuration)(device) != uci){ 415 if((status = (*usb->set_configuration)(device, uci)) != B_OK){ 416 TRACE_ALWAYS("device_added:setting configuration failed:%08x uci: %08x\n", 417 (*usb->get_configuration)(device), uci); 418 TRACE_ALWAYS("device_added:setting configuration failed:%s\n", 419 strerror(status)); 420 421 goto Failed;/* Break - is it right?*/ 422 } 423 } 424 if(B_OK != setup_endpoints(uii, udi)){ 425 continue; /* skip to next interface */ 426 } 427 if((status = allocate_resources(udi)) == B_OK){ 428 udi->b_trace = b_log_protocol; 429 udi->trace = usb_scsi_trace; 430 udi->trace_bytes = usb_scsi_trace_bytes; 431 udi->trans_timeout = TRANS_TIMEOUT; 432 udi->usb_m = usb; 433 udi->not_ready_luns = 0xff; /*assume all LUNs initially not ready */ 434 if((status = (*udi->protocol_m->init)(udi)) == B_OK){ 435 TRACE("device_added[%d]: SUCCESS! Enjoy using!\n", udi->dev_num); 436 *cookie = udi; 437 b_found = true; /* we have found something useful - time to go out! */ 438 break; /* ... now break alternatives iteration.*/ 439 } else { 440 release_resources(udi); 441 } 442 } 443 /* go to next iteration - check all configurations for possible devices */ 444 }/* for(...) iterate interface alternates*/ 445 }/* for(...) iterate interfaces*/ 446 }/* while(...) iterate configurations */ 447 if(status == B_OK){ 448 (*cam->minfo.rescan)(); 449 } else { 450 free(udi); 451 } 452 } /* if(udi){ */ 453 if(status != B_OK){ 454 TRACE("device_added: probing failed (%s) for: %04x/%04x\n", 455 strerror(status), udd->vendor_id, udd->product_id); 456 } 457 Failed: 458 return status; 459 } 460 /** 461 \fn:device_removed 462 \param cookie:??? 463 \return:??? 464 465 ??? 466 */ 467 status_t device_removed(void *cookie) 468 { 469 status_t status = B_OK; 470 usb_device_info *udi = (usb_device_info *)cookie; 471 acquire_sem(udi->lock_sem); /* wait for possible I/O operation complete */ 472 release_resources(udi); 473 /* no corresponding call of release_sem(udi->lock_sem); 474 - semaphore was deleted in release_resources, any waiting thread 475 was failed with BAD_SEM_ID. 476 */ 477 TRACE_ALWAYS("device_removed[%d]:All The Best !!!\n", udi->dev_num); 478 free(udi); 479 (*cam->minfo.rescan)(); 480 return status; 481 } 482 /** 483 \fn:sim_init 484 \return: ??? 485 486 called on SIM init 487 */ 488 static long sim_init(void) 489 { 490 status_t status = B_OK; 491 TRACE("sim_init\n"); 492 return status; 493 } 494 495 /** 496 */ 497 static bool 498 pre_check_scsi_io_request(usb_device_info *udi, CCB_SCSIIO *ccbio, 499 status_t *ret_status) 500 { 501 int target_id = ccbio->cam_ch.cam_target_id; 502 uint8 target_lun = ccbio->cam_ch.cam_target_lun; 503 *ret_status = B_OK; 504 /* handle reserved device and luns entries */ 505 if(b_reservation_on && udi == NULL && 506 target_id < reserved_devices && 507 target_lun < reserved_luns) 508 { 509 *ret_status = fake_scsi_io(ccbio); 510 return false; 511 } 512 /* no device for this target | LUN */ 513 if(udi == NULL || target_lun > udi->max_lun){ 514 ccbio->cam_ch.cam_status = CAM_DEV_NOT_THERE; 515 *ret_status = B_DEV_BAD_DRIVE_NUM; 516 return false; 517 } 518 /* check command length */ 519 if(ccbio->cam_cdb_len != 6 && 520 ccbio->cam_cdb_len != 10 && 521 ccbio->cam_cdb_len != 12) 522 { 523 TRACE("Bad SCSI command length:%d.Ignore\n", ccbio->cam_cdb_len); 524 ccbio->cam_ch.cam_status = CAM_REQ_INVALID; 525 *ret_status = B_BAD_VALUE; 526 return false; 527 } 528 /* Clean up auto sense buffer. 529 To avoid misunderstanding in not ready luns logic detection.*/ 530 if(NULL == ccbio->cam_sense_ptr){ 531 memset(&udi->autosense_data, 0, sizeof(udi->autosense_data)); 532 } else { 533 memset(ccbio->cam_sense_ptr, 0, ccbio->cam_sense_len); 534 } 535 return true; 536 } 537 /** 538 */ 539 static bool 540 pre_handle_features(usb_device_info *udi, CCB_SCSIIO *ccbio, 541 scsi_cmd_generic *command, sg_buffer *sgb, 542 status_t *ret_status) 543 { 544 uint8 target_lun = ccbio->cam_ch.cam_target_lun; 545 *ret_status = B_OK; 546 udi->trans_timeout = TRANS_TIMEOUT; 547 switch(command->opcode){ 548 case MODE_SELECT_6: 549 case MODE_SENSE_6:{ 550 bool b_select = (MODE_SELECT_6 == command->opcode); 551 const char*cmd_name = b_select ? "MODE_SELECT" : "MODE_SENSE"; 552 if(udi->not_ready_luns & (1 << target_lun)){ 553 TRACE("pre_handle_features:%s_6 bypassed for LUN:%d\n", cmd_name, target_lun); 554 goto set_REQ_INVALID_and_return; 555 } 556 if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){ 557 if( B_OK != realloc_sg_buffer(sgb, ccbio->cam_dxfer_len + 4)){ /* TODO: 4 - hardcoded? */ 558 TRACE_ALWAYS("pre_handle_features:error allocating %d bytes for %s_10\n", 559 ccbio->cam_dxfer_len + 4, cmd_name); 560 goto set_REQ_INVALID_and_return; 561 } 562 if(b_select){ 563 sg_buffer sgb_sense_6; 564 /*TODO: implemenet and try refragment_sg_buffer - to check handling of real scatter/gather!!*/ 565 if(B_OK != init_sg_buffer(&sgb_sense_6, ccbio)){ 566 /* In case of error TRACE-ing was already performed in sg_ functions */ 567 goto set_REQ_INVALID_and_return; 568 } 569 TRACE_MODE_SENSE_SGB("MODE SELECT 6:", &sgb_sense_6); 570 if(B_OK != sg_memcpy(sgb, 1, &sgb_sense_6, 0, 3) || 571 B_OK != sg_memcpy(sgb, 7, &sgb_sense_6, 3, 1) || 572 B_OK != sg_memcpy(sgb, 8, &sgb_sense_6, 4, ccbio->cam_dxfer_len - sizeof(scsi_mode_param_header_6))) 573 { 574 /* In case of error TRACE-ing was already performed in sg_ functions */ 575 goto set_REQ_INVALID_and_return; 576 } 577 TRACE_MODE_SENSE_SGB("MODE SELECT 10:", sgb); 578 } 579 } /*else { 580 if(b_select){ / * trace data if configured in settings * / 581 TRACE_MODE_SENSE_DATA("MODE_SELECT:", ccbio->cam_data_ptr, ccbio->cam_dxfer_len); 582 } 583 }*/ 584 }break; 585 case INQUIRY: /* fake INQUIRY request */ 586 if(HAS_FIXES(udi->properties, FIX_NO_INQUIRY)){ 587 fake_inquiry_request(udi, ccbio); 588 goto set_REQ_CMP_and_return; 589 } break; 590 case TEST_UNIT_READY: /* fake INQUIRY request */ 591 if(HAS_FIXES(udi->properties, FIX_NO_TEST_UNIT)){ 592 goto set_REQ_CMP_and_return; 593 } break; 594 case PREVENT_ALLOW_MEDIA_REMOVAL: /* fake PREVENT_ALLOW_MEDIA_REMOVAL request */ 595 if(HAS_FIXES(udi->properties, FIX_NO_PREVENT_MEDIA)){ 596 goto set_REQ_CMP_and_return; 597 } break; 598 case FORMAT_UNIT: 599 udi->trans_timeout = B_INFINITE_TIMEOUT; 600 break; 601 default: break; 602 } 603 return true; 604 605 set_REQ_CMP_and_return: 606 ccbio->cam_ch.cam_status = CAM_REQ_CMP; 607 return false;//*ret_status = B_OK; 608 609 set_REQ_INVALID_and_return: 610 ccbio->cam_ch.cam_status = CAM_REQ_INVALID; 611 *ret_status = B_BAD_VALUE; 612 return false; 613 } 614 /** 615 */ 616 static bool 617 post_handle_features(usb_device_info *udi, CCB_SCSIIO *ccbio, 618 scsi_cmd_generic *command, sg_buffer *sgb, 619 status_t *ret_status) 620 { 621 bool b_cmd_ok = (ccbio->cam_ch.cam_status == CAM_REQ_CMP); 622 uint8 target_lun = ccbio->cam_ch.cam_target_lun; 623 switch(command->opcode){ 624 case READ_6: 625 case WRITE_6: 626 #if 0 627 /* Disabled - single problem can switch to 6-bytes mode. If device doesn't 628 support 6-bytes command all goes totally wrong. That's bad. */ 629 if(!b_cmd_ok && !HAS_FEATURES(udi->descr.properties, PROP_FORCE_RW_TO_6)){ 630 TRACE("post_handle_features:READ(10)/WRITE(10) failed - retry 6-byte one\n"); 631 udi->descr.properties |= PROP_FORCE_RW_TO_6; 632 ccbio->cam_scsi_status = SCSI_STATUS_OK; /* clear the scsi_status. */ 633 ccbio->cam_ch.cam_status = CAM_REQ_INPROG; /* set status in progress again. */ 634 b_retry = true; /* inform caller about retry */ 635 } 636 #endif 637 break; 638 case MODE_SENSE_6: 639 if(!b_cmd_ok && !HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){ 640 TRACE("post_handle_features:MODE SENSE(6) failed - retry 10-byte one\n"); 641 udi->properties |= FIX_FORCE_MS_TO_10; 642 ccbio->cam_scsi_status = SCSI_STATUS_OK; /* clear the scsi_status. */ 643 ccbio->cam_ch.cam_status = CAM_REQ_INPROG; /* set status in progress again. */ 644 return true; /* inform caller about retry */ 645 } /* no break, - fallthrough! */ 646 case MODE_SELECT_6:{ 647 if(MODE_SENSE_6 == command->opcode){ 648 sg_buffer sgb_sense_6; 649 if(B_OK != init_sg_buffer(&sgb_sense_6, ccbio)){ 650 TRACE_ALWAYS("post_hanlde_features: initialize sgb failed\n"); 651 goto set_REQ_INVALID_and_return; 652 } 653 /* convert sense information from 10-byte request result to 6-byte one */ 654 if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){ 655 uchar mode_data_len_msb = 0, block_descr_len_msb = 0; 656 if( B_OK != sg_access_byte(sgb, 0, &mode_data_len_msb, false) || 657 B_OK != sg_access_byte(sgb, 6, &block_descr_len_msb, false) || 658 0 != mode_data_len_msb || 0 != block_descr_len_msb) 659 { 660 /* In case of error TRACE-ing was already performed in sg_ functions */ 661 TRACE_ALWAYS("MODE_SENSE 10->6 conversion overflow: %d, %d\n", 662 mode_data_len_msb, block_descr_len_msb); 663 goto set_REQ_INVALID_and_return; 664 } 665 TRACE_MODE_SENSE_SGB("MODE SENSE 10:", sgb); 666 if( B_OK != sg_memcpy(&sgb_sense_6, 0, sgb, 1, 3) || 667 B_OK != sg_memcpy(&sgb_sense_6, 3, sgb, 7, 1) || 668 B_OK != sg_memcpy(&sgb_sense_6, 4, sgb, 8, ccbio->cam_dxfer_len - sizeof(scsi_mode_param_header_6))) 669 { 670 /* In case of error TRACE-ing was already performed in sg_ functions */ 671 TRACE_ALWAYS("MODE_SENSE 10->6 conversion failed\n"); 672 goto set_REQ_INVALID_and_return; 673 } 674 } 675 /* set write-protected flag if required by user */ 676 if(HAS_FIXES(udi->properties, FIX_FORCE_READ_ONLY)){ 677 status_t status = B_OK; 678 uchar device_spec_params = 0; 679 if(B_OK == (status = sg_access_byte(sgb, 2, &device_spec_params, false))){ 680 device_spec_params |= 0x80; 681 status = sg_access_byte(sgb, 2, &device_spec_params, true); 682 } 683 if(B_OK != status){ 684 TRACE_ALWAYS("MODE_SENSE set READ-ONLY mode failed. Writing ALLOWED!\n"); 685 /*goto set_req_invalid_and_return;*/ /* not urgent. do not fail processing... */ 686 } 687 } 688 TRACE_MODE_SENSE_SGB("MODE SENSE 6:", &sgb_sense_6); 689 } 690 if(HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)){ 691 free_sg_buffer(sgb); 692 } 693 } break; 694 case TEST_UNIT_READY: { /* set the not ready luns flag */ 695 scsi_sense_data *sense_data = (ccbio->cam_sense_ptr == NULL) ? 696 &udi->autosense_data : (scsi_sense_data *)ccbio->cam_sense_ptr; 697 if((sense_data->flags & SSD_KEY) == SSD_KEY_NOT_READY){ 698 udi->not_ready_luns |= (1 << target_lun); 699 } else { 700 udi->not_ready_luns &= ~(1 << target_lun); 701 } 702 usb_scsi_trace_bytes("NOT_READY_LUNS:", &udi->not_ready_luns, 1); 703 } break; 704 case READ_CAPACITY:{ 705 /*uint8 *bts = sgb->piov->iov_base; 706 uint32 capacity = (bts[0]<<24) + (bts[1]<<16) + (bts[2]<<8) + (bts[3]); 707 TRACE_ALWAYS("CAPAC:%d\n", capacity); 708 //bts[3] -= 3;*/ 709 TRACE_CAPACITY("READ_CAPACITY:", sgb); 710 } break; 711 default: break; 712 } 713 return false; /* do not retry - all is OK */ //b_retry; 714 715 set_REQ_INVALID_and_return: 716 ccbio->cam_ch.cam_status = CAM_REQ_INVALID; 717 *ret_status = B_BAD_VALUE; 718 return false; /* do not retry - fatal error */ 719 } 720 /** 721 \fn:xpt_scsi_io 722 \param ccbio: ???? 723 \return: ??? 724 725 xpt_scsi_io - handle XPT_SCSI_IO sim action 726 */ 727 status_t xpt_scsi_io(CCB_SCSIIO *ccbio) 728 { 729 status_t status = B_OK; 730 usb_device_info *udi = usb_devices[ccbio->cam_ch.cam_target_id/*target_id*/]; 731 uint8 *cmd; 732 sg_buffer sgb; 733 734 /* clear the scsi_status. It can come from system with some garbage value ...*/ 735 ccbio->cam_scsi_status = SCSI_STATUS_OK; 736 /* check the request for correct parameters, valid targets, luns etc ... */ 737 if(false == pre_check_scsi_io_request(udi, ccbio, &status)){ 738 return status; 739 } 740 741 #if 0 /* activate if you need detailed logging of CCB_SCSI request*/ 742 usb_scsi_trace_CCB_SCSIIO(ccbio); 743 #endif 744 745 746 /* acquire semaphore - avoid re-enters */ 747 /* if((status = acquire_sem_etc(udi->lock_sem, 1, 748 B_RELATIVE_TIMEOUT, 749 udi->trans_timeout)) != B_OK) */ 750 // TRACE_ALWAYS("sem before acq:%08x\n",udi->lock_sem); 751 if((status = acquire_sem(udi->lock_sem)) != B_OK){ 752 /* disabled - CAM_BUSY flag is not recognized by BeOS ... :-( 753 if(status == B_WOULD_BLOCK){ 754 TRACE("locked sema bypass OK\n"); 755 ccbio->cam_ch.cam_status = CAM_BUSY; 756 return B_OK; 757 }*/ 758 TRACE("xpt_scsi_io:acquire_sem_etc() failed:%08x\n", status); 759 ccbio->cam_ch.cam_status = CAM_DEV_NOT_THERE; 760 return B_DEV_BAD_DRIVE_NUM; 761 } 762 /* set the command data pointer */ 763 if(ccbio->cam_ch.cam_flags & CAM_CDB_POINTER){ 764 cmd = ccbio->cam_cdb_io.cam_cdb_ptr; 765 }else{ 766 cmd = ccbio->cam_cdb_io.cam_cdb_bytes; 767 } 768 /* NOTE: using stack copy of sg_buffer sgb. It can be modified/freed in 769 *_handle_features() functions! Twice reallocation is also not awaited! 770 Note this on refactoring!!! */ 771 /*TODO returns!*/ 772 init_sg_buffer(&sgb, ccbio); 773 do{ /* <-- will be repeated if 6-byte RW/MS commands failed */ 774 uint8 *rcmd; 775 uint8 rcmdlen; 776 uint32 transfer_len = 0; 777 EDirection dir = eDirNone; 778 /* handle various features for this device */ 779 if(!pre_handle_features(udi, ccbio, (scsi_cmd_generic *)cmd, &sgb, &status)){ 780 release_sem(udi->lock_sem); 781 return status; 782 } 783 /* transform command as required by protocol */ 784 rcmd = udi->scsi_command_buf; 785 rcmdlen = sizeof(udi->scsi_command_buf); 786 if((status = (*udi->transform_m->transform)(udi, cmd, ccbio->cam_cdb_len & 0x1f, 787 &rcmd, &rcmdlen)) != B_OK) 788 { 789 TRACE_ALWAYS("xpt_scsi_io: transform failed: %08x\n", status); 790 ccbio->cam_ch.cam_status = CAM_REQ_INVALID; 791 release_sem(udi->lock_sem); 792 return B_BAD_VALUE; 793 } 794 /* set correct direction flag */ 795 switch(CAM_DIR_MASK & ccbio->cam_ch.cam_flags){ 796 case CAM_DIR_IN: dir = eDirIn; break; 797 case CAM_DIR_OUT: dir = eDirOut; break; 798 default: dir = eDirNone; break; 799 } 800 801 TRACE_DATA_IO_SG(sgb.piov, sgb.count); 802 803 /*TODO: return!*/ 804 sg_buffer_len(&sgb, &transfer_len); 805 /* transfer command to device. SCSI status will be handled in callback */ 806 (*udi->protocol_m->transfer)(udi, rcmd, rcmdlen, sgb.piov, sgb.count, 807 transfer_len/*ccbio->cam_dxfer_len*/, 808 dir, ccbio, transfer_callback); 809 /* perform some post-tranfer features handling 810 and automatic 6-10 bytes command support detection */ 811 } while(post_handle_features(udi, ccbio, (scsi_cmd_generic *)cmd, &sgb, &status)); 812 release_sem(udi->lock_sem); 813 return status; 814 } 815 /** 816 \fn:xpt_path_inquiry 817 \param ccbp: 818 \return: 819 820 xpt_path_inquiry - handle XPT_PATH_INQ sim action 821 */ 822 status_t xpt_path_inquiry(CCB_PATHINQ *ccbp) 823 { 824 status_t status = B_OK; 825 826 ccbp->cam_version_num = SIM_VERSION; 827 ccbp->cam_target_sprt = 0; 828 ccbp->cam_hba_eng_cnt = 0; 829 memset (ccbp->cam_vuhba_flags, 0, VUHBA); 830 ccbp->cam_sim_priv = SIM_PRIV; 831 ccbp->cam_async_flags = 0; 832 ccbp->cam_initiator_id = CONTROLLER_SCSI_ID; 833 ccbp->cam_hba_inquiry = CONTROLLER_SCSI_BUS; /* Narrow SCSI bus */ 834 ccbp->cam_hba_misc = PIM_NOINQUIRY; 835 ccbp->cam_osd_usage = 0; 836 /* There should be special handling of path_id == 0xff 837 but looks like it's not used by BeOS now */ 838 /*ccbp->cam_hpath_id = path_id;*/ 839 strncpy(ccbp->cam_sim_vid, sim_vendor_name, SIM_ID); 840 strncpy(ccbp->cam_hba_vid, hba_vendor_name, HBA_ID); 841 ccbp->cam_ch.cam_status = CAM_REQ_CMP; 842 return status; 843 } 844 /** 845 \fn:xpt_extended_path_inquiry 846 \param ccbep: ??? 847 \return:??? 848 849 xpt_extended_path_inquiry - handle XPT_EXTENDED_PATH_INQ sim action 850 */ 851 status_t xpt_extended_path_inquiry(CCB_EXTENDED_PATHINQ *ccbep) 852 { 853 status_t status = B_OK; 854 xpt_path_inquiry((CCB_PATHINQ *)ccbep); 855 sprintf(ccbep->cam_sim_version, "%d.0", SIM_VERSION); 856 sprintf(ccbep->cam_hba_version, "%d.0", HBA_VERSION); 857 strncpy(ccbep->cam_controller_family, controller_family, FAM_ID); 858 strncpy(ccbep->cam_controller_type, "USB-SCSI", TYPE_ID); 859 return status; 860 } 861 /** 862 \fn:sim_action 863 \param ccbh: ???? 864 \return: ???? 865 866 This fucntion performs SCSI interface module actions - 867 calls corresponding xpt_* - functions. 868 */ 869 static long sim_action(CCB_HEADER *ccbh) 870 { 871 status_t status = B_ERROR; 872 if(path_id != ccbh->cam_path_id){ 873 TRACE_ALWAYS("sim_action:path_id mismatch of func:%d:our:%d,requested:%d\n", 874 ccbh->cam_func_code, path_id, ccbh->cam_path_id); 875 ccbh->cam_status = CAM_PATH_INVALID; 876 } else { 877 ccbh->cam_status = CAM_REQ_INPROG; 878 switch(ccbh->cam_func_code){ 879 case XPT_SCSI_IO: 880 status = xpt_scsi_io((CCB_SCSIIO *)ccbh); 881 break; 882 case XPT_PATH_INQ: 883 status = xpt_path_inquiry((CCB_PATHINQ *)ccbh); 884 break; 885 case XPT_EXTENDED_PATH_INQ: 886 status = xpt_extended_path_inquiry((CCB_EXTENDED_PATHINQ *)ccbh); 887 break; 888 default: 889 TRACE_ALWAYS("sim_action: unsupported function: %x\n", ccbh->cam_func_code); 890 ccbh->cam_status = CAM_REQ_INVALID; 891 break; 892 } 893 } 894 return status; 895 } 896 /** 897 \fn:std_ops 898 \param op: operation to be performed on this module 899 \param ...: possible additional arguments 900 \return: B_OK on success, error status on failure 901 902 This function deals with standard module operations. Currently, the only 903 two things that entails are initialization and uninitialization. 904 - get the SCSI Bus Manager Module and USB Manager Module 905 - put them when we're finished 906 */ 907 static status_t std_ops(int32 op, ...) 908 { 909 int i; 910 status_t status = B_ERROR; 911 CAM_SIM_ENTRY entry; 912 switch(op) { 913 case B_MODULE_INIT: 914 if(0 == atomic_add(&load_count, 1)){ 915 thread_info tinfo = {0}; 916 load_module_settings(); 917 get_thread_info(find_thread(0), &tinfo); 918 if(!b_ignore_sysinit2 || (0 != strcmp(tinfo.name, "sysinit2"))){ 919 create_log(); 920 if(get_module(B_USB_MODULE_NAME, (module_info **)&usb) == B_OK){ 921 if(get_module(B_CAM_FOR_SIM_MODULE_NAME, (module_info **)&cam) == B_OK){ 922 for(i = 0; i < MAX_DEVICES_COUNT; i++) 923 usb_devices[i] = NULL; 924 925 if((*usb->register_driver)(MODULE_NAME, supported_devices, SIZEOF(supported_devices), "usb_dsk") == B_OK){ 926 if((*usb->install_notify)(MODULE_NAME, ¬ify_hooks) == B_OK){ 927 entry.sim_init = sim_init; 928 entry.sim_action = sim_action; 929 path_id =(*cam->xpt_bus_register)(&entry); 930 usb_serial_lock = create_sem(1, MODULE_NAME"_devices_table_lock"); 931 status = B_OK; 932 break; 933 } 934 } 935 put_module(B_CAM_FOR_SIM_MODULE_NAME); 936 } 937 put_module(B_USB_MODULE_NAME); 938 } 939 } else { 940 TRACE_ALWAYS("std_ops INIT call was ignored for thread:%s\n", tinfo.name); 941 } 942 } else { 943 atomic_add(&load_count, -1); 944 } 945 break; 946 case B_MODULE_UNINIT: 947 if(1 == atomic_add(&load_count, -1)){ 948 (*usb->uninstall_notify)(MODULE_NAME); 949 status = B_OK; 950 if(path_id != -1){ 951 (*cam->xpt_bus_deregister)(path_id); 952 path_id = -1; 953 } 954 delete_sem(usb_serial_lock); 955 put_module(B_USB_MODULE_NAME); 956 put_module(B_CAM_FOR_SIM_MODULE_NAME); 957 } else { 958 atomic_add(&load_count, 1); 959 } 960 break; 961 } 962 return status; 963 } 964 965 /** 966 Declare our module_info so we can be loaded as a kernel module 967 */ 968 static sim_module_info sim_usb_module = { 969 { "busses/scsi/usb/v1", 0, &std_ops } 970 }; 971 /** 972 Export module_info-s list 973 */ 974 _EXPORT module_info *modules[] = { 975 (module_info *) &sim_usb_module, 976 NULL 977 }; 978 979