#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/policy.h>
#include <sys/priv.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>
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 void dadk_create_errstats(struct dadk *dadkp, int instance);
static void dadk_destroy_errstats(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);
static int dadk_ctl_ioctl(struct dadk *, uint32_t, uintptr_t, int);
struct tgcom_objops dadk_com_ops = {
nodev,
nodev,
dadk_pkt,
dadk_transport,
0, 0
};
#if defined(__sparc)
static ddi_dma_attr_t dadk_alloc_attr = {
DMA_ATTR_V0,
0x0,
0xFFFFFFFFull,
0xFFFFFFFFull,
1,
1,
1,
0xFFFFFFFFull,
0xFFFFFFFFull,
1,
512,
0,
};
#elif defined(__x86)
static ddi_dma_attr_t dadk_alloc_attr = {
DMA_ATTR_V0,
0x0,
0x0,
0xFFFFull,
512,
1,
1,
0xFFFFFFFFull,
0xFFFFFFFFull,
0,
512,
0,
};
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
};
#ifdef DADK_DEBUG
#define DENT 0x0001
#define DERR 0x0002
#define DIO 0x0004
#define DGEOM 0x0010
#define DSTATE 0x0020
static int dadk_debug = DGEOM;
#endif
static int dadk_check_media_time = 3000000;
static int dadk_dk_maxphys = 0x80000;
static char *dadk_cmds[] = {
"\000Unknown",
"\001read sector",
"\002write sector",
"\003format track",
"\004format whole drive",
"\005recalibrate",
"\006seek sector",
"\007read verify",
"\010read defect list",
"\011lock door",
"\012unlock door",
"\013start motor",
"\014stop motor",
"\015eject",
"\016update geometry",
"\017get state",
"\020cdrom pause",
"\021cdrom resume",
"\022cdrom play track index",
"\023cdrom play msf",
"\024cdrom sub channel",
"\025cdrom read mode 1",
"\026cdrom read toc header",
"\027cdrom read toc entry",
"\030cdrom read offset",
"\031cdrom read mode 2",
"\032cdrom volume control",
"\033flush cache",
NULL
};
static char *dadk_sense[] = {
"\000Success",
"\001address mark not found",
"\002track 0 not found",
"\003aborted command",
"\004write fault",
"\005ID not found",
"\006drive busy",
"\007uncorrectable data error",
"\010bad block detected",
"\011invalid command",
"\012device hard error",
"\013illegal length indicated",
"\014end of media",
"\015media change requested",
"\016recovered from error",
"\017device not ready",
"\020medium error",
"\021hardware error",
"\022illegal request",
"\023unit attention",
"\024data protection",
"\025miscompare",
"\026ICRC error during UDMA",
"\027reserved",
NULL
};
static char *dadk_name = "Disk";
#include <sys/modctl.h>
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops,
"Direct Attached Disk"
};
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)
dadk_alloc_attr.dma_attr_addr_hi = dadk_max_phys_addr;
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;
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;
mutex_init(&dadkp->dad_cmd_mutex, NULL, MUTEX_DRIVER, NULL);
dadkp->dad_cmd_count = 0;
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;
}
mutex_destroy(&dadkp->dad_cmd_mutex);
}
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:
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;
(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);
}
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;
if (dadkp->dad_bbhobjp)
BBH_FREE(dadkp->dad_bbhobjp);
dadkp->dad_bbhobjp = bbhobjp;
BBH_INIT(bbhobjp);
return (DDI_SUCCESS);
}
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);
}
error = dadk_ctl_ioctl(dadkp, DIOCTL_GETWCE,
(uintptr_t)&wce, FKIOCTL | FNATIVE);
mutex_enter(&dadkp->dad_mutex);
dadkp->dad_wce = (error != 0) || (wce != 0);
mutex_exit(&dadkp->dad_mutex);
(void) dadk_ctl_ioctl(dadkp, DIOCTL_GETGEOM,
(uintptr_t)&dadkp->dad_logg, FKIOCTL | FNATIVE);
if (dadkp->dad_logg.g_cap == 0)
return (DDI_FAILURE);
(void) dadk_ctl_ioctl(dadkp, DIOCTL_GETPHYGEOM,
(uintptr_t)&dadkp->dad_phyg, FKIOCTL | FNATIVE);
if (dadkp->dad_phyg.g_cap == 0)
return (DDI_FAILURE);
dadk_setcap(dadkp);
dadk_create_errstats(dadkp,
ddi_get_instance(CTL_DIP_DEV(dadkp->dad_ctlobjp)));
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 {
totsize &= ~(NBPSCTR-1);
}
dadkp->dad_phyg.g_secsiz = totsize;
totsize >>= SCTRSHFT;
for (i = 0; totsize != 1; i++, totsize >>= 1)
;
dadkp->dad_blkshf = i;
dadkp->dad_secshf = i + SCTRSHFT;
}
static void
dadk_create_errstats(struct dadk *dadkp, int instance)
{
dadk_errstats_t *dep;
char kstatname[KSTAT_STRLEN];
dadk_ioc_string_t dadk_ioc_string;
if (dadkp->dad_errstats)
return;
(void) sprintf(kstatname, "cmdk%d,error", instance);
dadkp->dad_errstats = kstat_create("cmdkerror", instance,
kstatname, "device_error", KSTAT_TYPE_NAMED,
sizeof (dadk_errstats_t) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT);
if (!dadkp->dad_errstats)
return;
dep = (dadk_errstats_t *)dadkp->dad_errstats->ks_data;
kstat_named_init(&dep->dadk_softerrs,
"Soft Errors", KSTAT_DATA_UINT32);
kstat_named_init(&dep->dadk_harderrs,
"Hard Errors", KSTAT_DATA_UINT32);
kstat_named_init(&dep->dadk_transerrs,
"Transport Errors", KSTAT_DATA_UINT32);
kstat_named_init(&dep->dadk_model,
"Model", KSTAT_DATA_CHAR);
kstat_named_init(&dep->dadk_revision,
"Revision", KSTAT_DATA_CHAR);
kstat_named_init(&dep->dadk_serial,
"Serial No", KSTAT_DATA_CHAR);
kstat_named_init(&dep->dadk_capacity,
"Size", KSTAT_DATA_ULONGLONG);
kstat_named_init(&dep->dadk_rq_media_err,
"Media Error", KSTAT_DATA_UINT32);
kstat_named_init(&dep->dadk_rq_ntrdy_err,
"Device Not Ready", KSTAT_DATA_UINT32);
kstat_named_init(&dep->dadk_rq_nodev_err,
"No Device", KSTAT_DATA_UINT32);
kstat_named_init(&dep->dadk_rq_recov_err,
"Recoverable", KSTAT_DATA_UINT32);
kstat_named_init(&dep->dadk_rq_illrq_err,
"Illegal Request", KSTAT_DATA_UINT32);
dadkp->dad_errstats->ks_private = dep;
dadkp->dad_errstats->ks_update = nulldev;
kstat_install(dadkp->dad_errstats);
dep->dadk_model.value.c[0] = 0;
dadk_ioc_string.is_buf = &dep->dadk_model.value.c[0];
dadk_ioc_string.is_size = sizeof (dep->dadk_model.value.c);
(void) dadk_ctl_ioctl(dadkp, DIOCTL_GETMODEL,
(uintptr_t)&dadk_ioc_string, FKIOCTL | FNATIVE);
dep->dadk_serial.value.c[0] = 0;
dadk_ioc_string.is_buf = &dep->dadk_serial.value.c[0];
dadk_ioc_string.is_size = sizeof (dep->dadk_serial.value.c);
(void) dadk_ctl_ioctl(dadkp, DIOCTL_GETSERIAL,
(uintptr_t)&dadk_ioc_string, FKIOCTL | FNATIVE);
dep->dadk_revision.value.c[0] = 0;
dep->dadk_capacity.value.ui64 =
(uint64_t)dadkp->dad_logg.g_cap *
(uint64_t)dadkp->dad_logg.g_secsiz;
}
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);
dadk_destroy_errstats(dadkp);
return (DDI_SUCCESS);
}
static void
dadk_destroy_errstats(struct dadk *dadkp)
{
if (!dadkp->dad_errstats)
return;
kstat_delete(dadkp->dad_errstats);
dadkp->dad_errstats = NULL;
}
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)));
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count++;
mutex_exit(&dadkp->dad_cmd_mutex);
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);
}
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);
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;
bp->b_forw = (struct buf *)dadkp;
bp->b_back = (struct buf *)DCMD_GETDEF;
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count++;
mutex_exit(&dadkp->dad_cmd_mutex);
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;
rwcmdp = (struct dadkio_rwcmd *)(intptr_t)arg;
switch (rwcmdp->cmd) {
case DADKIO_RWCMD_READ :
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 DKIOC_UPDATEFW:
if (PRIV_POLICY(cred_p, PRIV_ALL, B_FALSE, EPERM, NULL) != 0)
return (EPERM);
else
return (dadk_ctl_ioctl(dadkp, cmd, arg, flag));
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 ((flag & FKIOCTL) && dkc != NULL &&
dkc->dkc_callback != NULL) {
(*dkc->dkc_callback)(dkc->dkc_cookie,
err);
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));
bp->b_private = dkc2;
bp->b_iodone = dadk_flushdone;
is_sync = 0;
}
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);
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count++;
mutex_exit(&dadkp->dad_cmd_mutex);
FLC_ENQUE(dadkp->dad_flcobjp, bp);
if (is_sync) {
err = biowait(bp);
freerbuf(bp);
}
return (err);
}
default:
if (!dadkp->dad_rmb)
return (dadk_ctl_ioctl(dadkp, 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);
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 = bp->b_private;
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;
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);
}
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, NULL);
freerbuf(bp);
}
kmem_free(iobp, sizeof (*iobp));
}
return (DDI_SUCCESS);
}
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;
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count++;
mutex_exit(&dadkp->dad_cmd_mutex);
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));
}
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;
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 {
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);
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)) {
bioerror(bp, ENOTSUP);
mutex_enter(&dadkp->dad_mutex);
dadkp->dad_noflush = 1;
mutex_exit(&dadkp->dad_mutex);
} else {
bioerror(bp, EIO);
}
}
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;
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 (ddi_in_panic() && action == QUE_COMMAND)
action = COMMAND_DONE_ERROR;
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},
{QUE_COMMAND, GDA_FATAL},
{QUE_COMMAND, GDA_FATAL},
{COMMAND_DONE_ERROR, GDA_INFORMATIONAL},
{QUE_COMMAND, GDA_RETRYABLE},
{QUE_COMMAND, GDA_FATAL},
{JUST_RETURN, GDA_INFORMATIONAL},
{QUE_COMMAND, GDA_FATAL},
{QUE_COMMAND, GDA_RETRYABLE},
{COMMAND_DONE_ERROR, GDA_FATAL},
{COMMAND_DONE_ERROR, GDA_FATAL},
{COMMAND_DONE_ERROR, GDA_FATAL},
{COMMAND_DONE_ERROR, GDA_FATAL},
{COMMAND_DONE, GDA_INFORMATIONAL},
{COMMAND_DONE, GDA_INFORMATIONAL},
{COMMAND_DONE_ERROR, GDA_FATAL},
{QUE_COMMAND, GDA_RETRYABLE},
{COMMAND_DONE_ERROR, GDA_FATAL},
{COMMAND_DONE, GDA_FATAL},
{COMMAND_DONE, GDA_FATAL},
{COMMAND_DONE_ERROR, GDA_FATAL},
{COMMAND_DONE_ERROR, GDA_FATAL},
{QUE_COMMAND, GDA_RETRYABLE},
{COMMAND_DONE_ERROR, GDA_FATAL},
};
static int
dadk_chkerr(struct cmpkt *pktp)
{
daddr_t err_blkno;
struct dadk *dadkp = PKT2DADK(pktp);
dadk_errstats_t *dep;
int scb = *(char *)pktp->cp_scbp;
if (scb == DERR_SUCCESS) {
if (pktp->cp_retry != 0 && dadkp->dad_errstats != NULL) {
dep = (dadk_errstats_t *)
dadkp->dad_errstats->ks_data;
dep->dadk_rq_recov_err.value.ui32++;
}
return (COMMAND_DONE);
}
if (pktp->cp_retry) {
err_blkno = pktp->cp_srtsec + ((pktp->cp_bytexfer -
pktp->cp_resid) >> dadkp->dad_secshf);
} else
err_blkno = -1;
if (dadkp->dad_errstats != NULL) {
dep = (dadk_errstats_t *)dadkp->dad_errstats->ks_data;
switch (dadk_errtab[scb].d_severity) {
case GDA_RETRYABLE:
dep->dadk_softerrs.value.ui32++;
break;
case GDA_FATAL:
dep->dadk_harderrs.value.ui32++;
break;
default:
break;
}
switch (scb) {
case DERR_INVCDB:
case DERR_ILI:
case DERR_EOM:
case DERR_HW:
case DERR_ICRC:
dep->dadk_transerrs.value.ui32++;
break;
case DERR_AMNF:
case DERR_TKONF:
case DERR_DWF:
case DERR_BBK:
case DERR_UNC:
case DERR_HARD:
case DERR_MEDIUM:
case DERR_DATA_PROT:
case DERR_MISCOMP:
dep->dadk_rq_media_err.value.ui32++;
break;
case DERR_NOTREADY:
dep->dadk_rq_ntrdy_err.value.ui32++;
break;
case DERR_IDNF:
case DERR_UNIT_ATTN:
dep->dadk_rq_nodev_err.value.ui32++;
break;
case DERR_ILL:
case DERR_RESV:
dep->dadk_rq_illrq_err.value.ui32++;
break;
default:
break;
}
}
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);
}
static void
dadk_polldone(struct buf *bp)
{
struct cmpkt *pktp;
struct dadk *dadkp;
pktp = GDA_BP_PKT(bp);
dadkp = PKT2DADK(pktp);
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count--;
mutex_exit(&dadkp->dad_cmd_mutex);
}
static void
dadk_iodone(struct buf *bp)
{
struct cmpkt *pktp;
struct dadk *dadkp;
pktp = GDA_BP_PKT(bp);
dadkp = PKT2DADK(pktp);
pktp->cp_byteleft -= pktp->cp_bytexfer;
if (geterror(bp) == 0 && pktp->cp_byteleft != 0) {
pktp->cp_retry = 0;
(void) dadk_iosetup(dadkp, pktp);
if (CTL_TRANSPORT(dadkp->dad_ctlobjp, pktp) == CTL_SEND_SUCCESS)
return;
if ((dadk_ioretry(pktp, QUE_COMMAND)) == JUST_RETURN)
return;
}
FLC_DEQUE(dadkp->dad_flcobjp, bp);
if (pktp->cp_private)
BBH_FREEHANDLE(dadkp->dad_bbhobjp, pktp->cp_private);
gda_free(dadkp->dad_ctlobjp, pktp, NULL);
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count--;
mutex_exit(&dadkp->dad_cmd_mutex);
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 != dadkp->dad_iostate) {
*state = dadkp->dad_iostate;
return (0);
}
mutex_enter(&dadkp->dad_mutex);
if (dadkp->dad_thread_cnt == 0) {
(void) thread_create(NULL, 0, dadk_watch_thread, dadkp, 0, &p0,
TS_RUN, v.v_maxsyspri - 2);
}
dadkp->dad_thread_cnt++;
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)) {
state = dadkp->dad_iostate;
}
if (state != dadkp->dad_iostate) {
dadkp->dad_iostate = state;
if (state == DKIO_INSERTED) {
(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 = dadk_ctl_ioctl(dadkp, 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);
FLC_DEQUE(dadkp->dad_flcobjp, bp);
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count--;
mutex_exit(&dadkp->dad_cmd_mutex);
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;
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;
status = physio(dadk_dk_strategy, bp, dev, rw, dadkmin, uio);
freerbuf(bp);
return (status);
}
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);
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count++;
mutex_exit(&dadkp->dad_cmd_mutex);
FLC_ENQUE(dadkp->dad_flcobjp, bp);
}
int
dadk_getcmds(opaque_t objp)
{
struct dadk *dadkp = (struct dadk *)objp;
int count;
mutex_enter(&dadkp->dad_cmd_mutex);
count = dadkp->dad_cmd_count;
mutex_exit(&dadkp->dad_cmd_mutex);
return (count);
}
static int
dadk_ctl_ioctl(struct dadk *dadkp, uint32_t cmd, uintptr_t arg, int flag)
{
int error;
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count++;
mutex_exit(&dadkp->dad_cmd_mutex);
error = CTL_IOCTL(dadkp->dad_ctlobjp, cmd, arg, flag);
mutex_enter(&dadkp->dad_cmd_mutex);
dadkp->dad_cmd_count--;
mutex_exit(&dadkp->dad_cmd_mutex);
return (error);
}