mcd.c revision 1.3 1 /*
2 * Copyright 1993 by Holger Veit (data part)
3 * Copyright 1993 by Brian Moore (audio part)
4 * CHANGES Copyright 1993 Gary Clark II (gclarkii (at) freefall.cdrom.com)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This software was developed by Holger Veit and Brian Moore
18 * for use with "386BSD" and similar operating systems.
19 * "Similar operating systems" includes mainly non-profit oriented
20 * systems for research and education, including but not restricted to
21 * "NetBSD", "FreeBSD", "Mach" (by CMU).
22 * 4. Neither the name of the developer(s) nor the name "386BSD"
23 * may be used to endorse or promote products derived from this
24 * software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
27 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
32 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 *
38 * $Id: mcd.c,v 1.3 1993/12/20 09:06:26 mycroft Exp $
39 */
40
41 static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
42
43 #include "mcd.h"
44 #if NMCD > 0
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/conf.h>
49 #include <sys/file.h>
50 #include <sys/buf.h>
51 #include <sys/stat.h>
52 #include <sys/uio.h>
53 #include <sys/ioctl.h>
54 #include <sys/cdio.h>
55 #include <sys/errno.h>
56 #include <sys/dkbad.h>
57 #include <sys/disklabel.h>
58
59 #include <machine/pio.h>
60
61 #include <i386/isa/isa.h>
62 #include <i386/isa/isa_device.h>
63 #include <i386/isa/mcdreg.h>
64
65 /* user definable options */
66 /*#define MCD_TO_WARNING_ON*/ /* define to get timeout messages */
67 /*#define MCDMINI*/ /* define for a mini configuration for boot kernel */
68
69
70 #ifdef MCDMINI
71 #define MCD_TRACE(fmt,a,b,c,d)
72 #ifdef MCD_TO_WARNING_ON
73 #undef MCD_TO_WARNING_ON
74 #endif
75 #else
76 #define MCD_TRACE(fmt,a,b,c,d) {if (mcd_data[unit].debug) {printf("mcd%d st=%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}}
77 #endif
78
79 #define mcd_part(dev) ((minor(dev)) & 7)
80 #define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3)
81 #define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6)
82 #define RAW_PART 3
83
84 /* flags */
85 #define MCDOPEN 0x0001 /* device opened */
86 #define MCDVALID 0x0002 /* parameters loaded */
87 #define MCDINIT 0x0004 /* device is init'd */
88 #define MCDWAIT 0x0008 /* waiting for something */
89 #define MCDLABEL 0x0010 /* label is read */
90 #define MCDPROBING 0x0020 /* probing */
91 #define MCDREADRAW 0x0040 /* read raw mode (2352 bytes) */
92 #define MCDVOLINFO 0x0080 /* already read volinfo */
93 #define MCDTOC 0x0100 /* already read toc */
94 #define MCDMBXBSY 0x0200 /* local mbx is busy */
95
96 /* status */
97 #define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */
98 #define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */
99 #define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */
100 #define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */
101
102 /* toc */
103 #define MCD_MAXTOCS 104 /* from the Linux driver */
104 #define MCD_LASTPLUS1 170 /* special toc entry */
105
106 struct mcd_mbx {
107 short unit;
108 short port;
109 short retry;
110 short nblk;
111 int sz;
112 u_long skip;
113 struct buf *bp;
114 int p_offset;
115 short count;
116 };
117
118 struct mcd_data {
119 short config;
120 short flags;
121 short status;
122 int blksize;
123 u_long disksize;
124 int iobase;
125 struct disklabel dlabel;
126 int partflags[MAXPARTITIONS];
127 int openflags;
128 struct mcd_volinfo volinfo;
129 #ifndef MCDMINI
130 struct mcd_qchninfo toc[MCD_MAXTOCS];
131 short audio_status;
132 struct mcd_read2 lastpb;
133 #endif
134 short debug;
135 struct buf head; /* head of buf queue */
136 struct mcd_mbx mbx;
137 } mcd_data[NMCD];
138
139 /* reader state machine */
140 #define MCD_S_BEGIN 0
141 #define MCD_S_BEGIN1 1
142 #define MCD_S_WAITSTAT 2
143 #define MCD_S_WAITMODE 3
144 #define MCD_S_WAITREAD 4
145
146 /* prototypes */
147 int mcdopen(dev_t dev);
148 int mcdclose(dev_t dev);
149 int mcdstrategy(struct buf *bp);
150 int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
151 int mcdsize(dev_t dev);
152 static void mcd_done(struct mcd_mbx *mbx);
153 static void mcd_start(int unit);
154 static int mcd_getdisklabel(int unit);
155 static void mcd_configure(struct mcd_data *cd);
156 static int mcd_get(int unit, char *buf, int nmax);
157 static void mcd_setflags(int unit,struct mcd_data *cd);
158 static int mcd_getstat(int unit,int sflg);
159 static int mcd_send(int unit, int cmd,int nretrys);
160 static int bcd2bin(bcd_t b);
161 static bcd_t bin2bcd(int b);
162 static void hsg2msf(int hsg, bcd_t *msf);
163 static int msf2hsg(bcd_t *msf);
164 static int mcd_volinfo(int unit);
165 static int mcd_waitrdy(int port,int dly);
166 static void mcd_doread(int state, struct mcd_mbx *mbxin);
167 #ifndef MCDMINI
168 static int mcd_setmode(int unit, int mode);
169 static int mcd_getqchan(int unit, struct mcd_qchninfo *q);
170 static int mcd_subchan(int unit, struct ioc_read_subchannel *sc);
171 static int mcd_toc_header(int unit, struct ioc_toc_header *th);
172 static int mcd_read_toc(int unit);
173 static int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
174 static int mcd_stop(int unit);
175 static int mcd_playtracks(int unit, struct ioc_play_track *pt);
176 static int mcd_play(int unit, struct mcd_read2 *pb);
177 static int mcd_pause(int unit);
178 static int mcd_resume(int unit);
179 #endif
180
181 extern int hz;
182 extern int mcd_probe(struct isa_device *dev);
183 extern int mcd_attach(struct isa_device *dev);
184 struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" };
185
186 #define mcd_put(port,byte) outb(port,byte)
187
188 #define MCD_RETRYS 5
189 #define MCD_RDRETRYS 8
190
191 #define MCDBLK 2048 /* for cooked mode */
192 #define MCDRBLK 2352 /* for raw mode */
193
194 /* several delays */
195 #define RDELAY_WAITSTAT 300
196 #define RDELAY_WAITMODE 300
197 #define RDELAY_WAITREAD 800
198
199 #define DELAY_STATUS 10000l /* 10000 * 1us */
200 #define DELAY_GETREPLY 200000l /* 200000 * 2us */
201 #define DELAY_SEEKREAD 20000l /* 20000 * 1us */
202 #define mcd_delay DELAY
203
204 int mcd_attach(struct isa_device *dev)
205 {
206 struct mcd_data *cd = mcd_data + dev->id_unit;
207 int i;
208
209 cd->iobase = dev->id_iobase;
210 cd->flags |= MCDINIT;
211 cd->openflags = 0;
212 for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
213
214 #ifdef NOTYET
215 /* wire controller for interrupts and dma */
216 mcd_configure(cd);
217 #endif
218
219 return 1;
220 }
221
222 int mcdopen(dev_t dev)
223 {
224 int unit,part,phys;
225 struct mcd_data *cd;
226
227 unit = mcd_unit(dev);
228 if (unit >= NMCD)
229 return ENXIO;
230
231 cd = mcd_data + unit;
232 part = mcd_part(dev);
233 phys = mcd_phys(dev);
234
235 /* not initialized*/
236 if (!(cd->flags & MCDINIT))
237 return ENXIO;
238
239 /* invalidated in the meantime? mark all open part's invalid */
240 if (!(cd->flags & MCDVALID) && cd->openflags)
241 return ENXIO;
242
243 if (mcd_getstat(unit,1) < 0)
244 return ENXIO;
245
246 /* XXX get a default disklabel */
247 mcd_getdisklabel(unit);
248
249 if (mcdsize(dev) < 0) {
250 printf("mcd%d: failed to get disk size\n",unit);
251 return ENXIO;
252 } else
253 cd->flags |= MCDVALID;
254
255 MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
256 part,cd->disksize,cd->blksize,0);
257
258 if (part == RAW_PART ||
259 (part < cd->dlabel.d_npartitions &&
260 cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
261 cd->partflags[part] |= MCDOPEN;
262 cd->openflags |= (1<<part);
263 if (part == RAW_PART && phys != 0)
264 cd->partflags[part] |= MCDREADRAW;
265 return 0;
266 }
267
268 return ENXIO;
269 }
270
271 int mcdclose(dev_t dev)
272 {
273 int unit,part,phys;
274 struct mcd_data *cd;
275
276 unit = mcd_unit(dev);
277 if (unit >= NMCD)
278 return ENXIO;
279
280 cd = mcd_data + unit;
281 part = mcd_part(dev);
282 phys = mcd_phys(dev);
283
284 if (!(cd->flags & MCDINIT))
285 return ENXIO;
286
287 mcd_getstat(unit,1); /* get status */
288
289 /* close channel */
290 cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
291 cd->openflags &= ~(1<<part);
292 MCD_TRACE("close: partition=%d\n",part,0,0,0);
293
294 return 0;
295 }
296
297 int mcdstrategy(struct buf *bp)
298 {
299 struct mcd_data *cd;
300 struct buf *qp;
301 int s;
302
303 int unit = mcd_unit(bp->b_dev);
304
305 cd = mcd_data + unit;
306
307 /* test validity */
308 /*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
309 bp,unit,bp->b_blkno,bp->b_bcount);*/
310 if (unit >= NMCD || bp->b_blkno < 0) {
311 printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
312 unit, bp->b_blkno, bp->b_bcount);
313 pg("mcd: mcdstratregy failure");
314 bp->b_error = EINVAL;
315 bp->b_flags |= B_ERROR;
316 goto bad;
317 }
318
319 /* if device invalidated (e.g. media change, door open), error */
320 if (!(cd->flags & MCDVALID)) {
321 MCD_TRACE("strategy: drive not valid\n",0,0,0,0);
322 bp->b_error = EIO;
323 goto bad;
324 }
325
326 /* read only */
327 if (!(bp->b_flags & B_READ)) {
328 bp->b_error = EROFS;
329 goto bad;
330 }
331
332 /* no data to read */
333 if (bp->b_bcount == 0)
334 goto done;
335
336 /* for non raw access, check partition limits */
337 if (mcd_part(bp->b_dev) != RAW_PART) {
338 if (!(cd->flags & MCDLABEL)) {
339 bp->b_error = EIO;
340 goto bad;
341 }
342 /* adjust transfer if necessary */
343 if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
344 goto done;
345 }
346 }
347
348 /* queue it */
349 qp = &cd->head;
350 s = splbio();
351 disksort(qp,bp);
352 splx(s);
353
354 /* now check whether we can perform processing */
355 mcd_start(unit);
356 return;
357
358 bad:
359 bp->b_flags |= B_ERROR;
360 done:
361 bp->b_resid = bp->b_bcount;
362 biodone(bp);
363 return;
364 }
365
366 static void mcd_start(int unit)
367 {
368 struct mcd_data *cd = mcd_data + unit;
369 struct buf *bp, *qp = &cd->head;
370 struct partition *p;
371 int part;
372 register s = splbio();
373
374 if (cd->flags & MCDMBXBSY)
375 return;
376
377 if ((bp = qp->b_actf) != 0) {
378 /* block found to process, dequeue */
379 /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
380 qp->b_actf = bp->av_forw;
381 splx(s);
382 } else {
383 /* nothing to do */
384 splx(s);
385 return;
386 }
387
388 /* changed media? */
389 if (!(cd->flags & MCDVALID)) {
390 MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
391 return;
392 }
393
394 p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
395
396 cd->flags |= MCDMBXBSY;
397 cd->mbx.unit = unit;
398 cd->mbx.port = cd->iobase;
399 cd->mbx.retry = MCD_RETRYS;
400 cd->mbx.bp = bp;
401 cd->mbx.p_offset = p->p_offset;
402
403 /* calling the read routine */
404 mcd_doread(MCD_S_BEGIN,&(cd->mbx));
405 /* triggers mcd_start, when successful finished */
406 return;
407 }
408
409 int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
410 {
411 struct mcd_data *cd;
412 int unit,part;
413
414 unit = mcd_unit(dev);
415 part = mcd_part(dev);
416 cd = mcd_data + unit;
417
418 #ifdef MCDMINI
419 return ENOTTY;
420 #else
421 if (!(cd->flags & MCDVALID))
422 return EIO;
423 MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
424
425 switch (cmd) {
426 case DIOCSBAD:
427 return EINVAL;
428 case DIOCGDINFO:
429 case DIOCGPART:
430 case DIOCWDINFO:
431 case DIOCSDINFO:
432 case DIOCWLABEL:
433 return ENOTTY;
434 case CDIOCPLAYTRACKS:
435 return mcd_playtracks(unit, (struct ioc_play_track *) addr);
436 case CDIOCPLAYBLOCKS:
437 return mcd_play(unit, (struct mcd_read2 *) addr);
438 case CDIOCREADSUBCHANNEL:
439 return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
440 case CDIOREADTOCHEADER:
441 return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
442 case CDIOREADTOCENTRYS:
443 return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
444 case CDIOCSETPATCH:
445 case CDIOCGETVOL:
446 case CDIOCSETVOL:
447 case CDIOCSETMONO:
448 case CDIOCSETSTERIO:
449 case CDIOCSETMUTE:
450 case CDIOCSETLEFT:
451 case CDIOCSETRIGHT:
452 return EINVAL;
453 case CDIOCRESUME:
454 return mcd_resume(unit);
455 case CDIOCPAUSE:
456 return mcd_pause(unit);
457 case CDIOCSTART:
458 return EINVAL;
459 case CDIOCSTOP:
460 return mcd_stop(unit);
461 case CDIOCEJECT:
462 return EINVAL;
463 case CDIOCSETDEBUG:
464 cd->debug = 1;
465 return 0;
466 case CDIOCCLRDEBUG:
467 cd->debug = 0;
468 return 0;
469 case CDIOCRESET:
470 return EINVAL;
471 default:
472 return ENOTTY;
473 }
474 /*NOTREACHED*/
475 #endif /*!MCDMINI*/
476 }
477
478 /* this could have been taken from scsi/cd.c, but it is not clear
479 * whether the scsi cd driver is linked in
480 */
481 static int mcd_getdisklabel(int unit)
482 {
483 struct mcd_data *cd = mcd_data + unit;
484
485 if (cd->flags & MCDLABEL)
486 return -1;
487
488 bzero(&cd->dlabel,sizeof(struct disklabel));
489 strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
490 strncpy(cd->dlabel.d_packname,"unknown ",16);
491 cd->dlabel.d_secsize = cd->blksize;
492 cd->dlabel.d_nsectors = 100;
493 cd->dlabel.d_ntracks = 1;
494 cd->dlabel.d_ncylinders = (cd->disksize/100)+1;
495 cd->dlabel.d_secpercyl = 100;
496 cd->dlabel.d_secperunit = cd->disksize;
497 cd->dlabel.d_rpm = 300;
498 cd->dlabel.d_interleave = 1;
499 cd->dlabel.d_flags = D_REMOVABLE;
500 cd->dlabel.d_npartitions= 1;
501 cd->dlabel.d_partitions[0].p_offset = 0;
502 cd->dlabel.d_partitions[0].p_size = cd->disksize;
503 cd->dlabel.d_partitions[0].p_fstype = 9;
504 cd->dlabel.d_magic = DISKMAGIC;
505 cd->dlabel.d_magic2 = DISKMAGIC;
506 cd->dlabel.d_checksum = dkcksum(&cd->dlabel);
507
508 cd->flags |= MCDLABEL;
509 return 0;
510 }
511
512 int mcdsize(dev_t dev)
513 {
514 int size;
515 int unit = mcd_unit(dev);
516 struct mcd_data *cd = mcd_data + unit;
517
518 if (mcd_volinfo(unit) >= 0) {
519 cd->blksize = MCDBLK;
520 size = msf2hsg(cd->volinfo.vol_msf);
521 cd->disksize = size * (MCDBLK/DEV_BSIZE);
522 return 0;
523 }
524 return -1;
525 }
526
527 /***************************************************************
528 * lower level of driver starts here
529 **************************************************************/
530
531 #ifdef NOTDEF
532 static char irqs[] = {
533 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
534 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
535 };
536
537 static char drqs[] = {
538 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
539 };
540 #endif
541
542 static void mcd_configure(struct mcd_data *cd)
543 {
544 outb(cd->iobase+mcd_config,cd->config);
545 }
546
547 /* check if there is a cdrom */
548 /* Heavly hacked by gclarkii (at) sugar.neosoft.com */
549
550 int mcd_probe(struct isa_device *dev)
551 {
552 int port = dev->id_iobase;
553 int unit = dev->id_unit;
554 int i;
555 int st;
556 int check;
557 int junk;
558
559 mcd_data[unit].flags = MCDPROBING;
560
561 #ifdef NOTDEF
562 /* get irq/drq configuration word */
563 mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
564 #else
565 mcd_data[unit].config = 0;
566 #endif
567
568 /* send a reset */
569 outb(port+MCD_FLAGS,0);
570 DELAY(100000);
571 /* get any pending status and throw away...*/
572 for (i=10; i != 0; i--) {
573 inb(port+MCD_DATA);
574 }
575 DELAY(1000);
576
577 outb(port+MCD_DATA,MCD_CMDGETSTAT); /* Send get status command */
578
579 /* Loop looking for avail of status */
580 /* XXX May have to increase for fast machinces */
581 for (i = 1000; i != 0; i--) {
582 if ((inb(port+MCD_FLAGS) & 0xF ) == STATUS_AVAIL) {
583 break;
584 }
585 DELAY(10);
586 }
587 /* get status */
588
589 if (i == 0) {
590 #ifdef DEBUG
591 printf ("Mitsumi drive NOT detected\n");
592 #endif
593 return 0;
594 }
595
596 /*
597 * The following code uses the 0xDC command, it returns a M from the
598 * second byte and a number in the third. Does anyone know what the
599 * number is for? Better yet, how about someone thats REAL good in
600 * i80x86 asm looking at the Dos driver... Most of this info came
601 * from a friend of mine spending a whole weekend.....
602 */
603
604 DELAY (2000);
605 outb(port+MCD_DATA,MCD_CMDCONTINFO);
606 for (i = 0; i < 100000; i++) {
607 if ((inb(port+MCD_FLAGS) & 0xF) == STATUS_AVAIL)
608 break;
609 }
610 if (i > 100000) {
611 #ifdef DEBUG
612 printf ("Mitsumi drive error\n");
613 #endif
614 return 0;
615 }
616 DELAY (40000);
617 st = inb(port+MCD_DATA);
618 DELAY (500);
619 check = inb(port+MCD_DATA);
620 DELAY (500);
621 junk = inb(port+MCD_DATA); /* What is byte used for?!?!? */
622
623 if (check = 'M') {
624 #ifdef DEBUG
625 printf("Mitsumi drive detected\n");
626 #endif
627 return 4;
628 } else {
629 printf("Mitsumi drive NOT detected\n");
630 printf("Mitsumi drive error\n");
631 return 0;
632 }
633 }
634
635 static int mcd_waitrdy(int port,int dly)
636 {
637 int i;
638
639 /* wait until xfer port senses data ready */
640 for (i=0; i<dly; i++) {
641 if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0)
642 return 0;
643 mcd_delay(1);
644 }
645 return -1;
646 }
647
648 static int mcd_getreply(int unit,int dly)
649 {
650 int i;
651 struct mcd_data *cd = mcd_data + unit;
652 int port = cd->iobase;
653
654 /* wait data to become ready */
655 if (mcd_waitrdy(port,dly)<0) {
656 #ifdef MCD_TO_WARNING_ON
657 printf("mcd%d: timeout getreply\n",unit);
658 #endif
659 return -1;
660 }
661
662 /* get the data */
663 return inb(port+mcd_status) & 0xFF;
664 }
665
666 static int mcd_getstat(int unit,int sflg)
667 {
668 int i;
669 struct mcd_data *cd = mcd_data + unit;
670 int port = cd->iobase;
671
672 /* get the status */
673 if (sflg)
674 outb(port+mcd_command, MCD_CMDGETSTAT);
675 i = mcd_getreply(unit,DELAY_GETREPLY);
676 if (i<0) return -1;
677
678 cd->status = i;
679
680 mcd_setflags(unit,cd);
681 return cd->status;
682 }
683
684 static void mcd_setflags(int unit, struct mcd_data *cd)
685 {
686 /* check flags */
687 if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
688 MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
689 cd->flags &= ~MCDVALID;
690 }
691
692 #ifndef MCDMINI
693 if (cd->status & MCDAUDIOBSY)
694 cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
695 else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
696 cd->audio_status = CD_AS_PLAY_COMPLETED;
697 #endif
698 }
699
700 static int mcd_get(int unit, char *buf, int nmax)
701 {
702 int port = mcd_data[unit].iobase;
703 int i,k;
704
705 for (i=0; i<nmax; i++) {
706
707 /* wait for data */
708 if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
709 #ifdef MCD_TO_WARNING_ON
710 printf("mcd%d: timeout mcd_get\n",unit);
711 #endif
712 return -1;
713 }
714 buf[i] = k;
715 }
716 return i;
717 }
718
719 static int mcd_send(int unit, int cmd,int nretrys)
720 {
721 int i,k;
722 int port = mcd_data[unit].iobase;
723
724 /*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
725 for (i=0; i<nretrys; i++) {
726 outb(port+mcd_command, cmd);
727 if ((k=mcd_getstat(unit,0)) != -1)
728 break;
729 }
730 if (i == nretrys) {
731 printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
732 return -1;
733 }
734 /*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
735 return 0;
736 }
737
738 static int bcd2bin(bcd_t b)
739 {
740 return (b >> 4) * 10 + (b & 15);
741 }
742
743 static bcd_t bin2bcd(int b)
744 {
745 return ((b / 10) << 4) | (b % 10);
746 }
747
748 static void hsg2msf(int hsg, bcd_t *msf)
749 {
750 hsg += 150;
751 M_msf(msf) = bin2bcd(hsg / 4500);
752 hsg %= 4500;
753 S_msf(msf) = bin2bcd(hsg / 75);
754 F_msf(msf) = bin2bcd(hsg % 75);
755 }
756
757 static int msf2hsg(bcd_t *msf)
758 {
759 return (bcd2bin(M_msf(msf)) * 60 +
760 bcd2bin(S_msf(msf))) * 75 +
761 bcd2bin(F_msf(msf)) - 150;
762 }
763
764 static int mcd_volinfo(int unit)
765 {
766 struct mcd_data *cd = mcd_data + unit;
767 int i;
768
769 /*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
770
771 /* Get the status, in case the disc has been changed */
772 if (mcd_getstat(unit, 1) < 0) return EIO;
773
774 /* Just return if we already have it */
775 if (cd->flags & MCDVOLINFO) return 0;
776
777 /* send volume info command */
778 if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
779 return -1;
780
781 /* get data */
782 if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
783 printf("mcd%d: mcd_volinfo: error read data\n",unit);
784 return -1;
785 }
786
787 if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
788 cd->flags |= MCDVOLINFO; /* volinfo is OK */
789 return 0;
790 }
791
792 return -1;
793 }
794
795 int mcdintr(unit)
796 {
797 int port = mcd_data[unit].iobase;
798 u_int i;
799
800 MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
801
802 /* just read out status and ignore the rest */
803 if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
804 i = inb(port+mcd_status);
805 }
806 }
807
808 /* state machine to process read requests
809 * initialize with MCD_S_BEGIN: calculate sizes, and read status
810 * MCD_S_WAITSTAT: wait for status reply, set mode
811 * MCD_S_WAITMODE: waits for status reply from set mode, set read command
812 * MCD_S_WAITREAD: wait for read ready, read data
813 */
814 static struct mcd_mbx *mbxsave;
815
816 static void mcd_doread(int state, struct mcd_mbx *mbxin)
817 {
818 struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
819 int unit = mbx->unit;
820 int port = mbx->port;
821 struct buf *bp = mbx->bp;
822 struct mcd_data *cd = mcd_data + unit;
823
824 int rm,i,k;
825 struct mcd_read2 rbuf;
826 int blknum;
827 caddr_t addr;
828
829 loop:
830 switch (state) {
831 case MCD_S_BEGIN:
832 mbx = mbxsave = mbxin;
833
834 case MCD_S_BEGIN1:
835 /* get status */
836 outb(port+mcd_command, MCD_CMDGETSTAT);
837 mbx->count = RDELAY_WAITSTAT;
838 timeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITSTAT,hz/100);
839 return;
840 case MCD_S_WAITSTAT:
841 untimeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITSTAT);
842 if (mbx->count-- >= 0) {
843 if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
844 timeout((timeout_t) mcd_doread,
845 (caddr_t) MCD_S_WAITSTAT,hz/100);
846 return;
847 }
848 mcd_setflags(unit,cd);
849 MCD_TRACE("got WAITSTAT delay=%d\n",RDELAY_WAITSTAT-mbx->count,0,0,0);
850 /* reject, if audio active */
851 if (cd->status & MCDAUDIOBSY) {
852 printf("mcd%d: audio is active\n",unit);
853 goto readerr;
854 }
855
856 /* to check for raw/cooked mode */
857 if (cd->flags & MCDREADRAW) {
858 rm = MCD_MD_RAW;
859 mbx->sz = MCDRBLK;
860 } else {
861 rm = MCD_MD_COOKED;
862 mbx->sz = cd->blksize;
863 }
864
865 mbx->count = RDELAY_WAITMODE;
866
867 mcd_put(port+mcd_command, MCD_CMDSETMODE);
868 mcd_put(port+mcd_command, rm);
869 timeout((timeout_t) mcd_doread,
870 (caddr_t) MCD_S_WAITMODE,hz/100);
871 return;
872 } else {
873 #ifdef MCD_TO_WARNING_ON
874 printf("mcd%d: timeout getstatus\n",unit);
875 #endif
876 goto readerr;
877 }
878
879 case MCD_S_WAITMODE:
880 untimeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITMODE);
881 if (mbx->count-- < 0) {
882 #ifdef MCD_TO_WARNING_ON
883 printf("mcd%d: timeout set mode\n",unit);
884 #endif
885 goto readerr;
886 }
887 if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
888 timeout((timeout_t) mcd_doread,
889 (caddr_t) MCD_S_WAITMODE,hz/100);
890 return;
891 }
892 mcd_setflags(unit,cd);
893 MCD_TRACE("got WAITMODE delay=%d\n",RDELAY_WAITMODE-mbx->count,0,0,0);
894 /* for first block */
895 mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
896 mbx->skip = 0;
897
898 nextblock:
899 blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE))
900 + mbx->p_offset + mbx->skip/mbx->sz;
901
902 MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",blknum,bp,0,0);
903
904 /* build parameter block */
905 hsg2msf(blknum,rbuf.start_msf);
906
907 /* send the read command */
908 mcd_put(port+mcd_command,MCD_CMDREAD2);
909 mcd_put(port+mcd_command,rbuf.start_msf[0]);
910 mcd_put(port+mcd_command,rbuf.start_msf[1]);
911 mcd_put(port+mcd_command,rbuf.start_msf[2]);
912 mcd_put(port+mcd_command,0);
913 mcd_put(port+mcd_command,0);
914 mcd_put(port+mcd_command,1);
915 mbx->count = RDELAY_WAITREAD;
916 timeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITREAD,hz/100);
917 return;
918 case MCD_S_WAITREAD:
919 untimeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITREAD);
920 if (mbx->count-- > 0) {
921 k = inb(port+mcd_xfer);
922 if ((k & 2)==0) {
923 MCD_TRACE("got data delay=%d\n",RDELAY_WAITREAD-mbx->count,0,0,0);
924 /* data is ready */
925 addr = bp->b_un.b_addr + mbx->skip;
926 outb(port+mcd_ctl2,0x04); /* XXX */
927 for (i=0; i<mbx->sz; i++)
928 *addr++ = inb(port+mcd_rdata);
929 outb(port+mcd_ctl2,0x0c); /* XXX */
930
931 if (--mbx->nblk > 0) {
932 mbx->skip += mbx->sz;
933 goto nextblock;
934 }
935
936 /* return buffer */
937 bp->b_resid = 0;
938 biodone(bp);
939
940 cd->flags &= ~MCDMBXBSY;
941 mcd_start(mbx->unit);
942 return;
943 }
944 if ((k & 4)==0)
945 mcd_getstat(unit,0);
946 timeout((timeout_t) mcd_doread,
947 (caddr_t) MCD_S_WAITREAD,hz/100);
948 return;
949 } else {
950 #ifdef MCD_TO_WARNING_ON
951 printf("mcd%d: timeout read data\n",unit);
952 #endif
953 goto readerr;
954 }
955 }
956
957 readerr:
958 if (mbx->retry-- > 0) {
959 #ifdef MCD_TO_WARNING_ON
960 printf("mcd%d: retrying\n",unit);
961 #endif
962 state = MCD_S_BEGIN1;
963 goto loop;
964 }
965
966 /* invalidate the buffer */
967 bp->b_flags |= B_ERROR;
968 bp->b_resid = bp->b_bcount;
969 biodone(bp);
970 mcd_start(mbx->unit);
971 return;
972
973 #ifdef NOTDEF
974 printf("mcd%d: unit timeout, resetting\n",mbx->unit);
975 outb(mbx->port+mcd_reset,MCD_CMDRESET);
976 DELAY(300000);
977 (void)mcd_getstat(mbx->unit,1);
978 (void)mcd_getstat(mbx->unit,1);
979 /*cd->status &= ~MCDDSKCHNG; */
980 cd->debug = 1; /* preventive set debug mode */
981
982 #endif
983
984 }
985
986 #ifndef MCDMINI
987 static int mcd_setmode(int unit, int mode)
988 {
989 struct mcd_data *cd = mcd_data + unit;
990 int port = cd->iobase;
991 int retry;
992
993 printf("mcd%d: setting mode to %d\n", unit, mode);
994 for(retry=0; retry<MCD_RETRYS; retry++)
995 {
996 outb(port+mcd_command, MCD_CMDSETMODE);
997 outb(port+mcd_command, mode);
998 if (mcd_getstat(unit, 0) != -1) return 0;
999 }
1000
1001 return -1;
1002 }
1003
1004 static int mcd_toc_header(int unit, struct ioc_toc_header *th)
1005 {
1006 struct mcd_data *cd = mcd_data + unit;
1007
1008 if (mcd_volinfo(unit) < 0)
1009 return ENXIO;
1010
1011 th->len = msf2hsg(cd->volinfo.vol_msf);
1012 th->starting_track = bcd2bin(cd->volinfo.trk_low);
1013 th->ending_track = bcd2bin(cd->volinfo.trk_high);
1014
1015 return 0;
1016 }
1017
1018 static int mcd_read_toc(int unit)
1019 {
1020 struct mcd_data *cd = mcd_data + unit;
1021 struct ioc_toc_header th;
1022 struct mcd_qchninfo q;
1023 int rc, trk, idx, retry;
1024
1025 /* Only read TOC if needed */
1026 if (cd->flags & MCDTOC) return 0;
1027
1028 printf("mcd%d: reading toc header\n", unit);
1029 if (mcd_toc_header(unit, &th) != 0)
1030 return ENXIO;
1031
1032 printf("mcd%d: stopping play\n", unit);
1033 if ((rc=mcd_stop(unit)) != 0)
1034 return rc;
1035
1036 /* try setting the mode twice */
1037 if (mcd_setmode(unit, MCD_MD_TOC) != 0)
1038 return EIO;
1039 if (mcd_setmode(unit, MCD_MD_TOC) != 0)
1040 return EIO;
1041
1042 printf("mcd%d: get_toc reading qchannel info\n",unit);
1043 for(trk=th.starting_track; trk<=th.ending_track; trk++)
1044 cd->toc[trk].idx_no = 0;
1045 trk = th.ending_track - th.starting_track + 1;
1046 for(retry=0; retry<300 && trk>0; retry++)
1047 {
1048 if (mcd_getqchan(unit, &q) < 0) break;
1049 idx = bcd2bin(q.idx_no);
1050 if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0)
1051 if (cd->toc[idx].idx_no == 0)
1052 {
1053 cd->toc[idx] = q;
1054 trk--;
1055 }
1056 }
1057
1058 if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1059 return EIO;
1060
1061 if (trk != 0) return ENXIO;
1062
1063 /* add a fake last+1 */
1064 idx = th.ending_track + 1;
1065 cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
1066 cd->toc[idx].trk_no = 0;
1067 cd->toc[idx].idx_no = 0xAA;
1068 cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1069 cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1070 cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1071
1072 cd->flags |= MCDTOC;
1073
1074 return 0;
1075 }
1076
1077 static int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
1078 {
1079 struct mcd_data *cd = mcd_data + unit;
1080 struct ret_toc
1081 {
1082 struct ioc_toc_header th;
1083 struct cd_toc_entry rt;
1084 } ret_toc;
1085 struct ioc_toc_header th;
1086 int rc, i;
1087
1088 /* Make sure we have a valid toc */
1089 if ((rc=mcd_read_toc(unit)) != 0)
1090 return rc;
1091
1092 /* find the toc to copy*/
1093 i = te->starting_track;
1094 if (i == MCD_LASTPLUS1)
1095 i = bcd2bin(cd->volinfo.trk_high) + 1;
1096
1097 /* verify starting track */
1098 if (i < bcd2bin(cd->volinfo.trk_low) ||
1099 i > bcd2bin(cd->volinfo.trk_high)+1)
1100 return EINVAL;
1101
1102 /* do we have room */
1103 if (te->data_len < sizeof(struct ioc_toc_header) +
1104 sizeof(struct cd_toc_entry)) return EINVAL;
1105
1106 /* Copy the toc header */
1107 if (mcd_toc_header(unit, &th) < 0) return EIO;
1108 ret_toc.th = th;
1109
1110 /* copy the toc data */
1111 ret_toc.rt.control = cd->toc[i].ctrl_adr;
1112 ret_toc.rt.addr_type = te->address_format;
1113 ret_toc.rt.track = i;
1114 if (te->address_format == CD_MSF_FORMAT)
1115 {
1116 ret_toc.rt.addr[1] = cd->toc[i].hd_pos_msf[0];
1117 ret_toc.rt.addr[2] = cd->toc[i].hd_pos_msf[1];
1118 ret_toc.rt.addr[3] = cd->toc[i].hd_pos_msf[2];
1119 }
1120
1121 /* copy the data back */
1122 copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
1123 + sizeof(struct ioc_toc_header));
1124
1125 return 0;
1126 }
1127
1128 static int mcd_stop(int unit)
1129 {
1130 struct mcd_data *cd = mcd_data + unit;
1131
1132 if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
1133 return ENXIO;
1134 cd->audio_status = CD_AS_PLAY_COMPLETED;
1135 return 0;
1136 }
1137
1138 static int mcd_getqchan(int unit, struct mcd_qchninfo *q)
1139 {
1140 struct mcd_data *cd = mcd_data + unit;
1141
1142 if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0)
1143 return -1;
1144 if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0)
1145 return -1;
1146 if (cd->debug)
1147 printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
1148 unit,
1149 q->ctrl_adr, q->trk_no, q->idx_no,
1150 q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
1151 q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
1152 return 0;
1153 }
1154
1155 static int mcd_subchan(int unit, struct ioc_read_subchannel *sc)
1156 {
1157 struct mcd_data *cd = mcd_data + unit;
1158 struct mcd_qchninfo q;
1159 struct cd_sub_channel_info data;
1160
1161 printf("mcd%d: subchan af=%d, df=%d\n", unit,
1162 sc->address_format,
1163 sc->data_format);
1164 if (sc->address_format != CD_MSF_FORMAT) return EIO;
1165 if (sc->data_format != CD_CURRENT_POSITION) return EIO;
1166
1167 if (mcd_getqchan(unit, &q) < 0) return EIO;
1168
1169 data.header.audio_status = cd->audio_status;
1170 data.what.position.data_format = CD_MSF_FORMAT;
1171 data.what.position.track_number = bcd2bin(q.trk_no);
1172
1173 if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0)
1174 return EFAULT;
1175 return 0;
1176 }
1177
1178 static int mcd_playtracks(int unit, struct ioc_play_track *pt)
1179 {
1180 struct mcd_data *cd = mcd_data + unit;
1181 struct mcd_read2 pb;
1182 int a = pt->start_track;
1183 int z = pt->end_track;
1184 int rc;
1185
1186 if ((rc = mcd_read_toc(unit)) != 0) return rc;
1187
1188 printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
1189 a, pt->start_index, z, pt->end_index);
1190
1191 if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
1192 z < cd->volinfo.trk_low || z > cd->volinfo.trk_high)
1193 return EINVAL;
1194
1195 pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
1196 pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
1197 pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
1198 pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
1199 pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
1200 pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
1201
1202 return mcd_play(unit, &pb);
1203 }
1204
1205 static int mcd_play(int unit, struct mcd_read2 *pb)
1206 {
1207 struct mcd_data *cd = mcd_data + unit;
1208 int port = cd->iobase;
1209 int retry, st;
1210
1211 cd->lastpb = *pb;
1212 for(retry=0; retry<MCD_RETRYS; retry++)
1213 {
1214 outb(port+mcd_command, MCD_CMDREAD2);
1215 outb(port+mcd_command, pb->start_msf[0]);
1216 outb(port+mcd_command, pb->start_msf[1]);
1217 outb(port+mcd_command, pb->start_msf[2]);
1218 outb(port+mcd_command, pb->end_msf[0]);
1219 outb(port+mcd_command, pb->end_msf[1]);
1220 outb(port+mcd_command, pb->end_msf[2]);
1221 if ((st=mcd_getstat(unit, 0)) != -1) break;
1222 }
1223
1224 if (cd->debug)
1225 printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
1226 if (st == -1) return ENXIO;
1227 cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1228 return 0;
1229 }
1230
1231 static int mcd_pause(int unit)
1232 {
1233 struct mcd_data *cd = mcd_data + unit;
1234 struct mcd_qchninfo q;
1235 int rc;
1236
1237 /* Verify current status */
1238 if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS)
1239 {
1240 printf("mcd%d: pause attempted when not playing\n", unit);
1241 return EINVAL;
1242 }
1243
1244 /* Get the current position */
1245 if (mcd_getqchan(unit, &q) < 0) return EIO;
1246
1247 /* Copy it into lastpb */
1248 cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1249 cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1250 cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1251
1252 /* Stop playing */
1253 if ((rc=mcd_stop(unit)) != 0) return rc;
1254
1255 /* Set the proper status and exit */
1256 cd->audio_status = CD_AS_PLAY_PAUSED;
1257 return 0;
1258 }
1259
1260 static int mcd_resume(int unit)
1261 {
1262 struct mcd_data *cd = mcd_data + unit;
1263
1264 if (cd->audio_status != CD_AS_PLAY_PAUSED) return EINVAL;
1265 return mcd_play(unit, &cd->lastpb);
1266 }
1267 #endif /*!MCDMINI*/
1268
1269 #endif /* NMCD > 0 */
1270