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