1 /* 2 * libdi - CD Audio Device Interface Library 3 * 4 * Copyright (C) 1993-2004 Ti Kan 5 * E-mail: xmcd@amb.org 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 22 /* 23 * Panasonic/Matsushita vendor-qunie support 24 * 25 * The name "Panasonic" is a trademark of Matsushita Electric 26 * Corporation, and is used here for identification purposes only. 27 */ 28 #ifndef lint 29 static char *_vu_pana_c_ident_ = "@(#)vu_pana.c 6.42 04/01/14"; 30 #endif 31 32 #include "common_d/appenv.h" 33 #include "common_d/util.h" 34 #include "libdi_d/libdi.h" 35 #include "libdi_d/scsipt.h" 36 37 #ifdef VENDOR_PANASONIC 38 39 40 #define MAX_NOTVAL 5 /* Max invalid status */ 41 42 extern appdata_t app_data; 43 extern di_client_t *di_clinfo; 44 extern vu_tbl_t scsipt_vutbl[]; 45 extern byte_t cdb[]; 46 extern di_dev_t *devp; 47 48 STATIC byte_t pana_route_left, /* left ch routing control */ 49 pana_route_right; /* Right ch routing control */ 50 51 52 /* 53 * pana_route_val 54 * Return the channel routing control value used in the 55 * Panasonic mode parameter page 0x2E (audio parameters). 56 * 57 * Args: 58 * route_mode - The channel routing mode value. 59 * channel - The channel number desired (0=left 1=right). 60 * 61 * Return: 62 * The routing control value. 63 */ 64 STATIC byte_t 65 pana_route_val(int route_mode, int channel) 66 { 67 /* Panasonic: note that the port control bits are reversed 68 * compared to the SCSI-2 audio page 0xe. 69 */ 70 71 switch (channel) { 72 case 0: 73 switch (route_mode) { 74 case 0: 75 return 0x2; 76 case 1: 77 return 0x1; 78 case 2: 79 return 0x2; 80 case 3: 81 return 0x1; 82 case 4: 83 return 0x3; 84 default: 85 /* Invalid value */ 86 return 0x0; 87 } 88 /*NOTREACHED*/ 89 90 case 1: 91 switch (route_mode) { 92 case 0: 93 return 0x1; 94 case 1: 95 return 0x2; 96 case 2: 97 return 0x2; 98 case 3: 99 return 0x1; 100 case 4: 101 return 0x3; 102 default: 103 /* Invalid value */ 104 return 0x0; 105 } 106 /*NOTREACHED*/ 107 108 default: 109 /* Invalid value */ 110 return 0x0; 111 } 112 } 113 114 115 /* 116 * pana_playaudio 117 * Play audio function: send vendor-unique play audio command 118 * to the drive. 119 * 120 * Args: 121 * addr_fmt - Flags indicating which address formats are passed in 122 * If ADDR_BLK, then: 123 * start_addr - The logical block starting address 124 * end_addr - The logical block ending address 125 * If ADD_MSF, then: 126 * start_msf - Pointer to the starting MSF address structure 127 * end_msf - Pointer to the ending MSF address structure 128 * If ADDR_TRKIDX, then: 129 * trk - The starting track number 130 * idx - The starting index number 131 * If ADDR_OPTEND, then the ending address, if specified, can be 132 * ignored if possible. 133 * 134 * Return: 135 * TRUE - success 136 * FALSE - failure 137 */ 138 /*ARGSUSED*/ 139 bool_t 140 pana_playaudio( 141 byte_t addr_fmt, 142 sword32_t start_addr, 143 sword32_t end_addr, 144 msf_t *start_msf, 145 msf_t *end_msf, 146 byte_t trk, 147 byte_t idx 148 ) 149 { 150 msf_t istart_msf, 151 iend_msf; 152 bool_t ret = FALSE; 153 154 if (!ret && (addr_fmt & ADDR_BLK) && !(addr_fmt & ADDR_MSF)) { 155 /* Convert block address to MSF format */ 156 util_blktomsf( 157 start_addr, 158 &istart_msf.min, &istart_msf.sec, &istart_msf.frame, 159 MSF_OFFSET 160 ); 161 162 util_blktomsf( 163 end_addr, 164 &iend_msf.min, &iend_msf.sec, &iend_msf.frame, 165 MSF_OFFSET 166 ); 167 168 /* Let the ADDR_MSF code handle the request */ 169 start_msf = &istart_msf; 170 end_msf = &iend_msf; 171 addr_fmt |= ADDR_MSF; 172 ret = FALSE; 173 } 174 175 if (!ret && addr_fmt & ADDR_MSF) { 176 SCSICDB_RESET(cdb); 177 cdb[0] = OP_VM_PLAYMSF; 178 cdb[3] = start_msf->min; 179 cdb[4] = start_msf->sec; 180 cdb[5] = start_msf->frame; 181 cdb[6] = end_msf->min; 182 cdb[7] = end_msf->sec; 183 cdb[8] = end_msf->frame; 184 185 ret = pthru_send(devp, DI_ROLE_MAIN, cdb, 10, NULL, 0, NULL, 0, 186 OP_NODATA, 10, TRUE); 187 } 188 189 return (ret); 190 } 191 192 193 /* 194 * pana_pause_resume 195 * Pause/resume function: send vendor-unique commands to implement 196 * the pause and resume capability. 197 * 198 * Args: 199 * resume - TRUE: resume, FALSE: pause 200 * 201 * Return: 202 * TRUE - success 203 * FALSE - failure 204 */ 205 bool_t 206 pana_pause_resume(bool_t resume) 207 { 208 SCSICDB_RESET(cdb); 209 cdb[0] = OP_VM_PAUSE; 210 cdb[8] = resume ? 0x01 : 0x00; 211 212 return (pthru_send(devp, DI_ROLE_MAIN, cdb, 10, NULL, 0, NULL, 0, 213 OP_NODATA, 5, TRUE)); 214 } 215 216 217 /* 218 * pana_get_playstatus 219 * Send vendor-unique command to obtain current audio playback 220 * status. 221 * 222 * Args: 223 * sp - Pointer to the caller-supplied cdstat_t return structure 224 * 225 * Return: 226 * TRUE - success 227 * FALSE - failure 228 */ 229 bool_t 230 pana_get_playstatus(cdstat_t *sp) 231 { 232 subq_hdr_t *h; 233 subq_01_t *p; 234 byte_t dbuf[SZ_VM_RDSUBQ], 235 status; 236 static int notvalid_cnt = 0; 237 static byte_t prev_stat = AUDIO_NOTVALID; 238 239 /* 240 * Send Panasonic Read Subchannel command 241 */ 242 243 (void) memset(dbuf, 0, sizeof(dbuf)); 244 245 SCSICDB_RESET(cdb); 246 cdb[0] = OP_VM_RDSUBQ; 247 cdb[1] = 0x02; 248 cdb[2] = 0x40; 249 cdb[3] = 0x01; 250 cdb[6] = 0x00; 251 cdb[7] = (SZ_VM_RDSUBQ & 0xff00) >> 8; 252 cdb[8] = SZ_VM_RDSUBQ & 0x00ff; 253 254 if (!pthru_send(devp, DI_ROLE_MAIN, cdb, 10, dbuf, SZ_VM_RDSUBQ, 255 NULL, 0, OP_DATAIN, 5, TRUE)) 256 return FALSE; 257 258 DBGDUMP(DBG_DEVIO)("pana: Read Subchannel data bytes", 259 dbuf, SZ_VM_RDSUBQ); 260 261 h = (subq_hdr_t *)(void *) dbuf; 262 p = (subq_01_t *)(void *) (dbuf + sizeof(subq_hdr_t)); 263 264 /* Hack: The Panasonic drive seems to spuriously return 265 * AUDIO_NOTVALID status during playback for no apparent 266 * reason. Work around this. 267 */ 268 if (h->audio_status == AUDIO_NOTVALID && notvalid_cnt < MAX_NOTVAL) { 269 /* Allow up to MAX_NOTVAL consecutive occurances 270 * of this anomaly. 271 */ 272 status = prev_stat; 273 if (prev_stat != AUDIO_NOTVALID) 274 notvalid_cnt++; 275 } 276 else { 277 status = prev_stat = h->audio_status; 278 notvalid_cnt = 0; 279 } 280 281 switch (status) { 282 case AUDIO_PLAYING: 283 sp->status = CDSTAT_PLAYING; 284 break; 285 case AUDIO_PAUSED: 286 sp->status = CDSTAT_PAUSED; 287 break; 288 case AUDIO_COMPLETED: 289 sp->status = CDSTAT_COMPLETED; 290 break; 291 case AUDIO_FAILED: 292 sp->status = CDSTAT_FAILED; 293 break; 294 case AUDIO_NOTVALID: 295 case AUDIO_NOSTATUS: 296 default: 297 sp->status = CDSTAT_NOSTATUS; 298 break; 299 } 300 301 sp->track = (int) p->trkno; 302 sp->index = (int) p->idxno; 303 304 sp->abs_addr.min = p->abs_addr.msf.min; 305 sp->abs_addr.sec = p->abs_addr.msf.sec; 306 sp->abs_addr.frame = p->abs_addr.msf.frame; 307 util_msftoblk( 308 sp->abs_addr.min, sp->abs_addr.sec, sp->abs_addr.frame, 309 &sp->abs_addr.addr, MSF_OFFSET 310 ); 311 312 sp->rel_addr.min = p->rel_addr.msf.min; 313 sp->rel_addr.sec = p->rel_addr.msf.sec; 314 sp->rel_addr.frame = p->rel_addr.msf.frame; 315 util_msftoblk( 316 sp->rel_addr.min, sp->rel_addr.sec, sp->rel_addr.frame, 317 &sp->rel_addr.addr, 0 318 ); 319 320 return TRUE; 321 } 322 323 324 /* 325 * pana_get_toc 326 * Send vendor-unique command to obtain the disc table-of-contents 327 * 328 * Args: 329 * s - Pointer to the curstat_t structure, which contains the TOC 330 * table to be updated. 331 * 332 * Return: 333 * TRUE - success 334 * FALSE - failure 335 */ 336 bool_t 337 pana_get_toc(curstat_t *s) 338 { 339 int i, 340 j, 341 xfer_len; 342 toc_hdr_t *h; 343 toc_trk_descr_t *p; 344 byte_t *cp, 345 buf[SZ_VM_RDTOC]; 346 347 (void) memset(buf, 0, sizeof(buf)); 348 349 /* Read TOC header to find the number of tracks */ 350 SCSICDB_RESET(cdb); 351 cdb[0] = OP_VM_RDTOC; 352 cdb[1] = 0x02; 353 cdb[8] = SZ_VM_TOCHDR; 354 355 if (!pthru_send(devp, DI_ROLE_MAIN, cdb, 10, buf, SZ_VM_TOCHDR, 356 NULL, 0, OP_DATAIN, 5, TRUE)) 357 return FALSE; 358 359 h = (toc_hdr_t *)(void *) buf; 360 361 s->first_trk = (byte_t) h->first_trk; 362 s->last_trk = (byte_t) h->last_trk; 363 364 xfer_len = SZ_VM_TOCHDR + 365 ((int) (h->last_trk - h->first_trk + 2) * SZ_VM_TOCENT); 366 367 if (xfer_len > SZ_VM_RDTOC) 368 xfer_len = SZ_VM_RDTOC; 369 370 SCSICDB_RESET(cdb); 371 cdb[0] = OP_VM_RDTOC; 372 cdb[1] = 0x02; 373 cdb[7] = (xfer_len & 0xff00) >> 8; 374 cdb[8] = xfer_len & 0x00ff; 375 376 if (!pthru_send(devp, DI_ROLE_MAIN, cdb, 10, buf, xfer_len, NULL, 0, 377 OP_DATAIN, 5, TRUE)) 378 return FALSE; 379 380 DBGDUMP(DBG_DEVIO)("pana: Read TOC data bytes", buf, xfer_len); 381 382 /* Get the starting position of each track */ 383 cp = buf + sizeof(toc_hdr_t); 384 for (i = 0, j = (int) s->first_trk; j <= (int) s->last_trk; i++, j++) { 385 p = (toc_trk_descr_t *)(void *) (cp + (i * SZ_VM_TOCENT)); 386 s->trkinfo[i].trkno = (byte_t) p->trkno; 387 s->trkinfo[i].min = (byte_t) p->abs_addr.msf.min; 388 s->trkinfo[i].sec = (byte_t) p->abs_addr.msf.sec; 389 s->trkinfo[i].frame = (byte_t) p->abs_addr.msf.frame; 390 util_msftoblk( 391 s->trkinfo[i].min, 392 s->trkinfo[i].sec, 393 s->trkinfo[i].frame, 394 &s->trkinfo[i].addr, 395 MSF_OFFSET 396 ); 397 s->trkinfo[i].type = (p->trktype == 0) ? TYP_AUDIO : TYP_DATA; 398 } 399 s->tot_trks = (byte_t) i; 400 401 /* Get the lead-out track position */ 402 p = (toc_trk_descr_t *)(void *) (cp + (i * SZ_VM_TOCENT)); 403 s->trkinfo[i].trkno = LEAD_OUT_TRACK; 404 s->discpos_tot.min = s->trkinfo[i].min = (byte_t) p->abs_addr.msf.min; 405 s->discpos_tot.sec = s->trkinfo[i].sec = (byte_t) p->abs_addr.msf.sec; 406 s->discpos_tot.frame = s->trkinfo[i].frame = 407 (byte_t) p->abs_addr.msf.frame; 408 util_msftoblk( 409 s->trkinfo[i].min, 410 s->trkinfo[i].sec, 411 s->trkinfo[i].frame, 412 &s->trkinfo[i].addr, 413 MSF_OFFSET 414 ); 415 s->discpos_tot.addr = s->trkinfo[i].addr; 416 return TRUE; 417 } 418 419 420 /* 421 * pana_volume 422 * Send vendor-unique command to query/control the playback volume. 423 * 424 * Args: 425 * vol - Volume level to set to 426 * s - Pointer to the curstat_t structure 427 * query - This call is to query the current volume setting rather 428 * than to set it. 429 * 430 * Return: 431 * The current volume value. 432 */ 433 int 434 pana_volume(int vol, curstat_t *s, bool_t query) 435 { 436 int vol1, 437 vol2; 438 mode_sense_6_data_t *ms_data; 439 blk_desc_t *bdesc; 440 audio_pg_t *audiopg; 441 byte_t buf[SZ_MSENSE]; 442 443 if (!app_data.mselvol_supp) 444 return -1; 445 446 (void) memset(buf, 0, SZ_MSENSE); 447 448 if (!scsipt_modesense(devp, DI_ROLE_MAIN, buf, 0, 449 PG_VM_AUDCTL, SZ_VM_AUDCTL)) 450 return -1; 451 452 ms_data = (mode_sense_6_data_t *)(void *) buf; 453 bdesc = (blk_desc_t *)(void *) ms_data->data; 454 audiopg = (audio_pg_t *)(void *) &ms_data->data[ms_data->bdescr_len]; 455 456 if (audiopg->pg_code == PG_VM_AUDCTL) { 457 if (query) { 458 vol1 = util_untaper_vol( 459 util_unscale_vol((int) audiopg->p0_vol) 460 ); 461 vol2 = util_untaper_vol( 462 util_unscale_vol((int) audiopg->p1_vol) 463 ); 464 pana_route_left = (byte_t) audiopg->p0_ch_ctrl; 465 pana_route_right = (byte_t) audiopg->p1_ch_ctrl; 466 467 if (vol1 == vol2) { 468 s->level_left = s->level_right = 100; 469 vol = vol1; 470 } 471 else if (vol1 > vol2) { 472 s->level_left = 100; 473 s->level_right = (byte_t)((vol2 * 100) / vol1); 474 vol = vol1; 475 } 476 else { 477 s->level_left = (byte_t) ((vol1 * 100) / vol2); 478 s->level_right = 100; 479 vol = vol2; 480 } 481 482 return (vol); 483 } 484 else { 485 ms_data->data_len = 0; 486 if (ms_data->bdescr_len > 0) 487 bdesc->num_blks = 0; 488 489 audiopg->p0_vol = util_scale_vol( 490 util_taper_vol(vol * (int) s->level_left / 100) 491 ); 492 audiopg->p1_vol = util_scale_vol( 493 util_taper_vol(vol * (int) s->level_right / 100) 494 ); 495 496 audiopg->p0_ch_ctrl = pana_route_left; 497 audiopg->p1_ch_ctrl = pana_route_right; 498 499 audiopg->sotc = 0; 500 audiopg->immed = 0; /* Panasonic: reserved */ 501 502 if (scsipt_modesel(devp, DI_ROLE_MAIN, buf, 503 PG_VM_AUDCTL, SZ_VM_AUDCTL)) { 504 /* Success */ 505 return (vol); 506 } 507 else if (audiopg->p0_vol != audiopg->p1_vol) { 508 /* Set the balance to the center 509 * and retry. 510 */ 511 audiopg->p0_vol = audiopg->p1_vol = 512 util_scale_vol(util_taper_vol(vol)); 513 514 if (scsipt_modesel(devp, DI_ROLE_MAIN, 515 buf, PG_VM_AUDCTL, 516 SZ_VM_AUDCTL)) { 517 /* Success: Warp balance control */ 518 s->level_left = s->level_right = 100; 519 SET_BAL_SLIDER(0); 520 521 return (vol); 522 } 523 524 /* Still failed: just drop through */ 525 } 526 } 527 } 528 529 return -1; 530 } 531 532 533 /* 534 * pana_route 535 * Configure channel routing via Sony Vendor-unique commands. 536 * 537 * Args: 538 * s - Pointer to the curstat_t structure 539 * 540 * Return: 541 * TRUE - success 542 * FALSE - failure 543 */ 544 bool_t 545 pana_route(curstat_t *s) 546 { 547 byte_t val0, 548 val1; 549 550 if (!app_data.chroute_supp) 551 return FALSE; 552 553 val0 = pana_route_val(app_data.ch_route, 0); 554 val1 = pana_route_val(app_data.ch_route, 1); 555 556 if (val0 == pana_route_left && val1 == pana_route_right) 557 /* No change: just return */ 558 return TRUE; 559 560 pana_route_left = val0; 561 pana_route_right = val1; 562 563 /* Panasonic channel routing is done with the volume control */ 564 (void) pana_volume(s->level, s, FALSE); 565 566 return TRUE; 567 } 568 569 570 /* 571 * pana_init 572 * Initialize the vendor-unique support module 573 * 574 * Args: 575 * Nothing. 576 * 577 * Return: 578 * Nothing. 579 */ 580 void 581 pana_init(void) 582 { 583 /* Register vendor_unique module entry points */ 584 scsipt_vutbl[VENDOR_PANASONIC].vendor = "Panasonic"; 585 scsipt_vutbl[VENDOR_PANASONIC].playaudio = pana_playaudio; 586 scsipt_vutbl[VENDOR_PANASONIC].pause_resume = pana_pause_resume; 587 scsipt_vutbl[VENDOR_PANASONIC].start_stop = NULL; 588 scsipt_vutbl[VENDOR_PANASONIC].get_playstatus = pana_get_playstatus; 589 scsipt_vutbl[VENDOR_PANASONIC].volume = pana_volume; 590 scsipt_vutbl[VENDOR_PANASONIC].route = pana_route; 591 scsipt_vutbl[VENDOR_PANASONIC].mute = NULL; 592 scsipt_vutbl[VENDOR_PANASONIC].get_toc = pana_get_toc; 593 scsipt_vutbl[VENDOR_PANASONIC].eject = NULL; 594 scsipt_vutbl[VENDOR_PANASONIC].start = NULL; 595 scsipt_vutbl[VENDOR_PANASONIC].halt = NULL; 596 } 597 598 599 #endif /* VENDOR_PANASONIC */ 600 601