File:  [DragonFly] / src / sys / kern / kern_device.c
Revision 1.10: download - view: text, annotated - select for diffs
Thu May 13 23:49:23 2004 UTC (10 years, 4 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
device switch 1/many: Remove d_autoq, add d_clone (where d_autoq was).

d_autoq was used to allow the device port dispatch to mix old-style synchronous
calls with new style messaging calls within a particular device.  It was never
used for that purpose.

d_clone will be more fully implemented as work continues.  We are going to
install d_port in the dev_t (struct specinfo) structure itself and d_clone
will be needed to allow devices to 'revector' the port on a minor-number
by minor-number basis, in particular allowing minor numbers to be directly
dispatched to distinct threads.  This is something we will be needing later
on.

/*
 * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com> All rights reserved.
 * cdevsw from kern/kern_conf.c Copyright (c) 1995 Terrence R. Lambert
 * cdevsw from kern/kern_conf.c Copyright (c) 1995 Julian R. Elishcer,
 *							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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
 *
 * $DragonFly: src/sys/kern/kern_device.c,v 1.10 2004/05/13 23:49:23 dillon Exp $
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <sys/queue.h>
#include <sys/msgport.h>
#include <sys/device.h>
#include <machine/stdarg.h>
#include <sys/proc.h>
#include <sys/thread2.h>
#include <sys/msgport2.h>

static struct cdevsw 	*cdevsw[NUMCDEVSW];
static struct lwkt_port	*cdevport[NUMCDEVSW];

static int cdevsw_putport(lwkt_port_t port, lwkt_msg_t msg);

/*
 * Initialize a message port to serve as the default message-handling port
 * for device operations.  This message port provides compatibility with
 * traditional cdevsw dispatch functions by running them synchronously.
 *
 * YYY NOTE: ms_cmd can now hold a function pointer, should this code be
 * converted from an integer op to a function pointer with a flag to
 * indicate legacy operation?
 */
static void
init_default_cdevsw_port(lwkt_port_t port)
{
    lwkt_initport(port, NULL);
    port->mp_putport = cdevsw_putport;
}

static
int
cdevsw_putport(lwkt_port_t port, lwkt_msg_t lmsg)
{
    cdevallmsg_t msg = (cdevallmsg_t)lmsg;
    struct cdevsw *csw = msg->am_msg.csw;
    int error;

    /*
     * Run the device switch function synchronously in the context of the
     * caller and return a synchronous error code (anything not EASYNC).
     */
    switch(msg->am_lmsg.ms_cmd.cm_op) {
    case CDEV_CMD_OPEN:
	error = csw->old_open(
		    msg->am_open.msg.dev,
		    msg->am_open.oflags,
		    msg->am_open.devtype,
		    msg->am_open.td);
	break;
    case CDEV_CMD_CLOSE:
	error = csw->old_close(
		    msg->am_close.msg.dev,
		    msg->am_close.fflag,
		    msg->am_close.devtype,
		    msg->am_close.td);
	break;
    case CDEV_CMD_STRATEGY:
	csw->old_strategy(msg->am_strategy.bp);
	error = 0;
	break;
    case CDEV_CMD_IOCTL:
	error = csw->old_ioctl(
		    msg->am_ioctl.msg.dev,
		    msg->am_ioctl.cmd,
		    msg->am_ioctl.data,
		    msg->am_ioctl.fflag,
		    msg->am_ioctl.td);
	break;
    case CDEV_CMD_DUMP:
	error = csw->old_dump(msg->am_ioctl.msg.dev);
	break;
    case CDEV_CMD_PSIZE:
	msg->am_psize.result = csw->old_psize(msg->am_psize.msg.dev);
	error = 0;	/* XXX */
	break;
    case CDEV_CMD_READ:
	error = csw->old_read(
		    msg->am_read.msg.dev,
		    msg->am_read.uio,
		    msg->am_read.ioflag);
	break;
    case CDEV_CMD_WRITE:
	error = csw->old_write(
		    msg->am_read.msg.dev,
		    msg->am_read.uio,
		    msg->am_read.ioflag);
	break;
    case CDEV_CMD_POLL:
	msg->am_poll.events = csw->old_poll(
				msg->am_poll.msg.dev,
				msg->am_poll.events,
				msg->am_poll.td);
	error = 0;
	break;
    case CDEV_CMD_KQFILTER:
	msg->am_kqfilter.result = csw->old_kqfilter(
				msg->am_kqfilter.msg.dev,
				msg->am_kqfilter.kn);
	error = 0;
	break;
    case CDEV_CMD_MMAP:
	msg->am_mmap.result = csw->old_mmap(
		    msg->am_mmap.msg.dev,
		    msg->am_mmap.offset,
		    msg->am_mmap.nprot);
	error = 0;	/* XXX */
	break;
    default:
	error = ENOSYS;
	break;
    }
    KKASSERT(error != EASYNC);
    return(error);
}

/*
 * These device dispatch functions provide convenient entry points for
 * any code wishing to make a dev call.
 *
 * YYY we ought to be able to optimize the port lookup by caching it in
 * the dev_t structure itself.
 */
static __inline
struct cdevsw *
_devsw(dev_t dev)
{
    if (dev == NULL)
	return(NULL);
    if (dev->si_devsw)
	return (dev->si_devsw);
    return(cdevsw[major(dev)]);
}

static __inline
lwkt_port_t
_init_cdevmsg(dev_t dev, cdevmsg_t msg, int cmd)
{
    struct cdevsw *csw;

    lwkt_initmsg_simple(&msg->msg, cmd);
    msg->dev = dev;
    msg->csw = csw = _devsw(dev);
    if (csw != NULL) {			/* YYY too hackish */
	KKASSERT(csw->d_port);		/* YYY too hackish */
	if (cdevport[major(dev)])	/* YYY too hackish */
	    return(cdevport[major(dev)]);
	return(csw->d_port);
    }
    return(NULL);
}

int
dev_dopen(dev_t dev, int oflags, int devtype, thread_t td)
{
    struct cdevmsg_open	msg;
    lwkt_port_t port;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN);
    if (port == NULL)
	return(ENXIO);
    msg.oflags = oflags;
    msg.devtype = devtype;
    msg.td = td;
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_dclose(dev_t dev, int fflag, int devtype, thread_t td)
{
    struct cdevmsg_close msg;
    lwkt_port_t port;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE);
    if (port == NULL)
	return(ENXIO);
    msg.fflag = fflag;
    msg.devtype = devtype;
    msg.td = td;
    return(lwkt_domsg(port, &msg.msg.msg));
}

