summaryrefslogtreecommitdiff
path: root/usr/src/uts/intel/io/dktp/dcdev/dadk.c
diff options
context:
space:
mode:
authormlf <none@none>2006-03-29 16:19:30 -0800
committermlf <none@none>2006-03-29 16:19:30 -0800
commit507c32411f3f101e90ca2120f042b5ee698ba1d5 (patch)
treed6c3d69e04d180c1fa52a92cb4d897fffb3c3ec0 /usr/src/uts/intel/io/dktp/dcdev/dadk.c
parent03831d35f7499c87d51205817c93e9a8d42c4bae (diff)
downloadillumos-joyent-507c32411f3f101e90ca2120f042b5ee698ba1d5.tar.gz
6392614 x86: ata driver should be open sourced
--HG-- rename : usr/src/uts/common/io/dktp/dcdev/dadk.c => usr/src/uts/intel/io/dktp/dcdev/dadk.c rename : usr/src/uts/common/io/dktp/dcdev/gda.c => usr/src/uts/intel/io/dktp/dcdev/gda.c rename : usr/src/uts/common/io/dktp/disk/cmdk.c => usr/src/uts/intel/io/dktp/disk/cmdk.c rename : usr/src/uts/common/io/dktp/drvobj/strategy.c => usr/src/uts/intel/io/dktp/drvobj/strategy.c rename : usr/src/uts/common/io/dktp/hba/ghd/ghd.h => usr/src/uts/intel/io/dktp/hba/ghd/ghd.h rename : usr/src/uts/common/io/dktp/hba/ghd/ghd_debug.h => usr/src/uts/intel/io/dktp/hba/ghd/ghd_debug.h rename : usr/src/uts/common/io/dktp/hba/ghd/ghd_dma.h => usr/src/uts/intel/io/dktp/hba/ghd/ghd_dma.h rename : usr/src/uts/common/io/dktp/hba/ghd/ghd_queue.h => usr/src/uts/intel/io/dktp/hba/ghd/ghd_queue.h rename : usr/src/uts/common/io/dktp/hba/ghd/ghd_scsa.h => usr/src/uts/intel/io/dktp/hba/ghd/ghd_scsa.h rename : usr/src/uts/common/io/dktp/hba/ghd/ghd_scsi.h => usr/src/uts/intel/io/dktp/hba/ghd/ghd_scsi.h rename : usr/src/uts/common/io/dktp/hba/ghd/ghd_waitq.h => usr/src/uts/intel/io/dktp/hba/ghd/ghd_waitq.h
Diffstat (limited to 'usr/src/uts/intel/io/dktp/dcdev/dadk.c')
-rw-r--r--usr/src/uts/intel/io/dktp/dcdev/dadk.c1606
1 files changed, 1606 insertions, 0 deletions
diff --git a/usr/src/uts/intel/io/dktp/dcdev/dadk.c b/usr/src/uts/intel/io/dktp/dcdev/dadk.c
new file mode 100644
index 0000000000..02b22ef9db
--- /dev/null
+++ b/usr/src/uts/intel/io/dktp/dcdev/dadk.c
@@ -0,0 +1,1606 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Direct Attached Disk
+ */
+
+#include <sys/file.h>
+#include <sys/scsi/scsi.h>
+#include <sys/var.h>
+#include <sys/proc.h>
+#include <sys/dktp/cm.h>
+#include <sys/vtoc.h>
+#include <sys/dkio.h>
+
+#include <sys/dktp/dadev.h>
+#include <sys/dktp/fctypes.h>
+#include <sys/dktp/flowctrl.h>
+#include <sys/dktp/tgcom.h>
+#include <sys/dktp/tgdk.h>
+#include <sys/dktp/bbh.h>
+#include <sys/dktp/dadkio.h>
+#include <sys/dktp/dadk.h>
+#include <sys/cdio.h>
+
+/*
+ * Local Function Prototypes
+ */
+static void dadk_restart(void *pktp);
+static void dadk_pktcb(struct cmpkt *pktp);
+static void dadk_iodone(struct buf *bp);
+static void dadk_polldone(struct buf *bp);
+static void dadk_setcap(struct dadk *dadkp);
+
+static int dadk_chkerr(struct cmpkt *pktp);
+static int dadk_ioprep(struct dadk *dadkp, struct cmpkt *pktp);
+static int dadk_iosetup(struct dadk *dadkp, struct cmpkt *pktp);
+static int dadk_ioretry(struct cmpkt *pktp, int action);
+
+static struct cmpkt *dadk_pktprep(struct dadk *dadkp, struct cmpkt *in_pktp,
+ struct buf *bp, void (*cb_func)(struct buf *), int (*func)(caddr_t),
+ caddr_t arg);
+
+static int dadk_pkt(opaque_t com_data, struct buf *bp, int (*func)(caddr_t),
+ caddr_t arg);
+static void dadk_transport(opaque_t com_data, struct buf *bp);
+
+struct tgcom_objops dadk_com_ops = {
+ nodev,
+ nodev,
+ dadk_pkt,
+ dadk_transport,
+ 0, 0
+};
+
+/*
+ * architecture dependent allocation restrictions for dadk_iob_alloc(). For
+ * x86, we'll set dma_attr_addr_hi to dadk_max_phys_addr and dma_attr_sgllen
+ * to dadk_sgl_size during _init().
+ */
+#if defined(__sparc)
+static ddi_dma_attr_t dadk_alloc_attr = {
+ DMA_ATTR_V0, /* version number */
+ 0x0, /* lowest usable address */
+ 0xFFFFFFFFull, /* high DMA address range */
+ 0xFFFFFFFFull, /* DMA counter register */
+ 1, /* DMA address alignment */
+ 1, /* DMA burstsizes */
+ 1, /* min effective DMA size */
+ 0xFFFFFFFFull, /* max DMA xfer size */
+ 0xFFFFFFFFull, /* segment boundary */
+ 1, /* s/g list length */
+ 512, /* granularity of device */
+ 0, /* DMA transfer flags */
+};
+#elif defined(__x86)
+static ddi_dma_attr_t dadk_alloc_attr = {
+ DMA_ATTR_V0, /* version number */
+ 0x0, /* lowest usable address */
+ 0x0, /* high DMA address range [set in _init()] */
+ 0xFFFFull, /* DMA counter register */
+ 512, /* DMA address alignment */
+ 1, /* DMA burstsizes */
+ 1, /* min effective DMA size */
+ 0xFFFFFFFFull, /* max DMA xfer size */
+ 0xFFFFFFFFull, /* segment boundary */
+ 0, /* s/g list length [set in _init()] */
+ 512, /* granularity of device */
+ 0, /* DMA transfer flags */
+};
+
+uint64_t dadk_max_phys_addr = 0xFFFFFFFFull;
+int dadk_sgl_size = 0xFF;
+#endif
+
+static int dadk_rmb_ioctl(struct dadk *dadkp, int cmd, intptr_t arg, int flags,
+ int silent);
+static void dadk_rmb_iodone(struct buf *bp);
+
+static int dadk_dk_buf_setup(struct dadk *dadkp, opaque_t *cmdp,
+ dev_t dev, enum uio_seg dataspace, int rw);
+static void dadk_dk(struct dadk *dadkp, struct dadkio_rwcmd *scmdp,
+ struct buf *bp);
+static void dadkmin(struct buf *bp);
+static int dadk_dk_strategy(struct buf *bp);
+static void dadk_recorderr(struct cmpkt *pktp, struct dadkio_rwcmd *rwcmdp);
+
+struct tgdk_objops dadk_ops = {
+ dadk_init,
+ dadk_free,
+ dadk_probe,
+ dadk_attach,
+ dadk_open,
+ dadk_close,
+ dadk_ioctl,
+ dadk_strategy,
+ dadk_setgeom,
+ dadk_getgeom,
+ dadk_iob_alloc,
+ dadk_iob_free,
+ dadk_iob_htoc,
+ dadk_iob_xfer,
+ dadk_dump,
+ dadk_getphygeom,
+ dadk_set_bbhobj,
+ dadk_check_media,
+ dadk_inquiry,
+ dadk_cleanup,
+ 0
+};
+
+/*
+ * Local static data
+ */
+
+#ifdef DADK_DEBUG
+#define DENT 0x0001
+#define DERR 0x0002
+#define DIO 0x0004
+#define DGEOM 0x0010
+#define DSTATE 0x0020
+static int dadk_debug = DGEOM;
+
+#endif /* DADK_DEBUG */
+
+static int dadk_check_media_time = 3000000; /* 3 Second State Check */
+static int dadk_dk_maxphys = 0x80000;
+
+static char *dadk_cmds[] = {
+ "\000Unknown", /* unknown */
+ "\001read sector", /* DCMD_READ 1 */
+ "\002write sector", /* DCMD_WRITE 2 */
+ "\003format track", /* DCMD_FMTTRK 3 */
+ "\004format whole drive", /* DCMD_FMTDRV 4 */
+ "\005recalibrate", /* DCMD_RECAL 5 */
+ "\006seek sector", /* DCMD_SEEK 6 */
+ "\007read verify", /* DCMD_RDVER 7 */
+ "\010read defect list", /* DCMD_GETDEF 8 */
+ "\011lock door", /* DCMD_LOCK 9 */
+ "\012unlock door", /* DCMD_UNLOCK 10 */
+ "\013start motor", /* DCMD_START_MOTOR 11 */
+ "\014stop motor", /* DCMD_STOP_MOTOR 12 */
+ "\015eject", /* DCMD_EJECT 13 */
+ "\016update geometry", /* DCMD_UPDATE_GEOM 14 */
+ "\017get state", /* DCMD_GET_STATE 15 */
+ "\020cdrom pause", /* DCMD_PAUSE 16 */
+ "\021cdrom resume", /* DCMD_RESUME 17 */
+ "\022cdrom play track index", /* DCMD_PLAYTRKIND 18 */
+ "\023cdrom play msf", /* DCMD_PLAYMSF 19 */
+ "\024cdrom sub channel", /* DCMD_SUBCHNL 20 */
+ "\025cdrom read mode 1", /* DCMD_READMODE1 21 */
+ "\026cdrom read toc header", /* DCMD_READTOCHDR 22 */
+ "\027cdrom read toc entry", /* DCMD_READTOCENT 23 */
+ "\030cdrom read offset", /* DCMD_READOFFSET 24 */
+ "\031cdrom read mode 2", /* DCMD_READMODE2 25 */
+ "\032cdrom volume control", /* DCMD_VOLCTRL 26 */
+ "\033flush cache", /* DCMD_FLUSH_CACHE 27 */
+ NULL
+};
+
+static char *dadk_sense[] = {
+ "\000Success", /* DERR_SUCCESS */
+ "\001address mark not found", /* DERR_AMNF */
+ "\002track 0 not found", /* DERR_TKONF */
+ "\003aborted command", /* DERR_ABORT */
+ "\004write fault", /* DERR_DWF */
+ "\005ID not found", /* DERR_IDNF */
+ "\006drive busy", /* DERR_BUSY */
+ "\007uncorrectable data error", /* DERR_UNC */
+ "\010bad block detected", /* DERR_BBK */
+ "\011invalid command", /* DERR_INVCDB */
+ "\012device hard error", /* DERR_HARD */
+ "\013illegal length indicated", /* DERR_ILI */
+ "\014end of media", /* DERR_EOM */
+ "\015media change requested", /* DERR_MCR */
+ "\016recovered from error", /* DERR_RECOVER */
+ "\017device not ready", /* DERR_NOTREADY */
+ "\020medium error", /* DERR_MEDIUM */
+ "\021hardware error", /* DERR_HW */
+ "\022illegal request", /* DERR_ILL */
+ "\023unit attention", /* DERR_UNIT_ATTN */
+ "\024data protection", /* DERR_DATA_PROT */
+ "\025miscompare", /* DERR_MISCOMPARE */
+ "\026ICRC error during UDMA", /* DERR_ICRC */
+ "\027reserved", /* DERR_RESV */
+ NULL
+};
+
+static char *dadk_name = "Disk";
+
+/*
+ * This is the loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+extern struct mod_ops mod_miscops;
+
+static struct modlmisc modlmisc = {
+ &mod_miscops, /* Type of module */
+ "Direct Attached Disk %I%"
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlmisc, NULL
+};
+
+int
+_init(void)
+{
+#ifdef DADK_DEBUG
+ if (dadk_debug & DENT)
+ PRF("dadk_init: call\n");
+#endif
+
+#if defined(__x86)
+ /* set the max physical address for iob allocs on x86 */
+ dadk_alloc_attr.dma_attr_addr_hi = dadk_max_phys_addr;
+
+ /*
+ * set the sgllen for iob allocs on x86. If this is set less than
+ * the number of pages the buffer will take (taking into account
+ * alignment), it would force the allocator to try and allocate
+ * contiguous pages.
+ */
+ dadk_alloc_attr.dma_attr_sgllen = dadk_sgl_size;
+#endif
+
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini(void)
+{
+#ifdef DADK_DEBUG
+ if (dadk_debug & DENT)
+ PRF("dadk_fini: call\n");
+#endif
+
+ return (mod_remove(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+struct tgdk_obj *
+dadk_create()
+{
+ struct tgdk_obj *dkobjp;
+ struct dadk *dadkp;
+
+ dkobjp = kmem_zalloc((sizeof (*dkobjp) + sizeof (*dadkp)), KM_NOSLEEP);
+ if (!dkobjp)
+ return (NULL);
+ dadkp = (struct dadk *)(dkobjp+1);
+
+ dkobjp->tg_ops = (struct tgdk_objops *)&dadk_ops;
+ dkobjp->tg_data = (opaque_t)dadkp;
+ dkobjp->tg_ext = &(dkobjp->tg_extblk);
+ dadkp->dad_extp = &(dkobjp->tg_extblk);
+
+#ifdef DADK_DEBUG
+ if (dadk_debug & DENT)
+ PRF("dadk_create: tgdkobjp= 0x%x dadkp= 0x%x\n", dkobjp, dadkp);
+#endif
+ return (dkobjp);
+}
+
+int
+dadk_init(opaque_t objp, opaque_t devp, opaque_t flcobjp, opaque_t queobjp,
+ opaque_t bbhobjp, void *lkarg)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ struct scsi_device *sdevp = (struct scsi_device *)devp;
+
+ dadkp->dad_sd = devp;
+ dadkp->dad_ctlobjp = (opaque_t)sdevp->sd_address.a_hba_tran;
+ sdevp->sd_private = (caddr_t)dadkp;
+
+ /* initialize the communication object */
+ dadkp->dad_com.com_data = (opaque_t)dadkp;
+ dadkp->dad_com.com_ops = &dadk_com_ops;
+
+ dadkp->dad_bbhobjp = bbhobjp;
+ BBH_INIT(bbhobjp);
+
+ dadkp->dad_flcobjp = flcobjp;
+ return (FLC_INIT(flcobjp, &(dadkp->dad_com), queobjp, lkarg));
+}
+
+int
+dadk_free(struct tgdk_obj *dkobjp)
+{
+ TGDK_CLEANUP(dkobjp);
+ kmem_free(dkobjp, (sizeof (*dkobjp) + sizeof (struct dadk)));
+
+ return (DDI_SUCCESS);
+}
+
+void
+dadk_cleanup(struct tgdk_obj *dkobjp)
+{
+ struct dadk *dadkp;
+
+ dadkp = (struct dadk *)(dkobjp->tg_data);
+ if (dadkp->dad_sd)
+ dadkp->dad_sd->sd_private = NULL;
+ if (dadkp->dad_bbhobjp) {
+ BBH_FREE(dadkp->dad_bbhobjp);
+ dadkp->dad_bbhobjp = NULL;
+ }
+ if (dadkp->dad_flcobjp) {
+ FLC_FREE(dadkp->dad_flcobjp);
+ dadkp->dad_flcobjp = NULL;
+ }
+}
+
+/* ARGSUSED */
+int
+dadk_probe(opaque_t objp, int kmsflg)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ struct scsi_device *devp;
+ char name[80];
+
+ devp = dadkp->dad_sd;
+ if (!devp->sd_inq || (devp->sd_inq->inq_dtype == DTYPE_NOTPRESENT) ||
+ (devp->sd_inq->inq_dtype == DTYPE_UNKNOWN)) {
+ return (DDI_PROBE_FAILURE);
+ }
+
+ switch (devp->sd_inq->inq_dtype) {
+ case DTYPE_DIRECT:
+ dadkp->dad_ctype = DKC_DIRECT;
+ dadkp->dad_extp->tg_nodetype = DDI_NT_BLOCK;
+ dadkp->dad_extp->tg_ctype = DKC_DIRECT;
+ break;
+ case DTYPE_RODIRECT: /* eg cdrom */
+ dadkp->dad_ctype = DKC_CDROM;
+ dadkp->dad_extp->tg_rdonly = 1;
+ dadkp->dad_rdonly = 1;
+ dadkp->dad_cdrom = 1;
+ dadkp->dad_extp->tg_nodetype = DDI_NT_CD;
+ dadkp->dad_extp->tg_ctype = DKC_CDROM;
+ break;
+ case DTYPE_WORM:
+ case DTYPE_OPTICAL:
+ default:
+ return (DDI_PROBE_FAILURE);
+ }
+
+ dadkp->dad_extp->tg_rmb = dadkp->dad_rmb = devp->sd_inq->inq_rmb;
+
+ dadkp->dad_secshf = SCTRSHFT;
+ dadkp->dad_blkshf = 0;
+
+ /* display the device name */
+ (void) strcpy(name, "Vendor '");
+ gda_inqfill((caddr_t)devp->sd_inq->inq_vid, 8, &name[strlen(name)]);
+ (void) strcat(name, "' Product '");
+ gda_inqfill((caddr_t)devp->sd_inq->inq_pid, 16, &name[strlen(name)]);
+ (void) strcat(name, "'");
+ gda_log(devp->sd_dev, dadk_name, CE_NOTE, "!<%s>\n", name);
+
+ return (DDI_PROBE_SUCCESS);
+}
+
+
+/* ARGSUSED */
+int
+dadk_attach(opaque_t objp)
+{
+ return (DDI_SUCCESS);
+}
+
+int
+dadk_set_bbhobj(opaque_t objp, opaque_t bbhobjp)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ /* free the old bbh object */
+ if (dadkp->dad_bbhobjp)
+ BBH_FREE(dadkp->dad_bbhobjp);
+
+ /* initialize the new bbh object */
+ dadkp->dad_bbhobjp = bbhobjp;
+ BBH_INIT(bbhobjp);
+
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+int
+dadk_open(opaque_t objp, int flag)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ int error;
+ int wce;
+
+ if (!dadkp->dad_rmb) {
+ if (dadkp->dad_phyg.g_cap) {
+ FLC_START_KSTAT(dadkp->dad_flcobjp, "disk",
+ ddi_get_instance(CTL_DIP_DEV(dadkp->dad_ctlobjp)));
+ return (DDI_SUCCESS);
+ }
+ } else {
+ mutex_enter(&dadkp->dad_mutex);
+ dadkp->dad_iostate = DKIO_NONE;
+ cv_broadcast(&dadkp->dad_state_cv);
+ mutex_exit(&dadkp->dad_mutex);
+
+ if (dadk_rmb_ioctl(dadkp, DCMD_START_MOTOR, 0, 0, DADK_SILENT) ||
+ dadk_rmb_ioctl(dadkp, DCMD_LOCK, 0, 0, DADK_SILENT) ||
+ dadk_rmb_ioctl(dadkp, DCMD_UPDATE_GEOM, 0, 0, DADK_SILENT)) {
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&dadkp->dad_mutex);
+ dadkp->dad_iostate = DKIO_INSERTED;
+ cv_broadcast(&dadkp->dad_state_cv);
+ mutex_exit(&dadkp->dad_mutex);
+ }
+
+ /*
+ * get write cache enable state
+ * If there is an error, must assume that write cache
+ * is enabled.
+ * NOTE: Since there is currently no Solaris mechanism to
+ * change the state of the Write Cache Enable feature,
+ * this code just checks the value of the WCE bit
+ * obtained at device init time. If a mechanism
+ * is added to the driver to change WCE, dad_wce
+ * must be updated appropriately.
+ */
+ error = CTL_IOCTL(dadkp->dad_ctlobjp, DIOCTL_GETWCE,
+ (uintptr_t)&wce, 0);
+ mutex_enter(&dadkp->dad_mutex);
+ dadkp->dad_wce = (error != 0) || (wce != 0);
+ mutex_exit(&dadkp->dad_mutex);
+
+ /* logical disk geometry */
+ CTL_IOCTL(dadkp->dad_ctlobjp, DIOCTL_GETGEOM,
+ (uintptr_t)&dadkp->dad_logg, 0);
+ if (dadkp->dad_logg.g_cap == 0)
+ return (DDI_FAILURE);
+
+ /* get physical disk geometry */
+ CTL_IOCTL(dadkp->dad_ctlobjp, DIOCTL_GETPHYGEOM,
+ (uintptr_t)&dadkp->dad_phyg, 0);
+ if (dadkp->dad_phyg.g_cap == 0)
+ return (DDI_FAILURE);
+
+ dadk_setcap(dadkp);
+
+ /* start profiling */
+ FLC_START_KSTAT(dadkp->dad_flcobjp, "disk",
+ ddi_get_instance(CTL_DIP_DEV(dadkp->dad_ctlobjp)));
+
+ return (DDI_SUCCESS);
+}
+
+static void
+dadk_setcap(struct dadk *dadkp)
+{
+ int totsize;
+ int i;
+
+ totsize = dadkp->dad_phyg.g_secsiz;
+
+ if (totsize == 0) {
+ if (dadkp->dad_cdrom) {
+ totsize = 2048;
+ } else {
+ totsize = NBPSCTR;
+ }
+ } else {
+ /* Round down sector size to multiple of 512B */
+ totsize &= ~(NBPSCTR-1);
+ }
+ dadkp->dad_phyg.g_secsiz = totsize;
+
+ /* set sec,block shift factor - (512->0, 1024->1, 2048->2, etc.) */
+ totsize >>= SCTRSHFT;
+ for (i = 0; totsize != 1; i++, totsize >>= 1);
+ dadkp->dad_blkshf = i;
+ dadkp->dad_secshf = i + SCTRSHFT;
+}
+
+
+int
+dadk_close(opaque_t objp)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+
+ if (dadkp->dad_rmb) {
+ (void) dadk_rmb_ioctl(dadkp, DCMD_STOP_MOTOR, 0, 0,
+ DADK_SILENT);
+ (void) dadk_rmb_ioctl(dadkp, DCMD_UNLOCK, 0, 0, DADK_SILENT);
+ }
+ FLC_STOP_KSTAT(dadkp->dad_flcobjp);
+ return (DDI_SUCCESS);
+}
+
+int
+dadk_strategy(opaque_t objp, struct buf *bp)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+
+ if (dadkp->dad_rdonly && !(bp->b_flags & B_READ)) {
+ bioerror(bp, EROFS);
+ return (DDI_FAILURE);
+ }
+
+ if (bp->b_bcount & (dadkp->DAD_SECSIZ-1)) {
+ bioerror(bp, ENXIO);
+ return (DDI_FAILURE);
+ }
+
+ SET_BP_SEC(bp, (LBLK2SEC(GET_BP_SEC(bp), dadkp->dad_blkshf)));
+ FLC_ENQUE(dadkp->dad_flcobjp, bp);
+
+ return (DDI_SUCCESS);
+}
+
+int
+dadk_dump(opaque_t objp, struct buf *bp)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ struct cmpkt *pktp;
+
+ if (dadkp->dad_rdonly) {
+ bioerror(bp, EROFS);
+ return (DDI_FAILURE);
+ }
+
+ if (bp->b_bcount & (dadkp->DAD_SECSIZ-1)) {
+ bioerror(bp, ENXIO);
+ return (DDI_FAILURE);
+ }
+
+ SET_BP_SEC(bp, (LBLK2SEC(GET_BP_SEC(bp), dadkp->dad_blkshf)));
+
+ pktp = dadk_pktprep(dadkp, NULL, bp, dadk_polldone, NULL, NULL);
+ if (!pktp) {
+ cmn_err(CE_WARN, "no resources for dumping");
+ bioerror(bp, EIO);
+ return (DDI_FAILURE);
+ }
+ pktp->cp_flags |= CPF_NOINTR;
+
+ (void) dadk_ioprep(dadkp, pktp);
+ dadk_transport(dadkp, bp);
+ pktp->cp_byteleft -= pktp->cp_bytexfer;
+
+ while (geterror(bp) == 0 && pktp->cp_byteleft != 0) {
+ (void) dadk_iosetup(dadkp, pktp);
+ dadk_transport(dadkp, bp);
+ pktp->cp_byteleft -= pktp->cp_bytexfer;
+ }
+
+ if (pktp->cp_private)
+ BBH_FREEHANDLE(dadkp->dad_bbhobjp, pktp->cp_private);
+ gda_free(dadkp->dad_ctlobjp, pktp, NULL);
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+int
+dadk_ioctl(opaque_t objp, dev_t dev, int cmd, intptr_t arg, int flag,
+ cred_t *cred_p, int *rval_p)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+
+ switch (cmd) {
+ case DKIOCGETDEF:
+ {
+ struct buf *bp;
+ int err, head;
+ unsigned char *secbuf;
+ STRUCT_DECL(defect_header, adh);
+
+ STRUCT_INIT(adh, flag & FMODELS);
+
+ /*
+ * copyin header ....
+ * yields head number and buffer address
+ */
+ if (ddi_copyin((caddr_t)arg, STRUCT_BUF(adh), STRUCT_SIZE(adh),
+ flag))
+ return (EFAULT);
+ head = STRUCT_FGET(adh, head);
+ if (head < 0 || head >= dadkp->dad_phyg.g_head)
+ return (ENXIO);
+ secbuf = kmem_zalloc(NBPSCTR, KM_SLEEP);
+ if (!secbuf)
+ return (ENOMEM);
+ bp = getrbuf(KM_SLEEP);
+ if (!bp) {
+ kmem_free(secbuf, NBPSCTR);
+ return (ENOMEM);
+ }
+
+ bp->b_edev = dev;
+ bp->b_dev = cmpdev(dev);
+ bp->b_flags = B_BUSY;
+ bp->b_resid = 0;
+ bp->b_bcount = NBPSCTR;
+ bp->b_un.b_addr = (caddr_t)secbuf;
+ bp->b_blkno = head; /* I had to put it somwhere! */
+ bp->b_forw = (struct buf *)dadkp;
+ bp->b_back = (struct buf *)DCMD_GETDEF;
+
+ FLC_ENQUE(dadkp->dad_flcobjp, bp);
+ err = biowait(bp);
+ if (!err) {
+ if (ddi_copyout((caddr_t)secbuf,
+ STRUCT_FGETP(adh, buffer), NBPSCTR, flag))
+ err = ENXIO;
+ }
+ kmem_free(secbuf, NBPSCTR);
+ freerbuf(bp);
+ return (err);
+ }
+ case DIOCTL_RWCMD:
+ {
+ struct dadkio_rwcmd *rwcmdp;
+ int status, rw;
+
+ /*
+ * copied in by cmdk and, if necessary, converted to the
+ * correct datamodel
+ */
+ rwcmdp = (struct dadkio_rwcmd *)(intptr_t)arg;
+
+ /*
+ * handle the complex cases here; we pass these
+ * through to the driver, which will queue them and
+ * handle the requests asynchronously. The simpler
+ * cases ,which can return immediately, fail here, and
+ * the request reverts to the dadk_ioctl routine, while
+ * will reroute them directly to the ata driver.
+ */
+ switch (rwcmdp->cmd) {
+ case DADKIO_RWCMD_READ :
+ /*FALLTHROUGH*/
+ case DADKIO_RWCMD_WRITE:
+ rw = ((rwcmdp->cmd == DADKIO_RWCMD_WRITE) ?
+ B_WRITE : B_READ);
+ status = dadk_dk_buf_setup(dadkp,
+ (opaque_t)rwcmdp, dev, ((flag &FKIOCTL) ?
+ UIO_SYSSPACE : UIO_USERSPACE), rw);
+ return (status);
+ default:
+ return (EINVAL);
+ }
+ }
+ case DKIOCFLUSHWRITECACHE:
+ {
+ struct buf *bp;
+ int err = 0;
+ struct dk_callback *dkc = (struct dk_callback *)arg;
+ struct cmpkt *pktp;
+ int is_sync = 1;
+
+ mutex_enter(&dadkp->dad_mutex);
+ if (dadkp->dad_noflush || ! dadkp->dad_wce) {
+ err = dadkp->dad_noflush ? ENOTSUP : 0;
+ mutex_exit(&dadkp->dad_mutex);
+ /*
+ * If a callback was requested: a
+ * callback will always be done if the
+ * caller saw the DKIOCFLUSHWRITECACHE
+ * ioctl return 0, and never done if the
+ * caller saw the ioctl return an error.
+ */
+ if ((flag & FKIOCTL) && dkc != NULL &&
+ dkc->dkc_callback != NULL) {
+ (*dkc->dkc_callback)(dkc->dkc_cookie,
+ err);
+ /*
+ * Did callback and reported error.
+ * Since we did a callback, ioctl
+ * should return 0.
+ */
+ err = 0;
+ }
+ return (err);
+ }
+ mutex_exit(&dadkp->dad_mutex);
+
+ bp = getrbuf(KM_SLEEP);
+
+ bp->b_edev = dev;
+ bp->b_dev = cmpdev(dev);
+ bp->b_flags = B_BUSY;
+ bp->b_resid = 0;
+ bp->b_bcount = 0;
+ SET_BP_SEC(bp, 0);
+
+ if ((flag & FKIOCTL) && dkc != NULL &&
+ dkc->dkc_callback != NULL) {
+ struct dk_callback *dkc2 =
+ (struct dk_callback *)kmem_zalloc(
+ sizeof (struct dk_callback), KM_SLEEP);
+
+ bcopy(dkc, dkc2, sizeof (*dkc2));
+ /*
+ * Borrow b_list to carry private data
+ * to the b_iodone func.
+ */
+ bp->b_list = (struct buf *)dkc2;
+ bp->b_iodone = dadk_flushdone;
+ is_sync = 0;
+ }
+
+ /*
+ * Setup command pkt
+ * dadk_pktprep() can't fail since DDI_DMA_SLEEP set
+ */
+ pktp = dadk_pktprep(dadkp, NULL, bp,
+ dadk_iodone, DDI_DMA_SLEEP, NULL);
+
+ pktp->cp_time = DADK_FLUSH_CACHE_TIME;
+
+ *((char *)(pktp->cp_cdbp)) = DCMD_FLUSH_CACHE;
+ pktp->cp_byteleft = 0;
+ pktp->cp_private = NULL;
+ pktp->cp_secleft = 0;
+ pktp->cp_srtsec = -1;
+ pktp->cp_bytexfer = 0;
+
+ CTL_IOSETUP(dadkp->dad_ctlobjp, pktp);
+
+ FLC_ENQUE(dadkp->dad_flcobjp, bp);
+
+ if (is_sync) {
+ err = biowait(bp);
+ freerbuf(bp);
+ }
+ return (err);
+ }
+ default:
+ if (!dadkp->dad_rmb)
+ return (CTL_IOCTL(dadkp->dad_ctlobjp, cmd, arg, flag));
+ }
+
+ switch (cmd) {
+ case CDROMSTOP:
+ return (dadk_rmb_ioctl(dadkp, DCMD_STOP_MOTOR, 0,
+ 0, DADK_SILENT));
+ case CDROMSTART:
+ return (dadk_rmb_ioctl(dadkp, DCMD_START_MOTOR, 0,
+ 0, DADK_SILENT));
+ case DKIOCLOCK:
+ return (dadk_rmb_ioctl(dadkp, DCMD_LOCK, 0, 0, DADK_SILENT));
+ case DKIOCUNLOCK:
+ return (dadk_rmb_ioctl(dadkp, DCMD_UNLOCK, 0, 0, DADK_SILENT));
+ case DKIOCEJECT:
+ case CDROMEJECT:
+ {
+ int ret;
+
+ if (ret = dadk_rmb_ioctl(dadkp, DCMD_UNLOCK, 0, 0,
+ DADK_SILENT)) {
+ return (ret);
+ }
+ if (ret = dadk_rmb_ioctl(dadkp, DCMD_EJECT, 0, 0,
+ DADK_SILENT)) {
+ return (ret);
+ }
+ mutex_enter(&dadkp->dad_mutex);
+ dadkp->dad_iostate = DKIO_EJECTED;
+ cv_broadcast(&dadkp->dad_state_cv);
+ mutex_exit(&dadkp->dad_mutex);
+
+ return (0);
+
+ }
+ default:
+ return (ENOTTY);
+ /*
+ * cdrom audio commands
+ */
+ case CDROMPAUSE:
+ cmd = DCMD_PAUSE;
+ break;
+ case CDROMRESUME:
+ cmd = DCMD_RESUME;
+ break;
+ case CDROMPLAYMSF:
+ cmd = DCMD_PLAYMSF;
+ break;
+ case CDROMPLAYTRKIND:
+ cmd = DCMD_PLAYTRKIND;
+ break;
+ case CDROMREADTOCHDR:
+ cmd = DCMD_READTOCHDR;
+ break;
+ case CDROMREADTOCENTRY:
+ cmd = DCMD_READTOCENT;
+ break;
+ case CDROMVOLCTRL:
+ cmd = DCMD_VOLCTRL;
+ break;
+ case CDROMSUBCHNL:
+ cmd = DCMD_SUBCHNL;
+ break;
+ case CDROMREADMODE2:
+ cmd = DCMD_READMODE2;
+ break;
+ case CDROMREADMODE1:
+ cmd = DCMD_READMODE1;
+ break;
+ case CDROMREADOFFSET:
+ cmd = DCMD_READOFFSET;
+ break;
+ }
+ return (dadk_rmb_ioctl(dadkp, cmd, arg, flag, 0));
+}
+
+int
+dadk_flushdone(struct buf *bp)
+{
+ struct dk_callback *dkc = (struct dk_callback *)bp->b_list;
+
+ ASSERT(dkc != NULL && dkc->dkc_callback != NULL);
+
+ (*dkc->dkc_callback)(dkc->dkc_cookie, geterror(bp));
+
+ kmem_free(dkc, sizeof (*dkc));
+ freerbuf(bp);
+ return (0);
+}
+
+int
+dadk_getphygeom(opaque_t objp, struct tgdk_geom *dkgeom_p)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+
+ bcopy((caddr_t)&dadkp->dad_phyg, (caddr_t)dkgeom_p,
+ sizeof (struct tgdk_geom));
+ return (DDI_SUCCESS);
+}
+
+int
+dadk_getgeom(opaque_t objp, struct tgdk_geom *dkgeom_p)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ bcopy((caddr_t)&dadkp->dad_logg, (caddr_t)dkgeom_p,
+ sizeof (struct tgdk_geom));
+ return (DDI_SUCCESS);
+}
+
+int
+dadk_setgeom(opaque_t objp, struct tgdk_geom *dkgeom_p)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+
+ dadkp->dad_logg.g_cyl = dkgeom_p->g_cyl;
+ dadkp->dad_logg.g_head = dkgeom_p->g_head;
+ dadkp->dad_logg.g_sec = dkgeom_p->g_sec;
+ dadkp->dad_logg.g_cap = dkgeom_p->g_cap;
+ return (DDI_SUCCESS);
+}
+
+
+tgdk_iob_handle
+dadk_iob_alloc(opaque_t objp, daddr_t blkno, ssize_t xfer, int kmsflg)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ struct buf *bp;
+ struct tgdk_iob *iobp;
+ size_t rlen;
+
+ iobp = kmem_zalloc(sizeof (*iobp), kmsflg);
+ if (iobp == NULL)
+ return (NULL);
+ if ((bp = getrbuf(kmsflg)) == NULL) {
+ kmem_free(iobp, sizeof (*iobp));
+ return (NULL);
+ }
+
+ iobp->b_psec = LBLK2SEC(blkno, dadkp->dad_blkshf);
+ iobp->b_pbyteoff = (blkno & ((1<<dadkp->dad_blkshf) - 1)) << SCTRSHFT;
+ iobp->b_pbytecnt = ((iobp->b_pbyteoff + xfer + dadkp->DAD_SECSIZ - 1)
+ >> dadkp->dad_secshf) << dadkp->dad_secshf;
+
+ bp->b_un.b_addr = 0;
+ /*
+ * use i_ddi_mem_alloc() for now until we have an interface to allocate
+ * memory for DMA which doesn't require a DMA handle. ddi_iopb_alloc()
+ * is obsolete and we want more flexibility in controlling the DMA
+ * address constraints..
+ */
+ if (i_ddi_mem_alloc((dadkp->dad_sd)->sd_dev, &dadk_alloc_attr,
+ (size_t)iobp->b_pbytecnt, ((kmsflg == KM_SLEEP) ? 1 : 0), 0, NULL,
+ &bp->b_un.b_addr, &rlen, NULL) != DDI_SUCCESS) {
+ freerbuf(bp);
+ kmem_free(iobp, sizeof (*iobp));
+ return (NULL);
+ }
+ iobp->b_flag |= IOB_BPALLOC | IOB_BPBUFALLOC;
+ iobp->b_bp = bp;
+ iobp->b_lblk = blkno;
+ iobp->b_xfer = xfer;
+ iobp->b_lblk = blkno;
+ iobp->b_xfer = xfer;
+ return (iobp);
+}
+
+/* ARGSUSED */
+int
+dadk_iob_free(opaque_t objp, struct tgdk_iob *iobp)
+{
+ struct buf *bp;
+
+ if (iobp) {
+ if (iobp->b_bp && (iobp->b_flag & IOB_BPALLOC)) {
+ bp = iobp->b_bp;
+ if (bp->b_un.b_addr && (iobp->b_flag & IOB_BPBUFALLOC))
+ i_ddi_mem_free((caddr_t)bp->b_un.b_addr, 0);
+ freerbuf(bp);
+ }
+ kmem_free(iobp, sizeof (*iobp));
+ }
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+caddr_t
+dadk_iob_htoc(opaque_t objp, struct tgdk_iob *iobp)
+{
+ return (iobp->b_bp->b_un.b_addr+iobp->b_pbyteoff);
+}
+
+
+caddr_t
+dadk_iob_xfer(opaque_t objp, struct tgdk_iob *iobp, int rw)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ struct buf *bp;
+ int err;
+
+ bp = iobp->b_bp;
+ if (dadkp->dad_rdonly && !(rw & B_READ)) {
+ bioerror(bp, EROFS);
+ return (NULL);
+ }
+
+ bp->b_flags |= (B_BUSY | rw);
+ bp->b_bcount = iobp->b_pbytecnt;
+ SET_BP_SEC(bp, iobp->b_psec);
+ bp->av_back = (struct buf *)0;
+ bp->b_resid = 0;
+
+ /* call flow control */
+ FLC_ENQUE(dadkp->dad_flcobjp, bp);
+ err = biowait(bp);
+
+ bp->b_bcount = iobp->b_xfer;
+ bp->b_flags &= ~(B_DONE|B_BUSY);
+
+ if (err)
+ return (NULL);
+
+ return (bp->b_un.b_addr+iobp->b_pbyteoff);
+}
+
+static void
+dadk_transport(opaque_t com_data, struct buf *bp)
+{
+ struct dadk *dadkp = (struct dadk *)com_data;
+
+ if (CTL_TRANSPORT(dadkp->dad_ctlobjp, GDA_BP_PKT(bp)) ==
+ CTL_SEND_SUCCESS)
+ return;
+ dadk_restart((void*)GDA_BP_PKT(bp));
+}
+
+static int
+dadk_pkt(opaque_t com_data, struct buf *bp, int (*func)(caddr_t), caddr_t arg)
+{
+ struct cmpkt *pktp;
+ struct dadk *dadkp = (struct dadk *)com_data;
+
+ if (GDA_BP_PKT(bp))
+ return (DDI_SUCCESS);
+
+ pktp = dadk_pktprep(dadkp, NULL, bp, dadk_iodone, func, arg);
+ if (!pktp)
+ return (DDI_FAILURE);
+
+ return (dadk_ioprep(dadkp, pktp));
+}
+
+/*
+ * Read, Write preparation
+ */
+static int
+dadk_ioprep(struct dadk *dadkp, struct cmpkt *pktp)
+{
+ struct buf *bp;
+
+ bp = pktp->cp_bp;
+ if (bp->b_forw == (struct buf *)dadkp)
+ *((char *)(pktp->cp_cdbp)) = (char)(intptr_t)bp->b_back;
+
+ else if (bp->b_flags & B_READ)
+ *((char *)(pktp->cp_cdbp)) = DCMD_READ;
+ else
+ *((char *)(pktp->cp_cdbp)) = DCMD_WRITE;
+ pktp->cp_byteleft = bp->b_bcount;
+
+ /* setup the bad block list handle */
+ pktp->cp_private = BBH_GETHANDLE(dadkp->dad_bbhobjp, bp);
+ return (dadk_iosetup(dadkp, pktp));
+}
+
+static int
+dadk_iosetup(struct dadk *dadkp, struct cmpkt *pktp)
+{
+ struct buf *bp;
+ bbh_cookie_t bbhckp;
+ int seccnt;
+
+ seccnt = pktp->cp_bytexfer >> dadkp->dad_secshf;
+ pktp->cp_secleft -= seccnt;
+
+ if (pktp->cp_secleft) {
+ pktp->cp_srtsec += seccnt;
+ } else {
+ /* get the first cookie from the bad block list */
+ if (!pktp->cp_private) {
+ bp = pktp->cp_bp;
+ pktp->cp_srtsec = GET_BP_SEC(bp);
+ pktp->cp_secleft = (bp->b_bcount >> dadkp->dad_secshf);
+ } else {
+ bbhckp = BBH_HTOC(dadkp->dad_bbhobjp,
+ pktp->cp_private);
+ pktp->cp_srtsec = BBH_GETCK_SECTOR(dadkp->dad_bbhobjp,
+ bbhckp);
+ pktp->cp_secleft = BBH_GETCK_SECLEN(dadkp->dad_bbhobjp,
+ bbhckp);
+ }
+ }
+
+ pktp->cp_bytexfer = pktp->cp_secleft << dadkp->dad_secshf;
+
+ if (CTL_IOSETUP(dadkp->dad_ctlobjp, pktp)) {
+ return (DDI_SUCCESS);
+ } else {
+ return (DDI_FAILURE);
+ }
+
+
+
+
+}
+
+static struct cmpkt *
+dadk_pktprep(struct dadk *dadkp, struct cmpkt *in_pktp, struct buf *bp,
+ void (*cb_func)(struct buf *), int (*func)(caddr_t), caddr_t arg)
+{
+ struct cmpkt *pktp;
+
+ pktp = gda_pktprep(dadkp->dad_ctlobjp, in_pktp, (opaque_t)bp, func,
+ arg);
+
+ if (pktp) {
+ pktp->cp_callback = dadk_pktcb;
+ pktp->cp_time = DADK_IO_TIME;
+ pktp->cp_flags = 0;
+ pktp->cp_iodone = cb_func;
+ pktp->cp_dev_private = (opaque_t)dadkp;
+
+ }
+
+ return (pktp);
+}
+
+
+static void
+dadk_restart(void *vpktp)
+{
+ struct cmpkt *pktp = (struct cmpkt *)vpktp;
+
+ if (dadk_ioretry(pktp, QUE_COMMAND) == JUST_RETURN)
+ return;
+ pktp->cp_iodone(pktp->cp_bp);
+}
+
+static int
+dadk_ioretry(struct cmpkt *pktp, int action)
+{
+ struct buf *bp;
+ struct dadk *dadkp = PKT2DADK(pktp);
+
+ switch (action) {
+ case QUE_COMMAND:
+ if (pktp->cp_retry++ < DADK_RETRY_COUNT) {
+ CTL_IOSETUP(dadkp->dad_ctlobjp, pktp);
+ if (CTL_TRANSPORT(dadkp->dad_ctlobjp, pktp) ==
+ CTL_SEND_SUCCESS) {
+ return (JUST_RETURN);
+ }
+ gda_log(dadkp->dad_sd->sd_dev, dadk_name,
+ CE_WARN,
+ "transport of command fails\n");
+ } else
+ gda_log(dadkp->dad_sd->sd_dev,
+ dadk_name, CE_WARN,
+ "exceeds maximum number of retries\n");
+ bioerror(pktp->cp_bp, ENXIO);
+ /*FALLTHROUGH*/
+ case COMMAND_DONE_ERROR:
+ bp = pktp->cp_bp;
+ bp->b_resid += pktp->cp_byteleft - pktp->cp_bytexfer +
+ pktp->cp_resid;
+ if (geterror(bp) == 0) {
+ if ((*((char *)(pktp->cp_cdbp)) == DCMD_FLUSH_CACHE) &&
+ (pktp->cp_dev_private == (opaque_t)dadkp) &&
+ ((int)(*(char *)pktp->cp_scbp) == DERR_ABORT)) {
+ /*
+ * Flag "unimplemented" responses for
+ * DCMD_FLUSH_CACHE as ENOTSUP
+ */
+ bioerror(bp, ENOTSUP);
+ mutex_enter(&dadkp->dad_mutex);
+ dadkp->dad_noflush = 1;
+ mutex_exit(&dadkp->dad_mutex);
+ } else {
+ bioerror(bp, EIO);
+ }
+ }
+ /*FALLTHROUGH*/
+ case COMMAND_DONE:
+ default:
+ return (COMMAND_DONE);
+ }
+}
+
+
+static void
+dadk_pktcb(struct cmpkt *pktp)
+{
+ int action;
+ struct dadkio_rwcmd *rwcmdp;
+
+ rwcmdp = (struct dadkio_rwcmd *)pktp->cp_passthru; /* ioctl packet */
+
+ if (pktp->cp_reason == CPS_SUCCESS) {
+ if (rwcmdp && (rwcmdp != (opaque_t)DADK_SILENT))
+ rwcmdp->status.status = DADKIO_STAT_NO_ERROR;
+ pktp->cp_iodone(pktp->cp_bp);
+ return;
+ }
+
+ if (rwcmdp && (rwcmdp != (opaque_t)DADK_SILENT)) {
+ if (pktp->cp_reason == CPS_CHKERR)
+ dadk_recorderr(pktp, rwcmdp);
+ dadk_iodone(pktp->cp_bp);
+ return;
+ }
+
+ if (pktp->cp_reason == CPS_CHKERR)
+ action = dadk_chkerr(pktp);
+ else
+ action = COMMAND_DONE_ERROR;
+
+ if (action == JUST_RETURN)
+ return;
+
+ if (action != COMMAND_DONE) {
+ if ((dadk_ioretry(pktp, action)) == JUST_RETURN)
+ return;
+ }
+ pktp->cp_iodone(pktp->cp_bp);
+}
+
+
+
+static struct dadkio_derr dadk_errtab[] = {
+ {COMMAND_DONE, GDA_INFORMATIONAL}, /* 0 DERR_SUCCESS */
+ {QUE_COMMAND, GDA_FATAL}, /* 1 DERR_AMNF */
+ {QUE_COMMAND, GDA_FATAL}, /* 2 DERR_TKONF */
+ {COMMAND_DONE_ERROR, GDA_INFORMATIONAL}, /* 3 DERR_ABORT */
+ {QUE_COMMAND, GDA_RETRYABLE}, /* 4 DERR_DWF */
+ {QUE_COMMAND, GDA_FATAL}, /* 5 DERR_IDNF */
+ {JUST_RETURN, GDA_INFORMATIONAL}, /* 6 DERR_BUSY */
+ {QUE_COMMAND, GDA_FATAL}, /* 7 DERR_UNC */
+ {QUE_COMMAND, GDA_RETRYABLE}, /* 8 DERR_BBK */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 9 DERR_INVCDB */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 10 DERR_HARD */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 11 DERR_ILI */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 12 DERR_EOM */
+ {COMMAND_DONE, GDA_INFORMATIONAL}, /* 13 DERR_MCR */
+ {COMMAND_DONE, GDA_INFORMATIONAL}, /* 14 DERR_RECOVER */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 15 DERR_NOTREADY */
+ {QUE_COMMAND, GDA_RETRYABLE}, /* 16 DERR_MEDIUM */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 17 DERR_HW */
+ {COMMAND_DONE, GDA_FATAL}, /* 18 DERR_ILL */
+ {COMMAND_DONE, GDA_FATAL}, /* 19 DERR_UNIT_ATTN */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 20 DERR_DATA_PROT */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 21 DERR_MISCOMPARE */
+ {QUE_COMMAND, GDA_RETRYABLE}, /* 22 DERR_ICRC */
+ {COMMAND_DONE_ERROR, GDA_FATAL}, /* 23 DERR_RESV */
+};
+
+static int
+dadk_chkerr(struct cmpkt *pktp)
+{
+ int err_blkno;
+ struct dadk *dadkp;
+ int scb;
+
+ if (*(char *)pktp->cp_scbp == DERR_SUCCESS)
+ return (COMMAND_DONE);
+
+ /* check error code table */
+ dadkp = PKT2DADK(pktp);
+ scb = (int)(*(char *)pktp->cp_scbp);
+ if (pktp->cp_retry) {
+ err_blkno = pktp->cp_srtsec + ((pktp->cp_bytexfer -
+ pktp->cp_resid) >> dadkp->dad_secshf);
+ } else
+ err_blkno = -1;
+
+ /* if attempting to read a sector from a cdrom audio disk */
+ if ((dadkp->dad_cdrom) &&
+ (*((char *)(pktp->cp_cdbp)) == DCMD_READ) &&
+ (scb == DERR_ILL)) {
+ return (COMMAND_DONE);
+ }
+ if (pktp->cp_passthru == NULL) {
+ gda_errmsg(dadkp->dad_sd, pktp, dadk_name,
+ dadk_errtab[scb].d_severity, pktp->cp_srtsec,
+ err_blkno, dadk_cmds, dadk_sense);
+ }
+
+ if (scb == DERR_BUSY) {
+ (void) timeout(dadk_restart, (void *)pktp, DADK_BSY_TIMEOUT);
+ }
+
+ return (dadk_errtab[scb].d_action);
+}
+
+static void
+dadk_recorderr(struct cmpkt *pktp, struct dadkio_rwcmd *rwcmdp)
+{
+ struct dadk *dadkp;
+ int scb;
+
+ dadkp = PKT2DADK(pktp);
+ scb = (int)(*(char *)pktp->cp_scbp);
+
+
+ rwcmdp->status.failed_blk = rwcmdp->blkaddr +
+ ((pktp->cp_bytexfer -
+ pktp->cp_resid) >> dadkp->dad_secshf);
+
+ rwcmdp->status.resid = pktp->cp_bp->b_resid +
+ pktp->cp_byteleft - pktp->cp_bytexfer + pktp->cp_resid;
+ switch ((int)(* (char *)pktp->cp_scbp)) {
+ case DERR_AMNF:
+ case DERR_ABORT:
+ rwcmdp->status.status = DADKIO_STAT_ILLEGAL_REQUEST;
+ break;
+ case DERR_DWF:
+ case DERR_IDNF:
+ rwcmdp->status.status = DADKIO_STAT_ILLEGAL_ADDRESS;
+ break;
+ case DERR_TKONF:
+ case DERR_UNC:
+ case DERR_BBK:
+ rwcmdp->status.status = DADKIO_STAT_MEDIUM_ERROR;
+ rwcmdp->status.failed_blk_is_valid = 1;
+ rwcmdp->status.resid = 0;
+ break;
+ case DERR_BUSY:
+ rwcmdp->status.status = DADKIO_STAT_NOT_READY;
+ break;
+ case DERR_INVCDB:
+ case DERR_HARD:
+ rwcmdp->status.status = DADKIO_STAT_HARDWARE_ERROR;
+ break;
+ case DERR_ICRC:
+ default:
+ rwcmdp->status.status = DADKIO_STAT_NOT_SUPPORTED;
+ }
+
+ if (rwcmdp->flags & DADKIO_FLAG_SILENT)
+ return;
+ gda_errmsg(dadkp->dad_sd, pktp, dadk_name, dadk_errtab[scb].d_severity,
+ rwcmdp->blkaddr, rwcmdp->status.failed_blk,
+ dadk_cmds, dadk_sense);
+}
+
+/*ARGSUSED*/
+static void
+dadk_polldone(struct buf *bp)
+{
+}
+
+static void
+dadk_iodone(struct buf *bp)
+{
+ struct cmpkt *pktp;
+ struct dadk *dadkp;
+
+ pktp = GDA_BP_PKT(bp);
+ dadkp = PKT2DADK(pktp);
+
+ /* check for all iodone */
+ pktp->cp_byteleft -= pktp->cp_bytexfer;
+ if (geterror(bp) == 0 && pktp->cp_byteleft != 0) {
+ pktp->cp_retry = 0;
+ (void) dadk_iosetup(dadkp, pktp);
+
+
+ /* transport the next one */
+ if (CTL_TRANSPORT(dadkp->dad_ctlobjp, pktp) == CTL_SEND_SUCCESS)
+ return;
+ if ((dadk_ioretry(pktp, QUE_COMMAND)) == JUST_RETURN)
+ return;
+ }
+
+ /* start next one */
+ FLC_DEQUE(dadkp->dad_flcobjp, bp);
+
+ /* free pkt */
+ if (pktp->cp_private)
+ BBH_FREEHANDLE(dadkp->dad_bbhobjp, pktp->cp_private);
+ gda_free(dadkp->dad_ctlobjp, pktp, NULL);
+ biodone(bp);
+}
+
+int
+dadk_check_media(opaque_t objp, int *state)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+
+ if (!dadkp->dad_rmb) {
+ return (ENXIO);
+ }
+#ifdef DADK_DEBUG
+ if (dadk_debug & DSTATE)
+ PRF("dadk_check_media: user state %x disk state %x\n",
+ *state, dadkp->dad_iostate);
+#endif
+ /*
+ * If state already changed just return
+ */
+ if (*state != dadkp->dad_iostate) {
+ *state = dadkp->dad_iostate;
+ return (0);
+ }
+
+ /*
+ * Startup polling on thread state
+ */
+ mutex_enter(&dadkp->dad_mutex);
+ if (dadkp->dad_thread_cnt == 0) {
+ /*
+ * One thread per removable dadk device
+ */
+ (void) thread_create(NULL, 0, dadk_watch_thread, dadkp, 0, &p0,
+ TS_RUN, v.v_maxsyspri - 2);
+ }
+ dadkp->dad_thread_cnt++;
+
+ /*
+ * Wait for state to change
+ */
+ do {
+ if (cv_wait_sig(&dadkp->dad_state_cv, &dadkp->dad_mutex) == 0) {
+ dadkp->dad_thread_cnt--;
+ mutex_exit(&dadkp->dad_mutex);
+ return (EINTR);
+ }
+ } while (*state == dadkp->dad_iostate);
+ *state = dadkp->dad_iostate;
+ dadkp->dad_thread_cnt--;
+ mutex_exit(&dadkp->dad_mutex);
+ return (0);
+}
+
+
+#define MEDIA_ACCESS_DELAY 2000000
+
+static void
+dadk_watch_thread(struct dadk *dadkp)
+{
+ enum dkio_state state;
+ int interval;
+
+ interval = drv_usectohz(dadk_check_media_time);
+
+ do {
+ if (dadk_rmb_ioctl(dadkp, DCMD_GET_STATE, (intptr_t)&state, 0,
+ DADK_SILENT)) {
+ /*
+ * Assume state remained the same
+ */
+ state = dadkp->dad_iostate;
+ }
+
+ /*
+ * now signal the waiting thread if this is *not* the
+ * specified state;
+ * delay the signal if the state is DKIO_INSERTED
+ * to allow the target to recover
+ */
+ if (state != dadkp->dad_iostate) {
+
+ dadkp->dad_iostate = state;
+ if (state == DKIO_INSERTED) {
+ /*
+ * delay the signal to give the drive a chance
+ * to do what it apparently needs to do
+ */
+ (void) timeout((void(*)(void *))cv_broadcast,
+ (void *)&dadkp->dad_state_cv,
+ drv_usectohz((clock_t)MEDIA_ACCESS_DELAY));
+ } else {
+ cv_broadcast(&dadkp->dad_state_cv);
+ }
+ }
+ delay(interval);
+ } while (dadkp->dad_thread_cnt);
+}
+
+int
+dadk_inquiry(opaque_t objp, opaque_t *inqpp)
+{
+ struct dadk *dadkp = (struct dadk *)objp;
+ struct scsi_inquiry **sinqpp = (struct scsi_inquiry **)inqpp;
+
+ if (dadkp && dadkp->dad_sd && dadkp->dad_sd->sd_inq) {
+ *sinqpp = dadkp->dad_sd->sd_inq;
+ return (DDI_SUCCESS);
+ }
+
+ return (DDI_FAILURE);
+}
+
+static int
+dadk_rmb_ioctl(struct dadk *dadkp, int cmd, intptr_t arg, int flags, int silent)
+
+{
+ struct buf *bp;
+ int err;
+ struct cmpkt *pktp;
+
+ if ((bp = getrbuf(KM_SLEEP)) == NULL) {
+ return (ENOMEM);
+ }
+ pktp = dadk_pktprep(dadkp, NULL, bp, dadk_rmb_iodone, NULL, NULL);
+ if (!pktp) {
+ freerbuf(bp);
+ return (ENOMEM);
+ }
+ bp->b_back = (struct buf *)arg;
+ bp->b_forw = (struct buf *)dadkp->dad_flcobjp;
+ pktp->cp_passthru = (opaque_t)(intptr_t)silent;
+
+ err = CTL_IOCTL(dadkp->dad_ctlobjp, cmd, (uintptr_t)pktp, flags);
+ freerbuf(bp);
+ gda_free(dadkp->dad_ctlobjp, pktp, NULL);
+ return (err);
+
+
+}
+
+static void
+dadk_rmb_iodone(struct buf *bp)
+{
+ struct cmpkt *pktp;
+ struct dadk *dadkp;
+
+ pktp = GDA_BP_PKT(bp);
+ dadkp = PKT2DADK(pktp);
+
+ bp->b_flags &= ~(B_DONE|B_BUSY);
+
+ /* Start next one */
+ FLC_DEQUE(dadkp->dad_flcobjp, bp);
+
+ biodone(bp);
+}
+
+static int
+dadk_dk_buf_setup(struct dadk *dadkp, opaque_t *cmdp, dev_t dev,
+ enum uio_seg dataspace, int rw)
+{
+ struct dadkio_rwcmd *rwcmdp = (struct dadkio_rwcmd *)cmdp;
+ struct buf *bp;
+ struct iovec aiov;
+ struct uio auio;
+ struct uio *uio = &auio;
+ int status;
+
+ bp = getrbuf(KM_SLEEP);
+
+ bp->av_forw = bp->b_forw = (struct buf *)dadkp;
+ bp->b_back = (struct buf *)rwcmdp; /* ioctl packet */
+
+ bzero((caddr_t)&auio, sizeof (struct uio));
+ bzero((caddr_t)&aiov, sizeof (struct iovec));
+ aiov.iov_base = rwcmdp->bufaddr;
+ aiov.iov_len = rwcmdp->buflen;
+ uio->uio_iov = &aiov;
+
+ uio->uio_iovcnt = 1;
+ uio->uio_resid = rwcmdp->buflen;
+ uio->uio_segflg = dataspace;
+
+ /* Let physio do the rest... */
+ status = physio(dadk_dk_strategy, bp, dev, rw, dadkmin, uio);
+
+ freerbuf(bp);
+ return (status);
+
+}
+
+/* Do not let a user gendisk request get too big or */
+/* else we could use to many resources. */
+
+static void
+dadkmin(struct buf *bp)
+{
+ if (bp->b_bcount > dadk_dk_maxphys)
+ bp->b_bcount = dadk_dk_maxphys;
+}
+
+static int
+dadk_dk_strategy(struct buf *bp)
+{
+ dadk_dk((struct dadk *)bp->av_forw, (struct dadkio_rwcmd *)bp->b_back,
+ bp);
+ return (0);
+}
+
+static void
+dadk_dk(struct dadk *dadkp, struct dadkio_rwcmd *rwcmdp, struct buf *bp)
+{
+ struct cmpkt *pktp;
+
+ pktp = dadk_pktprep(dadkp, NULL, bp, dadk_iodone, NULL, NULL);
+ if (!pktp) {
+ bioerror(bp, ENOMEM);
+ biodone(bp);
+ return;
+ }
+
+ pktp->cp_passthru = rwcmdp;
+
+ (void) dadk_ioprep(dadkp, pktp);
+
+ FLC_ENQUE(dadkp->dad_flcobjp, bp);
+}