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