Subject: Changes to the mcd.c driver for audio disks.
To: Jordan K. Hubbard <jkh@freefall.cdrom.com>
From: Dave Burgess <burgess@s069.infonet.net>
List: current-users
Date: 11/11/1994 23:37:48
> 

While I have been thwarted in my attempts to get Ada going, I have been
reasonably successful at getting audio CD support built into NetBSD.  If
no one but me uses this it was worth it.

As an aside, part of the change that I made is a set of changes to the
mcdreg.h file in preparation for getting DMA support built into the
mcd.c driver and other cool stuff.  The audio was more pressing, mainly
because it annoyed me that I couldn't listen to a music CD after I was
through slurping source code off of a CD-ROM.  The changes below make
this a real possibility.  The cdplayer program I wrote (and several of
you have) interfaces to these patches perfectly, allowing for cool
things like on screen track and time information in near real time.

Here are the patches (against NetBSD 1.0-RDN).  FreeBSD readers should
be able to use these changes without many (or maybe any) changes.


*** mcd.c	Fri Nov 11 21:17:31 1994
--- /usr/src/sys/arch/i386/isa/mcd.c	Fri Nov 11 23:26:33 1994
***************
*** 1,4 ****
--- 1,5 ----
  /*
+  * Copyright (c) 1994 David Burgess.
   * Copyright (c) 1993, 1994 Charles Hannum.
   * Copyright 1993 by Holger Veit (data part)
   * Copyright 1993 by Brian Moore (audio part)
***************
*** 37,43 ****
   *
   *	$Id: mcd.c,v 1.16.2.2 1994/08/05 23:00:24 mycroft Exp $
   */
! 
  /*static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";*/
  
  #include <sys/types.h>
--- 38,49 ----
   *
   *	$Id: mcd.c,v 1.16.2.2 1994/08/05 23:00:24 mycroft Exp $
   */
! /*
!  *  I have made changes to this software so that it will reliably work
!  *  with music CDs.  During my research, I have also found that this
!  *  driver should be capable of interacting with Photo CDs as well as
!  *  the normal ISO9660 and Music CDs.  Stay tuned 
!  */
  /*static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";*/
  
  #include <sys/types.h>
***************
*** 91,97 ****
  #define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
  
  /* toc */