void
dev_dstrategy(dev_t dev, struct buf *bp)
{
    struct cdevmsg_strategy msg;
    lwkt_port_t port;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY);
    KKASSERT(port);	/* 'nostrategy' function is NULL YYY */
    msg.bp = bp;
    lwkt_domsg(port, &msg.msg.msg);
}

int
dev_dioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td)
{
    struct cdevmsg_ioctl msg;
    lwkt_port_t port;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL);
    if (port == NULL)
	return(ENXIO);
    msg.cmd = cmd;
    msg.data = data;
    msg.fflag = fflag;
    msg.td = td;
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_ddump(dev_t dev)
{
    struct cdevmsg_dump	msg;
    lwkt_port_t port;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP);
    if (port == NULL)
	return(ENXIO);
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_dpsize(dev_t dev)
{
    struct cdevmsg_psize msg;
    lwkt_port_t port;
    int error;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE);
    if (port == NULL)
	return(-1);
    error = lwkt_domsg(port, &msg.msg.msg);
    if (error == 0)
	return(msg.result);
    return(-1);
}

int
dev_dread(dev_t dev, struct uio *uio, int ioflag)
{
    struct cdevmsg_read msg;
    lwkt_port_t port;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ);
    if (port == NULL)
	return(ENXIO);
    msg.uio = uio;
    msg.ioflag = ioflag;
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_dwrite(dev_t dev, struct uio *uio, int ioflag)
{
    struct cdevmsg_write msg;
    lwkt_port_t port;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE);
    if (port == NULL)
	return(ENXIO);
    msg.uio = uio;
    msg.ioflag = ioflag;
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_dpoll(dev_t dev, int events, thread_t td)
{
    struct cdevmsg_poll msg;
    lwkt_port_t port;
    int error;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL);
    if (port == NULL)
	return(ENXIO);
    msg.events = events;
    msg.td = td;
    error = lwkt_domsg(port, &msg.msg.msg);
    if (error == 0)
	return(msg.events);
    return(seltrue(dev, msg.events, td));
}

