summaryrefslogtreecommitdiff
path: root/sys/dev/atapi
diff options
context:
space:
mode:
authorJason Downs <downsj@cvs.openbsd.org>1996-06-08 09:12:48 +0000
committerJason Downs <downsj@cvs.openbsd.org>1996-06-08 09:12:48 +0000
commitfdc68389b9dfe6e5d35bc6cb6b180e9daf9e61e2 (patch)
treea4460d2ec87ff4da7cc88b4cae0ebcf64034dc98 /sys/dev/atapi
parent9a887c22a316f9b94faed82ec7af6c138ff71262 (diff)
Initial support ATAPI CD-ROMs.
Based on Manuel Bouyer's drivers.
Diffstat (limited to 'sys/dev/atapi')
-rw-r--r--sys/dev/atapi/acd.c1337
-rw-r--r--sys/dev/atapi/atapi.h250
-rw-r--r--sys/dev/atapi/atapiconf.c676
-rw-r--r--sys/dev/atapi/atapilink.h352
-rw-r--r--sys/dev/atapi/files.atapi14
5 files changed, 2629 insertions, 0 deletions
diff --git a/sys/dev/atapi/acd.c b/sys/dev/atapi/acd.c
new file mode 100644
index 00000000000..3b95e279722
--- /dev/null
+++ b/sys/dev/atapi/acd.c
@@ -0,0 +1,1337 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/disk.h>
+#include <sys/cdio.h>
+#include <sys/proc.h>
+
+#include <dev/atapi/atapilink.h>
+#include <dev/atapi/atapi.h>
+
+#define CDUNIT(z) DISKUNIT(z)
+#define CDPART(z) DISKPART(z)
+#define MAKECDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part)
+
+#ifdef ACD_DEBUG
+#define ACD_DEBUG_PRINT(args) printf args
+#else
+#define ACD_DEBUG_PRINT(args)
+#endif
+
+struct acd_softc {
+ struct device sc_dev;
+ struct disk sc_dk;
+
+ int flags;
+#define CDF_LOCKED 0x01
+#define CDF_WANTED 0x02
+#define CDF_WLABEL 0x04 /* label is writable */
+#define CDF_LABELLING 0x08 /* writing label */
+ struct at_dev_link *ad_link; /* contains our drive number, etc ... */
+ struct cd_parms {
+ int blksize;
+ u_long disksize; /* total number sectors */
+ } params;
+ struct buf buf_queue;
+};
+
+int acdmatch __P((struct device *, void *, void *));
+void acdattach __P((struct device *, struct device *, void *));
+
+struct cfattach acd_ca = {
+ sizeof(struct acd_softc), acdmatch, acdattach
+};
+
+struct cfdriver acd_cd = {
+ NULL, "acd", DV_DISK
+};
+
+void acdgetdisklabel __P((struct acd_softc *));
+int acd_get_parms __P((struct acd_softc *, int));
+void acdstrategy __P((struct buf *));
+void acdstart __P((struct acd_softc *));
+int acd_pause __P((struct acd_softc *, int));
+void acdminphys __P((struct buf*));
+u_long acd_size __P((struct acd_softc*, int));
+int acddone __P((struct atapi_command_packet *));
+
+struct dkdriver acddkdriver = { acdstrategy };
+
+/*
+ * Called by the low level atapi code to find the right driver
+ * for a drive on the bus.
+ */
+int
+acdmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct at_dev_link *sa = aux;
+
+#ifdef ATAPI_DEBUG_PROBE
+ printf("acdmatch: device %d\n",
+ sa->id.config.device_type & ATAPI_DEVICE_TYPE_MASK);
+#endif
+
+ if ((sa->id.config.device_type & ATAPI_DEVICE_TYPE_MASK) ==
+ ATAPI_DEVICE_TYPE_CD)
+ return 1;
+ return 0;
+}
+
+/*
+ * The routine called by the low level atapi routine when it discovers
+ * A device suitable for this driver
+ */
+void
+acdattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct acd_softc *acd = (void *)self;
+ struct at_dev_link *sa = aux;
+ struct mode_sense cmd;
+ struct cappage cap;
+ int print_type = 0;
+ char *mtype;
+
+ printf("\n");
+
+ sa->device_softc = acd;
+ sa->start = acdstart;
+ sa->done = acddone;
+ sa->flags |= ADEV_REMOVABLE;
+ sa->openings = 1;
+ acd->ad_link = sa;
+
+ /*
+ * Initialize and attach the disk structure.
+ */
+ acd->sc_dk.dk_driver = &acddkdriver;
+ acd->sc_dk.dk_name = acd->sc_dev.dv_xname;
+ disk_attach(&acd->sc_dk);
+
+ dk_establish(&acd->sc_dk, &acd->sc_dev);
+
+ (void)atapi_test_unit_ready(sa, A_POLLED | A_SILENT);
+ delay(1000);
+ (void)atapi_test_unit_ready(sa, A_POLLED | A_SILENT);
+
+ /* To clear media change, etc ...*/
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_MODE_SENSE;
+ cmd.page_code_control = CAP_PAGE;
+ _lto2b(sizeof(cap), cmd.length);
+ if (atapi_exec_cmd(sa, &cmd , sizeof(cmd), &cap, sizeof(cap),
+ B_READ, A_POLLED) != 0) {
+ printf("%s: can't MODE SENSE: atapi_exec_cmd failed\n",
+ self->dv_xname);
+ return;
+ }
+
+ /* Determine media type. */
+ switch (cap.medium_type) {
+ case MDT_UNKNOWN:
+ mtype = "medium type unknown";
+ break;
+
+ case MDT_DATA_120:
+ mtype = "120mm data disc";
+ break;
+
+ case MDT_AUDIO_120:
+ mtype = "120mm audio disc";
+ break;
+
+ case MDT_COMB_120:
+ mtype = "120mm data/audio disc";
+ break;
+
+ case MDT_PHOTO_120:
+ mtype = "120mm photo disc";
+ break;
+
+ case MDT_DATA_80:
+ mtype = "80mm data disc";
+ break;
+
+ case MDT_AUDIO_80:
+ mtype = "80mm audio disc";
+ break;
+
+ case MDT_COMB_80:
+ mtype = "80mm data/audio disc";
+ break;
+
+ case MDT_PHOTO_80:
+ mtype = "80mm photo disc";
+ break;
+
+ case MDT_NO_DISC:
+ mtype = "drive empty";
+ break;
+
+ case MDT_DOOR_OPEN:
+ mtype = "door open";
+ break;
+
+ case MDT_FMT_ERROR:
+ mtype = "medium format error";
+ break;
+
+ default:
+ mtype = "unknown type";
+ print_type = 1;
+ break;
+ }
+
+ /*
+ * Display information about the drive.
+ */
+ printf("%s: %s", self->dv_xname, mtype);
+ if (print_type)
+ printf(" (type 0x%x)", cap.medium_type);
+ printf("\n");
+}
+
+/*
+ * Wait interruptibly for an exclusive lock.
+ *
+ * XXX
+ * Several drivers do this; it should be abstracted and made MP-safe.
+ */
+int
+acdlock(acd)
+ struct acd_softc *acd;
+{
+ int error;
+
+ while ((acd->flags & CDF_LOCKED) != 0) {
+ acd->flags |= CDF_WANTED;
+ if ((error = tsleep(acd, PRIBIO | PCATCH, "acdlck", 0)) != 0)
+ return error;
+ }
+ acd->flags |= CDF_LOCKED;
+ return 0;
+}
+
+/*
+ * Unlock and wake up any waiters.
+ */
+void
+acdunlock(acd)
+ struct acd_softc *acd;
+{
+
+ acd->flags &= ~CDF_LOCKED;
+ if ((acd->flags & CDF_WANTED) != 0) {
+ acd->flags &= ~CDF_WANTED;
+ wakeup(acd);
+ }
+}
+
+/*
+ * open the device. Make sure the partition info is a up-to-date as can be.
+ */
+int
+acdopen(dev, flag, fmt)
+ dev_t dev;
+ int flag, fmt;
+{
+ struct acd_softc *acd;
+ struct at_dev_link *ad_link;
+ int unit, part;
+ int error;
+
+ ACD_DEBUG_PRINT(("acd: open\n"));
+
+ unit = CDUNIT(dev);
+ if (unit >= acd_cd.cd_ndevs)
+ return ENXIO;
+ acd = acd_cd.cd_devs[unit];
+ if (acd == NULL)
+ return ENXIO;
+
+ ad_link = acd->ad_link;
+
+ if ((error = atapi_test_unit_ready(ad_link,0)) != 0) {
+ if (error != UNIT_ATTENTION)
+ return EIO;
+ if ((ad_link->flags & ADEV_OPEN) != 0)
+ return EIO;
+ }
+
+ if (error = acdlock(acd))
+ return error;
+
+ if (acd->sc_dk.dk_openmask != 0) {
+ /*
+ * If any partition is open, but the disk has been invalidated,
+ * disallow further opens.
+ */
+ if ((ad_link->flags & ADEV_MEDIA_LOADED) == 0) {
+ error = EIO;
+ goto bad3;
+ }
+ } else {
+ ad_link->flags |= ADEV_OPEN;
+
+ /* Lock the pack in. */
+ if (error = atapi_prevent(ad_link, PR_PREVENT))
+ goto bad;
+
+ if ((ad_link->flags & ADEV_MEDIA_LOADED) == 0) {
+ ad_link->flags |= ADEV_MEDIA_LOADED;
+
+ /* Load the physical device parameters. */
+ if (acd_get_parms(acd, 0) != 0) {
+ error = ENXIO;
+ goto bad2;
+ }
+
+ /* Fabricate a disk label. */
+ acdgetdisklabel(acd);
+ }
+ }
+
+ part = CDPART(dev);
+
+ /* Check that the partition exists. */
+ if (part != RAW_PART &&
+ (part >= acd->sc_dk.dk_label->d_npartitions ||
+ acd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
+ error = ENXIO;
+ goto bad;
+ }
+
+ /* Insure only one open at a time. */
+ switch (fmt) {
+ case S_IFCHR:
+ acd->sc_dk.dk_copenmask |= (1 << part);
+ break;
+ case S_IFBLK:
+ acd->sc_dk.dk_bopenmask |= (1 << part);
+ break;
+ }
+ acd->sc_dk.dk_openmask =
+ acd->sc_dk.dk_copenmask | acd->sc_dk.dk_bopenmask;
+
+ ACD_DEBUG_PRINT(("acd: open complete\n"));
+
+ acdunlock(acd);
+ return 0;
+
+bad2:
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+
+bad:
+ if (acd->sc_dk.dk_openmask == 0) {
+ atapi_prevent(ad_link, PR_ALLOW);
+ ad_link->flags &= ~ADEV_OPEN;
+ }
+
+bad3:
+ acdunlock(acd);
+ return error;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+int
+acdclose(dev, flag, fmt)
+ dev_t dev;
+ int flag, fmt;
+{
+ struct acd_softc *acd = acd_cd.cd_devs[CDUNIT(dev)];
+ int part = CDPART(dev);
+ int error;
+
+ if ((error = acdlock(acd)) != 0)
+ return error;
+
+ switch (fmt) {
+ case S_IFCHR:
+ acd->sc_dk.dk_copenmask &= ~(1 << part);
+ break;
+ case S_IFBLK:
+ acd->sc_dk.dk_bopenmask &= ~(1 << part);
+ break;
+ }
+ acd->sc_dk.dk_openmask =
+ acd->sc_dk.dk_copenmask | acd->sc_dk.dk_bopenmask;
+
+ if (acd->sc_dk.dk_openmask == 0) {
+ /* XXXX Must wait for I/O to complete! */
+
+ atapi_prevent(acd->ad_link, PR_ALLOW);
+ acd->ad_link->flags &= ~ADEV_OPEN;
+ }
+
+ acdunlock(acd);
+ return 0;
+}
+
+/*
+ * Actually translate the requested transfer into one the physical driver can
+ * understand. The transfer is described by a buf and will include only one
+ * physical transfer.
+ */
+void
+acdstrategy(bp)
+ struct buf *bp;
+{
+ struct acd_softc *acd = acd_cd.cd_devs[CDUNIT(bp->b_dev)];
+ int opri;
+
+ ACD_DEBUG_PRINT(("acdstrategy\n"));
+
+ /*
+ * The transfer must be a whole number of blocks.
+ */
+ if ((bp->b_bcount % acd->sc_dk.dk_label->d_secsize) != 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ if ((bp->b_flags & (B_READ|B_WRITE)) == B_WRITE) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+ /*
+ * If the device has been made invalid, error out
+ * maybe the media changed
+ */
+ if ((acd->ad_link->flags & ADEV_MEDIA_LOADED) == 0) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * If it's a null transfer, return immediately
+ */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /*
+ * Do bounds checking, adjust transfer. if error, process.
+ * If end of partition, just return.
+ */
+ if (CDPART(bp->b_dev) != RAW_PART &&
+ bounds_check_with_label(bp, acd->sc_dk.dk_label,
+ (acd->flags & (CDF_WLABEL|CDF_LABELLING)) != 0) <= 0)
+ goto done;
+
+ opri = splbio();
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+ disksort(&acd->buf_queue, bp);
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ */
+ acdstart(acd);
+
+ splx(opri);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+}
+
+/*
+ * acdstart looks to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It deques the buf and creates a atapi command to perform the
+ * transfer in the buf. The transfer request will call atapi_done
+ * on completion, which will in turn call this routine again
+ * so that the next queued transfer is performed.
+ * The bufs are queued by the strategy routine (cdstrategy)
+ *
+ * This routine is also called after other non-queued requests
+ * have been made of the atapi driver, to ensure that the queue
+ * continues to be drained.
+ *
+ * must be called at the correct (highish) spl level
+ * cdstart() is called at splbio from cdstrategy and atapi_done
+ */
+void
+acdstart(acd)
+ struct acd_softc *acd;
+{
+ struct at_dev_link *ad_link;
+ struct buf *bp = 0;
+ struct buf *dp;
+ struct read cmd;
+ u_int32_t blkno, nblks;
+ struct partition *p;
+
+ ACD_DEBUG_PRINT(("acd: acdstart\n"));
+
+#ifdef DIAGNOSTIC
+ if (acd == NULL) {
+ printf("acdstart: null acd\n");
+ return;
+ }
+#endif
+
+ ad_link = acd->ad_link;
+
+#ifdef DIAGNOSTIC
+ if (ad_link == NULL) {
+ printf("acdstart: null ad_link\n");
+ return;
+ }
+#endif
+ /*
+ * Check if the device has room for another command
+ */
+ while (ad_link->openings > 0) {
+ /*
+ * there is excess capacity, but a special waits
+ * It'll need the adapter as soon as we clear out of the
+ * way and let it run (user level wait).
+ */
+ if (ad_link->flags & ADEV_WAITING) {
+#ifdef ATAPI_DEBUG
+ printf("acdstart: waking up\n");
+#endif
+ ad_link->flags &= ~ADEV_WAITING;
+ wakeup((caddr_t)ad_link);
+ return;
+ }
+
+ /*
+ * See if there is a buf with work for us to do..
+ */
+ dp = &acd->buf_queue;
+#ifdef ACD_DEBUG
+ if (dp == NULL) {
+ printf("acdstart: null dp\n");
+ return;
+ }
+#endif
+ if ((bp = dp->b_actf) == NULL) /* yes, an assign */
+ return;
+
+ ACD_DEBUG_PRINT(("acdstart: a buf\n"));
+
+ dp->b_actf = bp->b_actf;
+
+ /*
+ * If the device has become invalid, abort all the
+ * reads and writes until all files have been closed and
+ * re-opened
+ */
+ if ((ad_link->flags & ADEV_MEDIA_LOADED) == 0) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ continue;
+ }
+
+ /*
+ *
+ * First, translate the block to absolute and put it in terms
+ * of the logical blocksize of the device.
+ */
+ blkno =
+ bp->b_blkno / (acd->sc_dk.dk_label->d_secsize / DEV_BSIZE);
+ if (CDPART(bp->b_dev) != RAW_PART) {
+ p =
+ &acd->sc_dk.dk_label->d_partitions[CDPART(bp->b_dev)];
+ blkno += p->p_offset;
+ }
+ nblks = howmany(bp->b_bcount, acd->sc_dk.dk_label->d_secsize);
+
+ ACD_DEBUG_PRINT(("acdstart: blkno %d nblk %d\n",
+ blkno, nblks));
+
+ /*
+ * Fill out the atapi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_READ;
+ _lto4b(blkno, cmd.lba);
+ _lto2b(nblks, cmd.length);
+
+ /* Instrumentation. */
+ disk_busy(&acd->sc_dk);
+
+ /*
+ * Call the routine that chats with the adapter.
+ * Note: we cannot sleep as we may be an interrupt
+ */
+ if (atapi_exec_io(ad_link, &cmd, sizeof(cmd), bp, A_NOSLEEP))
+ printf("%s: not queued", acd->sc_dev.dv_xname);
+ }
+}
+
+int
+acdread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+
+ return (physio(acdstrategy, NULL, dev, B_READ, acdminphys, uio));
+}
+
+int
+acdwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+
+ return (physio(acdstrategy, NULL, dev, B_WRITE, acdminphys, uio));
+}
+
+/*
+ * Perform special action on behalf of the user.
+ * Knows about the internals of this device
+ */
+int
+acdioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct acd_softc *acd = acd_cd.cd_devs[CDUNIT(dev)];
+ int error;
+
+ /*
+ * If the device is not valid.. abandon ship
+ */
+ if ((acd->ad_link->flags & ADEV_MEDIA_LOADED) == 0)
+ return EIO;
+
+ switch (cmd) {
+ case DIOCGDINFO:
+ *(struct disklabel *)addr = *acd->sc_dk.dk_label;
+ return 0;
+
+ case DIOCGPART:
+ ((struct partinfo *)addr)->disklab = acd->sc_dk.dk_label;
+ ((struct partinfo *)addr)->part =
+ &acd->sc_dk.dk_label->d_partitions[CDPART(dev)];
+ return 0;
+
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+
+ if (error = acdlock(acd))
+ return error;
+ acd->flags |= CDF_LABELLING;
+
+ error = setdisklabel(acd->sc_dk.dk_label,
+ (struct disklabel *)addr, /*acd->sc_dk.dk_openmask : */0,
+ acd->sc_dk.dk_cpulabel);
+ if (error == 0) {
+ /* XXX ?? */
+ }
+
+ acd->flags &= ~CDF_LABELLING;
+ acdunlock(acd);
+ return error;
+
+ case DIOCWLABEL:
+ return EROFS;
+
+#ifdef notyet
+ case CDIOCPLAYTRACKS:
+ {
+ struct ioc_play_track *args = (struct ioc_play_track *)addr;
+ struct acd_mode_data data;
+ if (error = acd_get_mode(acd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = acd_set_mode(acd, &data))
+ return error;
+ return acd_play_tracks(acd, args->start_track,
+ args->start_index, args->end_track, args->end_index);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCPLAYMSF:
+ {
+ struct ioc_play_msf *args
+ = (struct ioc_play_msf *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(cd, &data))
+ return error;
+ return cd_play_msf(cd, args->start_m, args->start_s,
+ args->start_f, args->end_m, args->end_s, args->end_f);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCPLAYBLOCKS:
+ {
+ struct ioc_play_blocks *args
+ = (struct ioc_play_blocks *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(cd, &data))
+ return error;
+ return cd_play(cd, args->blk, args->len);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCREADSUBCHANNEL:
+ {
+ struct ioc_read_subchannel *args
+ = (struct ioc_read_subchannel *)addr;
+ struct cd_sub_channel_info data;
+ int len = args->data_len;
+ if (len > sizeof(data) ||
+ len < sizeof(struct cd_sub_channel_header))
+ return EINVAL;
+ if (error = cd_read_subchannel(cd, args->address_format,
+ args->data_format, args->track, &data, len))
+ return error;
+ len = min(len, ((data.header.data_len[0] << 8) +
+ data.header.data_len[1] +
+ sizeof(struct cd_sub_channel_header)));
+ return copyout(&data, args->data, len);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOREADTOCHEADER:
+ {
+ struct ioc_toc_header th;
+ if (error = cd_read_toc(cd, 0, 0, &th, sizeof(th)))
+ return error;
+ th.len = ntohs(th.len);
+ bcopy(&th, addr, sizeof(th));
+ return 0;
+ }
+#endif
+
+#ifdef notyet
+ case CDIOREADTOCENTRYS:
+ {
+ struct cd_toc {
+ struct ioc_toc_header header;
+ struct cd_toc_entry entries[65];
+ } data;
+ struct ioc_read_toc_entry *te =
+ (struct ioc_read_toc_entry *)addr;
+ struct ioc_toc_header *th;
+ int len = te->data_len;
+ th = &data.header;
+
+ if (len > sizeof(data.entries) ||
+ len < sizeof(struct cd_toc_entry))
+ return EINVAL;
+ if (error = cd_read_toc(cd, te->address_format,
+ te->starting_track, (struct cd_toc_entry *)&data,
+ len + sizeof(struct ioc_toc_header)))
+ return error;
+ len = min(len, ntohs(th->len) - (sizeof(th->starting_track) +
+ sizeof(th->ending_track)));
+ return copyout(data.entries, te->data, len);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETPATCH:
+ {
+ struct ioc_patch *arg = (struct ioc_patch *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = arg->patch[0];
+ data.page.audio.port[RIGHT_PORT].channels = arg->patch[1];
+ data.page.audio.port[2].channels = arg->patch[2];
+ data.page.audio.port[3].channels = arg->patch[3];
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCGETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ arg->vol[LEFT_PORT] = data.page.audio.port[LEFT_PORT].volume;
+ arg->vol[RIGHT_PORT] = data.page.audio.port[RIGHT_PORT].volume;
+ arg->vol[2] = data.page.audio.port[2].volume;
+ arg->vol[3] = data.page.audio.port[3].volume;
+ return 0;
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = CHANNEL_0;
+ data.page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT];
+ data.page.audio.port[RIGHT_PORT].channels = CHANNEL_1;
+ data.page.audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT];
+ data.page.audio.port[2].volume = arg->vol[2];
+ data.page.audio.port[3].volume = arg->vol[3];
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETMONO:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels =
+ LEFT_CHANNEL | RIGHT_CHANNEL | 4 | 8;
+ data.page.audio.port[RIGHT_PORT].channels =
+ LEFT_CHANNEL | RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETSTEREO:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETMUTE:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = 0;
+ data.page.audio.port[RIGHT_PORT].channels = 0;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETLEFT:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+#ifdef notyet
+ case CDIOCSETRIGHT:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+#endif
+
+ case CDIOCRESUME:
+ return acd_pause(acd, PA_RESUME);
+
+ case CDIOCPAUSE:
+ return acd_pause(acd, PA_PAUSE);
+
+ case CDIOCSTART:
+ return atapi_start_stop(acd->ad_link, SSS_START, 0);
+
+ case CDIOCSTOP:
+ return atapi_start_stop(acd->ad_link, SSS_STOP, 0);
+
+ case CDIOCEJECT:
+ case DIOCEJECT:
+ return atapi_start_stop(acd->ad_link, SSS_STOP|SSS_LOEJ, 0);
+
+ case CDIOCALLOW:
+ return atapi_prevent(acd->ad_link, PR_ALLOW);
+
+ case CDIOCPREVENT:
+ return atapi_prevent(acd->ad_link, PR_PREVENT);
+
+ case DIOCLOCK:
+ return atapi_prevent(acd->ad_link,
+ (*(int *)addr) ? PR_PREVENT : PR_ALLOW);
+ case CDIOCRESET:
+ return acd_reset(acd);
+
+ default:
+ return ENOTTY;
+ }
+
+#ifdef DIAGNOSTIC
+ panic("cdioctl: impossible");
+#endif
+}
+
+/*
+ * Load the label information on the named device
+ * Actually fabricate a disklabel
+ *
+ * EVENTUALLY take information about different
+ * data tracks from the TOC and put it in the disklabel
+ */
+void
+acdgetdisklabel(acd)
+ struct acd_softc *acd;
+{
+ struct disklabel *lp = acd->sc_dk.dk_label;
+
+ bzero(lp, sizeof(struct disklabel));
+ bzero(acd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
+
+#if 0 /* XXX */
+ lp->d_secsize = acd->params.blksize;
+#endif
+ lp->d_secsize = 2048;
+ lp->d_ntracks = 1;
+ lp->d_nsectors = 100;
+ lp->d_ncylinders = (acd->params.disksize / 100) + 1;
+ lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+
+ strncpy(lp->d_typename, "ATAPI CD-ROM", 16);
+ lp->d_type = DTYPE_SCSI; /* XXX */
+ strncpy(lp->d_packname, "fictitious", 16);
+ lp->d_secperunit = acd->params.disksize;
+ lp->d_rpm = 300;
+ lp->d_interleave = 1;
+ lp->d_flags = D_REMOVABLE;
+
+ lp->d_partitions[0].p_offset = 0;
+ lp->d_partitions[0].p_size =
+ lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
+ lp->d_partitions[0].p_fstype = FS_ISO9660;
+ lp->d_partitions[RAW_PART].p_offset = 0;
+ lp->d_partitions[RAW_PART].p_size =
+ lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
+ lp->d_partitions[RAW_PART].p_fstype = FS_ISO9660;
+ lp->d_npartitions = RAW_PART + 1;
+
+ lp->d_magic = DISKMAGIC;
+ lp->d_magic2 = DISKMAGIC;
+ lp->d_checksum = dkcksum(lp);
+}
+
+/*
+ * Find out from the device what it's capacity is
+ */
+u_long
+acd_size(cd, flags)
+ struct acd_softc *cd;
+ int flags;
+{
+ struct read_cd_capacity_data rdcap;
+ struct read_cd_capacity cmd;
+ u_long blksize;
+ u_long size;
+
+ /*
+ * make up a atapi command and ask the atapi driver to do
+ * it for you.
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_READ_CD_CAPACITY;
+ cmd.len = sizeof(rdcap);
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks and a blocksize
+ */
+ if (atapi_exec_cmd(cd->ad_link, &cmd , sizeof(cmd),
+ &rdcap, sizeof(rdcap), B_READ, 0) != 0) {
+#ifdef ATAPI_DEBUG
+ printf("ATAPI_READ_CD_CAPACITY failed\n");
+#endif
+ return 0;
+ }
+
+ blksize = ntohl(rdcap.blksize);
+ if (blksize < 512)
+ blksize = 2048; /* some drives lie ! */
+ cd->params.blksize = blksize;
+
+ size = ntohl(size);
+ if (size < 100)
+ size = 400000; /* ditto */
+ cd->params.disksize = size;
+#ifdef ATAPI_DEBUG
+ printf("acd_size: %ld %ld\n",blksize,size);
+#endif
+
+ return size;
+}
+
+#ifdef notyet
+/*
+ * Get the requested page into the buffer given
+ */
+int
+cd_get_mode(cd, data, page)
+ struct acd_softc *cd;
+ struct cd_mode_data *data;
+ int page;
+{
+ struct scsi_mode_sense scsi_cmd;
+ int error;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ bzero(data, sizeof(*data));
+ scsi_cmd.opcode = MODE_SENSE;
+ scsi_cmd.page = page;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)data, sizeof(*data), CDRETRIES, 20000,
+ NULL, SCSI_DATA_IN);
+}
+
+/*
+ * Get the requested page into the buffer given
+ */
+int
+cd_set_mode(cd, data)
+ struct acd_softc *cd;
+ struct cd_mode_data *data;
+{
+ struct scsi_mode_select scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = MODE_SELECT;
+ scsi_cmd.byte2 |= SMS_PF;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ data->header.data_length = 0;
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)data, sizeof(*data), CDRETRIES, 20000,
+ NULL, SCSI_DATA_OUT);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play(cd, blkno, nblks)
+ struct acd_softc *cd;
+ int blkno, nblks;
+{
+ struct scsi_play scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY;
+ _lto4b(blkno, scsi_cmd.blk_addr);
+ _lto2b(nblks, scsi_cmd.xfer_len);
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 200000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play_big(cd, blkno, nblks)
+ struct acd_softc *cd;
+ int blkno, nblks;
+{
+ struct scsi_play_big scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_BIG;
+ _lto4b(blkno, scsi_cmd.blk_addr);
+ _lto4b(nblks, scsi_cmd.xfer_len);
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 20000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play_tracks(cd, strack, sindex, etrack, eindex)
+ struct acd_softc *cd;
+ int strack, sindex, etrack, eindex;
+{
+ struct scsi_play_track scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_TRACK;
+ scsi_cmd.start_track = strack;
+ scsi_cmd.start_index = sindex;
+ scsi_cmd.end_track = etrack;
+ scsi_cmd.end_index = eindex;
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 20000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "play msf" command
+ */
+int
+cd_play_msf(cd, startm, starts, startf, endm, ends, endf)
+ struct acd_softc *cd;
+ int startm, starts, startf, endm, ends, endf;
+{
+ struct scsi_play_msf scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_MSF;
+ scsi_cmd.start_m = startm;
+ scsi_cmd.start_s = starts;
+ scsi_cmd.start_f = startf;
+ scsi_cmd.end_m = endm;
+ scsi_cmd.end_s = ends;
+ scsi_cmd.end_f = endf;
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 2000, NULL, 0);
+}
+
+#endif /* notyet */
+
+/*
+ * Get atapi driver to send a "start up" command
+ */
+int
+acd_pause(acd, go)
+ struct acd_softc *acd;
+ int go;
+{
+ struct pause_resume cmd;
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_PAUSE_RESUME;
+ cmd.resume = go & 0xff;
+
+ return atapi_exec_cmd(acd->ad_link, &cmd , sizeof(cmd), 0, 0, 0, 0);
+}
+
+/*
+ * Get atapi driver to send a "RESET" command
+ */
+int
+acd_reset(acd)
+ struct acd_softc *acd;
+{
+#ifdef notyet
+ return atapi_soft_reset(acd->ad_link);
+#else
+ return 0;
+#endif
+}
+
+#ifdef notyet
+/*
+ * Read subchannel
+ */
+int
+cd_read_subchannel(cd, mode, format, track, data, len)
+ struct acd_softc *cd;
+ int mode, format, len;
+ struct cd_sub_channel_info *data;
+{
+ struct scsi_read_subchannel scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = READ_SUBCHANNEL;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.byte3 = SRS_SUBQ;
+ scsi_cmd.subchan_format = format;
+ scsi_cmd.track = track;
+ _lto2b(len, scsi_cmd.data_len);
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(struct scsi_read_subchannel), (u_char *)data, len,
+ CDRETRIES, 5000, NULL, SCSI_DATA_IN);
+}
+
+/*
+ * Read table of contents
+ */
+int
+cd_read_toc(cd, mode, start, data, len)
+ struct acd_softc *cd;
+ int mode, start, len;
+ struct cd_toc_entry *data;
+{
+ struct scsi_read_toc scsi_cmd;
+ int ntoc;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+#if 0
+ if (len != sizeof(struct ioc_toc_header))
+ ntoc = ((len) - sizeof(struct ioc_toc_header)) /
+ sizeof(struct cd_toc_entry);
+ else
+#endif
+ ntoc = len;
+
+ scsi_cmd.opcode = READ_TOC;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.from_track = start;
+ _lto2b(ntoc, scsi_cmd.data_len);
+
+ return scsi_scsi_cmd(cd->ad_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(struct scsi_read_toc), (u_char *)data, len, CDRETRIES,
+ 5000, NULL, SCSI_DATA_IN);
+}
+
+#endif /* notyet */
+
+/*
+ * Get the atapi driver to send a full inquiry to the device and use the
+ * results to fill out the disk parameter structure.
+ */
+int
+acd_get_parms(acd, flags)
+ struct acd_softc *acd;
+ int flags;
+{
+
+ /*
+ * give a number of sectors so that sec * trks * cyls
+ * is <= disk_size
+ */
+ if (acd_size(acd, flags) == 0)
+ return ENXIO;
+
+ return 0;
+}
+
+int
+acdsize(dev)
+ dev_t dev;
+{
+
+ /* CD-ROMs are read-only. */
+ return -1;
+}
+
+void acdminphys(bp)
+ struct buf *bp;
+{
+
+ minphys(bp);
+}
+
+int
+acddump(dev, blkno, va, size)
+ dev_t dev;
+ daddr_t blkno;
+ caddr_t va;
+ size_t size;
+{
+
+ /* Not implemented. */
+ return ENXIO;
+}
+
+int
+acddone(acp)
+ struct atapi_command_packet *acp;
+{
+ struct at_dev_link *ad_link = acp->ad_link;
+ struct acd_softc *acd = ad_link->device_softc;
+
+ if (acp->bp != NULL)
+ disk_unbusy(&acd->sc_dk,
+ (acp->bp->b_bcount - acp->bp->b_resid));
+
+ return (0);
+}
diff --git a/sys/dev/atapi/atapi.h b/sys/dev/atapi/atapi.h
new file mode 100644
index 00000000000..20ec1948981
--- /dev/null
+++ b/sys/dev/atapi/atapi.h
@@ -0,0 +1,250 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Definition of atapi commands and associated data structures
+ */
+
+/*
+ * TEST UNIT READY (mandatory)
+ */
+#define ATAPI_TEST_UNIT_READY 0x00
+
+struct test_unit_ready {
+ u_int8_t operation_code;
+ u_int8_t reserved1[15];
+};
+
+/*
+ * START/STOP UNIT (mandatory)
+ */
+#define ATAPI_START_STOP_UNIT 0x1b
+
+struct start_stop_unit {
+ u_int8_t operation_code;
+ u_int8_t flags;
+#define START_STOP_IMMED 0x01
+ u_int8_t reserved2[2];
+ u_int8_t how;
+#define SSS_STOP 0x00
+#define SSS_START 0x01
+#define SSS_LOEJ 0x02
+ u_int8_t reserved4[11];
+};
+
+/*
+ * PREVENT/ALLOW MEDIUM REMOVAL (mandatory)
+ */
+#define ATAPI_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+
+struct prevent_allow_medium_removal {
+ u_int8_t operation_code;
+ u_int8_t reserved1[3];
+ u_int8_t how;
+#define PR_PREVENT 0x01
+#define PR_ALLOW 0x00
+ u_int8_t reserved3[11];
+};
+
+
+/*
+ * READ CD CAPACITY (mandatory)
+ */
+#define ATAPI_READ_CD_CAPACITY 0x25
+
+struct read_cd_capacity {
+ u_int8_t operation_code;
+ u_int8_t reserved1[7];
+ u_int8_t len;
+ u_int8_t reserved2[7];
+};
+
+/*
+ * Volume size info.
+ */
+struct read_cd_capacity_data {
+ u_long size; /* Volume size in blocks */
+ u_long blksize; /* Block size in bytes */
+};
+
+/*
+ * READ (10) (mandatory)
+ */
+#define ATAPI_READ 0x28
+
+struct read {
+ u_int8_t operation_code;
+ u_int8_t reserved1;
+ u_int8_t lba[4];
+ u_int8_t reserved2;
+ u_int8_t length[2];
+ u_int8_t reserved3[7];
+};
+
+/*
+ * PAUSE/RESUME (optional)
+ */
+#define ATAPI_PAUSE_RESUME 0x4b
+
+struct pause_resume {
+ u_int8_t operation_code;
+ u_int8_t reserved1[7];
+ u_int8_t resume;
+#define PA_PAUSE 0x00
+#define PA_RESUME 0x01
+ u_int8_t reserved3[7];
+};
+
+/*
+ * MODE SENSE (mandatory)
+ */
+#define ATAPI_MODE_SENSE 0x5a
+
+struct mode_sense {
+ u_int8_t operation_code;
+ u_int8_t reserved1;
+ u_int8_t page_code_control;
+#define PAGE_CODE_MASK 0x3f
+#define PAGE_CONTROL_MASK 0xc0
+ u_int8_t reserved2[4];
+ u_int8_t length[2];
+ u_int8_t reserved3[7];
+};
+
+struct cappage {
+ /* Mode data header */
+ u_int8_t data_length[2];
+ u_int8_t medium_type;
+#define MDT_UNKNOWN 0x00
+#define MDT_DATA_120 0x01
+#define MDT_AUDIO_120 0x02
+#define MDT_COMB_120 0x03
+#define MDT_PHOTO_120 0x04
+#define MDT_DATA_80 0x05
+#define MDT_AUDIO_80 0x06
+#define MDT_COMB_80 0x07
+#define MDT_PHOTO_80 0x08
+#define MDT_NO_DISC 0x70
+#define MDT_DOOR_OPEN 0x71
+#define MDT_FMT_ERROR 0x72
+ u_int8_t reserved1[5];
+
+ /* Capabilities page */
+ u_int8_t page_code;
+#define CAP_PAGE 0x2a
+ u_int8_t param_len;
+ u_int8_t reserved2[2];
+
+ u_int8_t format_cap;
+#define FORMAT_AUDIO_PLAY 0x01 /* audio play supported */
+#define FORMAT_COMPOSITE 0x02 /* composite audio/video supported */
+#define FORMAT_DPORT1 0x04 /* digital audio on port 1 */
+#define FORMAT_DPORT2 0x08 /* digital audio on port 2 */
+#define FORMAT_MODE2_FORM1 0x10 /* mode 2 form 1 (XA) read */
+#define FORMAT_MODE2_FORM2 0x20 /* mode 2 form 2 format */
+#define FORMAT_MULTISESSION 0x40 /* multi-session photo-CD */
+
+ u_int8_t ops_cap;
+#define OPS_CDDA 0x01 /* audio-CD read supported */
+#define OPS_CDDA_STREAM 0x02 /* CDDA streaming */
+#define OPS_RW 0x04 /* combined R-W subchannels */
+#define OPS_RW_CORR 0x08 /* R-W subchannel data corrected */
+#define OPS_C2 0x10 /* C2 error pointers supported */
+#define OPS_ISRC 0x20 /* can return the ISRC info */
+#define OPS_UPC 0x40 /* can return the catalog number UPC */
+
+ u_int8_t hw_cap;
+#define HW_LOCK 0x01 /* could be locked */
+#define HW_LOCKED 0x02 /* current lock state */
+#define HW_PREVENT 0x04 /* prevent jumper installed */
+#define HW_EJECT 0x08 /* can eject */
+#define HW_MECH_MASK 0xe0 /* loading mechanism type mask */
+
+#define MECH_CADDY 0x00
+#define MECH_TRAY 0x20
+#define MECH_POPUP 0x40
+#define MECH_CHANGER 0x80
+#define MECH_CARTRIDGE 0xa0
+
+ u_int8_t sep_cap;
+#define SEP_VOL 0x01 /* independent volume controls */
+#define SEP_MUTE 0x02 /* independent mute controls */
+
+ u_int8_t max_speed[2]; /* max raw data rate in bytes/1000 */
+ u_int8_t max_vol_levels[2]; /* number of discrete volume levels */
+ u_int8_t buf_size[2]; /* internal buffer size in bytes/1024 */
+ u_int8_t cur_speed[2]; /* current data rate in bytes/1000 */
+
+ /* Digital drive output format description (optional?) */
+ u_int8_t reserved3;
+ u_int8_t ddofd;
+#define DDOFD_BCKF 0x01 /* data valid on failing edge of BCK */
+#define DDOFD_RCH 0x02 /* hight LRCK indicated left channel */
+#define DDOFD_LSBF 0x04 /* set if LSB first */
+#define DDOFD_DLEN_MASK 0x18 /* mask of DLEN values */
+
+#define DLEN_32 0x00 /* 32 BCKs */
+#define DLEN_16 0x08 /* 16 BCKs */
+#define DLEN_24 0x10 /* 24 BCKs */
+#define DLEN_24_I2S 0x18 /* 24 BCKs (I2S) */
+
+ u_int8_t reserved4[2];
+};
+
+/* ATAPI error codes */
+#define ATAPI_SK_NO_SENSE 0x0
+#define ATAPI_SK_REC_ERROR 0x1 /* recovered error */
+#define ATAPI_SK_NOT_READY 0x2
+#define ATAPI_SK_MEDIUM_ERROR 0x3
+#define ATAPI_SK_HARDWARE_ERROR 0x4
+#define ATAPI_SK_ILLEGAL_REQUEST 0x5
+#define ATAPI_SK_UNIT_ATTENTION 0x6
+#define ATAPI_SK_DATA_PROTECT 0x7
+ /* 0x8 reserved */
+ /* 0x9-0xa reserved */
+#define ATAPI_SK_ABORTED_COMMAND 0xb
+ /* 0xc-0xd not referenced */
+#define ATAPI_SK_MISCOMPARE 0xe
+ /* 0xf reserved */
+
+#define ATAPI_MCR 0x08 /* media change requested */
+#define ATAPI_ABRT 0x04 /* aborted command */
+#define ATAPI_EOM 0x02 /* end of media */
+#define ATAPI_ILI 0x01 /* illegal length indication */
+
+
+int atapi_exec_cmd __P((struct at_dev_link *, void *, int,
+ void *, int, long, int));
+int atapi_exec_io __P((struct at_dev_link *, void *, int,
+ struct buf *, int));
+int atapi_test_unit_ready __P((struct at_dev_link *, int));
+int atapi_start_stop __P((struct at_dev_link *, int, int));
+int atapi_prevent __P((struct at_dev_link *, int));
diff --git a/sys/dev/atapi/atapiconf.c b/sys/dev/atapi/atapiconf.c
new file mode 100644
index 00000000000..85095a4c0a4
--- /dev/null
+++ b/sys/dev/atapi/atapiconf.c
@@ -0,0 +1,676 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+
+#include <dev/atapi/atapilink.h>
+#include <dev/atapi/atapi.h>
+
+#define SILENT_PRINTF(flags,string) if (!(flags & A_SILENT)) printf string
+
+#ifdef ATAPI_DEBUG_CMD
+#define ATAPI_DEBUG_CMD_PRINT(args) printf args
+#else
+#define ATAPI_DEBUG_CMD_PRINT(args)
+#endif
+
+#ifdef ATAPI_DEBUG_FCTN
+#define ATAPI_DEBUG_FCTN_PRINT(args) printf args
+#else
+#define ATAPI_DEBUG_FCTN_PRINT(args)
+#endif
+
+struct atapibus_softc {
+ struct device sc_dev;
+ struct bus_link *b_link;
+};
+
+LIST_HEAD(pkt_free_list, atapi_command_packet) pkt_free_list;
+
+static void bswap __P((char *, int));
+static void btrim __P((char *, int));
+
+int atapi_error __P((struct atapi_command_packet *));
+void atapi_sense __P((struct atapi_command_packet *, u_int8_t, u_int8_t));
+void at_print_addr __P((struct at_dev_link *, u_int8_t));
+
+int atapibusmatch __P((struct device *, void *, void *));
+void atapibusattach __P((struct device *, struct device *, void *));
+
+struct cfattach atapibus_ca = {
+ sizeof(struct atapibus_softc), atapibusmatch, atapibusattach
+};
+
+struct cfdriver atapibus_cd = {
+ NULL, "atapibus", DV_DULL
+};
+
+
+int
+atapibusmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct bus_link *ab_link = aux;
+
+ if (ab_link == NULL)
+ return 0;
+ if (ab_link->type != BUS)
+ return 0;
+ return 1;
+}
+
+int
+atapiprint(aux, bus)
+ void *aux;
+ char *bus;
+{
+ struct at_dev_link *ad_link = aux;
+ struct atapi_identify *id = &ad_link->id;
+ char *dtype, *fixrem;
+
+ /*
+ * Figure out basic device type.
+ */
+ switch (id->config.device_type & ATAPI_DEVICE_TYPE_MASK) {
+ case ATAPI_DEVICE_TYPE_DAD:
+ dtype = "direct";
+ break;
+
+ case ATAPI_DEVICE_TYPE_CD:
+ dtype = "cdrom";
+ break;
+
+ case ATAPI_DEVICE_TYPE_OMD:
+ dtype = "optical";
+ break;
+
+ default:
+ dtype = "unknown";
+ break;
+ }
+
+ fixrem = (id->config.cmd_drq_rem & ATAPI_REMOVABLE) ?
+ "removable" : "fixed";
+
+ /*
+ * Shuffle string byte order.
+ * Mitsumi and NEC drives don't need this.
+ */
+ if (((id->model[0] == 'N' && id->model[1] == 'E') ||
+ (id->model[0] == 'F' && id->model[1] == 'X')) == 0)
+ bswap(id->model, sizeof(id->model));
+
+ /*
+ * XXX Poorly named... These appear to actually be in
+ * XXX network byte order, so bswap() is a no-op on
+ * XXX big-endian hosts. Clean me up, please.
+ */
+ bswap(id->serial_number, sizeof(id->serial_number));
+ bswap(id->firmware_revision, sizeof(id->firmware_revision));
+
+ /*
+ * Clean up the model name, serial and
+ * revision numbers.
+ */
+ btrim(id->model, sizeof(id->model));
+ btrim(id->serial_number, sizeof(id->serial_number));
+ btrim(id->firmware_revision, sizeof(id->firmware_revision));
+
+ if (bus != NULL)
+ printf("%s", bus);
+
+ printf(" drive %d: <%s, %s, %s> type %d/%s %s",
+ ad_link->drive, id->model, id->serial_number,
+ id->firmware_revision,
+ id->config.device_type & ATAPI_DEVICE_TYPE_MASK, dtype, fixrem);
+
+ return UNCONF;
+}
+
+
+void
+atapibusattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct atapibus_softc *ab = (struct atapibus_softc *)self;
+ struct bus_link *ab_link_proto = aux;
+ struct atapi_identify ids;
+ struct atapi_identify *id = &ids;
+ struct at_dev_link *ad_link;
+ int drive;
+
+ printf("\n");
+
+ ab_link_proto->atapibus_softc = (caddr_t)ab;
+ ab->b_link = ab_link_proto;
+
+ for (drive = 0; drive < 2 ; drive++) {
+ if (wdc_atapi_get_params(ab_link_proto, drive, id)) {
+#ifdef ATAPI_DEBUG_PROBE
+ printf("%s drive %d: cmdsz 0x%x drqtype 0x%x\n",
+ self->dv_xname, drive,
+ id->config.cmd_drq_rem & ATAPI_PACKET_SIZE_MASK,
+ id->config.cmd_drq_rem & ATAPI_DRQ_MASK);
+#endif
+
+ /*
+ * Allocate a device link and try and attach
+ * a driver to this device. If we fail, free
+ * the link.
+ */
+ ad_link = malloc(sizeof(*ad_link), M_DEVBUF, M_NOWAIT);
+ if (ad_link == NULL) {
+ printf("%s: can't allocate link for drive %d\n",
+ self->dv_xname, drive);
+ continue;
+ }
+
+ /* Fill in link. */
+ ad_link->drive = drive;
+ if (id->config.cmd_drq_rem & ATAPI_PACKET_SIZE_16)
+ ad_link->flags |= ACAP_LEN;
+ ad_link->flags |=
+ (id->config.cmd_drq_rem & ATAPI_DRQ_MASK) << 3;
+ ad_link->bus = ab_link_proto;
+ bcopy(id, &ad_link->id, sizeof(*id));
+
+ /* Try to find a match. */
+ if (config_found(self, ad_link, atapiprint) == NULL)
+ free(ad_link, M_DEVBUF);
+ }
+ }
+}
+
+static void
+bswap (buf, len)
+ char *buf;
+ int len;
+{
+ u_int16_t *p = (u_int16_t *)(buf + len);
+
+ while (--p >= (u_int16_t *)buf)
+ *p = ntohs(*p);
+}
+
+static void
+btrim (buf, len)
+ char *buf;
+ int len;
+{
+ char *p;
+
+ /* Remove the trailing spaces. */
+ for (p = buf; p < buf + len; ++p)
+ if (*p == '\0')
+ *p = ' ';
+
+ for (p = buf + len - 1; p >= buf && *p == ' '; --p)
+ *p = '\0';
+}
+
+int
+atapi_exec_cmd(ad_link, cmd, cmd_size, databuf, datalen, rw, flags)
+ struct at_dev_link *ad_link;
+ void *cmd;
+ int cmd_size;
+ void *databuf;
+ int datalen;
+ long rw;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ struct bus_link *b_link = ad_link->bus;
+ int status, s;
+
+ /* Allocate packet. */
+ pkt = atapi_get_pkt(ad_link, flags);
+ if (pkt == NULL)
+ return -1;
+
+ /* Fill it out. */
+ bcopy(cmd, &pkt->cmd_store, cmd_size);
+ pkt->command = &pkt->cmd_store;
+ pkt->command_size = (ad_link->flags & ACAP_LEN) ? 16 : 12;
+ pkt->databuf = databuf;
+ pkt->data_size = datalen;
+ pkt->flags = rw | (flags & 0xff) | (ad_link->flags & 0x0300);
+ pkt->drive = ad_link->drive;
+
+ /* Send it to drive. */
+ wdc_atapi_send_command_packet(b_link, pkt);
+ if ((flags & (A_POLLED | A_NOSLEEP)) == 0) {
+ ATAPI_DEBUG_CMD_PRINT(("atapi_exec_cmd: sleeping\n"));
+
+ s = splbio();
+ while ((pkt->status & ITSDONE) == 0)
+ tsleep(pkt, PRIBIO + 1,"atapicmd", 0);
+ splx(s);
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_exec_cmd: done sleeping\n"));
+
+ status = pkt->status & STATUS_MASK;
+ atapi_free_pkt(pkt);
+ } else {
+ if ((flags & A_POLLED) != 0) {
+ if ((pkt->status & ERROR) && (pkt->error)) {
+ atapi_error(pkt);
+ SILENT_PRINTF(flags,("\n"));
+ }
+ }
+ status = pkt->status & STATUS_MASK;
+ if ((flags & A_POLLED) != 0)
+ atapi_free_pkt(pkt);
+ }
+
+ return status;
+}
+
+int
+atapi_exec_io(ad_link, cmd, cmd_size, bp, flags)
+ struct at_dev_link *ad_link;
+ void *cmd;
+ int cmd_size;
+ struct buf *bp;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ struct bus_link *b_link = ad_link->bus;
+
+ /* Allocate a packet. */
+ pkt = atapi_get_pkt(ad_link, flags);
+ if (pkt == NULL) {
+ printf("atapi_exec_io: no pkt\n");
+ return ERROR;
+ }
+
+ /* Fill it in. */
+ bcopy(cmd, &pkt->cmd_store, cmd_size);
+ pkt->command = &pkt->cmd_store;
+ pkt->command_size = (ad_link->flags & ACAP_LEN) ? 16 : 12;
+ pkt->bp = bp;
+ pkt->databuf = bp->b_data;
+ pkt->data_size = bp->b_bcount;
+ pkt->flags = bp->b_flags & (B_READ|B_WRITE) | (flags & 0xff) |
+ (ad_link->flags & 0x0300);
+ pkt->drive = ad_link->drive;
+
+ wdc_atapi_send_command_packet(b_link, pkt);
+ return (pkt->status & STATUS_MASK);
+}
+
+void
+atapi_done(acp)
+ struct atapi_command_packet *acp;
+{
+ struct at_dev_link *ad_link = acp->ad_link;
+ struct buf *bp = acp->bp;
+ int error = 0;
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_done\n"));
+
+ if ((acp->status & ERROR) && (acp->error)) {
+ atapi_error(acp);
+ if (acp->status & RETRY) {
+ if (acp->retries <ATAPI_NRETRIES) {
+ acp->retries++;
+ acp->status = 0;
+ acp->error = 0;
+ SILENT_PRINTF(acp->flags & 0xff,
+ (", retry #%d\n", acp->retries));
+ wdc_atapi_send_command_packet(ad_link->bus,
+ acp);
+ return;
+ } else
+ acp->status = ERROR;
+ }
+ SILENT_PRINTF(acp->flags & 0xff,("\n"));
+ }
+ acp->status |= ITSDONE;
+
+ if (ad_link->done) {
+ ATAPI_DEBUG_CMD_PRINT(("calling private done\n"));
+ error = (*ad_link->done)(acp);
+ if (error == EJUSTRETURN)
+ return;
+ }
+ if (acp->bp == NULL) {
+ ATAPI_DEBUG_CMD_PRINT(("atapidone: wakeup acp\n"));
+ wakeup(acp);
+ return;
+ }
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_done: status %d\n", acp->status));
+
+ switch (acp->status & 0x0f) {
+ case MEDIA_CHANGE:
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+
+ error = EIO;
+ break;
+
+ case NO_ERROR:
+ error = 0;
+ break;
+
+ case ERROR:
+ case END_OF_MEDIA:
+ default:
+ error = EIO;
+ break;
+ }
+
+ switch (acp->status & 0xf0) {
+ case NOT_READY:
+ case UNIT_ATTENTION:
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+
+ error = EIO;
+ break;
+ }
+
+ if (error) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = 0;
+ bp->b_resid = acp->data_size;
+ }
+ biodone(bp);
+ atapi_free_pkt(acp);
+}
+
+struct atapi_command_packet *
+atapi_get_pkt(ad_link, flags)
+ struct at_dev_link *ad_link;
+ int flags;
+{
+ struct atapi_command_packet *pkt;
+ int s;
+
+ s = splbio();
+ while (ad_link->openings <= 0) {
+ if (flags & A_NOSLEEP) {
+ splx(s);
+ return 0;
+ }
+
+ ATAPI_DEBUG_CMD_PRINT(("atapi_get_pkt: sleeping\n"));
+
+ ad_link->flags |= ADEV_WAITING;
+ (void)tsleep(ad_link, PRIBIO, "getpkt", 0);
+ }
+
+ ad_link->openings--;
+
+ if ((pkt = pkt_free_list.lh_first) != 0) {
+ LIST_REMOVE(pkt, free_list);
+ splx(s);
+ } else {
+ splx(s);
+ pkt = malloc(sizeof(struct atapi_command_packet), M_DEVBUF,
+ ((flags & A_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK));
+ if (pkt == NULL) {
+ printf("atapi_get_pkt: cannot allocate pkt\n");
+ ad_link->openings++;
+ return 0;
+ }
+ }
+
+ bzero(pkt, sizeof(struct atapi_command_packet));
+ pkt->ad_link = ad_link;
+ return pkt;
+}
+
+void
+atapi_free_pkt(pkt)
+ struct atapi_command_packet *pkt;
+{
+ struct at_dev_link *ad_link = pkt->ad_link;
+ int s;
+
+ s = splbio();
+ LIST_INSERT_HEAD(&pkt_free_list, pkt, free_list);
+
+ ad_link->openings++;
+
+ if ((ad_link->flags & ADEV_WAITING) != 0) {
+ ad_link->flags &= ~ADEV_WAITING;
+ wakeup(ad_link);
+ } else {
+ if (ad_link->start) {
+ ATAPI_DEBUG_CMD_PRINT(("atapi_free_pkt: calling private start\n"));
+ (*ad_link->start)((void *)ad_link->device_softc);
+ }
+ }
+ splx(s);
+}
+
+int
+atapi_test_unit_ready(ad_link, flags)
+ struct at_dev_link *ad_link;
+ int flags;
+{
+ int ret;
+ struct test_unit_ready cmd;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_test_unit_ready: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_TEST_UNIT_READY;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0, 0, 0, flags);
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_test_unit_ready: ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_start_stop(ad_link, how, flags)
+ struct at_dev_link *ad_link;
+ int how;
+ int flags;
+{
+ struct start_stop_unit cmd;
+ int ret;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_start_stop: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_START_STOP_UNIT;
+ cmd.how = how;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0,0,0,flags);
+
+ ATAPI_DEBUG_FCTN_PRINT(("ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_prevent(ad_link, how)
+ struct at_dev_link *ad_link;
+ int how;
+{
+ struct prevent_allow_medium_removal cmd;
+ int ret;
+
+ ATAPI_DEBUG_FCTN_PRINT(("atapi_prevent: "));
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.operation_code = ATAPI_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ cmd.how = how & 0xff;
+
+ ret = atapi_exec_cmd(ad_link, &cmd, sizeof(cmd), 0,0,0,0);
+
+ ATAPI_DEBUG_FCTN_PRINT(("ret %d\n", ret));
+
+ return ret;
+}
+
+int
+atapi_error(acp)
+ struct atapi_command_packet* acp;
+{
+ int flags, error, ret = -1;
+ struct at_dev_link *ad_link = acp->ad_link;
+
+ flags = acp->flags & 0xff;
+ error = acp->error;
+
+ at_print_addr(ad_link, acp->flags & 0xff);
+
+ if (error & ATAPI_MCR) {
+ SILENT_PRINTF(flags,("media change requested"));
+ acp->status = MEDIA_CHANGE;
+ }
+
+ if (error & ATAPI_ABRT) {
+ SILENT_PRINTF(flags,("command aborted"));
+ acp->status = ERROR;
+ }
+
+ if (error & ATAPI_EOM) {
+ SILENT_PRINTF(flags,("end of media"));
+ acp->status = END_OF_MEDIA;
+ }
+
+ if (error & ATAPI_ILI) {
+ SILENT_PRINTF(flags,("illegal length indication"));
+ acp->status = ERROR;
+ }
+
+ if ((error & 0x0f) == 0)
+ ret = 0;
+
+ atapi_sense(acp, error >> 4, flags);
+
+ if (((flags & A_SILENT) == 0) && (acp->status != NO_ERROR)) {
+ int i;
+ printf(", command:");
+ for (i = 0; i < acp->command_size; i++)
+ printf(" %2x", ((u_int8_t *)acp->command)[i]);
+ }
+
+ return ret;
+}
+
+void
+atapi_sense(acp, sense_key, flags)
+ struct atapi_command_packet *acp;
+ u_int8_t sense_key;
+ u_int8_t flags;
+{
+ struct at_dev_link *ad_link = acp->ad_link;
+
+ switch (sense_key) {
+ case ATAPI_SK_NO_SENSE:
+ break;
+
+ case ATAPI_SK_REC_ERROR:
+ SILENT_PRINTF(flags,("recovered error"));
+ acp->status = 0;
+ break;
+
+ case ATAPI_SK_NOT_READY:
+ SILENT_PRINTF(flags,("not ready"));
+ acp->status = NOT_READY;
+ break;
+
+ case ATAPI_SK_MEDIUM_ERROR:
+ SILENT_PRINTF(flags,("medium error"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_HARDWARE_ERROR:
+ SILENT_PRINTF(flags,("hardware error"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_ILLEGAL_REQUEST:
+ SILENT_PRINTF(flags,("illegal request"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_UNIT_ATTENTION:
+ SILENT_PRINTF(flags,("unit attention"));
+ acp->status = UNIT_ATTENTION;
+ if (ad_link->flags & ADEV_REMOVABLE)
+ ad_link->flags &= ~ADEV_MEDIA_LOADED;
+ break;
+
+ case ATAPI_SK_DATA_PROTECT:
+ SILENT_PRINTF(flags,("data protect"));
+ acp->status = ERROR;
+ break;
+
+ case ATAPI_SK_ABORTED_COMMAND:
+ SILENT_PRINTF(flags,("aborted command"));
+ acp->status = RETRY;
+ break;
+
+ case ATAPI_SK_MISCOMPARE:
+ SILENT_PRINTF(flags,("miscompare"));
+ acp->status = ERROR;
+ break;
+
+ default:
+ SILENT_PRINTF(flags,("unexpected sense key %02x", sense_key));
+ acp->status = ERROR;
+ }
+}
+
+void
+at_print_addr(ad_link, flags)
+ struct at_dev_link *ad_link;
+ u_int8_t flags;
+{
+
+ if (flags & A_SILENT)
+ return;
+
+ printf("%s(%s:%d): ", ad_link->device_softc ?
+ ((struct device *)ad_link->device_softc)->dv_xname : "probe",
+ ((struct device *)ad_link->bus->wdc_softc)->dv_xname,
+ ad_link->drive);
+}
diff --git a/sys/dev/atapi/atapilink.h b/sys/dev/atapi/atapilink.h
new file mode 100644
index 00000000000..7d01102a2a7
--- /dev/null
+++ b/sys/dev/atapi/atapilink.h
@@ -0,0 +1,352 @@
+/* $NetBSD: $ */
+
+/*
+ * Copyright (c) 1996 Manuel Bouyer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#undef ATAPI_DEBUG
+#undef ATAPI_DEBUG_PROBE
+
+struct bus_link {
+ u_int8_t type;
+#define DRIVE 0
+#define BUS 1
+ caddr_t wdc_softc;
+ caddr_t atapibus_softc;
+ struct wdc_link *ctlr_link;
+ u_int8_t ctrl;
+};
+
+struct atapi_identify {
+ struct config_s {
+ u_int8_t cmd_drq_rem;
+#define ATAPI_PACKET_SIZE_MASK 0x02
+#define ATAPI_PACKET_SIZE_12 0x00
+#define ATAPI_PACKET_SIZE_16 0x01
+
+#define ATAPI_DRQ_MASK 0x60
+#define ATAPI_MICROPROCESSOR_DRQ 0x00
+#define ATAPI_INTERRUPT_DRQ 0x20
+#define ATAPI_ACCELERATED_DRQ 0x40
+
+#define ATAPI_REMOVABLE 0x80
+
+ u_int8_t device_type;
+#define ATAPI_DEVICE_TYPE_MASK 0x1f
+#define ATAPI_DEVICE_TYPE_DAD 0x00 /* direct access device */
+ /* 0x1-0x4 reserved */
+#define ATAPI_DEVICE_TYPE_CD 0x05 /* CD-ROM */
+ /* 0x6 reserved */
+#define ATAPI_DEVICE_TYPE_OMD 0x07 /* optical memory device */
+ /* 0x8-0x1e reserved */
+#define ATAPI_DEVICE_TYPE_UNKNOWN 0x1f
+
+#define ATAPI_GC_PROTOCOL_MASK 0xc0 /* mask of protocol bits */
+ /* 0x00 and 0x01 are ATA */
+#define ATAPI_GC_PROTO_TYPE_ATAPI 0x80
+#define ATAPI_GC_PROTO_TYPE_RESERVED 0xc0
+ } config; /* general configuration */
+
+ u_int8_t cylinders[2];
+ u_int8_t reserved1[2];
+ u_int8_t heads[2];
+ u_int8_t unf_bytes_per_track[2];
+ u_int8_t unf_bytes_per_sector[2];
+ u_int8_t sectors_per_track[2];
+ u_int8_t reserved2[6];
+ char serial_number[20];
+ u_int8_t buffer_type[2];
+ u_int8_t buffer_size[2];
+ u_int8_t ECC_bytes_available[2];
+ char firmware_revision[8];
+ char model[40];
+ u_int8_t sector_count[2];
+ u_int8_t double_word[2]; /* == 0 for CD-ROMs */
+
+ struct capabilities_s {
+ u_int8_t vendor;
+ u_int8_t capflags;
+#define ATAPI_CAP_DMA 0x01 /* DMA supported */
+#define ATAPI_CAP_LBA 0x02 /* LBA supported */
+#define ATAPI_IORDY_DISABLE 0x04 /* IORDY can be disabled */
+#define ATAPI_IORDY 0x08 /* IORDY supported */
+ } capabilities;
+
+ u_int8_t reserved3[2];
+ u_int8_t PIO_cycle_timing[2];
+ u_int8_t DMA_cycle_timing[2];
+ u_int8_t validity[2]; /* of words 54-58, 64-70 in this table */
+
+#define ATAPI_VALID_FIRST 0x0 /* == 1 => words 54-58 are valid */
+#define ATAPI_VALID_SECOND 0x1 /* == 1 => words 64-70 are valid */
+
+ u_int8_t current_chs[6]; /* cylinder/head/sector */
+ u_int8_t current_capacity[4];
+ u_int8_t reserved4[2];
+ u_int8_t user_addressable_sectors[4];
+ u_int8_t singleword_DMA_mode[2];
+
+#define ATAPI_SW_DMA_MODE_AVAIL 0x00ff /* Mode 0 is supported */
+#define ATAPI_SW_DMA_MODE_ACTIVE 0xff00 /* which mode is active */
+
+ u_int8_t multiword_DMA_mode[2];
+
+#define ATAPI_MW_DMA_MODE_AVAIL 0x00ff /* Mode 0 is supported */
+#define ATAPI_MW_DMA_MODE_ACTIVE 0xff00 /* which mode is active */
+
+ u_int8_t enhanced_PIO_mode[2];
+
+#define ATAPI_ENHANCED_PIO_AVAIL 0x0001 /* PIO Mode 3 is supported */
+
+ u_int8_t blind_PIO_minimum_cycles[2];
+ u_int8_t mw_dma_tct[2]; /* multi-word DMA transfer cycle time */
+ u_int8_t min_PIO_tct_no_flow_control[2];
+ u_int8_t min_PIO_tct_with_flow_control[2];
+ u_int8_t reserved5[4];
+ u_int8_t reserved6[114];
+ u_int8_t vendor[64]; /* vendor unique */
+ u_int8_t reserved7[192];
+};
+
+struct at_dev_link {
+ void *device_softc;
+ u_int8_t drive;
+ u_int8_t openings;
+ struct atapi_identify id;
+ struct bus_link *bus;
+ u_int16_t flags;
+#define ADEV_REMOVABLE 0x001 /* media is removable */
+#define ADEV_MEDIA_LOADED 0x002 /* device figures are still valid */
+#define ADEV_WAITING 0x004 /* a process is waiting for this */
+#define ADEV_OPEN 0x008 /* at least 1 open session */
+#define ACAP_DRQ_MPROC 0x000 /* microprocessor DRQ */
+#define ACAP_DRQ_INTR 0x100 /* interrupt DRQ */
+#define ACAP_DRQ_ACCEL 0x200 /* accelerated DRQ */
+#define ACAP_LEN 0x400 /* 16 bit commands */
+ void (*start)(); /* device start routine */
+ int (*done)(); /* device done routine */
+};
+
+struct atapi_command_packet {
+ void *ad_link;
+ void *command;
+ char cmd_store[16];
+ int command_size;
+ struct buf *bp;
+ void *databuf;
+ int data_size;
+ int flags; /* handle B_READ/B_WRITE mask 0x00f00000 */
+ /* controller flags maks 0x0000000f */
+ /* ATAPI flags mask 0x000000f0 */
+ /* Capabilities flags 0x00000f00 */
+ u_int8_t drive;
+ u_int16_t status;
+#define STATUS_MASK 0xff
+#define NO_ERROR 0x00
+#define ERROR 0x01
+#define MEDIA_CHANGE 0x02
+#define END_OF_MEDIA 0x03
+#define NOT_READY 0x10
+#define UNIT_ATTENTION 0x20
+#define RETRY 0x40
+#define ITSDONE 0x100
+ u_int8_t error;
+ u_int8_t retries;
+#define ATAPI_NRETRIES 5
+ LIST_ENTRY(atapi_command_packet) free_list;
+};
+
+int wdc_atapi_get_params __P((struct bus_link *, u_int8_t,
+ struct atapi_identify *));
+void wdc_atapi_send_command_packet __P((struct bus_link *,
+ struct atapi_command_packet*));
+
+#define A_POLLED 0x10
+#define A_NOSLEEP 0x20
+#define A_SILENT 0x40
+
+void atapi_done __P((struct atapi_command_packet *));
+struct atapi_command_packet *atapi_get_pkt __P((struct at_dev_link *, int));
+void atapi_free_pkt __P((struct atapi_command_packet *));
+
+/*
+ * Functions used for reading and writing 2, 3, and 4 byte values
+ * in ATAPI commands.
+ */
+
+static __inline void _lto2b __P((u_int32_t val, u_int8_t *bytes));
+static __inline void _lto3b __P((u_int32_t val, u_int8_t *bytes));
+static __inline void _lto4b __P((u_int32_t val, u_int8_t *bytes));
+static __inline u_int32_t _2btol __P((u_int8_t *bytes));
+static __inline u_int32_t _3btol __P((u_int8_t *bytes));
+static __inline u_int32_t _4btol __P((u_int8_t *bytes));
+
+static __inline void _lto2l __P((u_int32_t val, u_int8_t *bytes));
+static __inline void _lto3l __P((u_int32_t val, u_int8_t *bytes));
+static __inline void _lto4l __P((u_int32_t val, u_int8_t *bytes));
+static __inline u_int32_t _2ltol __P((u_int8_t *bytes));
+static __inline u_int32_t _3ltol __P((u_int8_t *bytes));
+static __inline u_int32_t _4ltol __P((u_int8_t *bytes));
+
+static __inline void
+_lto2b(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = (val >> 8) & 0xff;
+ bytes[1] = val & 0xff;
+}
+
+static __inline void
+_lto3b(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = (val >> 16) & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = val & 0xff;
+}
+
+static __inline void
+_lto4b(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = (val >> 24) & 0xff;
+ bytes[1] = (val >> 16) & 0xff;
+ bytes[2] = (val >> 8) & 0xff;
+ bytes[3] = val & 0xff;
+}
+
+static __inline u_int32_t
+_2btol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = (bytes[0] << 8) |
+ bytes[1];
+ return (rv);
+}
+
+static __inline u_int32_t
+_3btol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = (bytes[0] << 16) |
+ (bytes[1] << 8) |
+ bytes[2];
+ return (rv);
+}
+
+static __inline u_int32_t
+_4btol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = (bytes[0] << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+ return (rv);
+}
+
+static __inline void
+_lto2l(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = val & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+}
+
+static __inline void
+_lto3l(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = val & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = (val >> 16) & 0xff;
+}
+
+static __inline void
+_lto4l(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ bytes[0] = val & 0xff;
+ bytes[1] = (val >> 8) & 0xff;
+ bytes[2] = (val >> 16) & 0xff;
+ bytes[3] = (val >> 24) & 0xff;
+}
+
+static __inline u_int32_t
+_2ltol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = bytes[0] |
+ (bytes[1] << 8);
+ return (rv);
+}
+
+static __inline u_int32_t
+_3ltol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = bytes[0] |
+ (bytes[1] << 8) |
+ (bytes[2] << 16);
+ return (rv);
+}
+
+static __inline u_int32_t
+_4ltol(bytes)
+ u_int8_t *bytes;
+{
+ register u_int32_t rv;
+
+ rv = bytes[0] |
+ (bytes[1] << 8) |
+ (bytes[2] << 16) |
+ (bytes[3] << 24);
+ return (rv);
+}
diff --git a/sys/dev/atapi/files.atapi b/sys/dev/atapi/files.atapi
new file mode 100644
index 00000000000..508710c0960
--- /dev/null
+++ b/sys/dev/atapi/files.atapi
@@ -0,0 +1,14 @@
+#
+# Config.new file and device description for machine-independent ATAPI code.
+# Included by ports that need it. Ports that use it must provide
+# their own "major" declarations for the appropriate devices.
+
+define atapi {}
+file dev/atapi/atapiconf.c atapi
+
+device atapibus {drive = -1}
+attach atapibus at atapi
+
+device acd: disk
+attach acd at atapibus
+file dev/atapi/acd.c acd needs-flag