! #define MCD_MAXTOCS	104	/* from the Linux driver */
  #define MCD_LASTPLUS1	170	/* special toc entry */
  
  struct mcd_mbx {
--- 97,103 ----
  #define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
  
  /* toc */
! #define MCD_MAXTOCS	65	/* from the SCSI cd.c */
  #define MCD_LASTPLUS1	170	/* special toc entry */
  
  struct mcd_mbx {
***************
*** 160,165 ****
--- 166,172 ----
  int mcd_play __P((struct mcd_softc *, struct mcd_read2 *));
  int mcd_pause __P((struct mcd_softc *));
  int mcd_resume __P((struct mcd_softc *));
+ void mcd_get_sense __P((struct mcd_softc *));
  
  int mcdprobe();
  void mcdattach();
***************
*** 692,698 ****
  	/* Check flags. */
  	if (sc->status & (MCDDSKCHNG | MCDDOOROPEN)) {
  		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n", 0, 0, 0, 0);
! 		sc->flags &= ~MCDVALID;
  	}
  
  	if (sc->status & MCDAUDIOBSY)
--- 699,705 ----
  	/* Check flags. */
  	if (sc->status & (MCDDSKCHNG | MCDDOOROPEN)) {
  		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n", 0, 0, 0, 0);
! 		sc->flags &= ~(MCDVALID & MCDVOLINFO & MCDTOC);
  	}
  
  	if (sc->status & MCDAUDIOBSY)
***************
*** 1119,1125 ****
  {
  	struct ret_toc {
  		struct ioc_toc_header th;
! 		struct cd_toc_entry rt;
  	} ret_toc;
  	struct ioc_toc_header th;
  	int rc, i;
--- 1126,1132 ----
  {
  	struct ret_toc {
  		struct ioc_toc_header th;
! 		struct cd_toc_entry rt[MCD_MAXTOCS];
  	} ret_toc;
  	struct ioc_toc_header th;
  	int rc, i;
***************
*** 1129,1166 ****
  		return rc;
  
  	/* Find the TOC to copy. */
! 	i = te->starting_track;
! 	if (i == MCD_LASTPLUS1)
  		i = bcd2bin(sc->volinfo.trk_high) + 1;
  	
! 	/* Verify starting track. */
! 	if (i < bcd2bin(sc->volinfo.trk_low) ||
! 	    i > bcd2bin(sc->volinfo.trk_high) + 1)
  		return EINVAL;
  
! 	/* Do we have room? */
! 	if (te->data_len < sizeof(struct ioc_toc_header) +
! 	    sizeof(struct cd_toc_entry))
  		return EINVAL;
  
! 	/* Copy the TOC header. */
! 	if (mcd_toc_header(sc, &th) < 0)
  		return EIO;
! 	ret_toc.th = th;
  
! 	/* Copy the TOC data. */
! 	ret_toc.rt.control = sc->toc[i].ctrl_adr;
! 	ret_toc.rt.addr_type = te->address_format;
! 	ret_toc.rt.track = i;
! 	if (te->address_format == CD_MSF_FORMAT) {
! 		ret_toc.rt.addr[1] = sc->toc[i].hd_pos_msf[0];
! 		ret_toc.rt.addr[2] = sc->toc[i].hd_pos_msf[1];
! 		ret_toc.rt.addr[3] = sc->toc[i].hd_pos_msf[2];
  	}
- 
  	/* Copy the data back. */
! 	copyout(&ret_toc, te->data,
! 	    sizeof(struct cd_toc_entry) + sizeof(struct ioc_toc_header));
  
  	return 0;
  }
--- 1136,1173 ----
  		return rc;
  
  	/* Find the TOC to copy. */
! 	for (i = te->starting_track; i < MCD_MAXTOCS; i++) {
! 	    if (i >= MCD_LASTPLUS1)
  		i = bcd2bin(sc->volinfo.trk_high) + 1;
  	
! 	    /* Verify starting track. */
! 	    if (i < bcd2bin(sc->volinfo.trk_low) ||
! 	      i > bcd2bin(sc->volinfo.trk_high) + 1)
  		return EINVAL;
  
! 	    /* Do we have room? */
! 	    if (te->data_len < sizeof(struct ioc_toc_header) +
! 	      sizeof(struct cd_toc_entry))
  		return EINVAL;
  
! 	    /* Copy the TOC header. */
! 	    if (mcd_toc_header(sc, &th) < 0)
  		return EIO;
! 	    ret_toc.th = th;
  
! 	    
! 	    /* Copy the TOC data. */
! 	    ret_toc.rt[i].control = sc->toc[i].ctrl_adr;
! 	    ret_toc.rt[i].addr_type = te->address_format;
! 	    ret_toc.rt[i].track = i;
! 	    if (te->address_format == CD_MSF_FORMAT) {
! 		ret_toc.rt[i].addr[1] = sc->toc[i].hd_pos_msf[0];
! 		ret_toc.rt[i].addr[2] = sc->toc[i].hd_pos_msf[1];
! 		ret_toc.rt[i].addr[3] = sc->toc[i].hd_pos_msf[2];
! 	    }
  	}
  	/* Copy the data back. */
! 	copyout(ret_toc.rt, te->data, te->data_len);
  
  	return 0;
  }
***************
*** 1190,1196 ****
  		printf("%s: getqchan: ctl=%d t=%d i=%d ttm=%d:%d.%d dtm=%d:%d.%d\n",
  		    sc->sc_dev.dv_xname, q->ctrl_adr, q->trk_no, q->idx_no,
  		    q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
! 		    q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
  	return 0;
  }
  
--- 1197,1203 ----
  		printf("%s: getqchan: ctl=%d t=%d i=%d ttm=%d:%d.%d dtm=%d:%d.%d\n",
  		    sc->sc_dev.dv_xname, q->ctrl_adr, q->trk_no, q->idx_no,
  		    q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
! 		    q->hd_pos_msf[0], q->hd_pos_msf[1], q->hd_pos_msf[2]);
  	return 0;
  }
  
***************
*** 1201,1206 ****
--- 1208,1214 ----
  {
  	struct mcd_qchninfo q;
  	struct cd_sub_channel_info data;
+ 	int i;
  
  	if (sc->debug)
  		printf("%s: subchan: af=%d df=%d\n", sc->sc_dev.dv_xname,
***************
*** 1216,1223 ****
  	data.header.audio_status = sc->audio_status;
  	data.what.position.data_format = CD_MSF_FORMAT;
  	data.what.position.track_number = bcd2bin(q.trk_no);
  
! 	if (copyout(&data, ch->data, sizeof(struct cd_sub_channel_info)) != 0)
  		return EFAULT;
  	return 0;
  }
--- 1224,1235 ----
  	data.header.audio_status = sc->audio_status;
  	data.what.position.data_format = CD_MSF_FORMAT;
  	data.what.position.track_number = bcd2bin(q.trk_no);
+ 	for (i = 0; i < 3; i++) {
+ 	    data.what.position.reladdr[i] = bcd2bin(q.trk_size_msf[i]);
+ 	    data.what.position.absaddr[i] = bcd2bin(q.hd_pos_msf[i]);
+ 	}
  
! 	if (copyout(&data, ch->data, ch->data_len) != 0)
  		return EFAULT;
  	return 0;
  }
***************
*** 1230,1236 ****
  	struct mcd_read2 pb;
  	int a = pt->start_track;
  	int z = pt->end_track;
! 	int rc;
  
  	if ((rc = mcd_read_toc(sc)) != 0)
  		return rc;
--- 1242,1248 ----
  	struct mcd_read2 pb;
  	int a = pt->start_track;
  	int z = pt->end_track;
! 	int rc,i;
  
  	if ((rc = mcd_read_toc(sc)) != 0)
  		return rc;
***************
*** 1242,1253 ****
  	    z < sc->volinfo.trk_low || z > sc->volinfo.trk_high)
  		return EINVAL;
  
