1 /* 2 * Copyright 2004-2008, Haiku, Inc. All RightsReserved. 3 * Copyright 2002-2003, Thomas Kurschel. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 /*! Peripheral driver to handle CD-ROM drives. To be more 9 precisely, it supports CD-ROM and WORM drives (well - 10 I've never _seen_ a WORM driver). 11 12 Much work is done by scsi_periph and block_io. 13 */ 14 15 16 #include "scsi_cd.h" 17 18 #include <stdlib.h> 19 #include <string.h> 20 21 22 //#define TRACE_CD_DISK 23 #ifdef TRACE_CD_DISK 24 # define TRACE(x...) dprintf("scsi_cd: " x) 25 #else 26 # define TRACE(x...) ; 27 #endif 28 29 30 static scsi_periph_interface *sSCSIPeripheral; 31 static device_manager_info *sDeviceManager; 32 static block_io_for_driver_interface *sBlockIO; 33 34 35 #define SCSI_CD_STD_TIMEOUT 10 36 37 38 static status_t 39 update_capacity(cd_device_info *device) 40 { 41 TRACE("update_capacity()\n"); 42 43 scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device); 44 if (ccb == NULL) 45 return B_NO_MEMORY; 46 47 status_t status = sSCSIPeripheral->check_capacity( 48 device->scsi_periph_device, ccb); 49 50 device->scsi->free_ccb(ccb); 51 52 return status; 53 } 54 55 56 static status_t 57 get_geometry(cd_handle_info *handle, device_geometry *geometry) 58 { 59 cd_device_info *device = handle->device; 60 61 status_t status = update_capacity(device); 62 63 // it seems that Be expects B_GET_GEOMETRY to always succeed unless 64 // the medium has been changed; e.g. if we report B_DEV_NO_MEDIA, the 65 // device is ignored by the CDPlayer and CDBurner 66 if (status == B_DEV_MEDIA_CHANGED) 67 return B_DEV_MEDIA_CHANGED; 68 69 geometry->bytes_per_sector = device->block_size; 70 geometry->sectors_per_track = 1; 71 geometry->cylinder_count = device->capacity; 72 geometry->head_count = 1; 73 geometry->device_type = device->device_type; 74 geometry->removable = device->removable; 75 76 // TBD: for all but CD-ROMs, read mode sense - medium type 77 // (bit 7 of block device specific parameter for Optical Memory Block Device) 78 // (same for Direct-Access Block Devices) 79 // (same for write-once block devices) 80 // (same for optical memory block devices) 81 geometry->read_only = true; 82 geometry->write_once = device->device_type == scsi_dev_WORM; 83 84 TRACE("scsi_disk: get_geometry(): %ld, %ld, %ld, %ld, %d, %d, %d, %d\n", 85 geometry->bytes_per_sector, geometry->sectors_per_track, 86 geometry->cylinder_count, geometry->head_count, geometry->device_type, 87 geometry->removable, geometry->read_only, geometry->write_once); 88 89 return B_OK; 90 } 91 92 93 static status_t 94 get_toc(cd_device_info *device, scsi_toc *toc) 95 { 96 scsi_ccb *ccb; 97 status_t res; 98 scsi_cmd_read_toc *cmd; 99 size_t dataLength; 100 scsi_toc_general *short_response = (scsi_toc_general *)toc->toc_data; 101 102 TRACE("get_toc()\n"); 103 104 ccb = device->scsi->alloc_ccb(device->scsi_device); 105 if (ccb == NULL) 106 return B_NO_MEMORY; 107 108 // first read number of tracks only 109 ccb->flags = SCSI_DIR_IN; 110 111 cmd = (scsi_cmd_read_toc *)ccb->cdb; 112 113 memset(cmd, 0, sizeof(*cmd)); 114 cmd->opcode = SCSI_OP_READ_TOC; 115 cmd->time = 1; 116 cmd->format = SCSI_TOC_FORMAT_TOC; 117 cmd->track = 1; 118 cmd->allocation_length = B_HOST_TO_BENDIAN_INT16(sizeof(scsi_toc_general)); 119 120 ccb->cdb_length = sizeof(*cmd); 121 122 ccb->sort = -1; 123 ccb->timeout = SCSI_CD_STD_TIMEOUT; 124 125 ccb->data = toc->toc_data; 126 ccb->sg_list = NULL; 127 ccb->data_length = sizeof(toc->toc_data); 128 129 res = sSCSIPeripheral->safe_exec(device->scsi_periph_device, ccb); 130 if (res != B_OK) 131 goto err; 132 133 // then read all track infos 134 // (little hint: number of tracks is last - first + 1; 135 // but scsi_toc_toc has already one track, so we get 136 // last - first extra tracks; finally, we want the lead-out as 137 // well, so we add an extra track) 138 dataLength = (short_response->last - short_response->first + 1) 139 * sizeof(scsi_toc_track) + sizeof(scsi_toc_toc); 140 dataLength = min_c(dataLength, sizeof(toc->toc_data)); 141 142 TRACE(" tracks: %d - %d, data length %d\n", short_response->first, 143 short_response->last, (int)dataLength); 144 145 cmd->allocation_length = B_HOST_TO_BENDIAN_INT16(dataLength); 146 147 res = sSCSIPeripheral->safe_exec(device->scsi_periph_device, ccb); 148 149 err: 150 device->scsi->free_ccb(ccb); 151 152 return res; 153 } 154 155 156 static status_t 157 load_eject(cd_device_info *device, bool load) 158 { 159 TRACE("load_eject()\n"); 160 161 scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device); 162 if (ccb == NULL) 163 return B_NO_MEMORY; 164 165 err_res result = sSCSIPeripheral->send_start_stop( 166 device->scsi_periph_device, ccb, load, true); 167 168 device->scsi->free_ccb(ccb); 169 170 return result.error_code; 171 } 172 173 174 static status_t 175 get_position(cd_device_info *device, scsi_position *position) 176 { 177 scsi_cmd_read_subchannel cmd; 178 179 TRACE("get_position()\n"); 180 181 memset(&cmd, 0, sizeof(cmd)); 182 cmd.opcode = SCSI_OP_READ_SUB_CHANNEL; 183 cmd.time = 1; 184 cmd.subq = 1; 185 cmd.parameter_list = scsi_sub_channel_parameter_list_cd_pos; 186 cmd.track = 0; 187 cmd.allocation_length = B_HOST_TO_BENDIAN_INT16(sizeof(scsi_position)); 188 189 return sSCSIPeripheral->simple_exec(device->scsi_periph_device, 190 &cmd, sizeof(cmd), position, sizeof(*position), SCSI_DIR_IN); 191 } 192 193 194 static status_t 195 get_set_volume(cd_device_info *device, scsi_volume *volume, bool set) 196 { 197 scsi_cmd_mode_sense_6 cmd; 198 scsi_mode_param_header_6 header; 199 size_t len; 200 void *buffer; 201 scsi_modepage_audio *page; 202 status_t res; 203 204 TRACE("get_set_volume()\n"); 205 206 // determine size of block descriptor 207 memset(&cmd, 0, sizeof(cmd)); 208 cmd.opcode = SCSI_OP_MODE_SENSE_6; 209 cmd.page_code = SCSI_MODEPAGE_AUDIO; 210 cmd.page_control = SCSI_MODE_SENSE_PC_CURRENT; 211 cmd.allocation_length = sizeof(header); 212 213 memset(&header, -2, sizeof(header)); 214 215 res = sSCSIPeripheral->simple_exec(device->scsi_periph_device, &cmd, 216 sizeof(cmd), &header, sizeof(header), SCSI_DIR_IN); 217 if (res != B_OK) 218 return res; 219 220 TRACE(" block_desc_len=%d", header.block_desc_length); 221 #if 0 222 // ToDo: why this?? 223 return B_ERROR; 224 #endif 225 226 // retrieve param header, block descriptor and actual codepage 227 len = sizeof(header) + header.block_desc_length 228 + sizeof(scsi_modepage_audio); 229 230 buffer = malloc(len); 231 if (buffer == NULL) 232 return B_NO_MEMORY; 233 234 memset(buffer, -1, sizeof(buffer)); 235 236 cmd.allocation_length = len; 237 238 res = sSCSIPeripheral->simple_exec(device->scsi_periph_device, &cmd, 239 sizeof(cmd), buffer, len, SCSI_DIR_IN); 240 if (res != B_OK) { 241 free(buffer); 242 return res; 243 } 244 245 TRACE(" mode_data_len=%d, block_desc_len=%d", 246 ((scsi_mode_param_header_6 *)buffer)->mode_data_length, 247 ((scsi_mode_param_header_6 *)buffer)->block_desc_length); 248 249 // find control page and retrieve values 250 page = (scsi_modepage_audio *)((char *)buffer + sizeof(header) 251 + header.block_desc_length); 252 253 TRACE(" page=%p, codepage=%d", page, page->header.page_code); 254 255 if (!set) { 256 volume->port0_channel = page->ports[0].channel; 257 volume->port0_volume = page->ports[0].volume; 258 volume->port1_channel = page->ports[1].channel; 259 volume->port1_volume = page->ports[1].volume; 260 volume->port2_channel = page->ports[2].channel; 261 volume->port2_volume = page->ports[2].volume; 262 volume->port3_channel = page->ports[3].channel; 263 volume->port3_volume = page->ports[3].volume; 264 265 #if 0 266 SHOW_FLOW(3, "1: %d - %d", volume->port0_channel, volume->port0_volume); 267 SHOW_FLOW(3, "2: %d - %d", volume->port1_channel, volume->port1_volume); 268 SHOW_FLOW(3, "3: %d - %d", volume->port2_channel, volume->port2_volume); 269 SHOW_FLOW(3, "4: %d - %d", volume->port3_channel, volume->port3_volume); 270 #endif 271 res = B_OK; 272 } else { 273 scsi_cmd_mode_select_6 cmd; 274 275 if (volume->flags & 0x01) 276 page->ports[0].channel = volume->port0_channel; 277 if (volume->flags & 0x02) 278 page->ports[0].volume = volume->port0_volume; 279 if (volume->flags & 0x04) 280 page->ports[1].channel = volume->port1_channel; 281 if (volume->flags & 0x08) 282 page->ports[1].volume = volume->port1_volume; 283 if (volume->flags & 0x10) 284 page->ports[2].channel = volume->port2_channel; 285 if (volume->flags & 0x20) 286 page->ports[2].volume = volume->port2_volume; 287 if (volume->flags & 0x40) 288 page->ports[3].channel = volume->port3_channel; 289 if (volume->flags & 0x80) 290 page->ports[3].volume = volume->port3_volume; 291 292 memset(&cmd, 0, sizeof(cmd)); 293 cmd.opcode = SCSI_OP_MODE_SELECT_6; 294 cmd.pf = 1; 295 cmd.param_list_length = sizeof(header) + header.block_desc_length 296 + sizeof(*page); 297 298 res = sSCSIPeripheral->simple_exec(device->scsi_periph_device, 299 &cmd, sizeof(cmd), buffer, len, SCSI_DIR_OUT); 300 } 301 302 free(buffer); 303 return res; 304 } 305 306 307 /*! Play audio cd; time is in MSF */ 308 static status_t 309 play_msf(cd_device_info *device, const scsi_play_position *position) 310 { 311 scsi_cmd_play_msf cmd; 312 313 TRACE("play_msf(): %d:%d:%d-%d:%d:%d\n", position->start_m, 314 position->start_s, position->start_f, position->end_m, position->end_s, 315 position->end_f); 316 317 memset(&cmd, 0, sizeof(cmd)); 318 319 cmd.opcode = SCSI_OP_PLAY_MSF; 320 cmd.start_minute = position->start_m; 321 cmd.start_second = position->start_s; 322 cmd.start_frame = position->start_f; 323 cmd.end_minute = position->end_m; 324 cmd.end_second = position->end_s; 325 cmd.end_frame = position->end_f; 326 327 return sSCSIPeripheral->simple_exec(device->scsi_periph_device, 328 &cmd, sizeof(cmd), NULL, 0, 0); 329 } 330 331 332 /*! Play audio cd; time is in track/index */ 333 static status_t 334 play_track_index(cd_device_info *device, const scsi_play_track *buf) 335 { 336 scsi_toc generic_toc; 337 scsi_toc_toc *toc; 338 status_t res; 339 int start_track, end_track; 340 scsi_play_position position; 341 342 TRACE("play_track_index(): %d-%d\n", buf->start_track, buf->end_track); 343 344 // the corresponding command PLAY AUDIO TRACK/INDEX is deprecated, 345 // so we have to simulate it by converting track to time via TOC 346 res = get_toc(device, &generic_toc); 347 if (res != B_OK) 348 return res; 349 350 toc = (scsi_toc_toc *)&generic_toc.toc_data[0]; 351 352 start_track = buf->start_track; 353 end_track = buf->end_track; 354 355 if (start_track > toc->last_track) 356 return B_BAD_INDEX; 357 358 if (end_track > toc->last_track) 359 end_track = toc->last_track + 1; 360 361 if (end_track < toc->last_track + 1) 362 ++end_track; 363 364 start_track -= toc->first_track; 365 end_track -= toc->first_track; 366 367 if (start_track < 0 || end_track < 0) 368 return B_BAD_INDEX; 369 370 position.start_m = toc->tracks[start_track].start.time.minute; 371 position.start_s = toc->tracks[start_track].start.time.second; 372 position.start_f = toc->tracks[start_track].start.time.frame; 373 374 position.end_m = toc->tracks[end_track].start.time.minute; 375 position.end_s = toc->tracks[end_track].start.time.second; 376 position.end_f = toc->tracks[end_track].start.time.frame; 377 378 return play_msf(device, &position); 379 } 380 381 382 static status_t 383 stop_audio(cd_device_info *device) 384 { 385 scsi_cmd_stop_play cmd; 386 387 TRACE("stop_audio()\n"); 388 389 memset( &cmd, 0, sizeof( cmd )); 390 cmd.opcode = SCSI_OP_STOP_PLAY; 391 392 return sSCSIPeripheral->simple_exec(device->scsi_periph_device, 393 &cmd, sizeof(cmd), NULL, 0, 0); 394 } 395 396 397 static status_t 398 pause_resume(cd_device_info *device, bool resume) 399 { 400 scsi_cmd_pause_resume cmd; 401 402 TRACE("pause_resume()\n"); 403 404 memset(&cmd, 0, sizeof(cmd)); 405 cmd.opcode = SCSI_OP_PAUSE_RESUME; 406 cmd.resume = resume; 407 408 return sSCSIPeripheral->simple_exec(device->scsi_periph_device, 409 &cmd, sizeof(cmd), NULL, 0, 0); 410 } 411 412 413 static status_t 414 scan(cd_device_info *device, const scsi_scan *buf) 415 { 416 scsi_cmd_scan cmd; 417 scsi_position curPos; 418 scsi_cd_current_position *cdPos; 419 420 TRACE("scan(direction =% d)\n", buf->direction); 421 422 status_t res = get_position(device, &curPos); 423 if (res != B_OK) 424 return res; 425 426 cdPos = (scsi_cd_current_position *)((char *)&curPos 427 + sizeof(scsi_subchannel_data_header)); 428 429 if (buf->direction == 0) { 430 scsi_play_position playPos; 431 432 // to stop scan, we issue play command with "open end" 433 playPos.start_m = cdPos->absolute_address.time.minute; 434 playPos.start_s = cdPos->absolute_address.time.second; 435 playPos.start_f = cdPos->absolute_address.time.frame; 436 playPos.end_m = 99; 437 playPos.end_s = 59; 438 playPos.end_f = 24; 439 440 return play_msf(device, &playPos); 441 } 442 443 memset(&cmd, 0, sizeof(cmd)); 444 445 cmd.opcode = SCSI_OP_SCAN; 446 cmd.direct = buf->direction < 0; 447 cmd.start.time = cdPos->absolute_address.time; 448 cmd.type = scsi_scan_msf; 449 450 /* 451 tmp = (uint8 *)&cmd; 452 dprintf("%d %d %d %d %d %d %d %d %d %d %d %d\n", 453 tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], 454 tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11]); 455 */ 456 457 return sSCSIPeripheral->simple_exec(device->scsi_periph_device, 458 &cmd, sizeof(cmd), NULL, 0, 0); 459 } 460 461 462 static status_t 463 read_cd(cd_device_info *device, const scsi_read_cd *readCD) 464 { 465 scsi_cmd_read_cd *cmd; 466 uint32 lba, length; 467 scsi_ccb *ccb; 468 status_t res; 469 470 // we use safe_exec instead of simple_exec as we want to set 471 // the sorting order manually (only makes much sense if you grab 472 // multiple tracks at once, but we are prepared) 473 ccb = device->scsi->alloc_ccb(device->scsi_device); 474 475 if (ccb == NULL) 476 return B_NO_MEMORY; 477 478 cmd = (scsi_cmd_read_cd *)ccb->cdb; 479 memset(cmd, 0, sizeof(*cmd)); 480 cmd->opcode = SCSI_OP_READ_CD; 481 cmd->sector_type = 1; 482 483 // skip first two seconds, they are lead-in 484 lba = (readCD->start_m * 60 + readCD->start_s) * 75 + readCD->start_f 485 - 2 * 75; 486 length = (readCD->length_m * 60 + readCD->length_s) * 75 + readCD->length_f; 487 488 cmd->lba = B_HOST_TO_BENDIAN_INT32(lba); 489 cmd->high_length = (length >> 16) & 0xff; 490 cmd->mid_length = (length >> 8) & 0xff; 491 cmd->low_length = length & 0xff; 492 493 cmd->error_field = scsi_read_cd_error_none; 494 cmd->edc_ecc = 0; 495 cmd->user_data = 1; 496 cmd->header_code = scsi_read_cd_header_none; 497 cmd->sync = 0; 498 cmd->sub_channel_selection = scsi_read_cd_sub_channel_none; 499 500 ccb->cdb_length = sizeof(*cmd); 501 502 ccb->flags = SCSI_DIR_IN | SCSI_DIS_DISCONNECT; 503 ccb->sort = lba; 504 // are 10 seconds enough for timeout? 505 ccb->timeout = 10; 506 507 // TODO: we pass a user buffer here! 508 ccb->data = (uint8 *)readCD->buffer; 509 ccb->sg_list = NULL; 510 ccb->data_length = readCD->buffer_length; 511 512 res = sSCSIPeripheral->safe_exec(device->scsi_periph_device, ccb); 513 514 device->scsi->free_ccb(ccb); 515 516 return res; 517 } 518 519 520 static int 521 log2(uint32 x) 522 { 523 int y; 524 525 for (y = 31; y >= 0; --y) { 526 if (x == (1UL << y)) 527 break; 528 } 529 530 return y; 531 } 532 533 534 // #pragma mark - block_io API 535 536 537 static void 538 cd_set_device(cd_device_info *info, block_io_device device) 539 { 540 info->block_io_device = device; 541 } 542 543 544 static status_t 545 cd_open(cd_device_info *device, cd_handle_info **_cookie) 546 { 547 TRACE("open()\n"); 548 549 cd_handle_info *handle = (cd_handle_info *)malloc(sizeof(*handle)); 550 if (handle == NULL) 551 return B_NO_MEMORY; 552 553 handle->device = device; 554 555 status_t status = sSCSIPeripheral->handle_open(device->scsi_periph_device, 556 (periph_handle_cookie)handle, &handle->scsi_periph_handle); 557 if (status < B_OK) { 558 free(handle); 559 return status; 560 } 561 562 if (device->block_size == 0 || device->capacity == 0) { 563 scsi_ccb *request = device->scsi->alloc_ccb(device->scsi_device); 564 if (request != NULL) { 565 // don't care if no test was possible 566 sSCSIPeripheral->check_capacity(device->scsi_periph_device, request); 567 device->scsi->free_ccb(request); 568 } 569 } 570 571 *_cookie = handle; 572 return B_OK; 573 } 574 575 576 static status_t 577 cd_close(cd_handle_info *handle) 578 { 579 TRACE("close()\n"); 580 581 sSCSIPeripheral->handle_close(handle->scsi_periph_handle); 582 return B_OK; 583 } 584 585 586 static status_t 587 cd_free(cd_handle_info *handle) 588 { 589 TRACE("free()\n"); 590 591 sSCSIPeripheral->handle_free(handle->scsi_periph_handle); 592 free(handle); 593 return B_OK; 594 } 595 596 597 static status_t 598 cd_read(cd_handle_info *handle, const phys_vecs *vecs, off_t pos, 599 size_t num_blocks, uint32 block_size, size_t *bytes_transferred) 600 { 601 return sSCSIPeripheral->read(handle->scsi_periph_handle, vecs, pos, 602 num_blocks, block_size, bytes_transferred, 10); 603 } 604 605 606 static status_t 607 cd_write(cd_handle_info *handle, const phys_vecs *vecs, off_t pos, 608 size_t num_blocks, uint32 block_size, size_t *bytes_transferred) 609 { 610 return sSCSIPeripheral->write(handle->scsi_periph_handle, vecs, pos, 611 num_blocks, block_size, bytes_transferred, 10); 612 } 613 614 615 static status_t 616 cd_ioctl(cd_handle_info *handle, int op, void *buffer, size_t length) 617 { 618 cd_device_info *device = handle->device; 619 620 TRACE("ioctl(op = %d)\n", op); 621 622 switch (op) { 623 case B_GET_DEVICE_SIZE: 624 { 625 status_t status = update_capacity(device); 626 if (status != B_OK) 627 return status; 628 629 size_t size = device->capacity * device->block_size; 630 return user_memcpy(buffer, &size, sizeof(size_t)); 631 } 632 633 case B_GET_GEOMETRY: 634 { 635 if (buffer == NULL /*|| length != sizeof(device_geometry)*/) 636 return B_BAD_VALUE; 637 638 device_geometry geometry; 639 status_t status = get_geometry(handle, &geometry); 640 if (status != B_OK) 641 return status; 642 643 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 644 } 645 646 case B_GET_ICON: 647 return sSCSIPeripheral->get_icon(icon_type_cd, 648 (device_icon *)buffer); 649 650 case B_SCSI_GET_TOC: 651 // TODO: we pass a user buffer here! 652 return get_toc(device, (scsi_toc *)buffer); 653 654 case B_EJECT_DEVICE: 655 case B_SCSI_EJECT: 656 return load_eject(device, false); 657 658 case B_LOAD_MEDIA: 659 return load_eject(device, true); 660 661 case B_SCSI_GET_POSITION: 662 { 663 if (buffer == NULL) 664 return B_BAD_VALUE; 665 666 scsi_position position; 667 status_t status = get_position(device, &position); 668 if (status != B_OK) 669 return status; 670 671 return user_memcpy(buffer, &position, sizeof(scsi_position)); 672 } 673 674 case B_SCSI_GET_VOLUME: 675 // TODO: we pass a user buffer here! 676 return get_set_volume(device, (scsi_volume *)buffer, false); 677 case B_SCSI_SET_VOLUME: 678 // TODO: we pass a user buffer here! 679 return get_set_volume(device, (scsi_volume *)buffer, true); 680 681 case B_SCSI_PLAY_TRACK: 682 { 683 scsi_play_track track; 684 if (user_memcpy(&track, buffer, sizeof(scsi_play_track)) != B_OK) 685 return B_BAD_ADDRESS; 686 687 return play_track_index(device, &track); 688 } 689 case B_SCSI_PLAY_POSITION: 690 { 691 scsi_play_position position; 692 if (user_memcpy(&position, buffer, sizeof(scsi_play_position)) 693 != B_OK) 694 return B_BAD_ADDRESS; 695 696 return play_msf(device, &position); 697 } 698 699 case B_SCSI_STOP_AUDIO: 700 return stop_audio(device); 701 case B_SCSI_PAUSE_AUDIO: 702 return pause_resume(device, false); 703 case B_SCSI_RESUME_AUDIO: 704 return pause_resume(device, true); 705 706 case B_SCSI_SCAN: 707 { 708 scsi_scan scanBuffer; 709 if (user_memcpy(&scanBuffer, buffer, sizeof(scsi_scan)) != B_OK) 710 return B_BAD_ADDRESS; 711 712 return scan(device, &scanBuffer); 713 } 714 case B_SCSI_READ_CD: 715 // TODO: we pass a user buffer here! 716 return read_cd(device, (scsi_read_cd *)buffer); 717 718 default: 719 return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op, 720 buffer, length); 721 } 722 } 723 724 725 // #pragma mark - scsi_periph callbacks 726 727 728 static void 729 cd_set_capacity(cd_device_info *device, uint64 capacity, uint32 blockSize) 730 { 731 TRACE("das_set_capacity(device = %p, capacity = %Ld, blockSize = %ld)\n", 732 device, capacity, blockSize); 733 734 // get log2, if possible 735 uint32 blockShift = log2(blockSize); 736 737 if ((1UL << blockShift) != blockSize) 738 blockShift = 0; 739 740 device->capacity = capacity; 741 device->block_size = blockSize; 742 743 sBlockIO->set_media_params(device->block_io_device, blockSize, 744 blockShift, capacity); 745 } 746 747 748 static void 749 cd_media_changed(cd_device_info *device, scsi_ccb *request) 750 { 751 // do a capacity check 752 // TBD: is this a good idea (e.g. if this is an empty CD)? 753 sSCSIPeripheral->check_capacity(device->scsi_periph_device, request); 754 } 755 756 757 scsi_periph_callbacks callbacks = { 758 (void (*)(periph_device_cookie, uint64, uint32))cd_set_capacity, 759 (void (*)(periph_device_cookie, scsi_ccb *))cd_media_changed 760 }; 761 762 763 // #pragma mark - driver module API 764 765 766 static float 767 cd_supports_device(device_node *parent) 768 { 769 const char *bus; 770 uint8 deviceType; 771 772 // make sure parent is really the SCSI bus manager 773 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 774 return -1; 775 776 if (strcmp(bus, "scsi")) 777 return 0.0; 778 779 // check whether it's really a CD-ROM or WORM 780 if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM, 781 &deviceType, true) != B_OK 782 || (deviceType != scsi_dev_CDROM && deviceType != scsi_dev_WORM)) 783 return 0.0; 784 785 return 0.6; 786 } 787 788 789 /*! Called whenever a new device was added to system; 790 if we really support it, we create a new node that gets 791 server by the block_io module 792 */ 793 static status_t 794 cd_register_device(device_node *node) 795 { 796 const scsi_res_inquiry *deviceInquiry = NULL; 797 size_t inquiryLength; 798 uint32 maxBlocks; 799 800 // get inquiry data 801 if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM, 802 (const void **)&deviceInquiry, &inquiryLength, true) != B_OK 803 || inquiryLength < sizeof(deviceInquiry)) 804 return B_ERROR; 805 806 // get block limit of underlying hardware to lower it (if necessary) 807 if (sDeviceManager->get_attr_uint32(node, B_BLOCK_DEVICE_MAX_BLOCKS_ITEM, 808 &maxBlocks, true) != B_OK) 809 maxBlocks = INT_MAX; 810 811 // using 10 byte commands, at most 0xffff blocks can be transmitted at once 812 // (sadly, we cannot update this value later on if only 6 byte commands 813 // are supported, but the block_io module can live with that) 814 maxBlocks = min_c(maxBlocks, 0xffff); 815 816 // ready to register 817 device_attr attrs[] = { 818 // tell block_io whether the device is removable 819 {"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}}, 820 // impose own max block restriction 821 {B_BLOCK_DEVICE_MAX_BLOCKS_ITEM, B_UINT32_TYPE, {ui32: maxBlocks}}, 822 { NULL } 823 }; 824 825 return sDeviceManager->register_node(node, SCSI_CD_MODULE_NAME, attrs, 826 NULL, NULL); 827 } 828 829 830 static status_t 831 cd_init_driver(device_node *node, void **cookie) 832 { 833 cd_device_info *device; 834 status_t status; 835 uint8 removable; 836 837 TRACE("cd_init_driver"); 838 839 status = sDeviceManager->get_attr_uint8(node, "removable", 840 &removable, false); 841 if (status != B_OK) 842 return status; 843 844 device = (cd_device_info *)malloc(sizeof(*device)); 845 if (device == NULL) 846 return B_NO_MEMORY; 847 848 memset(device, 0, sizeof(*device)); 849 850 device->node = node; 851 device->removable = removable; 852 853 // set capacity to zero, so it get checked on first opened handle 854 device->capacity = 0; 855 device->block_size = 0; 856 857 sDeviceManager->get_attr_uint8(node, SCSI_DEVICE_TYPE_ITEM, 858 &device->device_type, true); 859 860 device_node *parent = sDeviceManager->get_parent_node(node); 861 sDeviceManager->get_driver(parent, (driver_module_info **)&device->scsi, 862 (void **)&device->scsi_device); 863 sDeviceManager->put_node(parent); 864 865 status = sSCSIPeripheral->register_device((periph_device_cookie)device, 866 &callbacks, device->scsi_device, device->scsi, device->node, 867 device->removable, &device->scsi_periph_device); 868 if (status != B_OK) { 869 free(device); 870 return status; 871 } 872 873 *cookie = device; 874 return B_OK; 875 } 876 877 878 static void 879 cd_uninit_driver(void *_cookie) 880 { 881 cd_device_info *device = (cd_device_info *)_cookie; 882 883 sSCSIPeripheral->unregister_device(device->scsi_periph_device); 884 free(device); 885 } 886 887 888 static status_t 889 cd_register_child_devices(void *_cookie) 890 { 891 cd_device_info *device = (cd_device_info *)_cookie; 892 status_t status; 893 char *name; 894 895 name = sSCSIPeripheral->compose_device_name(device->node, "disk/scsi"); 896 if (name == NULL) 897 return B_ERROR; 898 899 status = sDeviceManager->publish_device(device->node, name, 900 B_BLOCK_IO_DEVICE_MODULE_NAME); 901 902 free(name); 903 return status; 904 } 905 906 907 module_dependency module_dependencies[] = { 908 {SCSI_PERIPH_MODULE_NAME, (module_info **)&sSCSIPeripheral}, 909 {B_BLOCK_IO_FOR_DRIVER_MODULE_NAME, (module_info **)&sBlockIO}, 910 {B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager}, 911 {} 912 }; 913 914 block_device_interface sSCSICDModule = { 915 { 916 { 917 SCSI_CD_MODULE_NAME, 918 0, 919 NULL 920 }, 921 922 cd_supports_device, 923 cd_register_device, 924 cd_init_driver, 925 cd_uninit_driver, 926 cd_register_child_devices, 927 NULL, // rescan 928 NULL, // removed 929 }, 930 931 (void (*)(block_device_cookie *, block_io_device)) &cd_set_device, 932 (status_t (*)(block_device_cookie *, block_device_handle_cookie **))&cd_open, 933 (status_t (*)(block_device_handle_cookie *)) &cd_close, 934 (status_t (*)(block_device_handle_cookie *)) &cd_free, 935 936 (status_t (*)(block_device_handle_cookie *, const phys_vecs *, 937 off_t, size_t, uint32, size_t *)) &cd_read, 938 (status_t (*)(block_device_handle_cookie *, const phys_vecs *, 939 off_t, size_t, uint32, size_t *)) &cd_write, 940 941 (status_t (*)(block_device_handle_cookie *, int, void *, size_t))&cd_ioctl, 942 }; 943 944 module_info *modules[] = { 945 (module_info *)&sSCSICDModule, 946 NULL 947 }; 948