Coherent4.2.10/conf/haicd/src/haicd.c

Compare this file to the similar file:
Show the results in this format:

/*
 *  haicd.c --	  Device module for CD-ROM.
 *
 *  Copyright (c) 1993, Christopher Sean Hilton - All rights reserved
 *
 * $Id$
 *
 * $Log$
 */

static char rcsid[] = "#(@) haicd.c $Revision$";

#define LOCAL		static
#define Register	register

#define _KERNEL	1

#include <errno.h>
#include <stddef.h>
#include <sys/cdrom.h>
#include <sys/cmn_err.h>
#include <sys/coherent.h>
#include <sys/file.h>
#include <sys/haiscsi.h>
#include <sys/inode.h>
#include <sys/sched.h>
#include <sys/stat.h>

/***********************************************************************
 *  SCSI Command Opcodes.
 */

#define INQUIRY		0x12
#define STARTSTOPUNIT	0x1b
#define	  IMMEDIATE	0x0001
#define	  STARTUNIT	0x0002
#define	  EJECT		0x0004
#define READCAPACITY	0x25
#define G1READ		0x28
#define READSUBCHNL	0x42
#define READTOC		0x43
#define PLAYMSF		0x47
#define PLAYTRKIND	0x48
#define	PAUSERESUME	0x4b

#pragma align 1

typedef struct readcap_s *readcap_p;

typedef struct readcap_s {
    unsigned long   blockcount;
    unsigned long   blocksize;
} readcap_t;

typedef struct msf_s *msf_p;

typedef struct msf_s {
    unsigned char   reserved,
                    minutes,
                    seconds,
                    frame;
} msf_t;

typedef struct toc_entry_s *toc_entry_p;

typedef struct toc_entry_s {
	unsigned char   reserved0;
	unsigned char   adrcontrol;
	unsigned char   tracknumber;
	unsigned char   reserved1;
	union {
		msf_t		msf;
		unsigned long   lba;
	} address;
} toc_entry_t;

typedef struct toc_data_s *toc_data_p;

typedef struct toc_data_s {
    unsigned short  datalength;
    unsigned char   firsttrack;
    unsigned char   lasttrack;
    toc_entry_t     toc_entries[100];
} toc_data_t;

typedef struct subchnlq_s *subchnlq_p;

typedef struct subchnlq_s {
	unsigned char	sq_reserved,
			sq_audiostatus;
	unsigned short	sq_datalength;
	unsigned char	sq_dataformat,
			sq_adrcontrol,
			sq_track,
			sq_index;
	union {
		msf_t		msf;		
		unsigned long 	lba;
	}		sq_absaddr;
	union {
		msf_t		msf;		
		unsigned long 	lba;
	}		sq_reladdr;
} subchnlq_t;

#pragma align

/***********************************************************************
 *  CD-ROM control structure.
 */

typedef struct cdrctrl_s *cdrctrl_p;

typedef struct cdrctrl_s {
    	haft_t		* haft;		/* Host adapter function table */
	short	   	inuse;		/* In use flag */
	srb_t	  	srb;		/* SCSI request block: this device */
	readcap_t	cdrsize;	/* Results of Read Capacity Command */
	toc_data_p	toclba;		/* Results of Read TOC Command (lba) */
	toc_data_p	tocmsf;		/* Results of Read TOC Command (msf) */
} cdrctrl_t;

cdrctrl_p cdrdevs[MAXDEVS] = {
	NULL,			   /* ID 0 */
	NULL,			   /* ID 1 */
	NULL,			   /* ID 2 */
	NULL,			   /* ID 3 */
	NULL,			   /* ID 4 */
	NULL,			   /* ID 5 */
	NULL,			   /* ID 6 */
	NULL			   /* ID 7 */
};

LOCAL void	cd_open		__PROTO((dev_t, int));
LOCAL void	cd_close	__PROTO((dev_t));
LOCAL void	cd_ioctl	__PROTO((dev_t, int, char *));
LOCAL int	cd_load		__PROTO((haft_t *, int));
LOCAL void	cd_read		__PROTO((dev_t, IO *));

static dca_t dca = {
	cd_open,	/* Open */
	cd_close,	/* Close */
	hainonblk,	/* Block */
	cd_read,	/* Read */
	NULL,		/* Write */
	cd_ioctl,	/* Ioctl */
	cd_load,	/* Load */
	NULL,		/* Unload */
	NULL		/* Poll */
};

