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  *   Chinon CDx-431 and CDx-435 support
24  *
25  *   The name "Chinon" is a trademark of Chinon Industries, and is
26  *   used here for identification purposes only.
27  */
28 #ifndef lint
29 static char *_vu_chin_c_ident_ = "@(#)vu_chin.c	6.46 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_CHINON
38 
39 extern appdata_t	app_data;
40 extern di_client_t	*di_clinfo;
41 extern vu_tbl_t		scsipt_vutbl[];
42 extern byte_t		cdb[];
43 extern di_dev_t		*devp;
44 
45 STATIC bool_t		chin_bcd_hack;
46 
47 
48 /*
49  * chin_playaudio
50  *	Play audio function: send vendor-unique play audio command
51  *	to the drive.
52  *
53  * Args:
54  *	addr_fmt - Flags indicating which address formats are passed in
55  *	If ADDR_BLK, then:
56  *	    start_addr - The logical block starting address
57  *	    end_addr - The logical block ending address
58  *	If ADD_MSF, then:
59  *	    start_msf - Pointer to the starting MSF address structure
60  *	    end_msf - Pointer to the ending MSF address structure
61  *	If ADDR_TRKIDX, then:
62  *	    trk - The starting track number
63  *	    idx - The starting index number
64  *	If ADDR_OPTEND, then the ending address, if specified, can be
65  *	ignored if possible.
66  *
67  * Return:
68  *	TRUE - success
69  *	FALSE - failure
70  */
71 /*ARGSUSED*/
72 bool_t
chin_playaudio(byte_t addr_fmt,sword32_t start_addr,sword32_t end_addr,msf_t * start_msf,msf_t * end_msf,byte_t trk,byte_t idx)73 chin_playaudio(
74 	byte_t		addr_fmt,
75 	sword32_t	start_addr,
76 	sword32_t	end_addr,
77 	msf_t		*start_msf,
78 	msf_t		*end_msf,
79 	byte_t		trk,
80 	byte_t		idx
81 )
82 {
83 	msf_t		istart_msf,
84 			iend_msf;
85 	bool_t		ret = FALSE;
86 
87 	/* Chinon only supports the Play Audio MSF command on the
88 	 * CDx-43x, which is identical to the SCSI-2 command of
89 	 * the same name and opcode.
90 	 */
91 
92 	if (!ret && (addr_fmt & ADDR_BLK) && !(addr_fmt & ADDR_MSF)) {
93 		/* Convert block address to MSF format */
94 		util_blktomsf(
95 			start_addr,
96 			&istart_msf.min, &istart_msf.sec, &istart_msf.frame,
97 			MSF_OFFSET
98 		);
99 
100 		util_blktomsf(
101 			end_addr,
102 			&iend_msf.min, &iend_msf.sec, &iend_msf.frame,
103 			MSF_OFFSET
104 		);
105 
106 		/* Let the ADDR_MSF code handle the request */
107 		start_msf = &istart_msf;
108 		end_msf = &iend_msf;
109 		addr_fmt |= ADDR_MSF;
110 		ret = FALSE;
111 	}
112 
113 	if (!ret && (addr_fmt & ADDR_MSF))
114 		ret = scsipt_playmsf(devp, DI_ROLE_MAIN, start_msf, end_msf);
115 
116 	return (ret);
117 }
118 
119 
120 /*
121  * chin_start_stop
122  *      Start/stop function.
123  *
124  * Args:
125  *	start - TRUE: start unit, FALSE: stop unit
126  *	loej - TRUE: eject caddy, FALSE: do not eject
127  *
128  * Return:
129  *	TRUE - success
130  *	FALSE - failure
131  */
132 bool_t
chin_start_stop(bool_t start,bool_t loej)133 chin_start_stop(bool_t start, bool_t loej)
134 {
135 	bool_t	ret;
136 
137 	if (start)
138 		/* Chinon CDx-43x does not support a start command so
139 		 * just quietly return success.
140 		 */
141 		return TRUE;
142 
143 	/* Stop the playback */
144 	SCSICDB_RESET(cdb);
145 	cdb[0] = OP_VC_STOP;
146 
147 	ret = pthru_send(devp, DI_ROLE_MAIN, cdb, 10, NULL, 0, NULL, 0,
148 			 OP_NODATA, 10, TRUE);
149 
150 	/* Eject the caddy if necessary */
151 	if (ret && loej) {
152 		SCSICDB_RESET(cdb);
153 		cdb[0] = OP_VC_EJECT;
154 
155 		ret = pthru_send(devp, DI_ROLE_MAIN, cdb, 10, NULL, 0, NULL, 0,
156 				 OP_NODATA, 20, TRUE);
157 	}
158 
159 	return (ret);
160 }
161 
162 
163 /*
164  * chin_get_playstatus
165  *	Send vendor-unique command to obtain current audio playback
166  *	status.
167  *
168  * Args:
169  *	sp - Pointer to the caller-supplied cdstat_t return structure.
170  *
171  * Return:
172  *	TRUE - success
173  *	FALSE - failure
174  */
175 bool_t
chin_get_playstatus(cdstat_t * sp)176 chin_get_playstatus(cdstat_t *sp)
177 {
178 	/* Chinon CDx-43x supports the Read Subchannel command which
179 	 * is identical to the SCSI-2 command of the same name and
180 	 * opcode, but the audio status codes are different.
181 	 */
182 	byte_t		buf[SZ_RDSUBQ],
183 			*cp;
184 	subq_hdr_t	*h;
185 	subq_01_t	*p;
186 	size_t		xfer_len;
187 	bool_t		ret;
188 
189 
190 	xfer_len = 48;
191 	(void) memset(buf, 0, sizeof(buf));
192 
193 	SCSICDB_RESET(cdb);
194 	cdb[0] = OP_M_RDSUBQ;
195 	cdb[1] = app_data.subq_lba ? 0x00 : 0x02;
196 	cdb[2] = 0x40;
197 	cdb[3] = SUB_ALL;
198 	cdb[6] = 0;
199 	cdb[7] = (xfer_len & 0xff00) >> 8;
200 	cdb[8] = xfer_len & 0x00ff;
201 
202 	if ((ret = pthru_send(devp, DI_ROLE_MAIN, cdb, 10, buf, xfer_len,
203 			      NULL, 0, OP_DATAIN, 5, TRUE)) == FALSE)
204 		return FALSE;
205 
206 	DBGDUMP(DBG_DEVIO)("Read Subchannel data", buf, xfer_len);
207 
208 	h = (subq_hdr_t *)(void *) buf;
209 
210 	/* Check the subchannel data */
211 	cp = (byte_t *) h + sizeof(subq_hdr_t);
212 	switch (*cp) {
213 	case SUB_ALL:
214 	case SUB_CURPOS:
215 		p = (subq_01_t *)(void *) cp;
216 
217 		/* Hack: to work around firmware anomalies
218 		 * in some CD-ROM drives.
219 		 */
220 		if (p->trkno >= MAXTRACK &&
221 		    p->trkno != LEAD_OUT_TRACK) {
222 			sp->status = CDSTAT_NOSTATUS;
223 			break;
224 		}
225 
226 		/* Map Chinon status into cdstat_t */
227 
228 		switch (h->audio_status) {
229 		case CAUD_PLAYING:
230 			sp->status = CDSTAT_PLAYING;
231 			break;
232 		case CAUD_PAUSED:
233 			sp->status = CDSTAT_PAUSED;
234 			break;
235 		case CAUD_INVALID:
236 		default:
237 			sp->status = CDSTAT_NOSTATUS;
238 			break;
239 		}
240 
241 		if (chin_bcd_hack) {
242 			/* Hack: BUGLY CD-ROM firmware */
243 			sp->track = (int) util_bcdtol(p->trkno);
244 			sp->index = (int) util_bcdtol(p->idxno);
245 		}
246 		else {
247 			sp->track = (int) p->trkno;
248 			sp->index = (int) p->idxno;
249 		}
250 
251 		if (app_data.subq_lba) {
252 			/* LBA mode */
253 			sp->abs_addr.addr = util_xlate_blk((sword32_t)
254 				util_bswap32(p->abs_addr.logical)
255 			);
256 			util_blktomsf(
257 				sp->abs_addr.addr,
258 				&sp->abs_addr.min,
259 				&sp->abs_addr.sec,
260 				&sp->abs_addr.frame,
261 				MSF_OFFSET
262 			);
263 
264 			sp->rel_addr.addr = util_xlate_blk((sword32_t)
265 				util_bswap32(p->rel_addr.logical)
266 			);
267 			util_blktomsf(
268 				sp->rel_addr.addr,
269 				&sp->rel_addr.min,
270 				&sp->rel_addr.sec,
271 				&sp->rel_addr.frame,
272 				0
273 			);
274 		}
275 		else {
276 			/* MSF mode */
277 			sp->abs_addr.min = p->abs_addr.msf.min;
278 			sp->abs_addr.sec = p->abs_addr.msf.sec;
279 			sp->abs_addr.frame = p->abs_addr.msf.frame;
280 			util_msftoblk(
281 				sp->abs_addr.min,
282 				sp->abs_addr.sec,
283 				sp->abs_addr.frame,
284 				&sp->abs_addr.addr,
285 				MSF_OFFSET
286 			);
287 
288 			sp->rel_addr.min = p->rel_addr.msf.min;
289 			sp->rel_addr.sec = p->rel_addr.msf.sec;
290 			sp->rel_addr.frame = p->rel_addr.msf.frame;
291 			util_msftoblk(
292 				sp->rel_addr.min,
293 				sp->rel_addr.sec,
294 				sp->rel_addr.frame,
295 				&sp->rel_addr.addr,
296 				0
297 			);
298 		}
299 		break;
300 
301 	default:
302 		/* Something is wrong with the data */
303 		ret = FALSE;
304 		break;
305 	}
306 
307 	return (ret);
308 }
309 
310 
311 /*
312  * chin_get_toc
313  *	Send vendor-unique command to obtain the disc table-of-contents
314  *
315  * Args:
316  *	s - Pointer to the curstat_t structure, which contains the TOC
317  *	    table to be updated.
318  *
319  * Return:
320  *	TRUE - success
321  *	FALSE - failure
322  */
323 bool_t
chin_get_toc(curstat_t * s)324 chin_get_toc(curstat_t *s)
325 {
326 	int			i,
327 				ntrks;
328 	byte_t			buf[SZ_RDTOC],
329 				*cp,
330 				*toc_end;
331 	toc_hdr_t		*h;
332 	toc_trk_descr_t		*p;
333 
334 
335 	/* Chinon CDx-43x supports the Read TOC command which is
336 	 * identical to the SCSI-2 command of the same name and opcode.
337 	 */
338 
339 	(void) memset(buf, 0, sizeof(buf));
340 
341 	if (!scsipt_rdtoc(devp, DI_ROLE_MAIN, buf, sizeof(buf), 0, 0,
342 			  (bool_t) !app_data.toc_lba, TRUE))
343 		return FALSE;
344 
345 	/* Fill curstat structure with TOC data */
346 	h = (toc_hdr_t *)(void *) buf;
347 	toc_end = (byte_t *) h + util_bswap16(h->data_len) + 2;
348 
349 	s->first_trk = h->first_trk;
350 	s->last_trk = h->last_trk;
351 
352 	ntrks = (int) (h->last_trk - h->first_trk) + 1;
353 
354 	/* Hack: workaround CD-ROM firmware bug
355 	 * Some Chinon drives return track numbers in BCD
356 	 * rather than binary.
357 	 */
358 	cp = (byte_t *) h + sizeof(toc_hdr_t);
359 
360 	for (i = 0; i <= ntrks; i++) {
361 		int	trk0,
362 			trk1;
363 
364 		p = (toc_trk_descr_t *)(void *) cp;
365 
366 		if (p->trkno == LEAD_OUT_TRACK && i != ntrks) {
367 			trk0 = util_bcdtol(h->first_trk);
368 			trk1 = util_bcdtol(h->last_trk);
369 
370 			if (i == (trk1 - trk0 + 1)) {
371 				/* BUGLY firmware detected! */
372 				chin_bcd_hack = TRUE;
373 				s->first_trk = (byte_t) trk0;
374 				s->last_trk = (byte_t) trk1;
375 				break;
376 			}
377 		}
378 	}
379 
380 	/*
381 	 * Fill in TOC data
382 	 */
383 	cp = (byte_t *) h + sizeof(toc_hdr_t);
384 
385 	for (i = 0; cp < toc_end && i < MAXTRACK; i++) {
386 		p = (toc_trk_descr_t *)(void *) cp;
387 
388 		if (chin_bcd_hack)
389 			/* Hack: BUGLY CD-ROM firmware */
390 			s->trkinfo[i].trkno = util_bcdtol(p->trkno);
391 		else
392 			s->trkinfo[i].trkno = p->trkno;
393 
394 		s->trkinfo[i].type = (p->trktype == 0) ?
395 			TYP_AUDIO : TYP_DATA;
396 
397 		if (app_data.toc_lba) {
398 			/* LBA mode */
399 			s->trkinfo[i].addr = util_xlate_blk((sword32_t)
400 				util_bswap32(p->abs_addr.logical)
401 			);
402 			util_blktomsf(
403 				s->trkinfo[i].addr,
404 				&s->trkinfo[i].min,
405 				&s->trkinfo[i].sec,
406 				&s->trkinfo[i].frame,
407 				MSF_OFFSET
408 			);
409 		}
410 		else {
411 			/* MSF mode */
412 			s->trkinfo[i].min = p->abs_addr.msf.min;
413 			s->trkinfo[i].sec = p->abs_addr.msf.sec;
414 			s->trkinfo[i].frame = p->abs_addr.msf.frame;
415 			util_msftoblk(
416 				s->trkinfo[i].min,
417 				s->trkinfo[i].sec,
418 				s->trkinfo[i].frame,
419 				&s->trkinfo[i].addr,
420 				MSF_OFFSET
421 			);
422 		}
423 
424 		if (p->trkno == LEAD_OUT_TRACK ||
425 		    s->trkinfo[i-1].trkno == s->last_trk ||
426 		    i == (MAXTRACK - 1)) {
427 			s->discpos_tot.min = s->trkinfo[i].min;
428 			s->discpos_tot.sec = s->trkinfo[i].sec;
429 			s->discpos_tot.frame = s->trkinfo[i].frame;
430 			s->tot_trks = (byte_t) i;
431 			s->discpos_tot.addr = s->trkinfo[i].addr;
432 
433 			break;
434 		}
435 
436 		cp += sizeof(toc_trk_descr_t);
437 	}
438 
439 	return TRUE;
440 }
441 
442 
443 /*
444  * chin_eject
445  *	Send vendor-unique command to eject the caddy
446  *
447  * Args:
448  *	Nothing.
449  *
450  * Return:
451  *	TRUE - success
452  *	FALSE - failure
453  */
454 bool_t
chin_eject(void)455 chin_eject(void)
456 {
457 	SCSICDB_RESET(cdb);
458 	cdb[0] = OP_VC_EJECT;
459 
460 	return(
461 		pthru_send(devp, DI_ROLE_MAIN, cdb, 10, NULL, 0, NULL, 0,
462 			   OP_NODATA, 20, TRUE)
463 	);
464 }
465 
466 
467 /*
468  * chin_init
469  *	Initialize the vendor-unique support module
470  *
471  * Args:
472  *	Nothing.
473  *
474  * Return:
475  *	Nothing.
476  */
477 void
chin_init(void)478 chin_init(void)
479 {
480 	/* Register vendor_unique module entry points */
481 	scsipt_vutbl[VENDOR_CHINON].vendor = "Chinon";
482 	scsipt_vutbl[VENDOR_CHINON].playaudio = chin_playaudio;
483 	scsipt_vutbl[VENDOR_CHINON].pause_resume = NULL;
484 	scsipt_vutbl[VENDOR_CHINON].start_stop = chin_start_stop;
485 	scsipt_vutbl[VENDOR_CHINON].get_playstatus = chin_get_playstatus;
486 	scsipt_vutbl[VENDOR_CHINON].volume = NULL;
487 	scsipt_vutbl[VENDOR_CHINON].route = NULL;
488 	scsipt_vutbl[VENDOR_CHINON].mute = NULL;
489 	scsipt_vutbl[VENDOR_CHINON].get_toc = chin_get_toc;
490 	scsipt_vutbl[VENDOR_CHINON].eject = chin_eject;
491 	scsipt_vutbl[VENDOR_CHINON].start = NULL;
492 	scsipt_vutbl[VENDOR_CHINON].halt = NULL;
493 }
494 
495 
496 #endif	/* VENDOR_CHINON */
497 
498