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