dca_p cdrdca =&dca;

extern int ukcopy	__PROTO((vaddr_t, void *, size_t));
extern int kucopy	__PROTO((void *, vaddr_t, size_t));

/***********************************************************************
 *  cd_load() --	   Load entry point for CD-ROM drives.
 *  
 *  Allocate space for the control structure and the CD-ROM drive's
 *  buffer (very small buffer).
 */

#if __USE_PROTO__
LOCAL int cd_load(haft_t * haft, int id)
#else
LOCAL int
cd_load(haft, id)
haft_t * haft;
int	 id;
#endif
{
	cdrctrl_p c;

	if (!haft) {
	    cmn_err(CE_WARN,
		    "haicd: No host adapter function table - load failed");
	    return 0;
	}

	c = kalloc(sizeof(cdrctrl_t));
	if (!c) {
		cmn_err(CE_WARN, "CD-ROM Module load failed, no memory.");
		return 0;
	}
	memset(c, 0, sizeof(cdrctrl_t));
	c->haft = haft;
	c->srb.target = id;
	c->srb.lun = 0;
	cdrdevs[id] = c;
	
	cmn_err(CE_CONT, "%d: HAI CD-ROM device module: v1.9\n", id);
	return 1;
}   /* cd_load() */

/***********************************************************************
 *  checkmedia() --	 Check for new media in the drive.
 *  
 *  Use the Test Unit Read SCSI Command to check for new or any media
 *  in the drive.
 */
#if __USE_PROTO__
LOCAL int checkmedia(cdrctrl_p c)
#else
LOCAL int
checkmedia(c)
cdrctrl_p  c;
#endif
{
	register srb_p r = &(c->srb);
	
	r->buf.space = PHYS_ADDR | WATCH_REQACK;
	r->buf.ba_phys = NULL;
	r->buf.size = 0;
	r->xferdir = 0;
	r->timeout = 6;
	memset(&(r->cdb.g0), 0, sizeof(cdb_t));	/* Test Unit Ready */
	memset(r->sensebuf, 0, sizeof(r->sensebuf));
	doscsi(c->haft, r, 4, pritape, slpriSigCatch, "cdrchkmda");
	return (r->status == ST_GOOD);
}   /* checkmedia() */

/***********************************************************************
 *  iscdromdev() --	 Check the Inquiry bytes to make sure this is
 *					  a CD-ROM Drive.
 *  
 *  Get the first 16 bytes of inquiry information for this drive to
 *  make sure that the device out there on the bus is a CD-ROM Device.
 *  Used during initializaion.
 */

#if __USE_PROTO__
LOCAL int iscdromdev(cdrctrl_p c)
#else
LOCAL int
iscdromdev(c)
cdrctrl_p  c;
#endif
{
	register srb_p  r = &(c->srb);
	unsigned char   inqbuf[16];

	memset(inqbuf, 0, sizeof(inqbuf));
	r->buf.space = KRNL_ADDR | WATCH_REQACK;
	r->buf.ba_virt = (vaddr_t) inqbuf;
	r->xferdir = DMAREAD;
	memset(&(r->cdb), 0, sizeof(cdb_t));
	r->cdb.g0.opcode = INQUIRY;
	r->cdb.g0.xfr_len = r->buf.size = sizeof(inqbuf);
	doscsi(c->haft, r, 4, pritape, slpriSigCatch, "cdropen");
	return (r->status == ST_GOOD && (inqbuf[0] & 0x1f) == 0x05);
}   /* iscdromdev() */

/***********************************************************************
 *  readcapacity() --   Read the Capacity of the CD in the drive.
 *  
 *  Use the Read Capacity command to get the number of blocks on the
 *  CD ROM and the size of each block. This information can be accessed
 *  by user programs through the HAICD_READCAP I/O Control command.
 */

#if __USE_PROTO__
LOCAL int readcapacity(cdrctrl_p c)
#else
LOCAL int
readcapacity(c)
cdrctrl_p  c;
#endif
{
	register srb_p r = &(c->srb);

	r->buf.space = KRNL_ADDR | WATCH_REQACK;
	r->buf.ba_virt = (vaddr_t) &(c->cdrsize);
	r->buf.size = sizeof(c->cdrsize);
	memset(&(r->cdb), 0, sizeof(cdb_t));
	r->cdb.g1.opcode = READCAPACITY;
	doscsi(c->haft, r, 4, pritape, slpriSigCatch, "cdropen");
	if (r->status != ST_GOOD)
		return 0;
	else {
		flip(c->cdrsize.blockcount);
		flip(c->cdrsize.blocksize);
		return 1;
	}
}   /* readcapacity() */

