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