! 	pb.start_msf[0] = sc->toc[a].hd_pos_msf[0];
! 	pb.start_msf[1] = sc->toc[a].hd_pos_msf[1];
! 	pb.start_msf[2] = sc->toc[a].hd_pos_msf[2];
! 	pb.end_msf[0] = sc->toc[z+1].hd_pos_msf[0];
! 	pb.end_msf[1] = sc->toc[z+1].hd_pos_msf[1];
! 	pb.end_msf[2] = sc->toc[z+1].hd_pos_msf[2];
  
  	return mcd_play(sc, &pb);
  }
--- 1254,1263 ----
  	    z < sc->volinfo.trk_low || z > sc->volinfo.trk_high)
  		return EINVAL;
  
! 	for (i = 0; i < 3; i ++) {
! 	    pb.start_msf[i] = sc->toc[a].hd_pos_msf[i];
! 	    pb.end_msf[i] = sc->toc[z+1].hd_pos_msf[i];
! 	}
  
  	return mcd_play(sc, &pb);
  }
***************
*** 1275,1285 ****
--- 1285,1314 ----
  	if (sc->debug)
  		printf("%s: play: retry=%d status=%d\n", sc->sc_dev.dv_xname,
  		    retry, st);
+ 	if (st & (MCD_ST_BUSY)) {
+ 		mcd_get_sense(sc);
+ 	}
  	if (!retry)
  		return ENXIO;
  
  	sc->audio_status = CD_AS_PLAY_IN_PROGRESS;
  	return 0;
+ }
+ 
+ void
+ mcd_get_sense(sc)
+ 	struct mcd_softc *sc;
+ {
+ 	u_short iobase = sc->iobase;
+ 	int retry, st;
+ 
+ 	for (retry = MCD_RETRIES; retry; retry--) {
+ 		outb(iobase + mcd_command, MCD_CMDGETSENSE);
+ 		if ((st = mcd_getstat(sc, 0)) != -1)
+ 			break;
+ 	}
+ 	printf("%s: getsense: retry=%d sense=%d\n", sc->sc_dev.dv_xname,
+ 	    retry, st);
  }
  
  int