/***********************************************************************
 *  readtoclba() --		Read the Disk's Table of Contents.
 *  
 *  Read the CD's TOC into a kalloced buffer.  The buffer only stays
 *  around while the Drive is openned and is allocated just large enough
 *  to hold the TOC info for this disk. This information is returned
 *  by the HAICD_READTOC I/O control command.
 */

#if __USE_PROTO__
LOCAL toc_data_p readtoclba(cdrctrl_p c)
#else
LOCAL toc_data_p
readtoclba(c)
cdrctrl_p  c;
#endif
{
	register srb_p		r = &(c->srb);
	register int		entries;
	static toc_data_t   	toclba;
	toc_data_p		p;
	toc_entry_p		q;

	r->buf.space = KRNL_ADDR | WATCH_REQACK;
	r->buf.ba_virt = (caddr_t) &toclba;
	memset(&(r->cdb), 0, sizeof(cdb_t));
	r->cdb.g1.opcode = READTOC;
	r->cdb.g1.xfr_len = r->buf.size = sizeof(toclba);
	flip(r->cdb.g1.xfr_len);
	doscsi(c->haft, r, 4, pritape, slpriSigCatch, "cdropen");
	if (r->status != ST_GOOD)
		return NULL;

	flip(toclba.datalength);
	if (!toclba.datalength) {
	    cmn_err(CE_WARN, "haicd: Bad or empty table of contents.");
	    return NULL;
	}
	if (!(p = kalloc(toclba.datalength)))
		return NULL;

	entries = (toclba.datalength - 2) / sizeof(toc_entry_t);
	q = toclba.toc_entries;
	while (entries-- > 0) {
		flip(q->address. lba);
		++q;		
	}
	memcpy(p, &toclba, toclba.datalength + sizeof(toclba.datalength));
	return (c->toclba = p);
}   /* readtoclba() */

/***********************************************************************
 *  readtocmsf() --		Read the Disk's Table of Contents.
 *  
 *  Read the CD's TOC into a kalloced buffer.  The buffer only stays
 *  around while the Drive is openned and is allocated just large enough
 *  to hold the TOC info for this disk. This information is returned
 *  by the HAICD_READTOC I/O control command.
 */

#if __USE_PROTO__
LOCAL toc_data_p readtocmsf(cdrctrl_p c)
#else
LOCAL toc_data_p
readtocmsf(c)
cdrctrl_p  c;
#endif
{
	register srb_p		r = &(c->srb);
	static toc_data_t   	tocmsf;
	toc_data_p		p;

	r->buf.space = KRNL_ADDR | WATCH_REQACK;
	r->buf.ba_virt = (caddr_t) &tocmsf;
	memset(&(r->cdb), 0, sizeof(cdb_t));
	r->cdb.g1.opcode = READTOC;
	r->cdb.g1.lun = 2;
	r->cdb.g1.xfr_len = r->buf.size = sizeof(tocmsf);
	flip(r->cdb.g1.xfr_len);
	doscsi(c->haft, r, 4, pritape, slpriSigCatch, "cdropen");
	if (r->status != ST_GOOD)
		return NULL;

	flip(tocmsf.datalength);
	if (!tocmsf.datalength) {
	    cmn_err(CE_WARN, "haicd: Bad or empty table of contents.");
	    return NULL;
	}
	if (!(p = kalloc(tocmsf.datalength)))
		return NULL;

	memcpy(p, &tocmsf, tocmsf.datalength + sizeof(tocmsf.datalength));
	return (c->tocmsf = p);
}   /* readtocmsf() */

/***********************************************************************
 *  cd_open() --	   Open entry point for CD-ROM drives.
 *  
 *  Check to make sure that there is a CD-ROM in the drive and if so
 *  read the TOC.
 */

