1 /* 2 * Copyright 2004-2009, Haiku, Inc. All rights reserved. 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 #include <io_requests.h> 22 23 24 //#define TRACE_CD_DISK 25 #ifdef TRACE_CD_DISK 26 # define TRACE(x...) dprintf("scsi_cd: " x) 27 #else 28 # define TRACE(x...) ; 29 #endif 30 31 32 static const uint8 kCDIcon[] = { 33 0x6e, 0x63, 0x69, 0x66, 0x05, 0x05, 0x00, 0x02, 0x03, 0x06, 0x05, 0xb8, 34 0x12, 0xa5, 0xbe, 0x03, 0xe1, 0x3d, 0xe7, 0x84, 0xb8, 0x02, 0x10, 0x49, 35 0xf7, 0x9f, 0x49, 0xed, 0xd8, 0x00, 0xf1, 0xf1, 0xf1, 0x36, 0xd9, 0xdd, 36 0xf4, 0x8a, 0x99, 0x96, 0xb9, 0xb4, 0xb8, 0xbe, 0xdb, 0xff, 0xf4, 0xf4, 37 0xf4, 0x04, 0xeb, 0xd0, 0x02, 0x00, 0x06, 0x02, 0x3c, 0x92, 0xc0, 0x38, 38 0x8f, 0x5f, 0xb8, 0x54, 0x50, 0x3c, 0x57, 0x63, 0x48, 0xd8, 0xdf, 0x48, 39 0x89, 0x5b, 0x00, 0x41, 0x37, 0xa9, 0xff, 0xb9, 0xb9, 0xb9, 0x04, 0x01, 40 0x7e, 0x04, 0x02, 0x04, 0x3f, 0x2c, 0x4e, 0x2c, 0x30, 0x2c, 0x22, 0x40, 41 0x22, 0x34, 0x22, 0x4c, 0x3f, 0x54, 0x30, 0x54, 0x4e, 0x54, 0x5c, 0x40, 42 0x5c, 0x4c, 0x5c, 0x34, 0x02, 0x04, 0x3f, 0x3a, 0x43, 0x3a, 0x3b, 0x3a, 43 0x39, 0x3e, 0x39, 0x3c, 0x39, 0x40, 0x3f, 0x42, 0x3b, 0x42, 0x43, 0x42, 44 0x45, 0x3e, 0x45, 0x40, 0x45, 0x3c, 0x02, 0x04, 0x4b, 0x3e, 0x4b, 0x3a, 45 0x4b, 0x42, 0x3f, 0x46, 0x47, 0x46, 0x37, 0x46, 0x33, 0x3e, 0x33, 0x42, 46 0x33, 0x3a, 0x3f, 0xbb, 0xf7, 0x37, 0xbb, 0xf7, 0x47, 0xbb, 0xf7, 0x02, 47 0x04, 0x40, 0x2a, 0x54, 0x2a, 0x50, 0x2c, 0x5c, 0x40, 0x5c, 0x34, 0x5c, 48 0x4c, 0x40, 0x56, 0x50, 0x54, 0x54, 0x56, 0x60, 0x40, 0x60, 0x4c, 0x60, 49 0x34, 0x06, 0x0a, 0x04, 0x01, 0x03, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x01, 50 0x18, 0x15, 0xff, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x00, 0x02, 0x00, 51 0x01, 0x18, 0x00, 0x15, 0x01, 0x17, 0x86, 0x00, 0x04, 0x0a, 0x01, 0x02, 52 0x00, 0x02, 0x00, 0x0a, 0x02, 0x02, 0x02, 0x01, 0x00, 0x0a, 0x03, 0x01, 53 0x02, 0x10, 0x01, 0x17, 0x82, 0x00, 0x04 54 }; 55 56 57 static scsi_periph_interface *sSCSIPeripheral; 58 static device_manager_info *sDeviceManager; 59 60 61 #define SCSI_CD_STD_TIMEOUT 10 62 63 64 static status_t 65 update_capacity(cd_driver_info *info) 66 { 67 TRACE("update_capacity()\n"); 68 69 scsi_ccb *ccb = info->scsi->alloc_ccb(info->scsi_device); 70 if (ccb == NULL) 71 return B_NO_MEMORY; 72 73 status_t status = sSCSIPeripheral->check_capacity( 74 info->scsi_periph_device, ccb); 75 76 info->scsi->free_ccb(ccb); 77 78 return status; 79 } 80 81 82 static status_t 83 get_geometry(cd_handle *handle, device_geometry *geometry) 84 { 85 cd_driver_info *info = handle->info; 86 87 status_t status = update_capacity(info); 88 89 // it seems that Be expects B_GET_GEOMETRY to always succeed unless 90 // the medium has been changed; e.g. if we report B_DEV_NO_MEDIA, the 91 // info is ignored by the CDPlayer and CDBurner 92 if (status == B_DEV_MEDIA_CHANGED) 93 return B_DEV_MEDIA_CHANGED; 94 95 geometry->bytes_per_sector = info->block_size; 96 geometry->sectors_per_track = 1; 97 geometry->cylinder_count = info->capacity; 98 geometry->head_count = 1; 99 geometry->device_type = info->device_type; 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 = true; 108 geometry->write_once = info->device_type == scsi_dev_WORM; 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 get_toc(cd_driver_info *info, scsi_toc *toc) 121 { 122 scsi_ccb *ccb; 123 status_t res; 124 scsi_cmd_read_toc *cmd; 125 size_t dataLength; 126 scsi_toc_general *short_response = (scsi_toc_general *)toc->toc_data; 127 128 TRACE("get_toc()\n"); 129 130 ccb = info->scsi->alloc_ccb(info->scsi_device); 131 if (ccb == NULL) 132 return B_NO_MEMORY; 133 134 // first read number of tracks only 135 ccb->flags = SCSI_DIR_IN; 136 137 cmd = (scsi_cmd_read_toc *)ccb->cdb; 138 139 memset(cmd, 0, sizeof(*cmd)); 140 cmd->opcode = SCSI_OP_READ_TOC; 141 cmd->time = 1; 142 cmd->format = SCSI_TOC_FORMAT_TOC; 143 cmd->track = 1; 144 cmd->allocation_length = B_HOST_TO_BENDIAN_INT16(sizeof(scsi_toc_general)); 145 146 ccb->cdb_length = sizeof(*cmd); 147 148 ccb->sort = -1; 149 ccb->timeout = SCSI_CD_STD_TIMEOUT; 150 151 ccb->data = toc->toc_data; 152 ccb->sg_list = NULL; 153 ccb->data_length = sizeof(toc->toc_data); 154 155 res = sSCSIPeripheral->safe_exec(info->scsi_periph_device, ccb); 156 if (res != B_OK) 157 goto err; 158 159 // then read all track infos 160 // (little hint: number of tracks is last - first + 1; 161 // but scsi_toc_toc has already one track, so we get 162 // last - first extra tracks; finally, we want the lead-out as 163 // well, so we add an extra track) 164 dataLength = (short_response->last - short_response->first + 1) 165 * sizeof(scsi_toc_track) + sizeof(scsi_toc_toc); 166 dataLength = min_c(dataLength, sizeof(toc->toc_data)); 167 168 TRACE(" tracks: %d - %d, data length %d\n", short_response->first, 169 short_response->last, (int)dataLength); 170 171 cmd->allocation_length = B_HOST_TO_BENDIAN_INT16(dataLength); 172 173 res = sSCSIPeripheral->safe_exec(info->scsi_periph_device, ccb); 174 175 err: 176 info->scsi->free_ccb(ccb); 177 178 return res; 179 } 180 181 182 static status_t 183 load_eject(cd_driver_info *info, bool load) 184 { 185 TRACE("load_eject()\n"); 186 187 scsi_ccb *ccb = info->scsi->alloc_ccb(info->scsi_device); 188 if (ccb == NULL) 189 return B_NO_MEMORY; 190 191 err_res result = sSCSIPeripheral->send_start_stop( 192 info->scsi_periph_device, ccb, load, true); 193 194 info->scsi->free_ccb(ccb); 195 196 return result.error_code; 197 } 198 199 200 static status_t 201 get_position(cd_driver_info *info, scsi_position *position) 202 { 203 scsi_cmd_read_subchannel cmd; 204 205 TRACE("get_position()\n"); 206 207 memset(&cmd, 0, sizeof(cmd)); 208 cmd.opcode = SCSI_OP_READ_SUB_CHANNEL; 209 cmd.time = 1; 210 cmd.subq = 1; 211 cmd.parameter_list = scsi_sub_channel_parameter_list_cd_pos; 212 cmd.track = 0; 213 cmd.allocation_length = B_HOST_TO_BENDIAN_INT16(sizeof(scsi_position)); 214 215 return sSCSIPeripheral->simple_exec(info->scsi_periph_device, 216 &cmd, sizeof(cmd), position, sizeof(*position), SCSI_DIR_IN); 217 } 218 219 220 static status_t 221 get_set_volume(cd_driver_info *info, scsi_volume *volume, bool set) 222 { 223 scsi_cmd_mode_sense_6 cmd; 224 scsi_mode_param_header_6 header; 225 size_t len; 226 void *buffer; 227 scsi_modepage_audio *page; 228 status_t res; 229 230 TRACE("get_set_volume()\n"); 231 232 // determine size of block descriptor 233 memset(&cmd, 0, sizeof(cmd)); 234 cmd.opcode = SCSI_OP_MODE_SENSE_6; 235 cmd.page_code = SCSI_MODEPAGE_AUDIO; 236 cmd.page_control = SCSI_MODE_SENSE_PC_CURRENT; 237 cmd.allocation_length = sizeof(header); 238 239 memset(&header, -2, sizeof(header)); 240 241 res = sSCSIPeripheral->simple_exec(info->scsi_periph_device, &cmd, 242 sizeof(cmd), &header, sizeof(header), SCSI_DIR_IN); 243 if (res != B_OK) 244 return res; 245 246 TRACE(" block_desc_len=%d", header.block_desc_length); 247 #if 0 248 // ToDo: why this?? 249 return B_ERROR; 250 #endif 251 252 // retrieve param header, block descriptor and actual codepage 253 len = sizeof(header) + header.block_desc_length 254 + sizeof(scsi_modepage_audio); 255 256 buffer = malloc(len); 257 if (buffer == NULL) 258 return B_NO_MEMORY; 259 260 memset(buffer, -1, sizeof(buffer)); 261 262 cmd.allocation_length = len; 263 264 res = sSCSIPeripheral->simple_exec(info->scsi_periph_device, &cmd, 265 sizeof(cmd), buffer, len, SCSI_DIR_IN); 266 if (res != B_OK) { 267 free(buffer); 268 return res; 269 } 270 271 TRACE(" mode_data_len=%d, block_desc_len=%d", 272 ((scsi_mode_param_header_6 *)buffer)->mode_data_length, 273 ((scsi_mode_param_header_6 *)buffer)->block_desc_length); 274 275 // find control page and retrieve values 276 page = (scsi_modepage_audio *)((char *)buffer + sizeof(header) 277 + header.block_desc_length); 278 279 TRACE(" page=%p, codepage=%d", page, page->header.page_code); 280 281 if (!set) { 282 volume->port0_channel = page->ports[0].channel; 283 volume->port0_volume = page->ports[0].volume; 284 volume->port1_channel = page->ports[1].channel; 285 volume->port1_volume = page->ports[1].volume; 286 volume->port2_channel = page->ports[2].channel; 287 volume->port2_volume = page->ports[2].volume; 288 volume->port3_channel = page->ports[3].channel; 289 volume->port3_volume = page->ports[3].volume; 290 291 #if 0 292 SHOW_FLOW(3, "1: %d - %d", volume->port0_channel, volume->port0_volume); 293 SHOW_FLOW(3, "2: %d - %d", volume->port1_channel, volume->port1_volume); 294 SHOW_FLOW(3, "3: %d - %d", volume->port2_channel, volume->port2_volume); 295 SHOW_FLOW(3, "4: %d - %d", volume->port3_channel, volume->port3_volume); 296 #endif 297 res = B_OK; 298 } else { 299 scsi_cmd_mode_select_6 cmd; 300 301 if (volume->flags & 0x01) 302 page->ports[0].channel = volume->port0_channel; 303 if (volume->flags & 0x02) 304 page->ports[0].volume = volume->port0_volume; 305 if (volume->flags & 0x04) 306 page->ports[1].channel = volume->port1_channel; 307 if (volume->flags & 0x08) 308 page->ports[1].volume = volume->port1_volume; 309 if (volume->flags & 0x10) 310 page->ports[2].channel = volume->port2_channel; 311 if (volume->flags & 0x20) 312 page->ports[2].volume = volume->port2_volume; 313 if (volume->flags & 0x40) 314 page->ports[3].channel = volume->port3_channel; 315 if (volume->flags & 0x80) 316 page->ports[3].volume = volume->port3_volume; 317 318 memset(&cmd, 0, sizeof(cmd)); 319 cmd.opcode = SCSI_OP_MODE_SELECT_6; 320 cmd.pf = 1; 321 cmd.param_list_length = sizeof(header) + header.block_desc_length 322 + sizeof(*page); 323 324 res = sSCSIPeripheral->simple_exec(info->scsi_periph_device, 325 &cmd, sizeof(cmd), buffer, len, SCSI_DIR_OUT); 326 } 327 328 free(buffer); 329 return res; 330 } 331 332 333 /*! Play audio cd; time is in MSF */ 334 static status_t 335 play_msf(cd_driver_info *info, const scsi_play_position *position) 336 { 337 scsi_cmd_play_msf cmd; 338 339 TRACE("play_msf(): %d:%d:%d-%d:%d:%d\n", position->start_m, 340 position->start_s, position->start_f, position->end_m, position->end_s, 341 position->end_f); 342 343 memset(&cmd, 0, sizeof(cmd)); 344 345 cmd.opcode = SCSI_OP_PLAY_MSF; 346 cmd.start_minute = position->start_m; 347 cmd.start_second = position->start_s; 348 cmd.start_frame = position->start_f; 349 cmd.end_minute = position->end_m; 350 cmd.end_second = position->end_s; 351 cmd.end_frame = position->end_f; 352 353 return sSCSIPeripheral->simple_exec(info->scsi_periph_device, 354 &cmd, sizeof(cmd), NULL, 0, SCSI_DIR_NONE); 355 } 356 357 358 /*! Play audio cd; time is in track/index */ 359 static status_t 360 play_track_index(cd_driver_info *info, const scsi_play_track *buf) 361 { 362 scsi_toc generic_toc; 363 scsi_toc_toc *toc; 364 status_t res; 365 int start_track, end_track; 366 scsi_play_position position; 367 368 TRACE("play_track_index(): %d-%d\n", buf->start_track, buf->end_track); 369 370 // the corresponding command PLAY AUDIO TRACK/INDEX is deprecated, 371 // so we have to simulate it by converting track to time via TOC 372 res = get_toc(info, &generic_toc); 373 if (res != B_OK) 374 return res; 375 376 toc = (scsi_toc_toc *)&generic_toc.toc_data[0]; 377 378 start_track = buf->start_track; 379 end_track = buf->end_track; 380 381 if (start_track > toc->last_track) 382 return B_BAD_INDEX; 383 384 if (end_track > toc->last_track) 385 end_track = toc->last_track + 1; 386 387 if (end_track < toc->last_track + 1) 388 ++end_track; 389 390 start_track -= toc->first_track; 391 end_track -= toc->first_track; 392 393 if (start_track < 0 || end_track < 0) 394 return B_BAD_INDEX; 395 396 position.start_m = toc->tracks[start_track].start.time.minute; 397 position.start_s = toc->tracks[start_track].start.time.second; 398 position.start_f = toc->tracks[start_track].start.time.frame; 399 400 position.end_m = toc->tracks[end_track].start.time.minute; 401 position.end_s = toc->tracks[end_track].start.time.second; 402 position.end_f = toc->tracks[end_track].start.time.frame; 403 404 return play_msf(info, &position); 405 } 406 407 408 static status_t 409 stop_audio(cd_driver_info *info) 410 { 411 scsi_cmd_stop_play cmd; 412 413 TRACE("stop_audio()\n"); 414 415 memset( &cmd, 0, sizeof( cmd )); 416 cmd.opcode = SCSI_OP_STOP_PLAY; 417 418 return sSCSIPeripheral->simple_exec(info->scsi_periph_device, 419 &cmd, sizeof(cmd), NULL, 0, SCSI_DIR_NONE); 420 } 421 422 423 static status_t 424 pause_resume(cd_driver_info *info, bool resume) 425 { 426 scsi_cmd_pause_resume cmd; 427 428 TRACE("pause_resume()\n"); 429 430 memset(&cmd, 0, sizeof(cmd)); 431 cmd.opcode = SCSI_OP_PAUSE_RESUME; 432 cmd.resume = resume; 433 434 return sSCSIPeripheral->simple_exec(info->scsi_periph_device, 435 &cmd, sizeof(cmd), NULL, 0, SCSI_DIR_NONE); 436 } 437 438 439 static status_t 440 scan(cd_driver_info *info, const scsi_scan *buf) 441 { 442 scsi_cmd_scan cmd; 443 scsi_position curPos; 444 scsi_cd_current_position *cdPos; 445 446 TRACE("scan(direction =% d)\n", buf->direction); 447 448 status_t res = get_position(info, &curPos); 449 if (res != B_OK) 450 return res; 451 452 cdPos = (scsi_cd_current_position *)((char *)&curPos 453 + sizeof(scsi_subchannel_data_header)); 454 455 if (buf->direction == 0) { 456 scsi_play_position playPos; 457 458 // to stop scan, we issue play command with "open end" 459 playPos.start_m = cdPos->absolute_address.time.minute; 460 playPos.start_s = cdPos->absolute_address.time.second; 461 playPos.start_f = cdPos->absolute_address.time.frame; 462 playPos.end_m = 99; 463 playPos.end_s = 59; 464 playPos.end_f = 24; 465 466 return play_msf(info, &playPos); 467 } 468 469 memset(&cmd, 0, sizeof(cmd)); 470 471 cmd.opcode = SCSI_OP_SCAN; 472 cmd.direct = buf->direction < 0; 473 cmd.start.time = cdPos->absolute_address.time; 474 cmd.type = scsi_scan_msf; 475 476 /* 477 tmp = (uint8 *)&cmd; 478 dprintf("%d %d %d %d %d %d %d %d %d %d %d %d\n", 479 tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], 480 tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11]); 481 */ 482 483 return sSCSIPeripheral->simple_exec(info->scsi_periph_device, 484 &cmd, sizeof(cmd), NULL, 0, SCSI_DIR_NONE); 485 } 486 487 488 static status_t 489 read_cd(cd_driver_info *info, const scsi_read_cd *readCD) 490 { 491 scsi_cmd_read_cd *cmd; 492 uint32 lba, length; 493 scsi_ccb *ccb; 494 status_t res; 495 496 // we use safe_exec instead of simple_exec as we want to set 497 // the sorting order manually (only makes much sense if you grab 498 // multiple tracks at once, but we are prepared) 499 ccb = info->scsi->alloc_ccb(info->scsi_device); 500 501 if (ccb == NULL) 502 return B_NO_MEMORY; 503 504 cmd = (scsi_cmd_read_cd *)ccb->cdb; 505 memset(cmd, 0, sizeof(*cmd)); 506 cmd->opcode = SCSI_OP_READ_CD; 507 cmd->sector_type = 1; 508 509 // skip first two seconds, they are lead-in 510 lba = (readCD->start_m * 60 + readCD->start_s) * 75 + readCD->start_f 511 - 2 * 75; 512 length = (readCD->length_m * 60 + readCD->length_s) * 75 + readCD->length_f; 513 514 cmd->lba = B_HOST_TO_BENDIAN_INT32(lba); 515 cmd->high_length = (length >> 16) & 0xff; 516 cmd->mid_length = (length >> 8) & 0xff; 517 cmd->low_length = length & 0xff; 518 519 cmd->error_field = scsi_read_cd_error_none; 520 cmd->edc_ecc = 0; 521 cmd->user_data = 1; 522 cmd->header_code = scsi_read_cd_header_none; 523 cmd->sync = 0; 524 cmd->sub_channel_selection = scsi_read_cd_sub_channel_none; 525 526 ccb->cdb_length = sizeof(*cmd); 527 528 ccb->flags = SCSI_DIR_IN | SCSI_DIS_DISCONNECT; 529 ccb->sort = lba; 530 // are 10 seconds enough for timeout? 531 ccb->timeout = 10; 532 533 // TODO: we pass a user buffer here! 534 ccb->data = (uint8 *)readCD->buffer; 535 ccb->sg_list = NULL; 536 ccb->data_length = readCD->buffer_length; 537 538 res = sSCSIPeripheral->safe_exec(info->scsi_periph_device, ccb); 539 540 info->scsi->free_ccb(ccb); 541 542 return res; 543 } 544 545 546 static int 547 log2(uint32 x) 548 { 549 int y; 550 551 for (y = 31; y >= 0; --y) { 552 if (x == (1UL << y)) 553 break; 554 } 555 556 return y; 557 } 558 559 560 static status_t 561 do_io(void* cookie, IOOperation* operation) 562 { 563 cd_driver_info* info = (cd_driver_info*)cookie; 564 565 // TODO: this can go away as soon as we pushed the IOOperation to the upper 566 // layers - we can then set scsi_periph::io() as callback for the scheduler 567 size_t bytesTransferred; 568 status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation, 569 &bytesTransferred); 570 571 info->io_scheduler->OperationCompleted(operation, status, bytesTransferred); 572 return status; 573 } 574 575 576 // #pragma mark - device module API 577 578 579 static status_t 580 cd_init_device(void* _info, void** _cookie) 581 { 582 cd_driver_info* info = (cd_driver_info*)_info; 583 584 // and get (initial) capacity 585 scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device); 586 if (request == NULL) 587 return B_NO_MEMORY; 588 589 sSCSIPeripheral->check_capacity(info->scsi_periph_device, request); 590 info->scsi->free_ccb(request); 591 592 *_cookie = info; 593 return B_OK; 594 } 595 596 597 static void 598 cd_uninit_device(void* _cookie) 599 { 600 cd_driver_info* info = (cd_driver_info*)_cookie; 601 602 delete info->io_scheduler; 603 delete info->dma_resource; 604 } 605 606 607 static status_t 608 cd_open(void* _info, const char* path, int openMode, void** _cookie) 609 { 610 cd_driver_info* info = (cd_driver_info*)_info; 611 612 cd_handle* handle = (cd_handle*)malloc(sizeof(cd_handle)); 613 if (handle == NULL) 614 return B_NO_MEMORY; 615 616 handle->info = info; 617 618 status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device, 619 (periph_handle_cookie)handle, &handle->scsi_periph_handle); 620 if (status < B_OK) { 621 free(handle); 622 return status; 623 } 624 625 *_cookie = handle; 626 return B_OK; 627 } 628 629 630 static status_t 631 cd_close(void* cookie) 632 { 633 cd_handle* handle = (cd_handle*)cookie; 634 TRACE("close()\n"); 635 636 sSCSIPeripheral->handle_close(handle->scsi_periph_handle); 637 return B_OK; 638 } 639 640 641 static status_t 642 cd_free(void* cookie) 643 { 644 cd_handle* handle = (cd_handle*)cookie; 645 TRACE("free()\n"); 646 647 sSCSIPeripheral->handle_free(handle->scsi_periph_handle); 648 free(handle); 649 return B_OK; 650 } 651 652 653 static status_t 654 cd_read(void* cookie, off_t pos, void* buffer, size_t* _length) 655 { 656 cd_handle* handle = (cd_handle*)cookie; 657 size_t length = *_length; 658 659 if (handle->info->capacity == 0) 660 return B_DEV_NO_MEDIA; 661 662 IORequest request; 663 status_t status = request.Init(pos, buffer, length, false, 0); 664 if (status != B_OK) 665 return status; 666 667 status = handle->info->io_scheduler->ScheduleRequest(&request); 668 if (status != B_OK) 669 return status; 670 671 status = request.Wait(0, 0); 672 if (status == B_OK) 673 *_length = length; 674 else 675 dprintf("cd_read(): request.Wait() returned: %s\n", strerror(status)); 676 677 return status; 678 } 679 680 681 static status_t 682 cd_write(void* cookie, off_t pos, const void* buffer, size_t* _length) 683 { 684 cd_handle* handle = (cd_handle*)cookie; 685 size_t length = *_length; 686 687 if (handle->info->capacity == 0) 688 return B_DEV_NO_MEDIA; 689 690 IORequest request; 691 status_t status = request.Init(pos, (void*)buffer, length, true, 0); 692 if (status != B_OK) 693 return status; 694 695 status = handle->info->io_scheduler->ScheduleRequest(&request); 696 if (status != B_OK) 697 return status; 698 699 status = request.Wait(0, 0); 700 if (status == B_OK) 701 *_length = length; 702 else 703 dprintf("cd_write(): request.Wait() returned: %s\n", strerror(status)); 704 705 return status; 706 } 707 708 709 static status_t 710 cd_io(void* cookie, io_request* request) 711 { 712 cd_handle* handle = (cd_handle*)cookie; 713 714 if (handle->info->capacity == 0) { 715 notify_io_request(request, B_DEV_NO_MEDIA); 716 return B_DEV_NO_MEDIA; 717 } 718 719 return handle->info->io_scheduler->ScheduleRequest(request); 720 } 721 722 723 static status_t 724 cd_ioctl(void* cookie, uint32 op, void* buffer, size_t length) 725 { 726 cd_handle* handle = (cd_handle*)cookie; 727 cd_driver_info *info = handle->info; 728 729 TRACE("ioctl(op = %lu)\n", op); 730 731 switch (op) { 732 case B_GET_DEVICE_SIZE: 733 { 734 status_t status = update_capacity(info); 735 if (status != B_OK) 736 return status; 737 738 size_t size = info->capacity * info->block_size; 739 return user_memcpy(buffer, &size, sizeof(size_t)); 740 } 741 742 case B_GET_GEOMETRY: 743 { 744 if (buffer == NULL /*|| length != sizeof(device_geometry)*/) 745 return B_BAD_VALUE; 746 747 device_geometry geometry; 748 status_t status = get_geometry(handle, &geometry); 749 if (status != B_OK) 750 return status; 751 752 return user_memcpy(buffer, &geometry, sizeof(device_geometry)); 753 } 754 755 case B_GET_ICON_NAME: 756 return user_strlcpy((char*)buffer, "devices/drive-optical", 757 B_FILE_NAME_LENGTH); 758 759 case B_GET_VECTOR_ICON: 760 { 761 device_icon iconData; 762 if (length != sizeof(device_icon)) 763 return B_BAD_VALUE; 764 if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) 765 return B_BAD_ADDRESS; 766 767 if (iconData.icon_size >= (int32)sizeof(kCDIcon)) { 768 if (user_memcpy(iconData.icon_data, kCDIcon, 769 sizeof(kCDIcon)) != B_OK) 770 return B_BAD_ADDRESS; 771 } 772 773 iconData.icon_size = sizeof(kCDIcon); 774 return user_memcpy(buffer, &iconData, sizeof(device_icon)); 775 } 776 777 case B_SCSI_GET_TOC: 778 // TODO: we pass a user buffer here! 779 return get_toc(info, (scsi_toc *)buffer); 780 781 case B_EJECT_DEVICE: 782 case B_SCSI_EJECT: 783 return load_eject(info, false); 784 785 case B_LOAD_MEDIA: 786 return load_eject(info, true); 787 788 case B_SCSI_GET_POSITION: 789 { 790 if (buffer == NULL) 791 return B_BAD_VALUE; 792 793 scsi_position position; 794 status_t status = get_position(info, &position); 795 if (status != B_OK) 796 return status; 797 798 return user_memcpy(buffer, &position, sizeof(scsi_position)); 799 } 800 801 case B_SCSI_GET_VOLUME: 802 // TODO: we pass a user buffer here! 803 return get_set_volume(info, (scsi_volume *)buffer, false); 804 case B_SCSI_SET_VOLUME: 805 // TODO: we pass a user buffer here! 806 return get_set_volume(info, (scsi_volume *)buffer, true); 807 808 case B_SCSI_PLAY_TRACK: 809 { 810 scsi_play_track track; 811 if (user_memcpy(&track, buffer, sizeof(scsi_play_track)) != B_OK) 812 return B_BAD_ADDRESS; 813 814 return play_track_index(info, &track); 815 } 816 case B_SCSI_PLAY_POSITION: 817 { 818 scsi_play_position position; 819 if (user_memcpy(&position, buffer, sizeof(scsi_play_position)) 820 != B_OK) 821 return B_BAD_ADDRESS; 822 823 return play_msf(info, &position); 824 } 825 826 case B_SCSI_STOP_AUDIO: 827 return stop_audio(info); 828 case B_SCSI_PAUSE_AUDIO: 829 return pause_resume(info, false); 830 case B_SCSI_RESUME_AUDIO: 831 return pause_resume(info, true); 832 833 case B_SCSI_SCAN: 834 { 835 scsi_scan scanBuffer; 836 if (user_memcpy(&scanBuffer, buffer, sizeof(scsi_scan)) != B_OK) 837 return B_BAD_ADDRESS; 838 839 return scan(info, &scanBuffer); 840 } 841 case B_SCSI_READ_CD: 842 // TODO: we pass a user buffer here! 843 return read_cd(info, (scsi_read_cd *)buffer); 844 845 default: 846 return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op, 847 buffer, length); 848 } 849 } 850 851 852 // #pragma mark - scsi_periph callbacks 853 854 855 static void 856 cd_set_capacity(cd_driver_info* info, uint64 capacity, uint32 blockSize) 857 { 858 TRACE("cd_set_capacity(info = %p, capacity = %Ld, blockSize = %ld)\n", 859 info, capacity, blockSize); 860 861 // get log2, if possible 862 uint32 blockShift = log2(blockSize); 863 864 if ((1UL << blockShift) != blockSize) 865 blockShift = 0; 866 867 info->capacity = capacity; 868 869 if (info->block_size != blockSize) { 870 if (capacity == 0) { 871 // there is obviously no medium in the drive, don't try to update 872 // the DMA resource 873 return; 874 } 875 876 if (info->block_size != 0) { 877 dprintf("old %ld, new %ld\n", info->block_size, blockSize); 878 panic("updating DMAResource not yet implemented..."); 879 } 880 881 // TODO: we need to replace the DMAResource in our IOScheduler 882 status_t status = info->dma_resource->Init(info->node, blockSize, 1024, 883 32); 884 if (status != B_OK) 885 panic("initializing DMAResource failed: %s", strerror(status)); 886 887 info->io_scheduler = new(std::nothrow) IOScheduler(info->dma_resource); 888 if (info->io_scheduler == NULL) 889 panic("allocating IOScheduler failed."); 890 891 // TODO: use whole device name here 892 status = info->io_scheduler->Init("scsi"); 893 if (status != B_OK) 894 panic("initializing IOScheduler failed: %s", strerror(status)); 895 896 info->io_scheduler->SetCallback(do_io, info); 897 } 898 899 info->block_size = blockSize; 900 } 901 902 903 static void 904 cd_media_changed(cd_driver_info* info, scsi_ccb* request) 905 { 906 // do a capacity check 907 // TBD: is this a good idea (e.g. if this is an empty CD)? 908 sSCSIPeripheral->check_capacity(info->scsi_periph_device, request); 909 } 910 911 912 scsi_periph_callbacks callbacks = { 913 (void (*)(periph_device_cookie, uint64, uint32))cd_set_capacity, 914 (void (*)(periph_device_cookie, scsi_ccb *))cd_media_changed 915 }; 916 917 918 // #pragma mark - driver module API 919 920 921 static float 922 cd_supports_device(device_node* parent) 923 { 924 const char* bus; 925 uint8 deviceType; 926 927 // make sure parent is really the SCSI bus manager 928 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 929 return -1; 930 931 if (strcmp(bus, "scsi")) 932 return 0.0; 933 934 // check whether it's really a CD-ROM or WORM 935 if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM, 936 &deviceType, true) != B_OK 937 || (deviceType != scsi_dev_CDROM && deviceType != scsi_dev_WORM)) 938 return 0.0; 939 940 return 0.6; 941 } 942 943 944 /*! Called whenever a new device was added to system; 945 if we really support it, we create a new node that gets 946 server by the block_io module 947 */ 948 static status_t 949 cd_register_device(device_node* node) 950 { 951 const scsi_res_inquiry* deviceInquiry = NULL; 952 size_t inquiryLength; 953 uint32 maxBlocks; 954 955 // get inquiry data 956 if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM, 957 (const void**)&deviceInquiry, &inquiryLength, true) != B_OK 958 || inquiryLength < sizeof(deviceInquiry)) 959 return B_ERROR; 960 961 // get block limit of underlying hardware to lower it (if necessary) 962 if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS, 963 &maxBlocks, true) != B_OK) 964 maxBlocks = INT_MAX; 965 966 // using 10 byte commands, at most 0xffff blocks can be transmitted at once 967 // (sadly, we cannot update this value later on if only 6 byte commands 968 // are supported, but the block_io module can live with that) 969 maxBlocks = min_c(maxBlocks, 0xffff); 970 971 // ready to register 972 device_attr attrs[] = { 973 {"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}}, 974 {B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {ui32: maxBlocks}}, 975 { NULL } 976 }; 977 978 return sDeviceManager->register_node(node, SCSI_CD_DRIVER_MODULE_NAME, 979 attrs, NULL, NULL); 980 } 981 982 983 static status_t 984 cd_init_driver(device_node* node, void** _cookie) 985 { 986 TRACE("cd_init_driver"); 987 988 uint8 removable; 989 status_t status = sDeviceManager->get_attr_uint8(node, "removable", 990 &removable, false); 991 if (status != B_OK) 992 return status; 993 994 cd_driver_info* info = (cd_driver_info*)malloc(sizeof(cd_driver_info)); 995 if (info == NULL) 996 return B_NO_MEMORY; 997 998 memset(info, 0, sizeof(cd_driver_info)); 999 1000 info->dma_resource = new(std::nothrow) DMAResource; 1001 if (info->dma_resource == NULL) { 1002 free(info); 1003 return B_NO_MEMORY; 1004 } 1005 1006 info->node = node; 1007 info->removable = removable; 1008 1009 // set capacity to zero, so it get checked on first opened handle 1010 info->capacity = 0; 1011 info->block_size = 0; 1012 1013 sDeviceManager->get_attr_uint8(node, SCSI_DEVICE_TYPE_ITEM, 1014 &info->device_type, true); 1015 1016 device_node *parent = sDeviceManager->get_parent_node(node); 1017 sDeviceManager->get_driver(parent, (driver_module_info**)&info->scsi, 1018 (void**)&info->scsi_device); 1019 sDeviceManager->put_node(parent); 1020 1021 status = sSCSIPeripheral->register_device((periph_device_cookie)info, 1022 &callbacks, info->scsi_device, info->scsi, info->node, 1023 info->removable, 10, &info->scsi_periph_device); 1024 if (status != B_OK) { 1025 free(info); 1026 return status; 1027 } 1028 1029 *_cookie = info; 1030 return B_OK; 1031 } 1032 1033 1034 static void 1035 cd_uninit_driver(void* _cookie) 1036 { 1037 cd_driver_info* info = (cd_driver_info*)_cookie; 1038 1039 sSCSIPeripheral->unregister_device(info->scsi_periph_device); 1040 free(info); 1041 } 1042 1043 1044 static status_t 1045 cd_register_child_devices(void* _cookie) 1046 { 1047 cd_driver_info* info = (cd_driver_info*)_cookie; 1048 1049 char* name = sSCSIPeripheral->compose_device_name(info->node, "disk/scsi"); 1050 if (name == NULL) 1051 return B_ERROR; 1052 1053 status_t status = sDeviceManager->publish_device(info->node, name, 1054 SCSI_CD_DEVICE_MODULE_NAME); 1055 1056 free(name); 1057 return status; 1058 } 1059 1060 1061 module_dependency module_dependencies[] = { 1062 {SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral}, 1063 {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager}, 1064 {} 1065 }; 1066 1067 struct device_module_info sSCSICDDevice = { 1068 { 1069 SCSI_CD_DEVICE_MODULE_NAME, 1070 0, 1071 NULL 1072 }, 1073 1074 cd_init_device, 1075 cd_uninit_device, 1076 NULL, // remove, 1077 1078 cd_open, 1079 cd_close, 1080 cd_free, 1081 cd_read, 1082 cd_write, 1083 cd_io, 1084 cd_ioctl, 1085 1086 NULL, // select 1087 NULL, // deselect 1088 }; 1089 1090 struct driver_module_info sSCSICDDriver = { 1091 { 1092 SCSI_CD_DRIVER_MODULE_NAME, 1093 0, 1094 NULL 1095 }, 1096 1097 cd_supports_device, 1098 cd_register_device, 1099 cd_init_driver, 1100 cd_uninit_driver, 1101 cd_register_child_devices, 1102 NULL, // rescan 1103 NULL, // removed 1104 }; 1105 1106 module_info* modules[] = { 1107 (module_info*)&sSCSICDDriver, 1108 (module_info*)&sSCSICDDevice, 1109 NULL 1110 }; 1111