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 static status_t 160 trim_device(das_driver_info* device, fs_trim_data* trimData) 161 { 162 TRACE("trim_device()\n"); 163 164 scsi_ccb* request = device->scsi->alloc_ccb(device->scsi_device); 165 if (request == NULL) 166 return B_NO_MEMORY; 167 168 uint64 trimmedSize = 0; 169 for (uint32 i = 0; i < trimData->range_count; i++) { 170 trimmedSize += trimData->ranges[i].size; 171 } 172 status_t status = sSCSIPeripheral->trim_device(device->scsi_periph_device, 173 request, (scsi_block_range*)&trimData->ranges[0], 174 trimData->range_count); 175 176 device->scsi->free_ccb(request); 177 if (status == B_OK) 178 trimData->trimmed_size = trimmedSize; 179 180 return status; 181 } 182 183 184 static int 185 log2(uint32 x) 186 { 187 int y; 188 189 for (y = 31; y >= 0; --y) { 190 if (x == ((uint32)1 << y)) 191 break; 192 } 193 194 return y; 195 } 196 197 198 static status_t 199 do_io(void* cookie, IOOperation* operation) 200 { 201 das_driver_info* info = (das_driver_info*)cookie; 202 203 // TODO: this can go away as soon as we pushed the IOOperation to the upper 204 // layers - we can then set scsi_periph::io() as callback for the scheduler 205 size_t bytesTransferred; 206 status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation, 207 &bytesTransferred); 208 209 info->io_scheduler->OperationCompleted(operation, status, bytesTransferred); 210 return status; 211 } 212 213 214 // #pragma mark - device module API 215 216 217 static status_t 218 das_init_device(void* _info, void** _cookie) 219 { 220 das_driver_info* info = (das_driver_info*)_info; 221 222 // and get (initial) capacity 223 scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device); 224 if (request == NULL) 225 return B_NO_MEMORY; 226 227 sSCSIPeripheral->check_capacity(info->scsi_periph_device, request); 228 info->scsi->free_ccb(request); 229 230 *_cookie = info; 231 return B_OK; 232 } 233 234 235 static void 236 das_uninit_device(void* _cookie) 237 { 238 das_driver_info* info = (das_driver_info*)_cookie; 239 240 delete info->io_scheduler; 241 } 242 243 244 static status_t 245 das_open(void* _info, const char* path, int openMode, void** _cookie) 246 { 247 das_driver_info* info = (das_driver_info*)_info; 248 249 das_handle* handle = (das_handle*)malloc(sizeof(das_handle)); 250 if (handle == NULL) 251 return B_NO_MEMORY; 252 253 handle->info = info; 254 255 status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device, 256 (periph_handle_cookie)handle, &handle->scsi_periph_handle); 257 if (status < B_OK) { 258 free(handle); 259 return status; 260 } 261 262 *_cookie = handle; 263 return B_OK; 264 } 265 266 267 static status_t 268 das_close(void* cookie) 269 { 270 das_handle* handle = (das_handle*)cookie; 271 TRACE("close()\n"); 272 273 sSCSIPeripheral->handle_close(handle->scsi_periph_handle); 274 return B_OK; 275 } 276 277 278 static status_t 279 das_free(void* cookie) 280 { 281 das_handle* handle = (das_handle*)cookie; 282 TRACE("free()\n"); 283 284 sSCSIPeripheral->handle_free(handle->scsi_periph_handle); 285 free(handle); 286 return B_OK; 287 } 288 289 290 static status_t 291 das_read(void* cookie, off_t pos, void* buffer, size_t* _length) 292 { 293 das_handle* handle = (das_handle*)cookie; 294 size_t length = *_length; 295 296 IORequest request; 297 status_t status = request.Init(pos, (addr_t)buffer, length, false, 0); 298 if (status != B_OK) 299 return status; 300 301 status = handle->info->io_scheduler->ScheduleRequest(&request); 302 if (status != B_OK) 303 return status; 304 305 status = request.Wait(0, 0); 306 if (status == B_OK) 307 *_length = length; 308 else 309 dprintf("das_read(): request.Wait() returned: %s\n", strerror(status)); 310 311 return status; 312 } 313 314 315 static status_t 316 das_write(void* cookie, off_t pos, const void* buffer, size_t* _length) 317 { 318 das_handle* handle = (das_handle*)cookie; 319 size_t length = *_length; 320 321 IORequest request; 322 status_t status = request.Init(pos, (addr_t)buffer, length, true, 0); 323 if (status != B_OK) 324 return status; 325 326 status = handle->info->io_scheduler->ScheduleRequest(&request); 327 if (status != B_OK) 328 return status; 329 330 status = request.Wait(0, 0); 331 if (status == B_OK) 332 *_length = length; 333 else 334 dprintf("das_write(): request.Wait() returned: %s\n", strerror(status)); 335 336 return status; 337 } 338 339 340 static status_t 341 das_io(void *cookie, io_request *request) 342 { 343 das_handle* handle = (das_handle*)cookie; 344 345 return handle->info->io_scheduler->ScheduleRequest(request); 346 } 347 348 349 static status_t 350 das_ioctl(void* cookie, uint32 op, void* buffer, size_t length) 351 { 352 das_handle* handle = (das_handle*)cookie; 353 das_driver_info* info = handle->info; 354 355 TRACE("ioctl(op = %" B_PRIu32 ")\n", op); 356 357 switch (op) { 358 case B_GET_DEVICE_SIZE: 359 { 360 status_t status = update_capacity(info); 361 if (status != B_OK) 362 return status; 363 364 size_t size = info->capacity * info->block_size; 365 return user_memcpy(buffer, &size, sizeof(size_t)); 366 } 367 368 case B_GET_GEOMETRY: 369 { 370 if (buffer == NULL /*|| length != sizeof(device_geometry)*/) 371 return B_BAD_VALUE; 372 373 device_geometry geometry; 374 status_t status = get_geometry(handle, &geometry); 375 if (status != B_OK) 376 return status; 377 378 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 379 } 380 381 case B_GET_ICON_NAME: 382 // TODO: take device type into account! 383 return user_strlcpy((char*)buffer, info->removable 384 ? "devices/drive-removable-media" : "devices/drive-harddisk", 385 B_FILE_NAME_LENGTH); 386 387 case B_GET_VECTOR_ICON: 388 { 389 // TODO: take device type into account! 390 device_icon iconData; 391 if (length != sizeof(device_icon)) 392 return B_BAD_VALUE; 393 if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) 394 return B_BAD_ADDRESS; 395 396 if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) { 397 if (user_memcpy(iconData.icon_data, kDriveIcon, 398 sizeof(kDriveIcon)) != B_OK) 399 return B_BAD_ADDRESS; 400 } 401 402 iconData.icon_size = sizeof(kDriveIcon); 403 return user_memcpy(buffer, &iconData, sizeof(device_icon)); 404 } 405 406 case B_EJECT_DEVICE: 407 case B_SCSI_EJECT: 408 return load_eject(info, false); 409 410 case B_LOAD_MEDIA: 411 return load_eject(info, true); 412 413 case B_FLUSH_DRIVE_CACHE: 414 return synchronize_cache(info); 415 416 case B_TRIM_DEVICE: 417 { 418 fs_trim_data* trimData; 419 MemoryDeleter deleter; 420 status_t status = get_trim_data_from_user(buffer, length, deleter, 421 trimData); 422 if (status != B_OK) 423 return status; 424 425 status = trim_device(info, trimData); 426 if (status != B_OK) 427 return status; 428 429 return copy_trim_data_to_user(buffer, trimData); 430 } 431 432 default: 433 return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op, 434 buffer, length); 435 } 436 } 437 438 439 // #pragma mark - scsi_periph callbacks 440 441 442 static void 443 das_set_capacity(das_driver_info* info, uint64 capacity, uint32 blockSize) 444 { 445 TRACE("das_set_capacity(device = %p, capacity = %" B_PRIu64 446 ", blockSize = %" B_PRIu32 ")\n", info, capacity, blockSize); 447 448 // get log2, if possible 449 uint32 blockShift = log2(blockSize); 450 451 if ((1UL << blockShift) != blockSize) 452 blockShift = 0; 453 454 info->capacity = capacity; 455 456 if (info->block_size != blockSize) { 457 if (info->block_size != 0) { 458 dprintf("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size, 459 blockSize); 460 panic("updating DMAResource not yet implemented..."); 461 } 462 463 // TODO: we need to replace the DMAResource in our IOScheduler 464 status_t status = info->dma_resource->Init(info->node, blockSize, 1024, 465 32); 466 if (status != B_OK) 467 panic("initializing DMAResource failed: %s", strerror(status)); 468 469 info->io_scheduler = new(std::nothrow) IOSchedulerSimple( 470 info->dma_resource); 471 if (info->io_scheduler == NULL) 472 panic("allocating IOScheduler failed."); 473 474 // TODO: use whole device name here 475 status = info->io_scheduler->Init("scsi"); 476 if (status != B_OK) 477 panic("initializing IOScheduler failed: %s", strerror(status)); 478 479 info->io_scheduler->SetCallback(do_io, info); 480 } 481 482 info->block_size = blockSize; 483 } 484 485 486 static void 487 das_media_changed(das_driver_info *device, scsi_ccb *request) 488 { 489 // do a capacity check 490 // TODO: is this a good idea (e.g. if this is an empty CD)? 491 sSCSIPeripheral->check_capacity(device->scsi_periph_device, request); 492 } 493 494 495 scsi_periph_callbacks callbacks = { 496 (void (*)(periph_device_cookie, uint64, uint32))das_set_capacity, 497 (void (*)(periph_device_cookie, scsi_ccb *))das_media_changed 498 }; 499 500 501 // #pragma mark - driver module API 502 503 504 static float 505 das_supports_device(device_node *parent) 506 { 507 const char *bus; 508 uint8 deviceType; 509 510 // make sure parent is really the SCSI bus manager 511 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 512 return -1; 513 514 if (strcmp(bus, "scsi")) 515 return 0.0; 516 517 // check whether it's really a Direct Access Device 518 if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM, 519 &deviceType, true) != B_OK || deviceType != scsi_dev_direct_access) 520 return 0.0; 521 522 return 0.6; 523 } 524 525 526 /*! Called whenever a new device was added to system; 527 if we really support it, we create a new node that gets 528 server by the block_io module 529 */ 530 static status_t 531 das_register_device(device_node *node) 532 { 533 const scsi_res_inquiry *deviceInquiry = NULL; 534 size_t inquiryLength; 535 uint32 maxBlocks; 536 537 // get inquiry data 538 if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM, 539 (const void **)&deviceInquiry, &inquiryLength, true) != B_OK 540 || inquiryLength < sizeof(deviceInquiry)) 541 return B_ERROR; 542 543 // get block limit of underlying hardware to lower it (if necessary) 544 if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS, 545 &maxBlocks, true) != B_OK) 546 maxBlocks = INT_MAX; 547 548 // using 10 byte commands, at most 0xffff blocks can be transmitted at once 549 // (sadly, we cannot update this value later on if only 6 byte commands 550 // are supported, but the block_io module can live with that) 551 maxBlocks = min_c(maxBlocks, 0xffff); 552 553 // ready to register 554 device_attr attrs[] = { 555 // tell block_io whether the device is removable 556 {"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}}, 557 // impose own max block restriction 558 {B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {ui32: maxBlocks}}, 559 { NULL } 560 }; 561 562 return sDeviceManager->register_node(node, SCSI_DISK_DRIVER_MODULE_NAME, 563 attrs, NULL, NULL); 564 } 565 566 567 static status_t 568 das_init_driver(device_node *node, void **cookie) 569 { 570 TRACE("das_init_driver"); 571 572 uint8 removable; 573 status_t status = sDeviceManager->get_attr_uint8(node, "removable", 574 &removable, false); 575 if (status != B_OK) 576 return status; 577 578 das_driver_info* info = (das_driver_info*)malloc(sizeof(das_driver_info)); 579 if (info == NULL) 580 return B_NO_MEMORY; 581 582 memset(info, 0, sizeof(*info)); 583 584 info->dma_resource = new(std::nothrow) DMAResource; 585 if (info->dma_resource == NULL) { 586 free(info); 587 return B_NO_MEMORY; 588 } 589 590 info->node = node; 591 info->removable = removable; 592 593 device_node* parent = sDeviceManager->get_parent_node(node); 594 sDeviceManager->get_driver(parent, (driver_module_info **)&info->scsi, 595 (void **)&info->scsi_device); 596 sDeviceManager->put_node(parent); 597 598 status = sSCSIPeripheral->register_device((periph_device_cookie)info, 599 &callbacks, info->scsi_device, info->scsi, info->node, 600 info->removable, 10, &info->scsi_periph_device); 601 if (status != B_OK) { 602 delete info->dma_resource; 603 free(info); 604 return status; 605 } 606 607 *cookie = info; 608 return B_OK; 609 } 610 611 612 static void 613 das_uninit_driver(void *_cookie) 614 { 615 das_driver_info* info = (das_driver_info*)_cookie; 616 617 sSCSIPeripheral->unregister_device(info->scsi_periph_device); 618 delete info->dma_resource; 619 free(info); 620 } 621 622 623 static status_t 624 das_register_child_devices(void* _cookie) 625 { 626 das_driver_info* info = (das_driver_info*)_cookie; 627 status_t status; 628 629 char* name = sSCSIPeripheral->compose_device_name(info->node, 630 "disk/scsi"); 631 if (name == NULL) 632 return B_ERROR; 633 634 status = sDeviceManager->publish_device(info->node, name, 635 SCSI_DISK_DEVICE_MODULE_NAME); 636 637 free(name); 638 return status; 639 } 640 641 642 static status_t 643 das_rescan_child_devices(void* _cookie) 644 { 645 das_driver_info* info = (das_driver_info*)_cookie; 646 uint64 capacity = info->capacity; 647 update_capacity(info); 648 if (info->capacity != capacity) 649 sSCSIPeripheral->media_changed(info->scsi_periph_device); 650 return B_OK; 651 } 652 653 654 655 module_dependency module_dependencies[] = { 656 {SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral}, 657 {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager}, 658 {} 659 }; 660 661 struct device_module_info sSCSIDiskDevice = { 662 { 663 SCSI_DISK_DEVICE_MODULE_NAME, 664 0, 665 NULL 666 }, 667 668 das_init_device, 669 das_uninit_device, 670 NULL, //das_remove, 671 672 das_open, 673 das_close, 674 das_free, 675 das_read, 676 das_write, 677 das_io, 678 das_ioctl, 679 680 NULL, // select 681 NULL, // deselect 682 }; 683 684 struct driver_module_info sSCSIDiskDriver = { 685 { 686 SCSI_DISK_DRIVER_MODULE_NAME, 687 0, 688 NULL 689 }, 690 691 das_supports_device, 692 das_register_device, 693 das_init_driver, 694 das_uninit_driver, 695 das_register_child_devices, 696 das_rescan_child_devices, 697 NULL, // removed 698 }; 699 700 module_info* modules[] = { 701 (module_info*)&sSCSIDiskDriver, 702 (module_info*)&sSCSIDiskDevice, 703 NULL 704 }; 705