#if __USE_PROTO__
LOCAL void cd_open(dev_t dev, int mode)
#else
LOCAL void
cd_open(dev, mode)
dev_t		dev;
int		mode;
#endif
{
	register cdrctrl_p  c = cdrdevs[tid(dev)];
	register srb_p	  r;
	int				 s;

	s = sphi();
	if (!c) {
		set_user_error (ENXIO);
		goto exit;		
	}

	if (c->inuse || mode != IPR) {
		set_user_error (EACCES);
		goto exit;		
	}

	c->inuse = 1;
	r = &(c->srb);
	r->dev = dev;
	r->target = tid(dev);
	r->lun = lun(dev);

	if (!checkmedia(c)) {
		if (printerror(r, "cdrom: <Door open - Check media>"))
			set_user_error (ENXIO);
		goto openfailed;
	}

	if (!iscdromdev(c)) {
		if (r->status == ST_GOOD) {
			cmn_err(CE_WARN,
				"(%d, %d) haicd: Device is not a CD-ROM",
				major(r->dev),
				minor(r->dev));
			set_user_error (ENXIO);
		}
		else {
			if (printerror(r, "cdrom: <Error on inquiry>"))
				set_user_error (EIO);
		}
		goto openfailed;
	}

	if (!readcapacity(c)) {
		if (printerror(r, "cdrom: <Error on read capacity>"))
			set_user_error (EIO);
		goto openfailed;
	}

	if (!readtoclba(c)) {
		if (printerror(r, "cdrom: <Error on read table of contents>"))
			set_user_error (EIO);
		goto openfailed;
	}

	if (!readtocmsf(c)) {
		if (printerror(r, "cdrom: <Error on read table of contents>"))
			set_user_error (EIO);
		goto openfailed;
	}

	/*******************************************************************
	 *  Success!
	 */
	 goto exit;

openfailed:
	c->inuse = 0;
	
exit:
	spl(s);
}   /* cd_open() */

/***********************************************************************
 *  cd_close() --	  Close routine for CD-ROM drive.
 */

#if __USE_PROTO__
LOCAL void cd_close(dev_t dev)
#else
LOCAL void cd_close(dev)
dev_t   dev;
#endif
{
	register cdrctrl_p c = cdrdevs[tid(dev)];

	if (!c)
		set_user_error (ENXIO);
	else {
		kfree(c->tocmsf);
		kfree(c->toclba);
		c->inuse = 0;
	}
}   /* cd_close() */

/***********************************************************************
 *  cd_read() --	   Read entry point for CD-ROM Devices.
 *  
 *  Read a block from the CD-ROM drive.
 */

#if __USE_PROTO__
LOCAL void cd_read(dev_t dev, IO *iop)
#else
LOCAL void
cd_read(dev, iop)
dev_t   dev;
IO	  *iop;
#endif
{
	register cdrctrl_p	c = cdrdevs[tid(dev)];
	register srb_p		r = &(c->srb);
	register g1cmd_p	g1 = &(r->cdb.g1);
	unsigned long		startblock,
				blockcount;

	if (!c) {
		set_user_error (EINVAL);
		return;
	}

	if (iop->io_seek % c->cdrsize.blocksize || iop->io_ioc % c->cdrsize.blocksize) {
		set_user_error (EINVAL);
		return;			
	}	

	startblock = iop->io_seek / c->cdrsize.blocksize;
	if (startblock > c->cdrsize.blockcount) {
		set_user_error (EINVAL);
		return;			
	}
	blockcount = iop->io_ioc / c->cdrsize.blocksize;
	if (startblock + blockcount > c->cdrsize.blockcount) {
		blockcount = c->cdrsize.blockcount - startblock;
		if (!blockcount) {
			iop->io_ioc -= blockcount * c->cdrsize.blocksize;
			return;
		}
	}
	memset(g1, 0, sizeof(cdb_t));
	g1->opcode = G1READ;
	g1->lun = (r->lun << 5);
	g1->lba = startblock;
	flip(g1->lba);
	g1->xfr_len = blockcount;
	flip(g1->xfr_len);

	r->target = tid(dev);
	r->lun = lun(dev);
	r->timeout = 4;
	switch (iop->io_seg) {
	case IOSYS:
		r->buf.space = KRNL_ADDR;
		r->buf.ba_virt = iop->io.vbase;
		break;
	case IOUSR:
		r->buf.space = USER_ADDR;
		r->buf.ba_virt = iop->io.vbase;
		break;
	case IOPHY:
		r->buf.space = PHYS_ADDR;
		r->buf.ba_phys = iop->io.pbase;
		break;
	}
	r->buf.size = (blockcount * c->cdrsize.blocksize);
	r->xferdir = DMAREAD;
	doscsi(c->haft, r, 1, pritape, slpriSigCatch, "cdrread");
	switch (r->status) {
	case ST_GOOD:
		iop->io_ioc -= r->buf.size;
		break;
	case ST_CHKCOND:
		printsense(r->dev, "Read failed", (extsense_p) r->sensebuf);
		set_user_error (EIO);
		break;
	case ST_USRABRT:
		break;
	default:
		cmn_err(CE_WARN,
			"(%d, %d) haicd: Read failed: status (0x%x)",
			major(r->dev),
			minor(r->dev),
			r->status);
		set_user_error (EIO);
		break;
	}
	return;
}   /* cd_read() */