int
dev_dkqfilter(dev_t dev, struct knote *kn)
{
    struct cdevmsg_kqfilter msg;
    lwkt_port_t port;
    int error;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER);
    if (port == NULL)
	return(ENXIO);
    msg.kn = kn;
    error = lwkt_domsg(port, &msg.msg.msg);
    if (error == 0)
	return(msg.result);
    return(ENODEV);
}

int
dev_dmmap(dev_t dev, vm_offset_t offset, int nprot)
{
    struct cdevmsg_mmap msg;
    lwkt_port_t port;
    int error;

    port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP);
    if (port == NULL)
	return(-1);
    msg.offset = offset;
    msg.nprot = nprot;
    error = lwkt_domsg(port, &msg.msg.msg);
    if (error == 0)
	return(msg.result);
    return(-1);
}

int
dev_port_dopen(lwkt_port_t port, dev_t dev, int oflags, int devtype, thread_t td)
{
    struct cdevmsg_open	msg;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN);
    if (port == NULL)
	return(ENXIO);
    msg.oflags = oflags;
    msg.devtype = devtype;
    msg.td = td;
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_port_dclose(lwkt_port_t port, dev_t dev, int fflag, int devtype, thread_t td)
{
    struct cdevmsg_close msg;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE);
    if (port == NULL)
	return(ENXIO);
    msg.fflag = fflag;
    msg.devtype = devtype;
    msg.td = td;
    return(lwkt_domsg(port, &msg.msg.msg));
}

void
dev_port_dstrategy(lwkt_port_t port, dev_t dev, struct buf *bp)
{
    struct cdevmsg_strategy msg;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY);
    KKASSERT(port);	/* 'nostrategy' function is NULL YYY */
    msg.bp = bp;
    lwkt_domsg(port, &msg.msg.msg);
}

int
dev_port_dioctl(lwkt_port_t port, dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td)
{
    struct cdevmsg_ioctl msg;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL);
    if (port == NULL)
	return(ENXIO);
    msg.cmd = cmd;
    msg.data = data;
    msg.fflag = fflag;
    msg.td = td;
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_port_ddump(lwkt_port_t port, dev_t dev)
{
    struct cdevmsg_dump	msg;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP);
    if (port == NULL)
	return(ENXIO);
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_port_dpsize(lwkt_port_t port, dev_t dev)
{
    struct cdevmsg_psize msg;
    int error;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE);
    if (port == NULL)
	return(-1);
    error = lwkt_domsg(port, &msg.msg.msg);
    if (error == 0)
	return(msg.result);
    return(-1);
}

int
dev_port_dread(lwkt_port_t port, dev_t dev, struct uio *uio, int ioflag)
{
    struct cdevmsg_read msg;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ);
    if (port == NULL)
	return(ENXIO);
    msg.uio = uio;
    msg.ioflag = ioflag;
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_port_dwrite(lwkt_port_t port, dev_t dev, struct uio *uio, int ioflag)
{
    struct cdevmsg_write msg;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE);
    if (port == NULL)
	return(ENXIO);
    msg.uio = uio;
    msg.ioflag = ioflag;
    return(lwkt_domsg(port, &msg.msg.msg));
}

int
dev_port_dpoll(lwkt_port_t port, dev_t dev, int events, thread_t td)
{
    struct cdevmsg_poll msg;
    int error;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL);
    if (port == NULL)
	return(ENXIO);
    msg.events = events;
    msg.td = td;
    error = lwkt_domsg(port, &msg.msg.msg);
    if (error == 0)
	return(msg.events);
    return(seltrue(dev, msg.events, td));
}

int
dev_port_dkqfilter(lwkt_port_t port, dev_t dev, struct knote *kn)
{
    struct cdevmsg_kqfilter msg;
    int error;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER);
    if (port == NULL)
	return(ENXIO);
    msg.kn = kn;
    error = lwkt_domsg(port, &msg.msg.msg);
    if (error == 0)
	return(msg.result);
    return(ENODEV);
}

int
dev_port_dmmap(lwkt_port_t port, dev_t dev, vm_offset_t offset, int nprot)
{
    struct cdevmsg_mmap msg;
    int error;

    _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP);
    if (port == NULL)
	return(-1);
    msg.offset = offset;
    msg.nprot = nprot;
    error = lwkt_domsg(port, &msg.msg.msg);
    if (error == 0)
	return(msg.result);
    return(-1);
}

