voltestdrv.c revision 18c2aff776a775d34a4c9893a4c72e0434d68e36
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* voltestdrv: test driver for Volume Management
*/
#include "voltestdrv.h"
/* names */
#define VTLONGNAME "Volume Management Test Driver, %I%"
/* long driver name */
#define VTNUNITS "nunits"
#define VTRWLOCK "vt_rwlock"
/*
* debug stuff
*/
#define VTDEBUG 0 /* default debug level */
#define VT_TABLE_KM_SFX 0 /* not used */
#define VT_COUNTERS 8
/*
* set reasonable limit for certain ioctls
* (label_cdrom reads 64k plus some slop)
*/
/*
* private device info
*/
static struct vt_tab {
char *vt_name; /* "label" for this dev */
int vt_label_errno; /* error for reads of label */
char *vt_label; /* fake label */
int vt_entry_errno; /* error for cdrom toc entry */
unsigned char vt_error_track; /* track to get error */
};
static int vt_nunits = 0; /* number of units driver is config'd for */
#define VT_LABEL_MUTEX "vt_lab_mx"
/* for vt_flags */
#define ST_OPEN 0x1
#define ST_EXCL 0x2
/*
* keep kvt_queue and kvt_event in sync. It is important that
* kve_next and kve_prev are in the same order and relative position
* in the resepctive structures.
*/
struct kvt_queue {
};
struct kvt_event {
};
static unsigned int vt_evcnt; /* number of events on the list */
static struct pollhead vt_pollhead;
static void vt_enqueue(enum vt_evtype, void *);
/*
* insertion event variables
*/
static kcondvar_t vtnm_read_cv;
static kcondvar_t vtnm_write_cv;
static int vtstrategy(struct buf *);
caddr_t, int *);
vtopen, /* open */
vtclose, /* close */
vtstrategy, /* strategy */
nodev, /* print */
nodev, /* dump */
vtread, /* read */
vtwrite, /* write */
vtioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
vtpoll, /* poll */
vtprop_op, /* prop_op */
NULL, /* streamtab */
};
#if SOLARIS2 < 10
static int vtidentify(dev_info_t *);
#endif
DEVO_REV, /* rev */
0, /* refcnt */
vtinfo, /* info */
#if SOLARIS2 >= 10
#else
vtidentify, /* identify */
#endif
nulldev, /* probe */
vtattach, /* attach */
vtdetach, /* detach */
nulldev, /* reset */
&vt_cb_ops, /* cb_ops */
NULL, /* bus_ops */
};
extern struct mod_ops mod_pseudodrvops;
extern struct mod_ops mod_driverops;
static struct modldrv vt_driver_info = {
&mod_driverops, /* modops */
VTLONGNAME, /* name */
&vt_ops, /* dev_ops */
};
static struct modlinkage vt_linkage = {
MODREV_1, /* rev */
};
#define VTBUFSIZE 128
#define VTMAXNAMLEN 80
/*
* Virtual driver loader entry points
*/
int
_init(void)
{
DPRINTF("vt: _init\n");
return (mod_install(&vt_linkage));
}
int
_fini(void)
{
DPRINTF("vt: _fini\n");
/* nope, no open devices, detach. */
return (mod_remove(&vt_linkage));
}
int
{
}
/*
* Driver administration entry points
*/
#if SOLARIS2 < 10
static int
{
return (DDI_IDENTIFIED);
}
return (DDI_NOT_IDENTIFIED);
}
#endif
static int
{
int length;
int nunits;
int i;
int error;
/* check unit */
if (unit != 0) {
return (ENXIO);
}
/* check command */
if (cmd != DDI_ATTACH) {
return (DDI_FAILURE);
}
"vt: attach: %d: already attached\n", unit);
return (DDI_FAILURE);
}
for (i = 0; i < VT_COUNTERS; i++) {
vt_mem_counters[i] = 0;
}
/* get number of units, must use DDI_DEV_T_ANY */
"vt: attach: %d: could not get nunits prop, error %d\n",
goto out;
}
/* create the vttab array */
KM_SLEEP);
/* init each unit */
/* initialize locks, and save dev info */
/* create minor nodes */
if (unit == 0) {
} else {
}
DDI_PSEUDO, 0)) != DDI_SUCCESS) {
"vt: attach: %d: ddi_create_minor_node '%s' failed\n",
goto out;
}
}
/* cleanup or return success */
out:
if (error != DDI_SUCCESS) {
}
} else {
}
return (error);
}
static int
{
int i, j;
/* check unit */
if (unit != 0) {
return (ENXIO);
}
/* process command */
switch (cmd) {
/* cleanup and detach */
case DDI_DETACH:
/* Check to see if there are any open devices. */
for (i = 0; (i < vt_nunits); ++i) {
for (j = 0; j < OTYPCNT; j++) {
return (DDI_FAILURE);
}
}
}
for (i = 0; i < vt_nunits; i++) {
/* clean up -- just in case */
}
tp->vt_namelen = 0;
}
}
tp->vt_entry_count *
sizeof (struct cdrom_tocentry));
tp->vt_entry_count = 0;
}
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/* ARGSUSED */
static int
{
int error = DDI_SUCCESS;
DPRINTF3("vt: info: dip %x cmd %d arg %x (%d.%d) result %x\n",
/* check unit, grab lock */
return (ENXIO);
}
/* process command */
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
break;
case DDI_INFO_DEVT2INSTANCE:
break;
default:
error = DDI_FAILURE;
break;
}
/* release lock, return success */
return (error);
}
/*
* Common entry points
*/
/* ARGSUSED3 */
static int
{
int error = 0;
DPRINTF("vt: open: devp %x (%d.%d) flag %x otyp %x credp %x\n",
/* check unit, grab lock */
return (ENXIO);
}
/* check type and flags */
goto out;
}
goto out;
}
/* window */
/* count and flag open */
}
/* release lock, return success */
out:
return (error);
}
/* ARGSUSED3 */
static int
{
int i;
DPRINTF("vt: close: dev %d.%d flag %x otyp %x credp %x\n",
/* check unit, grab lock */
return (ENXIO);
}
/* check type and flags */
return (EINVAL);
}
/* count and flag closed */
} else {
}
for (i = 0; i < OTYPCNT; i++) {
}
}
}
if (unit == 0) {
/* clear out any waiters */
}
return (0);
}
static int
{
DPRINTF("vt: strategy: bp %x dev %d.%d off %lu len %d\n",
/* we really should update the various things ... */
return (ENXIO);
}
/* ARGSUSED */
static int
{
long byte_count;
int error = 0;
int i;
long name_label_len;
DPRINTF("vt: read: dev %d.%d uiop %x credp %x\n",
/* check unit */
goto out;
}
/* ensure unit is open */
goto out;
}
/* transfer data */
/* transfer the label (or our name if no label) */
/* we have a fake label -- return that */
DPRINTF2("vt: read: reading fake label\n");
/* figure out how many bytes to copy */
if (error != 0) {
DPRINTF2("vt: generating read error %d\n",
error);
break;
}
/* copy label to user */
break;
}
}
} else {
/* we don't have a fake label -- return our name */
/* get length of our name (to be returned to caller) */
/* copy fake label to user */
uiop);
if (error != 0) {
error);
break;
}
}
}
/* if there's still work to do ... */
/* fill a buf with user-specified tag */
for (i = 0; i < VTBUFSIZE; i++) {
}
#ifdef DEBUG
DPRINTF2("vt: read: %d words (%d bytes) set to 0x%x\n",
#endif
/* send the rest of the data to the user */
/* figure out how many bytes to xfer to caller */
if (error != 0) {
DPRINTF2("vt: generating read error %d\n",
error);
break;
}
DPRINTF2("vt: read: returning %lu bytes\n",
/* copy tag data to user */
if (error != 0) {
break;
}
}
}
/* return status */
out:
#ifdef DEBUG
#endif
return (error);
}
/* ARGSUSED */
static int
{
/*
* simulate writes by taking each block "written" and enqueueing
* it "up" to vold (though /dev/voltestdrv, into dev_test.so.1)
*/
int error = 0;
int n;
int i;
DPRINTF("vt: write: dev %d.%d uiop %x credp %x\n",
/* check unit */
return (ENXIO);
}
/* ensure unit is open (locking struct to check) */
goto out;
}
/* get the data from the user and see if it's good stuff */
break;
}
for (i = 0; i < (n / sizeof (int)); i++) {
} else {
}
}
}
/* return results */
out:
return (error);
}
static int
{
int error = 0;
DPRINTF3("vt: prop_op: dev %d.%d dip %x prop_op %d flags %x\n",
flags);
DPRINTF3(" name '%s' valuep %x lengthp %x\n",
/* check unit, must use dip as dev could be a wildcard, grab lock */
return (ENXIO);
}
/* send our props on to ddi_prop_op */
goto out;
}
/* release lock, return error */
out:
return (error);
}
/* ARGSUSED */
static int
{
int error = 0;
char buf[VTMAXNAMLEN];
"vt: ioctl: dev %d.%d cmd %x arg %x mode %x\n",
/* check unit, grab lock */
return (ENXIO);
}
goto out;
}
if (unit == 0) {
/*
* unit-0 ioctls: these are meta-ioctls, used to talk
* to the test driver itself
*/
switch (cmd) {
case VTIOCLABEL:
case VTIOCLABEL_OLD:
{
char *lab;
DPRINTF2("vt: entering VTIOCLABEL\n");
sizeof (struct vt_lab))) != 0) {
goto out;
}
DPRINTF2("vt: label length is %d\n",
"vt: VTIOCLABEL too much memory error.\n");
goto out;
}
KM_SLEEP);
goto out;
}
} else {
}
}
break;
}
case VTIOCNAME:
case VTIOCNAME_OLD:
{
int unit;
sizeof (struct vt_name))) != 0) {
goto out;
}
&length)) != 0) {
goto out;
}
DPRINTF2("vt: VTIOCNAME: unit %d: %s (len %d)\n",
if (unit == 0) {
DPRINTF("vt: unit 0 is bogus!\n");
goto out;
}
goto out;
}
}
if (length > vt_too_much) {
"vt: VTIOCNAME too much memory error.\n");
goto out;
}
KM_SLEEP);
while (vtnm_dev != 0) {
if (cv_wait_sig(&vtnm_write_cv,
&vtnm_mut) == 0) {
break;
}
break;
}
}
if (error == 0) {
/*
* pass info to waiting event thread (if any)
*/
}
break;
}
case VTIOCTAG:
case VTIOCTAG_OLD:
{
sizeof (struct vt_tag))) != 0) {
goto out;
}
DPRINTF2("vt: VTIOCTAG: unit %lu tag set to 0x%x\n",
break;
}
case VTIOCEVENT:
case VTKIOCEVENT:
case VTIOCEVENT_OLD:
case VTKIOCEVENT_OLD:
{
DPRINTF2("vt: entering VTIOCEVENT\n");
while (vtnm_dev == 0) {
if (cv_wait_sig(&vtnm_read_cv,
&vtnm_mut) == 0) {
break;
}
}
}
vtnm_dev = 0;
if (error != 0) {
break;
}
if (cmd == VTKIOCEVENT) {
sizeof (struct vt_event));
} else {
sizeof (struct vt_event));
}
break;
}
case VTIOCSVTOC:
case VTIOCSVTOC_OLD:
{
DPRINTF2("vt: entered VTIOCSVTOC\n");
sizeof (struct vt_vtdes))) != 0) {
goto out;
}
DPRINTF2("vt: have memory for struct vt_vtoc.\n");
DPRINTF2("vt: freeing previous vt_vtoc.\n");
sizeof (struct vt_vtoc));
}
DPRINTF2("vt: leaving VTIOCSVTOC");
break;
}
case VTIOCSTOCHDR:
case VTIOCSTOCHDR_OLD:
{
struct vt_tochdr header_data;
DPRINTF2("vt: entered VTIOCSTOCHDR\n");
sizeof (struct vt_tochdr))) != 0) {
goto out;
}
DPRINTF2("vt: VTIOCSTOCHDR copyin succeeded\n");
DPRINTF2("vt: leaving VTIOCSTOCHDR\n");
break;
}
case VTIOCSTOCENTRIES:
case VTIOCSTOCENTRIES_OLD:
{
DPRINTF2("vt: entered VTIOCSTOCENTRIES\n");
sizeof (struct vt_tedes))) != 0) {
goto out;
}
if (entry_des.vttd_count > 0) {
DPRINTF2("vt: length of toc entries=%x\n",
sizeof (struct cdrom_tocentry) *
if ((sizeof (struct cdrom_tocentry) *
"vt: VTIOCSTOCENTRIES too much memory error. %d entries.\n",
goto out;
}
sizeof (struct cdrom_tocentry) *
"vt: have memory for struct cdrom_tocentry.\n");
sizeof (struct cdrom_tocentry) *
entry_des.vttd_count)) != 0) {
goto out;
}
}
"vt: freeing previous vt_toc_entries\n");
sizeof (struct cdrom_tocentry));
}
DPRINTF2("vt: leaving VTIOCSTOCENTRIES\n");
break;
}
case VTIOCUNITS:
case VTIOCUNITS_OLD:
sizeof (uint_t));
break;
/*
* Get an event. This is used by calling this
* ioctl until it returns EWOULDBLOCK. poll(2)
* is the mechanism for waiting around for an
* event to happen.
*/
case VTIOCSTATUS:
case VTIOCSTATUS_OLD:
{
DPRINTF2("vt: entering VTIOCSTATUS\n");
if (vt_evcnt != 0) {
vt_evcnt--;
}
if (error != 0) {
/* add it back on error */
vt_evcnt++;
break;
}
} else {
error = EWOULDBLOCK;
}
break;
}
default:
goto out;
}
} else {
/*
* non-unit-0 ioctls -- in general, we're emulating a device
* with these ioctls, since all non-unit-0 units are
* test devices
*/
switch (cmd) {
case CDROMEJECT:
case FDEJECT:
case DKIOCEJECT:
/* mark our unit as ejected */
break;
case CDROMREADTOCHDR:
{
struct cdrom_tochdr header;
DPRINTF2("vt: CDROMREADTOCHDR entered on unit %d\n",
unit);
}
if (error == 0) {
DPRINTF2("vt: copying out cdrom_tochdr\n");
sizeof (struct cdrom_tochdr));
if (error != 0) {
"vt: copy out cdrom_tochdr error %d\n",
error);
}
}
break;
}
case CDROMREADTOCENTRY:
{
struct cdrom_tocentry entry;
unsigned char track;
int entry_number;
DPRINTF2("vt: CDROMREADTOCENTRY entered on unit %d\n",
unit);
/* copy in the toc entry to get desired track. */
sizeof (struct cdrom_tocentry));
if (error != 0) {
break;
}
}
if (track == CDROM_LEADOUT) {
} else {
entry_number = track -
}
if (tp->vt_entry_errno != 0) {
/*
* Simulate errors:
* If errno < 0, simulate an error all
* tracks using the absolute value for the
* error. If errno > 0, then only simulate
* an error on vt_error_track
*/
if (error < 0) {
} else {
error = 0;
}
}
}
if ((entry_number >= 0) && (error == 0)) {
}
if (error != 0) {
"vt: no toc entry for track %d on unit %d\n",
break;
}
if (entry_number >= 0) {
sizeof (struct cdrom_tocentry));
} else {
}
}
if (error == 0) {
"vt: toc entry for track %d returned for unit %d\n",
}
DPRINTF2("vt: leaving CDROMREADTOCENTRY for unit %d\n",
unit);
break;
}
case DKIOCGVTOC:
{
} else {
}
if (error != 0) {
DPRINTF2("vt: no VTOC available for unit %d\n",
unit);
break;
}
if (error != 0) {
DPRINTF2("vt: DKIOCGVTOC copyout %d\n",
error);
}
}
break;
}
case DKIOCSVTOC:
/*
* do not allow setting the VTOC
*/
break;
default:
/* XXX: hey, we'll take anything, but should we? */
break;
}
}
out:
return (error);
}
{
"vt: poll: dev %d.%d events 0x%x anyyet 0x%x reventsp 0x%x\n",
(int)*reventsp);
if (unit == 0) {
if (events & POLLRDNORM) {
DPRINTF4("vt: poll: got a POLLIN\n");
if (vt_evcnt != 0) {
DPRINTF3("vt: poll: we have data\n");
return (0);
}
}
if (!anyyet) {
*phpp = &vt_pollhead;
*reventsp = 0;
}
return (0);
}
return (ENXIO);
}
static void
{
KM_SLEEP);
switch (type) {
case VSE_WRTERR:
break;
default:
return;
}
vt_evcnt++;
}
/*
* called from vtread() to determine how many bytes to copy out
*
* assume tp struct is locked in reader mode
*/
static long
{
long bytes;
*error = 0;
/* We are in the label area */
/*
* how many bytes to transfer = number of bytes left to
* xfer for this read, or bytes left in label, which ever
* is smaller
*/
} else {
/* either there's no label, or we're past the label */
/* bytes to xfer = bytes left to xfer for this read */
}
/* check for an error being requested (iff we have a label) */
/*
* it is possible to have an error because this
* read may go beyond the designated error point
*/
/*
* an error has been requested for a certain
* length, and we've reached that length
*/
if (bytes < 0) {
bytes = 0;
}
}
}
return (bytes);
}