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