#if __USE_PROTO__
LOCAL int cd_pauseresume(register cdrctrl_p c, int pauseflag)
#else
LOCAL int
cd_pauseresume(c, pauseflag)
register cdrctrl_p	c;
int			pauseflag;
#endif
{
	register srb_p	r = &(c->srb);
	register char	*g2 = (char *) &(r->cdb. g1);
	
	r->buf.space = PHYS_ADDR | WATCH_REQACK;
	r->buf.ba_phys = NULL;
	r->buf.size = 0;
	r->xferdir = 0;
	r->timeout = 6;
	memset(g2, 0, sizeof(cdb_t));
	g2[0] = PAUSERESUME;
	g2[8] = (pauseflag == 0) ? 0 : 1;
	doscsi(c->haft, r, 2, pritape, slpriSigCatch, "cdrioctl");

	return (r->status == ST_GOOD);
}	/* cd_pauseresume() */

#if __USE_PROTO__
LOCAL int cd_playmsf(register cdrctrl_p	c, caddr_t uvec)
#else
LOCAL int 
cd_playmsf(c, uvec)
register cdrctrl_p	c;
caddr_t			uvec;
#endif
{
	register srb_p		r = &(c->srb);
	register char		*g2 = (char *) &(r->cdb. g1);
	struct cdrom_msf	play_addr;

	if (!ukcopy(uvec, &play_addr, sizeof(struct cdrom_msf)))
		return 0;

	r->buf.space = PHYS_ADDR | WATCH_REQACK;
	r->buf.ba_phys = NULL;
	r->buf.size = 0;
	r->xferdir = 0;
	r->timeout = 6;
	memset(g2, 0, sizeof(cdb_t));
	g2[0] = PLAYMSF;
	g2[3] = play_addr. cdmsf_min0;
	g2[4] = play_addr. cdmsf_sec0;
	g2[5] = play_addr. cdmsf_frame0;
	g2[6] = play_addr. cdmsf_min1;
	g2[7] = play_addr. cdmsf_sec1;
	g2[8] = play_addr. cdmsf_frame1;
	doscsi(c->haft, r, 2, pritape, slpriSigCatch, "cdrioctl");
	if (r->status != ST_GOOD) {
		set_user_error(EIO);
		return 0;
        }
	else
	        return 1;
}	/* cd_playmsf() */

#if __USE_PROTO__
LOCAL int cd_playtrkind(register cdrctrl_p c, struct cdrom_ti *cdti)
#else
LOCAL int cd_playtrkind(c, cdti)
register cdrctrl_p	c;
struct cdrom_ti		*cdti;
#endif
{
	register srb_p		r = &(c->srb);
	register char		*g2 = (char *) &(r->cdb. g1);

	r->buf.space = PHYS_ADDR | WATCH_REQACK;
	r->buf.ba_phys = NULL;
	r->buf.size = 0;
	r->xferdir = 0;
	r->timeout = 6;
	memset(g2, 0, sizeof(cdb_t));
	g2[0] = PLAYTRKIND;
	g2[4] = cdti->cdti_trk0;
	g2[5] = cdti->cdti_ind0;
	g2[7] = cdti->cdti_trk1;
	g2[8] = cdti->cdti_ind1;
	doscsi(c->haft, r, 2, pritape, slpriSigCatch, "cdrioctl");
	if (r->status != ST_GOOD) {
		set_user_error(EIO);
		return 0;
	}
	else
 	        return 1;
}	/* cd_playtrkind() */

