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