*** mcdreg.h	Tue Feb 22 05:03:16 1994
--- /usr/src/sys/arch/i386/isa/mcdreg.h	Fri Nov 11 21:31:39 1994
***************
*** 64,69 ****
--- 64,72 ----
  #define	mcd_reset	1
  #define	mcd_xfer	1
  #define	mcd_ctl2	2 /* XXX Is this right? */
+ 			  /* This port is not documented in the Mitsumi
+ 			   * stuff that I have...
+ 			   */
  
  #define	mcd_config	3
  #define	MCD_MASK_DMA	0x07	/* bits 2-0 = DMA channel */
***************
*** 78,104 ****
  #define	MCD_ST_DOOROPEN		0x80
  #define	MCD_ST_DSKIN		0x40
  #define	MCD_ST_DSKCHNG		0x20
  #define	MCD_ST_BUSY		0x04
! #define	MCD_ST_AUDIOBSY		0x02
  
  /* commands known by the controller */
  #define	MCD_CMDRESET		0x00
  #define	MCD_CMDGETVOLINFO	0x10	/* gets mcd_volinfo */
  #define	MCD_CMDGETQCHN		0x20	/* gets mcd_qchninfo */
  #define	MCD_CMDGETSTAT		0x40	/* gets a byte of status */
  #define	MCD_CMDSETMODE		0x50	/* set transmission mode, needs byte */
! #define	MCD_MD_RAW		0x60
! #define	MCD_MD_COOKED		0x01
! #define	MCD_MD_TOC		0x05
  #define	MCD_CMDSTOPAUDIO	0x70
  #define	MCD_CMDGETVOLUME	0x8E	/* gets mcd_volume */
  #define	MCD_CMDSETVOLUME	0xAE	/* sets mcd_volume */
  #define	MCD_CMDREAD1		0xB0	/* read n sectors */
  #define	MCD_CMDREAD2		0xC0	/* read from-to */
  #define	MCD_CMDCONTINFO		0xDC	/* Get controller info */
  #define	MCD_CMDEJECTDISK	0xF6
  #define	MCD_CMDCLOSETRAY	0xF8
  #define	MCD_CMDLOCKDRV		0xFE	/* needs byte */
  #define	MCD_LK_UNLOCK	0x00
  #define	MCD_LK_LOCK	0x01
  #define	MCD_LK_TEST	0x02
--- 81,158 ----
  #define	MCD_ST_DOOROPEN		0x80
  #define	MCD_ST_DSKIN		0x40
  #define	MCD_ST_DSKCHNG		0x20
+ #define MCD_ST_SPINNING		0x10
+ #define MCD_ST_AUDIODISK	0x08	/* Audio Disk is in */
  #define	MCD_ST_BUSY		0x04
! #define	MCD_ST_AUDIOBSY		0x02	/* Audio Disk is Playing */
! #define MCD_ST_CMDCHECK		0x01	/* Command error */
  
  /* commands known by the controller */
+ 
  #define	MCD_CMDRESET		0x00
  #define	MCD_CMDGETVOLINFO	0x10	/* gets mcd_volinfo */
+ #define MCD_CMDGETDISCINFO	0x11	/* gets mcd_disk information */
  #define	MCD_CMDGETQCHN		0x20	/* gets mcd_qchninfo */
+ #define MCD_CMDGETSENSE		0x30	/* gets sense info */
  #define	MCD_CMDGETSTAT		0x40	/* gets a byte of status */
  #define	MCD_CMDSETMODE		0x50	/* set transmission mode, needs byte */
! #define	MCD_MD_RAW		0x60	/* resets drive to raw mode */
  #define	MCD_CMDSTOPAUDIO	0x70
+ #define MCD_CMDSTOPAUDIOTIME	0x80	/* ???? */
  #define	MCD_CMDGETVOLUME	0x8E	/* gets mcd_volume */
+ #define MCD_CMDSETDRIVEMODE	0xA0	/* Set drive mode */
+ #define MCD_CMDREADUPC		0xA2	/* Get UPC info */
  #define	MCD_CMDSETVOLUME	0xAE	/* sets mcd_volume */
  #define	MCD_CMDREAD1		0xB0	/* read n sectors */
  #define	MCD_CMDREAD2		0xC0	/* read from-to */
