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