#if __USE_PROTO__
LOCAL int cd_startstop(register cdrctrl_p c, int opflags)
#else
LOCAL int cd_startstop(c, opflags)
register cdrctrl_p	c;
int			opflags;
#endif
{
	register srb_p		r = &(c->srb);
	register g0cmd_p	g0 = &(r->cdb. g0);
	
	r->buf.space = PHYS_ADDR | WATCH_REQACK;
	r->buf.ba_phys = NULL;
	r->buf.size = 0;
	r->xferdir = 0;
	r->timeout = 6;
	memset(g0, 0, sizeof(cdb_t));
	g0->opcode = STARTSTOPUNIT;
	if (opflags & IMMEDIATE)
		g0->lun_lba |= 1;
	if (opflags & STARTUNIT)
		g0->xfr_len |= 1;

	/***************************************************************
         *  Because of the way I read the table of contents into memory
         *  and hold it until the drive is closed, Do not implement the
         *  Eject command right now. This prevents the bug of someone
         *  openning up the Drive with one CD and then changing the
         *  media via an eject command. This does nothing to prevent
         *  the user from using the front panel eject button to do
         *  the same thing.
         */

#if	0
	if (opflags & EJECT)
		g0->xfr_len |= 2;
#endif
	doscsi(c->haft, r, 2, pritape, slpriSigCatch, "cdrioctl");

	return (r->status == ST_GOOD);
}	/* cd_startstop() */

#if __USE_PROTO__
LOCAL int cd_readtocentry(register cdrctrl_p c, struct cdrom_tocentry *cdte)
#else
LOCAL int cd_readtocentry(c, cdte)
register cdrctrl_p	c;
struct cdrom_tocentry	*cdte;
#endif
{
	int			entries;
	toc_entry_p 		lba,
				msf;

	entries = (c->toclba->datalength - 2) / 8;
        lba = c->toclba->toc_entries;
        msf = c->tocmsf->toc_entries;
        while (entries--) {
                if (lba->tracknumber == cdte->cdte_track) {
                        cdte->cdte_adr = (lba->adrcontrol >> 4) & 0x0f;
                        cdte->cdte_ctrl = (lba->adrcontrol & 0x0f);
                        if (cdte->cdte_format == CDROM_LBA) {
                                cdte->cdte_addr.lba = lba->address.lba;
                        }
                        else if (cdte->cdte_format == CDROM_MSF) {
                                cdte->cdte_addr.msf.minute = msf->address.msf.minutes;
                                cdte->cdte_addr.msf.second = msf->address.msf.seconds;
                                cdte->cdte_addr.msf.frame = msf->address.msf.frame;
                        }
                        else {
                                set_user_error(ENXIO);
                                return 0;
                        }
                        return 1;
                }
                ++lba;
                ++msf;
        }
        set_user_error(ENXIO);
        return 0;
}	/* cd_readtocentry() */

#if __USE_PROTO__
LOCAL int cd_subchnl(register cdrctrl_p	c, struct cdrom_subchnl *cdsc)
#else
LOCAL int cd_subchnl(c, cdsc)
register cdrctrl_p	c;
struct cdrom_subchnl	*cdsc;
#endif
{
	register srb_p	r = &(c->srb);
	register char	*g2 = (char *) &(r->cdb. g1);
	subchnlq_t	sq;

	r->buf.space = KRNL_ADDR | WATCH_REQACK;
	r->buf.ba_virt = (caddr_t) &sq;
	r->buf.size = sizeof(subchnlq_t);
	memset(g2, 0, sizeof(cdb_t));
	g2[0] = READSUBCHNL;
	if (cdsc->cdsc_format == CDROM_MSF)
		g2[1] |= 0x02;	/* Specify M/S/F format */
	g2[2] = 0x40;		/* Specify Subchannel Q data required */
	g2[3] = 1;		/* Subchannel Q data format (CD-ROM position) */
	g2[7] = 0x00;
	g2[8] = sizeof(subchnlq_t);
	doscsi(c->haft, r, 4, pritape, slpriSigCatch, "cdropen");
	if (r->status != ST_GOOD) {
		set_user_error(EIO);
		return 0;
	}

	cdsc->cdsc_audiostatus = sq. sq_audiostatus;
	cdsc->cdsc_adr = (sq.sq_adrcontrol >> 4) & 0x0f;
	cdsc->cdsc_ctrl = (sq.sq_adrcontrol & 0x0f);
	cdsc->cdsc_trk = sq. sq_track;
	cdsc->cdsc_ind = sq. sq_index;
	if (cdsc->cdsc_format == CDROM_MSF) {
		cdsc->cdsc_absaddr.msf.minute = sq.sq_absaddr.msf.minutes;
		cdsc->cdsc_absaddr.msf.second = sq.sq_absaddr.msf.seconds;
		cdsc->cdsc_absaddr.msf.frame = sq.sq_absaddr.msf.frame;

		cdsc->cdsc_reladdr.msf.minute = sq.sq_reladdr.msf.minutes;
		cdsc->cdsc_reladdr.msf.second = sq.sq_reladdr.msf.seconds;
		cdsc->cdsc_reladdr.msf.frame = sq.sq_reladdr.msf.frame;
	}
	else {
		cdsc->cdsc_absaddr.lba = sq. sq_absaddr. lba;
		flip(cdsc->cdsc_absaddr.lba);
		cdsc->cdsc_reladdr.lba = sq. sq_reladdr. lba;
		flip(cdsc->cdsc_reladdr.lba);
	}
	return 1;
}	/* cd_subchnl() */
 