+ #define MCD_CMDSTARTAUDIOMSF	0xC1	/* read audio data */
+ #define MCD_CMDREADFAST		0xC1	/* Read lots of data from the drive */
+ #define MCD_CMDGETDRIVEMODE	0xC2	/* Get the drive mode */
+ #define MCD_CMDREAD		0xC3	/* Read data from the drive */
+ #define MCD_CMDSETINTERLEAVE	0xC8	/* Adjust the interleave */
  #define	MCD_CMDCONTINFO		0xDC	/* Get controller info */
+ #define MCD_CMDSTOP		0xF0	/* Stop everything */
  #define	MCD_CMDEJECTDISK	0xF6
  #define	MCD_CMDCLOSETRAY	0xF8
  #define	MCD_CMDLOCKDRV		0xFE	/* needs byte */
+ 
+ /* Transmission Mode Stuff */
+ 
+ #define MCD_MD_TESTMODE		0x80	/* 0 = DATALENGTH setting is valid */
+ #define MCD_MD_DATALENGTH	0x40	/* 0 = Read User Data only */
+ 					/* 1 = Read Raw sectors (2352 bytes) */
+ #define MCD_MD_ECCMODE		0x20	/* 0 = Use secondary correction */
+ 					/* 1 = Don't use secondary ECC */
+ #define MCD_MD_SPINDOWN		0x08	/* 0 = Spin Up, 1 = Spin Down */
+ #define MCD_MD_GET_TOC		0x04	/* 1 = Get TOC on GETQCHAN */
+ 					/* 0 = Get UPC on next GETQCHAN */
+ #define MCD_MD_MUTEDATA		0x01	/* 1 = Don't play back Data as
+ 						audio */
+ #define	MCD_MD_TOC		0x05	/* Get TOC and don't play it as audio */
+ #define	MCD_MD_COOKED		0x01	/* Not really, this simply means
+ 					 * it doesn't get played on the 
+ 					 * speakers */
+ 
+ /* DMA Enable Stuff */
+ #define MCD_DMA_IRQFLAG		0x10	/* Set data0 for IRQ click */
+ 
+ #define MCD_DMA_PREIRQ		0x01	/*  All of these are for */
+ #define MCD_DMA_POSTIRQ		0x02	/*  MCD_DMA_IRQFLAG..	*/
+ #define	MCD_DMA_ERRIRQ		0x04	/*			*/
+ 
+ #define MCD_DMA_TIMEOUT		0x08	/* Set data0  for DMA Timeout */
+ #define MCD_DMA_UPCFLAG		0x04	/* 1 = Next command will be READUPC */
+ 
+ #define MCD_DMA_DMAMODE		0x02	/* 1 = Data uses DMA */
+ #define MCD_DMA_TRANSFERLENGTH	0x01	/* data0 = MSB, data1 = LSB of
+ 					   block length */
+ struct mcd_dma_mode {
+ 	u_char	dma_mode;
+ 	u_char	data0;		/* If dma_mode & 0x10: Use IRQ settings */
+ 	u_char	data1;		/* Used if dma_mode & 0x01 */
+ } ;
+ 
+ /* Lock Stuff */
  #define	MCD_LK_UNLOCK	0x00
  #define	MCD_LK_LOCK	0x01
  #define	MCD_LK_TEST	0x02
***************
*** 126,131 ****
--- 180,190 ----
  	u_char	v0ls;
  };
  
+ struct mcd_holdtime {
+ 	u_char	units_of_ten_seconds;	
+ 			/* If this is 0, the default (12) is used) */
+ };
+ 
  struct mcd_read1 {
  	bcd_t	start_msf[3];
  	u_char	nsec[3];
***************
*** 134,137 ****
--- 193,205 ----
  struct mcd_read2 {
  	bcd_t	start_msf[3];
  	bcd_t	end_msf[3];
+ };
+ 
+ struct mcd_rawsector {
+ 	u_char	sync1[12];
+ 	u_char	header[4];
+ 	u_char	subheader1[4];
+ 	u_char	subheader2[4];
+ 	u_char	data[2048];
+ 	u_char	ecc_bits[280];
  };