1 /* 2 * Copyright 2008-2013, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2002/03, Thomas Kurschel. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 /*! Peripheral driver to handle any kind of SCSI disks, 9 i.e. hard disk and floopy disks (ZIP etc.) 10 11 Much work is done by scsi_periph and block_io. 12 13 You'll find das_... all over the place. This stands for 14 "Direct Access Storage" which is the official SCSI name for 15 normal (floppy/hard/ZIP)-disk drives. 16 */ 17 18 19 #include "scsi_disk.h" 20 21 #include <string.h> 22 #include <stdlib.h> 23 24 #include <AutoDeleter.h> 25 26 #include <fs/devfs.h> 27 #include <util/fs_trim_support.h> 28 29 #include "dma_resources.h" 30 #include "IORequest.h" 31 #include "IOSchedulerSimple.h" 32 33 34 //#define TRACE_SCSI_DISK 35 #ifdef TRACE_SCSI_DISK 36 # define TRACE(x...) dprintf("scsi_disk: " x) 37 #else 38 # define TRACE(x...) ; 39 #endif 40 41 42 static const uint8 kDriveIcon[] = { 43 0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16, 44 0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39, 45 0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02, 46 0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01, 47 0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47, 48 0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f, 49 0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0, 50 0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38, 51 0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48, 52 0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2, 53 0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80, 54 0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 55 0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39, 56 0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a, 57 0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27, 58 0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a, 59 0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08, 60 0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17, 61 0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02, 62 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01, 63 0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99, 64 0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2, 65 0xe6, 0x01, 0x17, 0x82, 0x00, 0x04 66 }; 67 68 69 static scsi_periph_interface* sSCSIPeripheral; 70 static device_manager_info* sDeviceManager; 71 72 73 static status_t 74 update_capacity(das_driver_info* device) 75 { 76 TRACE("update_capacity()\n"); 77 78 scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device); 79 if (ccb == NULL) 80 return B_NO_MEMORY; 81 82 status_t status = sSCSIPeripheral->check_capacity( 83 device->scsi_periph_device, ccb); 84 85 device->scsi->free_ccb(ccb); 86 87 return status; 88 } 89 90 91 static status_t 92 get_geometry(das_handle* handle, device_geometry* geometry) 93 { 94 das_driver_info* info = handle->info; 95 96 status_t status = update_capacity(info); 97 if (status != B_OK) 98 return status; 99 100 devfs_compute_geometry_size(geometry, info->capacity, info->block_size); 101 102 geometry->device_type = B_DISK; 103 geometry->removable = info->removable; 104 105 // TBD: for all but CD-ROMs, read mode sense - medium type 106 // (bit 7 of block device specific parameter for Optical Memory Block Device) 107 // (same for Direct-Access Block Devices) 108 // (same for write-once block devices) 109 // (same for optical memory block devices) 110 geometry->read_only = false; 111 geometry->write_once = false; 112 113 TRACE("scsi_disk: get_geometry(): %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 114 ", %" B_PRId32 ", %d, %d, %d, %d\n", geometry->bytes_per_sector, 115 geometry->sectors_per_track, geometry->cylinder_count, 116 geometry->head_count, geometry->device_type, 117 geometry->removable, geometry->read_only, geometry->write_once); 118 119 return B_OK; 120 } 121 122 123 static status_t 124 load_eject(das_driver_info *device, bool load) 125 { 126 TRACE("load_eject()\n"); 127 128 scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device); 129 if (ccb == NULL) 130 return B_NO_MEMORY; 131 132 err_res result = sSCSIPeripheral->send_start_stop( 133 device->scsi_periph_device, ccb, load, true); 134 135 device->scsi->free_ccb(ccb); 136 137 return result.error_code; 138 } 139 140 141 static status_t 142 synchronize_cache(das_driver_info *device) 143 { 144 TRACE("synchronize_cache()\n"); 145 146 scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device); 147 if (ccb == NULL) 148 return B_NO_MEMORY; 149 150 err_res result = sSCSIPeripheral->synchronize_cache( 151 device->scsi_periph_device, ccb); 152 153 device->scsi->free_ccb(ccb); 154 155 return result.error_code; 156 } 157 158 159 #if 0 160 static status_t 161 trim_device(das_driver_info* device, fs_trim_data* trimData) 162 { 163 TRACE("trim_device()\n"); 164 165 scsi_ccb* request = device->scsi->alloc_ccb(device->scsi_device); 166 if (request == NULL) 167 return B_NO_MEMORY; 168 169 uint64 trimmedSize = 0; 170 for (uint32 i = 0; i < trimData->range_count; i++) { 171 trimmedSize += trimData->ranges[i].size; 172 } 173 status_t status = sSCSIPeripheral->trim_device(device->scsi_periph_device, 174 request, (scsi_block_range*)&trimData->ranges[0], 175 trimData->range_count); 176 177 device->scsi->free_ccb(request); 178 if (status == B_OK) 179 trimData->trimmed_size = trimmedSize; 180 181 return status; 182 } 183 #endif 184 185 186 static int 187 log2(uint32 x) 188 { 189 int y; 190 191 for (y = 31; y >= 0; --y) { 192 if (x == ((uint32)1 << y)) 193 break; 194 } 195 196 return y; 197 } 198 199 200 static status_t 201 do_io(void* cookie, IOOperation* operation) 202 { 203 das_driver_info* info = (das_driver_info*)cookie; 204 205 // TODO: this can go away as soon as we pushed the IOOperation to the upper 206 // layers - we can then set scsi_periph::io() as callback for the scheduler 207 size_t bytesTransferred; 208 status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation, 209 &bytesTransferred); 210 211 info->io_scheduler->OperationCompleted(operation, status, bytesTransferred); 212 return status; 213 } 214 215 216 // #pragma mark - device module API 217 218 219 static status_t 220 das_init_device(void* _info, void** _cookie) 221 { 222 das_driver_info* info = (das_driver_info*)_info; 223 224 // and get (initial) capacity 225 scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device); 226 if (request == NULL) 227 return B_NO_MEMORY; 228 229 sSCSIPeripheral->check_capacity(info->scsi_periph_device, request); 230 info->scsi->free_ccb(request); 231 232 *_cookie = info; 233 return B_OK; 234 } 235 236 237 static void 238 das_uninit_device(void* _cookie) 239 { 240 das_driver_info* info = (das_driver_info*)_cookie; 241 242 delete info->io_scheduler; 243 } 244 245 246 static status_t 247 das_open(void* _info, const char* path, int openMode, void** _cookie) 248 { 249 das_driver_info* info = (das_driver_info*)_info; 250 251 das_handle* handle = (das_handle*)malloc(sizeof(das_handle)); 252 if (handle == NULL) 253 return B_NO_MEMORY; 254 255 handle->info = info; 256 257 status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device, 258 (periph_handle_cookie)handle, &handle->scsi_periph_handle); 259 if (status < B_OK) { 260 free(handle); 261 return status; 262 } 263 264 *_cookie = handle; 265 return B_OK; 266 } 267 268 269 static status_t 270 das_close(void* cookie) 271 { 272 das_handle* handle = (das_handle*)cookie; 273 TRACE("close()\n"); 274 275 sSCSIPeripheral->handle_close(handle->scsi_periph_handle); 276 return B_OK; 277 } 278 279 280 static status_t 281 das_free(void* cookie) 282 { 283 das_handle* handle = (das_handle*)cookie; 284 TRACE("free()\n"); 285 286 sSCSIPeripheral->handle_free(handle->scsi_periph_handle); 287 free(handle); 288 return B_OK; 289 } 290 291 292 static status_t 293 das_read(void* cookie, off_t pos, void* buffer, size_t* _length) 294 { 295 das_handle* handle = (das_handle*)cookie; 296 size_t length = *_length; 297 298 IORequest request; 299 status_t status = request.Init(pos, (addr_t)buffer, length, false, 0); 300 if (status != B_OK) 301 return status; 302 303 status = handle->info->io_scheduler->ScheduleRequest(&request); 304 if (status != B_OK) 305 return status; 306 307 status = request.Wait(0, 0); 308 if (status == B_OK) 309 *_length = length; 310 else 311 dprintf("das_read(): request.Wait() returned: %s\n", strerror(status)); 312 313 return status; 314 } 315 316 317 static status_t 318 das_write(void* cookie, off_t pos, const void* buffer, size_t* _length) 319 { 320 das_handle* handle = (das_handle*)cookie; 321 size_t length = *_length; 322 323 IORequest request; 324 status_t status = request.Init(pos, (addr_t)buffer, length, true, 0); 325 if (status != B_OK) 326 return status; 327 328 status = handle->info->io_scheduler->ScheduleRequest(&request); 329 if (status != B_OK) 330 return status; 331 332 status = request.Wait(0, 0); 333 if (status == B_OK) 334 *_length = length; 335 else 336 dprintf("das_write(): request.Wait() returned: %s\n", strerror(status)); 337 338 return status; 339 } 340 341 342 static status_t 343 das_io(void *cookie, io_request *request) 344 { 345 das_handle* handle = (das_handle*)cookie; 346 347 return handle->info->io_scheduler->ScheduleRequest(request); 348 } 349 350 351 static status_t 352 das_ioctl(void* cookie, uint32 op, void* buffer, size_t length) 353 { 354 das_handle* handle = (das_handle*)cookie; 355 das_driver_info* info = handle->info; 356 357 TRACE("ioctl(op = %" B_PRIu32 ")\n", op); 358 359 switch (op) { 360 case B_GET_DEVICE_SIZE: 361 { 362 status_t status = update_capacity(info); 363 if (status != B_OK) 364 return status; 365 366 size_t size = info->capacity * info->block_size; 367 return user_memcpy(buffer, &size, sizeof(size_t)); 368 } 369 370 case B_GET_GEOMETRY: 371 { 372 if (buffer == NULL /*|| length != sizeof(device_geometry)*/) 373 return B_BAD_VALUE; 374 375 device_geometry geometry; 376 status_t status = get_geometry(handle, &geometry); 377 if (status != B_OK) 378 return status; 379 380 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 381 } 382 383 case B_GET_ICON_NAME: 384 // TODO: take device type into account! 385 return user_strlcpy((char*)buffer, info->removable 386 ? "devices/drive-removable-media" : "devices/drive-harddisk", 387 B_FILE_NAME_LENGTH); 388 389 case B_GET_VECTOR_ICON: 390 { 391 // TODO: take device type into account! 392 device_icon iconData; 393 if (length != sizeof(device_icon)) 394 return B_BAD_VALUE; 395 if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) 396 return B_BAD_ADDRESS; 397 398 if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) { 399 if (user_memcpy(iconData.icon_data, kDriveIcon, 400 sizeof(kDriveIcon)) != B_OK) 401 return B_BAD_ADDRESS; 402 } 403 404 iconData.icon_size = sizeof(kDriveIcon); 405 return user_memcpy(buffer, &iconData, sizeof(device_icon)); 406 } 407 408 case B_EJECT_DEVICE: 409 case B_SCSI_EJECT: 410 return load_eject(info, false); 411 412 case B_LOAD_MEDIA: 413 return load_eject(info, true); 414 415 case B_FLUSH_DRIVE_CACHE: 416 return synchronize_cache(info); 417 418 #if 0 419 case B_TRIM_DEVICE: 420 { 421 fs_trim_data* trimData; 422 MemoryDeleter deleter; 423 status_t status = get_trim_data_from_user(buffer, length, deleter, 424 trimData); 425 if (status != B_OK) 426 return status; 427 428 status = trim_device(info, trimData); 429 if (status != B_OK) 430 return status; 431 432 return copy_trim_data_to_user(buffer, trimData); 433 } 434 #endif 435 436 default: 437 return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op, 438 buffer, length); 439 } 440 } 441 442 443 // #pragma mark - scsi_periph callbacks 444 445 446 static void 447 das_set_capacity(das_driver_info* info, uint64 capacity, uint32 blockSize) 448 { 449 TRACE("das_set_capacity(device = %p, capacity = %" B_PRIu64 450 ", blockSize = %" B_PRIu32 ")\n", info, capacity, blockSize); 451 452 // get log2, if possible 453 uint32 blockShift = log2(blockSize); 454 455 if ((1UL << blockShift) != blockSize) 456 blockShift = 0; 457 458 info->capacity = capacity; 459 460 if (info->block_size != blockSize) { 461 if (info->block_size != 0) { 462 dprintf("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size, 463 blockSize); 464 panic("updating DMAResource not yet implemented..."); 465 } 466 467 // TODO: we need to replace the DMAResource in our IOScheduler 468 status_t status = info->dma_resource->Init(info->node, blockSize, 1024, 469 32); 470 if (status != B_OK) 471 panic("initializing DMAResource failed: %s", strerror(status)); 472 473 info->io_scheduler = new(std::nothrow) IOSchedulerSimple( 474 info->dma_resource); 475 if (info->io_scheduler == NULL) 476 panic("allocating IOScheduler failed."); 477 478 // TODO: use whole device name here 479 status = info->io_scheduler->Init("scsi"); 480 if (status != B_OK) 481 panic("initializing IOScheduler failed: %s", strerror(status)); 482 483 info->io_scheduler->SetCallback(do_io, info); 484 } 485 486 info->block_size = blockSize; 487 } 488 489 490 static void 491 das_media_changed(das_driver_info *device, scsi_ccb *request) 492 { 493 // do a capacity check 494 // TODO: is this a good idea (e.g. if this is an empty CD)? 495 sSCSIPeripheral->check_capacity(device->scsi_periph_device, request); 496 } 497 498 499 scsi_periph_callbacks callbacks = { 500 (void (*)(periph_device_cookie, uint64, uint32))das_set_capacity, 501 (void (*)(periph_device_cookie, scsi_ccb *))das_media_changed 502 }; 503 504 505 // #pragma mark - driver module API 506 507 508 static float 509 das_supports_device(device_node *parent) 510 { 511 const char *bus; 512 uint8 deviceType; 513 514 // make sure parent is really the SCSI bus manager 515 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 516 return -1; 517 518 if (strcmp(bus, "scsi")) 519 return 0.0; 520 521 // check whether it's really a Direct Access Device 522 if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM, 523 &deviceType, true) != B_OK || deviceType != scsi_dev_direct_access) 524 return 0.0; 525 526 return 0.6; 527 } 528 529 530 /*! Called whenever a new device was added to system; 531 if we really support it, we create a new node that gets 532 server by the block_io module 533 */ 534 static status_t 535 das_register_device(device_node *node) 536 { 537 const scsi_res_inquiry *deviceInquiry = NULL; 538 size_t inquiryLength; 539 uint32 maxBlocks; 540 541 // get inquiry data 542 if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM, 543 (const void **)&deviceInquiry, &inquiryLength, true) != B_OK 544 || inquiryLength < sizeof(scsi_res_inquiry)) 545 return B_ERROR; 546 547 // get block limit of underlying hardware to lower it (if necessary) 548 if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS, 549 &maxBlocks, true) != B_OK) 550 maxBlocks = INT_MAX; 551 552 // using 10 byte commands, at most 0xffff blocks can be transmitted at once 553 // (sadly, we cannot update this value later on if only 6 byte commands 554 // are supported, but the block_io module can live with that) 555 maxBlocks = min_c(maxBlocks, 0xffff); 556 557 // ready to register 558 device_attr attrs[] = { 559 // tell block_io whether the device is removable 560 {"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}}, 561 // impose own max block restriction 562 {B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {ui32: maxBlocks}}, 563 { NULL } 564 }; 565 566 return sDeviceManager->register_node(node, SCSI_DISK_DRIVER_MODULE_NAME, 567 attrs, NULL, NULL); 568 } 569 570 571 static status_t 572 das_init_driver(device_node *node, void **cookie) 573 { 574 TRACE("das_init_driver"); 575 576 uint8 removable; 577 status_t status = sDeviceManager->get_attr_uint8(node, "removable", 578 &removable, false); 579 if (status != B_OK) 580 return status; 581 582 das_driver_info* info = (das_driver_info*)malloc(sizeof(das_driver_info)); 583 if (info == NULL) 584 return B_NO_MEMORY; 585 586 memset(info, 0, sizeof(*info)); 587 588 info->dma_resource = new(std::nothrow) DMAResource; 589 if (info->dma_resource == NULL) { 590 free(info); 591 return B_NO_MEMORY; 592 } 593 594 info->node = node; 595 info->removable = removable; 596 597 device_node* parent = sDeviceManager->get_parent_node(node); 598 sDeviceManager->get_driver(parent, (driver_module_info **)&info->scsi, 599 (void **)&info->scsi_device); 600 sDeviceManager->put_node(parent); 601 602 status = sSCSIPeripheral->register_device((periph_device_cookie)info, 603 &callbacks, info->scsi_device, info->scsi, info->node, 604 info->removable, 10, &info->scsi_periph_device); 605 if (status != B_OK) { 606 delete info->dma_resource; 607 free(info); 608 return status; 609 } 610 611 *cookie = info; 612 return B_OK; 613 } 614 615 616 static void 617 das_uninit_driver(void *_cookie) 618 { 619 das_driver_info* info = (das_driver_info*)_cookie; 620 621 sSCSIPeripheral->unregister_device(info->scsi_periph_device); 622 delete info->dma_resource; 623 free(info); 624 } 625 626 627 static status_t 628 das_register_child_devices(void* _cookie) 629 { 630 das_driver_info* info = (das_driver_info*)_cookie; 631 status_t status; 632 633 char* name = sSCSIPeripheral->compose_device_name(info->node, 634 "disk/scsi"); 635 if (name == NULL) 636 return B_ERROR; 637 638 status = sDeviceManager->publish_device(info->node, name, 639 SCSI_DISK_DEVICE_MODULE_NAME); 640 641 free(name); 642 return status; 643 } 644 645 646 static status_t 647 das_rescan_child_devices(void* _cookie) 648 { 649 das_driver_info* info = (das_driver_info*)_cookie; 650 uint64 capacity = info->capacity; 651 update_capacity(info); 652 if (info->capacity != capacity) 653 sSCSIPeripheral->media_changed(info->scsi_periph_device); 654 return B_OK; 655 } 656 657 658 659 module_dependency module_dependencies[] = { 660 {SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral}, 661 {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager}, 662 {} 663 }; 664 665 struct device_module_info sSCSIDiskDevice = { 666 { 667 SCSI_DISK_DEVICE_MODULE_NAME, 668 0, 669 NULL 670 }, 671 672 das_init_device, 673 das_uninit_device, 674 NULL, //das_remove, 675 676 das_open, 677 das_close, 678 das_free, 679 das_read, 680 das_write, 681 das_io, 682 das_ioctl, 683 684 NULL, // select 685 NULL, // deselect 686 }; 687 688 struct driver_module_info sSCSIDiskDriver = { 689 { 690 SCSI_DISK_DRIVER_MODULE_NAME, 691 0, 692 NULL 693 }, 694 695 das_supports_device, 696 das_register_device, 697 das_init_driver, 698 das_uninit_driver, 699 das_register_child_devices, 700 das_rescan_child_devices, 701 NULL, // removed 702 }; 703 704 module_info* modules[] = { 705 (module_info*)&sSCSIDiskDriver, 706 (module_info*)&sSCSIDiskDevice, 707 NULL 708 }; 709