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