const char *
dev_dname(dev_t dev)
{
    struct cdevsw *csw;

    if ((csw = _devsw(dev)) != NULL)
	return(csw->d_name);
    return(NULL);
}

int
dev_dflags(dev_t dev)
{
    struct cdevsw *csw;

    if ((csw = _devsw(dev)) != NULL)
	return(csw->d_flags);
    return(0);
}

int
dev_dmaj(dev_t dev)
{
    struct cdevsw *csw;

    if ((csw = _devsw(dev)) != NULL)
	return(csw->d_maj);
    return(0);
}

lwkt_port_t
dev_dport(dev_t dev)
{
    struct cdevsw *csw;

    if ((csw = _devsw(dev)) != NULL) {
	if (cdevport[major(dev)])	/* YYY too hackish */
	    return(cdevport[major(dev)]);
	return(csw->d_port);
    }
    return(NULL);
}

#if 0
/*
 * cdevsw[] array functions, moved from kern/kern_conf.c
 */
struct cdevsw *
devsw(dev_t dev)
{
    return(_devsw(dev));
}
#endif

/*
 * Convert a cdevsw template into the real thing, filling in fields the
 * device left empty with appropriate defaults.
 */
void
compile_devsw(struct cdevsw *devsw)
{
    static lwkt_port devsw_compat_port;

    if (devsw_compat_port.mp_putport == NULL)
	init_default_cdevsw_port(&devsw_compat_port);
    
    if (devsw->old_open == NULL)
	devsw->old_open = noopen;
    if (devsw->old_close == NULL)
	devsw->old_close = noclose;
    if (devsw->old_read == NULL)
	devsw->old_read = noread;
    if (devsw->old_write == NULL)
	devsw->old_write = nowrite;
    if (devsw->old_ioctl == NULL)
	devsw->old_ioctl = noioctl;
    if (devsw->old_poll == NULL)
	devsw->old_poll = nopoll;
    if (devsw->old_mmap == NULL)
	devsw->old_mmap = nommap;
    if (devsw->old_strategy == NULL)
	devsw->old_strategy = nostrategy;
    if (devsw->old_dump == NULL)
	devsw->old_dump = nodump;
    if (devsw->old_psize == NULL)
	devsw->old_psize = nopsize;
    if (devsw->old_kqfilter == NULL)
	devsw->old_kqfilter = nokqfilter;

    if (devsw->d_port == NULL)
	devsw->d_port = &devsw_compat_port;
}

/*
 * Add a cdevsw entry
 */
int
cdevsw_add(struct cdevsw *newentry)
{
    compile_devsw(newentry);
    if (newentry->d_maj < 0 || newentry->d_maj >= NUMCDEVSW) {
	printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
	    newentry->d_name, newentry->d_maj);
	return (EINVAL);
    }
    if (cdevsw[newentry->d_maj]) {
	printf("WARNING: \"%s\" is usurping \"%s\"'s cdevsw[]\n",
	    newentry->d_name, cdevsw[newentry->d_maj]->d_name);
    }
    cdevsw[newentry->d_maj] = newentry;
    return (0);
}

/*
 * Add a cdevsw entry and override the port.
 */
lwkt_port_t
cdevsw_add_override(struct cdevsw *newentry, lwkt_port_t port)
{
    int error;

    if ((error = cdevsw_add(newentry)) == 0)
	cdevport[newentry->d_maj] = port;
    return(newentry->d_port);
}

lwkt_port_t
cdevsw_dev_override(dev_t dev, lwkt_port_t port)
{
    struct cdevsw *csw;

    KKASSERT(major(dev) >= 0 && major(dev) < NUMCDEVSW);
    if ((csw = _devsw(dev)) != NULL) {
	cdevport[major(dev)] = port;
	return(csw->d_port);
    }
    return(NULL);
}

/*
 *  Remove a cdevsw entry
 */
int
cdevsw_remove(struct cdevsw *oldentry)
{
    if (oldentry->d_maj < 0 || oldentry->d_maj >= NUMCDEVSW) {
	printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
	    oldentry->d_name, oldentry->d_maj);
	return EINVAL;
    }
    cdevsw[oldentry->d_maj] = NULL;
    cdevport[oldentry->d_maj] = NULL;
    return 0;
}