/***********************************************************************
 *  cd_ioctl() --	  I/O Control routine for CD-ROM drives.
 */

#if __USE_PROTO__
LOCAL void cd_ioctl(dev_t dev, int cmd, char *vec)
#else
LOCAL void
cd_ioctl(dev, cmd, vec)
dev_t   dev;
int	cmd;
char	*vec;
#endif
{
	register cdrctrl_p 	c = cdrdevs[tid(dev)];
	struct cdrom_ti		cdti;
	struct cdrom_tochdr 	cdth;
	struct cdrom_tocentry 	cdte;
	struct cdrom_subchnl	cdsc;
	
	if (!c) {
		set_user_error (ENXIO);
		return;
	}

	switch (cmd)	 {
	case CDROMPAUSE:	/* pause */
		if (!cd_pauseresume(c, 0))
			set_user_error(EIO);
		return;
		
	case CDROMRESUME:	/* resume */
		if (!cd_pauseresume(c, 1))
			set_user_error(EIO);
		return;
		
	case CDROMPLAYMSF:	/* play audio */
		cd_playmsf(c, vec);
		return;

	case CDROMPLAYTRKIND:	/* play track */
		if (!ukcopy(vec, &cdti, sizeof(struct cdrom_ti)))
			return;
		cd_playtrkind(c, &cdti);
		return;
	case CDROMREADTOCHDR:	/* read the TOC header */
		cdth.cdth_trk0 = c->toclba->firsttrack;
		cdth.cdth_trk1 = c->toclba->lasttrack;
		kucopy(&cdth, vec, sizeof(struct cdrom_tochdr));
		return;
		
	case CDROMREADTOCENTRY:	/* read a TOC entry */
		if (!ukcopy(vec, &cdte, sizeof(struct cdrom_tocentry)))
			return;

		if (!cd_readtocentry(c, &cdte))
			return;
			
		kucopy(&cdte, vec, sizeof(struct cdrom_tocentry));
	    	return;

	case CDROMSTOP:		/* stop the drive motor */
		if (!cd_startstop(c, 0))
			set_user_error(EIO);
		return;

	case CDROMSTART:	/* turn the motor on */
		if (!cd_startstop(c, STARTUNIT))
			set_user_error(EIO);
		return;

	case CDROMEJECT:	/* eject CD-ROM media */
		if (!cd_startstop(c, EJECT))
			set_user_error(EIO);
		return;

	case CDROMVOLCTRL:	/* volume control */
		return;

	case CDROMSUBCHNL:	/* read sub-channel data */
		if (!ukcopy(vec, &cdsc, sizeof(struct cdrom_subchnl)))
			return;

		if (!cd_subchnl(c, &cdsc))
			return;

		kucopy(&cdsc, vec, sizeof(struct cdrom_subchnl));
		return;

	case CDROMREADMODE1:	/* read type-1 data */
		return;

	case CDROMREADMODE2:	/* read type-2 data */
	default:
		set_user_error(ENXIO);
		return;
	}
}   /* cd_ioctl() */

/* End of file */