Date: Thu, 6 Dec 2007 18:15:27 GMT From: Maxim Zhuravlev <thioretic@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 130355 for review Message-ID: <200712061815.lB6IFR7D014250@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=130355 Change 130355 by thioretic@thioretic on 2007/12/06 18:14:38 Drivers should be able to specify whether newbus to enqueue incoming io requests for possible additional processing (like prioritization) for devices controlled by the driver. Affected files ... .. //depot/projects/soc2007/thioretic_gidl2/kern/subr_bus.c#9 edit .. //depot/projects/soc2007/thioretic_gidl2/kern/subr_busio.c#6 edit Differences ... ==== //depot/projects/soc2007/thioretic_gidl2/kern/subr_bus.c#9 (text+ko) ==== @@ -1,5250 +1,5251 @@ -/*- - * Copyright (c) 1997,1998,2003 Doug Rabson - * 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. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD: src/sys/kern/subr_bus.c,v 1.184.2.5 2006/12/28 22:13:26 jhb Exp $"); - -#include "opt_bus.h" - -#include <sys/param.h> -#include <sys/conf.h> -#include <sys/filio.h> -#include <sys/lock.h> -#include <sys/kernel.h> -#include <sys/kobj.h> -#include <sys/malloc.h> -#include <sys/module.h> -#include <sys/mutex.h> -#include <sys/poll.h> -#include <sys/proc.h> -#include <sys/condvar.h> -#include <sys/queue.h> -#include <machine/bus.h> -#include <sys/rman.h> -#include <sys/selinfo.h> -#include <sys/signalvar.h> -#include <sys/sysctl.h> -#include <sys/systm.h> -#include <sys/uio.h> -#include <sys/bus.h> - -#include <machine/stdarg.h> - -#include <vm/uma.h> - -SYSCTL_NODE(_hw, OID_AUTO, bus, CTLFLAG_RW, NULL, NULL); -SYSCTL_NODE(, OID_AUTO, dev, CTLFLAG_RW, NULL, NULL); - -/* - * Used to attach drivers to devclasses. - */ -typedef struct driverlink *driverlink_t; -struct driverlink { - kobj_class_t driver; - TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ -}; - -typedef struct driverinfo* driverinfo_t; -struct driverinfo { - kobj_class_t driver; /**< kobj class, implementing driver - & bus interface methods (from outer space)*/ - kobj_class_t drvops; /**< kobj class, implementing driverops - interface methods (from outer space)*/ - kobj_t topology_ops; /**< object of class implemented by driver - (deeply internal:))*/ - kobj_t functional_ops; /**< object of class implemented by driverops - (deeply internal:))*/ - uint32_t flags; /**< driver-specific flags (from outer space)*/ - TAILQ_ENTRY(driverinfo) link; -}; - -typedef struct driverinfolink* driverinfolink_t; -struct driverinfolink { - driverinfo_t pdriver; - void *ivars; - void *softc; -#define DF_EXTERNALSOFTC 1 /* softc not allocated by us */ - int flags; - device_state_t state; - TAILQ_ENTRY(driverinfolink) link; -}; - -/* - * Forward declarations - */ -typedef TAILQ_HEAD(devclass_list, devclass) devclass_list_t; -typedef TAILQ_HEAD(devclasslink_list, devclasslink) devclasslink_list_t; - -typedef TAILQ_HEAD(driver_list, driverlink) driver_list_t; - -typedef TAILQ_HEAD(device_list, device) device_list_t; -typedef TAILQ_HEAD(devicelink_list, devicelink) devicelink_list_t; - -typedef TAILQ_HEAD(driverinfo_list, driverinfo) driverinfo_list_t; -typedef TAILQ_HEAD(driverinfolink_list, driverinfolink) driverinfolink_list_t; - -typedef struct devclasslink* devclasslink_t; -struct devclasslink{ - devclass_t devclass_ptr; - TAILQ_ENTRY(devclasslink) link; -}; - -struct devclass { - TAILQ_ENTRY(devclass) link; - devclass_t parent; /* parent in devclass hierarchy */ - devclasslink_list_t filters; /* these are used to hold information, - used for non-DRL_LOWEST drivers' classes */ - driver_list_t drivers; /* bus devclasses store drivers for bus */ - char *name; - device_t *devices; /* array of devices indexed by unit */ - int maxunit; /* size of devices array */ - - struct sysctl_ctx_list sysctl_ctx; +/*- + * Copyright (c) 1997,1998,2003 Doug Rabson + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/sys/kern/subr_bus.c,v 1.184.2.5 2006/12/28 22:13:26 jhb Exp $"); + +#include "opt_bus.h" + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/filio.h> +#include <sys/lock.h> +#include <sys/kernel.h> +#include <sys/kobj.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/poll.h> +#include <sys/proc.h> +#include <sys/condvar.h> +#include <sys/queue.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <sys/selinfo.h> +#include <sys/signalvar.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/bus.h> + +#include <machine/stdarg.h> + +#include <vm/uma.h> + +SYSCTL_NODE(_hw, OID_AUTO, bus, CTLFLAG_RW, NULL, NULL); +SYSCTL_NODE(, OID_AUTO, dev, CTLFLAG_RW, NULL, NULL); + +/* + * Used to attach drivers to devclasses. + */ +typedef struct driverlink *driverlink_t; +struct driverlink { + kobj_class_t driver; + TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ +}; + +typedef struct driverinfo* driverinfo_t; +struct driverinfo { + kobj_class_t driver; /**< kobj class, implementing driver + & bus interface methods (from outer space)*/ + kobj_class_t drvops; /**< kobj class, implementing driverops + interface methods (from outer space)*/ + kobj_t topology_ops; /**< object of class implemented by driver + (deeply internal:))*/ + kobj_t functional_ops; /**< object of class implemented by driverops + (deeply internal:))*/ + uint32_t flags; /**< driver-specific flags (from outer space)*/ + TAILQ_ENTRY(driverinfo) link; +}; + +typedef struct driverinfolink* driverinfolink_t; +struct driverinfolink { + driverinfo_t pdriver; + void *ivars; + void *softc; +#define DF_EXTERNALSOFTC 1 /* softc not allocated by us */ + int flags; + device_state_t state; + TAILQ_ENTRY(driverinfolink) link; +}; + +/* + * Forward declarations + */ +typedef TAILQ_HEAD(devclass_list, devclass) devclass_list_t; +typedef TAILQ_HEAD(devclasslink_list, devclasslink) devclasslink_list_t; + +typedef TAILQ_HEAD(driver_list, driverlink) driver_list_t; + +typedef TAILQ_HEAD(device_list, device) device_list_t; +typedef TAILQ_HEAD(devicelink_list, devicelink) devicelink_list_t; + +typedef TAILQ_HEAD(driverinfo_list, driverinfo) driverinfo_list_t; +typedef TAILQ_HEAD(driverinfolink_list, driverinfolink) driverinfolink_list_t; + +typedef struct devclasslink* devclasslink_t; +struct devclasslink{ + devclass_t devclass_ptr; + TAILQ_ENTRY(devclasslink) link; +}; + +struct devclass { + TAILQ_ENTRY(devclass) link; + devclass_t parent; /* parent in devclass hierarchy */ + devclasslink_list_t filters; /* these are used to hold information, + used for non-DRL_LOWEST drivers' classes */ + driver_list_t drivers; /* bus devclasses store drivers for bus */ + char *name; + device_t *devices; /* array of devices indexed by unit */ + int maxunit; /* size of devices array */ + + struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; #define DC_ATTACHCHILDDEV (1<<0) - uint32_t flags; -}; - -/** - * @brief Implementation of device. - */ -struct device { - /* - * A device is a kernel object. The first field must be the - * current ops table for the object. - */ - KOBJ_FIELDS; /**< !TRICK: will init it to drv_compat_ctrl_driver - which gonna work around stacked drivers*/ - - /* - * Device hierarchy. - */ - TAILQ_ENTRY(device) link; /**< list of devices in parent */ - TAILQ_ENTRY(device) devlink; /**< global device list membership */ - devicelink_list_t parents; - devicelink_list_t children; /**< list of child devices */ - - /* - * Details of this device. - */ - driverinfolink_t driver; /**< current driver to be probed/attached/...*/ - int driver_level; - driverinfolink_list_t drivers[DRL_LEVELS]; - int driverinfo_flags; - devclass_t devclass; /**< current device class */ - int unit; /**< current unit number */ - char* nameunit; /**< name+unit e.g. foodev0 */ - char* desc; /**< driver specific description */ - int busy; /**< count of calls to device_busy() */ - device_state_t state; /**< current device state */ - u_int32_t devflags; /**< api level flags for device_get_flags() */ - u_short flags; /**< internal device flags */ -#define DF_ENABLED 1 /* device should be probed/attached */ -#define DF_FIXEDCLASS 2 /* devclass specified at create time */ -#define DF_WILDCARD 4 /* unit was originally wildcard */ -#define DF_DESCMALLOCED 8 /* description was malloced */ -#define DF_QUIET 16 /* don't print verbose attach message */ -#define DF_DONENOMATCH 32 /* don't execute DEVICE_NOMATCH again */ -#define DF_REBID 128 /* Can rebid after attach */ -#define DF_PERSISTENT 256 /* Should not delete when refs == 0*/ - u_char order; /**< order from device_add_child_ordered() */ - u_char pad; - //void *ivars; /**< instance variables */ - //void *softc; /**< current driver's variables */ - u_long refs; - int raw; - - struct sysctl_ctx_list sysctl_ctx; /**< state for sysctl variables */ - struct sysctl_oid *sysctl_tree; /**< state for sysctl variables */ -}; - -typedef struct devicelink* devicelink_t; -struct devicelink { - device_t device_ptr; -#define DLF_ANCHOR 1 - int flags; - TAILQ(devicelink) link; -}; - -static MALLOC_DEFINE(M_BUS, "bus", "Bus data structures"); -static MALLOC_DEFINE(M_BUS_SC, "bus-sc", "Bus data structures, softc"); - -#ifdef BUS_DEBUG - -static int bus_debug = 1; -TUNABLE_INT("bus.debug", &bus_debug); -SYSCTL_INT(_debug, OID_AUTO, bus_debug, CTLFLAG_RW, &bus_debug, 0, - "Debug bus code"); - -#define PDEBUG(a) if (bus_debug) {printf("%s:%d: ", __func__, __LINE__), printf a; printf("\n");} -#define DEVICENAME(d) ((d)? device_get_name(d): "no device") -#define DRIVERNAME(d) ((d)? d->name : "no driver") -#define DEVCLANAME(d) ((d)? d->name : "no devclass") - -/** - * Produce the indenting, indent*2 spaces plus a '.' ahead of that to - * prevent syslog from deleting initial spaces - */ -#define indentprintf(p) do { int iJ; printf("."); for (iJ=0; iJ<indent; iJ++) printf(" "); printf p ; } while (0) - -static void print_device_short(device_t dev, int indent); -static void print_device(device_t dev, int indent); -void print_device_tree_short(device_t dev, int indent); -void print_device_tree(device_t dev, int indent); -static void print_driver_short(driver_t *driver, int indent); -static void print_driver(driver_t *driver, int indent); -static void print_driver_list(driver_list_t drivers, int indent); -static void print_devclass_short(devclass_t dc, int indent); -static void print_devclass(devclass_t dc, int indent); -void print_devclass_list_short(void); -void print_devclass_list(void); - -#else -/* Make the compiler ignore the function calls */ -#define PDEBUG(a) /* nop */ -#define DEVICENAME(d) /* nop */ -#define DRIVERNAME(d) /* nop */ -#define DEVCLANAME(d) /* nop */ - -#define print_device_short(d,i) /* nop */ -#define print_device(d,i) /* nop */ -#define print_device_tree_short(d,i) /* nop */ -#define print_device_tree(d,i) /* nop */ -#define print_driver_short(d,i) /* nop */ -#define print_driver(d,i) /* nop */ -#define print_driver_list(d,i) /* nop */ -#define print_devclass_short(d,i) /* nop */ -#define print_devclass(d,i) /* nop */ -#define print_devclass_list_short() /* nop */ -#define print_devclass_list() /* nop */ -#endif - -/* - * dev sysctl tree - */ - -enum { - DEVCLASS_SYSCTL_PARENT, -}; - -static int -devclass_sysctl_handler(SYSCTL_HANDLER_ARGS) -{ - devclass_t dc = (devclass_t)arg1; - const char *value; - - switch (arg2) { - case DEVCLASS_SYSCTL_PARENT: - value = dc->parent ? dc->parent->name : ""; - break; - default: - return (EINVAL); - } - return (SYSCTL_OUT(req, value, strlen(value))); -} - -static void -devclass_sysctl_init(devclass_t dc) -{ - - if (dc->sysctl_tree != NULL) - return; - sysctl_ctx_init(&dc->sysctl_ctx); - dc->sysctl_tree = SYSCTL_ADD_NODE(&dc->sysctl_ctx, - SYSCTL_STATIC_CHILDREN(_dev), OID_AUTO, dc->name, - CTLFLAG_RD, 0, ""); - SYSCTL_ADD_PROC(&dc->sysctl_ctx, SYSCTL_CHILDREN(dc->sysctl_tree), - OID_AUTO, "%parent", CTLFLAG_RD, - dc, DEVCLASS_SYSCTL_PARENT, devclass_sysctl_handler, "A", - "parent class"); -} - -enum { - DEVICE_SYSCTL_DESC, - DEVICE_SYSCTL_DRIVER, - DEVICE_SYSCTL_LOCATION, - DEVICE_SYSCTL_PNPINFO, - DEVICE_SYSCTL_PARENT, -}; - -static int -device_sysctl_handler(SYSCTL_HANDLER_ARGS) -{ - device_t dev = (device_t)arg1; - const char *value; - char *buf; - int error; - int level; - driverinfolink_t dil; - - buf = NULL; - switch (arg2) { - case DEVICE_SYSCTL_DESC: - value = dev->desc ? dev->desc : ""; - break; - case DEVICE_SYSCTL_DRIVER: - value = buf = malloc(1024, M_BUS, M_WAITOK | M_ZERO); - buf[0]='\0'; - for (level=DRL_LOWEST; level<=DRL_TOPMOST; level++){ - switch(level){ - case DRL_LOWEST: tmpbuf="LOWEST:"; break; - case DRL_LOWER: tmpbuf="LOWER:"; break; - case DRL_MIDDLE: tmpbuf="MIDDLE:"; break; - case DRL_UPPER: tmpbuf="UPPER:"; break; - case DRL_TOPMOST: tmpbuf="TOPMOST:"; break; - } - if (strlen(tmpbuf)+strlen(buf)>1023) break; - TAILQ_FOREACH(dil, &((dev->drivers)[level]), link){ - if(strlen(dil->pdriver->driver->name)+strlen(buf)>1022) - break; - strcat(buf,dil->pdriver->driver->name); - strcat(buf,","); - } - buf[strlen(buf)]='\0'; - strcat(buf,"\n"); - } - break; - case DEVICE_SYSCTL_LOCATION: - value = buf = malloc(1024, M_BUS, M_WAITOK | M_ZERO); - bus_child_location_str(dev, buf, 1024); - break; - case DEVICE_SYSCTL_PNPINFO: - value = buf = malloc(1024, M_BUS, M_WAITOK | M_ZERO); - bus_child_pnpinfo_str(dev, buf, 1024); - break; - case DEVICE_SYSCTL_PARENT: - value = dev->parent ? dev->parent->nameunit : ""; - break; - default: - return (EINVAL); - } - error = SYSCTL_OUT(req, value, strlen(value)); - if (buf != NULL) - free(buf, M_BUS); - return (error); -} - -static void -device_sysctl_init(device_t dev) -{ - devclass_t dc = dev->devclass; - - if (dev->sysctl_tree != NULL) - return; - devclass_sysctl_init(dc); - sysctl_ctx_init(&dev->sysctl_ctx); - dev->sysctl_tree = SYSCTL_ADD_NODE(&dev->sysctl_ctx, - SYSCTL_CHILDREN(dc->sysctl_tree), OID_AUTO, - dev->nameunit + strlen(dc->name), - CTLFLAG_RD, 0, ""); - SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree), - OID_AUTO, "%desc", CTLFLAG_RD, - dev, DEVICE_SYSCTL_DESC, device_sysctl_handler, "A", - "device description"); - SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree), - OID_AUTO, "%driver", CTLFLAG_RD, - dev, DEVICE_SYSCTL_DRIVER, device_sysctl_handler, "A", - "device drivers names"); - SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree), - OID_AUTO, "%location", CTLFLAG_RD, - dev, DEVICE_SYSCTL_LOCATION, device_sysctl_handler, "A", - "device location relative to parent"); - SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree), - OID_AUTO, "%pnpinfo", CTLFLAG_RD, - dev, DEVICE_SYSCTL_PNPINFO, device_sysctl_handler, "A", - "device identification"); - SYSCTL_ADD_PROC(&dev->sysctl_ctx, SYSCTL_CHILDREN(dev->sysctl_tree), - OID_AUTO, "%parent", CTLFLAG_RD, - dev, DEVICE_SYSCTL_PARENT, device_sysctl_handler, "A", - "parent device"); -} - -static void -device_sysctl_fini(device_t dev) -{ - if (dev->sysctl_tree == NULL) - return; - sysctl_ctx_free(&dev->sysctl_ctx); - dev->sysctl_tree = NULL; -} - -/* - * /dev/devctl implementation - */ - -/* - * This design allows only one reader for /dev/devctl. This is not desirable - * in the long run, but will get a lot of hair out of this implementation. - * Maybe we should make this device a clonable device. - * - * Also note: we specifically do not attach a device to the device_t tree - * to avoid potential chicken and egg problems. One could argue that all - * of this belongs to the root node. One could also further argue that the - * sysctl interface that we have not might more properly be an ioctl - * interface, but at this stage of the game, I'm not inclined to rock that - * boat. - * - * I'm also not sure that the SIGIO support is done correctly or not, as - * I copied it from a driver that had SIGIO support that likely hasn't been - * tested since 3.4 or 2.2.8! - */ - -static int sysctl_devctl_disable(SYSCTL_HANDLER_ARGS); -static int devctl_disable = 0; -TUNABLE_INT("hw.bus.devctl_disable", &devctl_disable); -SYSCTL_PROC(_hw_bus, OID_AUTO, devctl_disable, CTLTYPE_INT | CTLFLAG_RW, 0, 0, - sysctl_devctl_disable, "I", "devctl disable"); - -static d_open_t devopen; -static d_close_t devclose; -static d_read_t devread; -static d_ioctl_t devioctl; -static d_poll_t devpoll; - -static struct cdevsw dev_cdevsw = { - .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, - .d_open = devopen, - .d_close = devclose, - .d_read = devread, - .d_ioctl = devioctl, - .d_poll = devpoll, - .d_name = "devctl", -}; - -struct dev_event_info -{ - char *dei_data; - TAILQ_ENTRY(dev_event_info) dei_link; -}; - -TAILQ_HEAD(devq, dev_event_info); - -static struct dev_softc -{ - int inuse; - int nonblock; - struct mtx mtx; - struct cv cv; - struct selinfo sel; - struct devq devq; - struct proc *async_proc; -} devsoftc; - -static struct cdev *devctl_dev; - -static void -devinit(void) -{ - devctl_dev = make_dev(&dev_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, - "devctl"); - mtx_init(&devsoftc.mtx, "dev mtx", "devd", MTX_DEF); - cv_init(&devsoftc.cv, "dev cv"); - TAILQ_INIT(&devsoftc.devq); -} - -static int -devopen(struct cdev *dev, int oflags, int devtype, d_thread_t *td) -{ - if (devsoftc.inuse) - return (EBUSY); - /* move to init */ - devsoftc.inuse = 1; - devsoftc.nonblock = 0; - devsoftc.async_proc = NULL; - return (0); -} - -static int -devclose(struct cdev *dev, int fflag, int devtype, d_thread_t *td) -{ - devsoftc.inuse = 0; - mtx_lock(&devsoftc.mtx); - cv_broadcast(&devsoftc.cv); - mtx_unlock(&devsoftc.mtx); - - return (0); -} - -/* - * The read channel for this device is used to report changes to - * userland in realtime. We are required to free the data as well as - * the n1 object because we allocate them separately. Also note that - * we return one record at a time. If you try to read this device a - * character at a time, you will loose the rest of the data. Listening - * programs are expected to cope. - */ -static int -devread(struct cdev *dev, struct uio *uio, int ioflag) -{ - struct dev_event_info *n1; - int rv; - - mtx_lock(&devsoftc.mtx); - while (TAILQ_EMPTY(&devsoftc.devq)) { - if (devsoftc.nonblock) { - mtx_unlock(&devsoftc.mtx); - return (EAGAIN); - } - rv = cv_wait_sig(&devsoftc.cv, &devsoftc.mtx); - if (rv) { - /* - * Need to translate ERESTART to EINTR here? -- jake - */ - mtx_unlock(&devsoftc.mtx); - return (rv); - } - } - n1 = TAILQ_FIRST(&devsoftc.devq); - TAILQ_REMOVE(&devsoftc.devq, n1, dei_link); - mtx_unlock(&devsoftc.mtx); - rv = uiomove(n1->dei_data, strlen(n1->dei_data), uio); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); - return (rv); -} - -static int -devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, d_thread_t *td) -{ - switch (cmd) { - - case FIONBIO: - if (*(int*)data) - devsoftc.nonblock = 1; - else - devsoftc.nonblock = 0; - return (0); - case FIOASYNC: - if (*(int*)data) - devsoftc.async_proc = td->td_proc; - else - devsoftc.async_proc = NULL; - return (0); - - /* (un)Support for other fcntl() calls. */ - case FIOCLEX: - case FIONCLEX: - case FIONREAD: - case FIOSETOWN: - case FIOGETOWN: - default: - break; - } - return (ENOTTY); -} - -static int -devpoll(struct cdev *dev, int events, d_thread_t *td) -{ - int revents = 0; - - mtx_lock(&devsoftc.mtx); - if (events & (POLLIN | POLLRDNORM)) { - if (!TAILQ_EMPTY(&devsoftc.devq)) - revents = events & (POLLIN | POLLRDNORM); - else - selrecord(td, &devsoftc.sel); - } - mtx_unlock(&devsoftc.mtx); - - return (revents); -} - -/** - * @brief Queue data to be read from the devctl device - * - * Generic interface to queue data to the devctl device. It is - * assumed that @p data is properly formatted. It is further assumed - * that @p data is allocated using the M_BUS malloc type. - */ -void -devctl_queue_data(char *data) -{ - struct dev_event_info *n1 = NULL; - struct proc *p; - - n1 = malloc(sizeof(*n1), M_BUS, M_NOWAIT); - if (n1 == NULL) - return; - n1->dei_data = data; - mtx_lock(&devsoftc.mtx); - TAILQ_INSERT_TAIL(&devsoftc.devq, n1, dei_link); - cv_broadcast(&devsoftc.cv); - mtx_unlock(&devsoftc.mtx); - selwakeup(&devsoftc.sel); - p = devsoftc.async_proc; - if (p != NULL) { - PROC_LOCK(p); - psignal(p, SIGIO); - PROC_UNLOCK(p); - } -} - -/** - * @brief Send a 'notification' to userland, using standard ways - */ -void -devctl_notify(const char *system, const char *subsystem, const char *type, - const char *data) -{ - int len = 0; - char *msg; - - if (system == NULL) - return; /* BOGUS! Must specify system. */ - if (subsystem == NULL) - return; /* BOGUS! Must specify subsystem. */ - if (type == NULL) - return; /* BOGUS! Must specify type. */ - len += strlen(" system=") + strlen(system); - len += strlen(" subsystem=") + strlen(subsystem); - len += strlen(" type=") + strlen(type); - /* add in the data message plus newline. */ - if (data != NULL) - len += strlen(data); - len += 3; /* '!', '\n', and NUL */ - msg = malloc(len, M_BUS, M_NOWAIT); - if (msg == NULL) - return; /* Drop it on the floor */ - if (data != NULL) - snprintf(msg, len, "!system=%s subsystem=%s type=%s %s\n", - system, subsystem, type, data); - else - snprintf(msg, len, "!system=%s subsystem=%s type=%s\n", - system, subsystem, type); - devctl_queue_data(msg); -} - -/* - * Common routine that tries to make sending messages as easy as possible. - * We allocate memory for the data, copy strings into that, but do not - * free it unless there's an error. The dequeue part of the driver should - * free the data. We don't send data when the device is disabled. We do - * send data, even when we have no listeners, because we wish to avoid - * races relating to startup and restart of listening applications. - * - * devaddq is designed to string together the type of event, with the - * object of that event, plus the plug and play info and location info - * for that event. This is likely most useful for devices, but less - * useful for other consumers of this interface. Those should use - * the devctl_queue_data() interface instead. - */ -static void -devaddq(const char *type, const char *what, device_t dev) -{ - char *data = NULL; - char *loc = NULL; - char *pnp = NULL; - const char *parstr; - - if (devctl_disable) - return; - data = malloc(1024, M_BUS, M_NOWAIT); - if (data == NULL) - goto bad; - - /* get the bus specific location of this device */ - loc = malloc(1024, M_BUS, M_NOWAIT); - if (loc == NULL) - goto bad; - *loc = '\0'; - bus_child_location_str(dev, loc, 1024); - - /* Get the bus specific pnp info of this device */ - pnp = malloc(1024, M_BUS, M_NOWAIT); - if (pnp == NULL) - goto bad; - *pnp = '\0'; - bus_child_pnpinfo_str(dev, pnp, 1024); - - /* Get the parent of this device, or / if high enough in the tree. */ - if (device_get_parent(dev) == NULL) - parstr = "."; /* Or '/' ? */ - else - parstr = device_get_nameunit(device_get_parent(dev)); - /* String it all together. */ - snprintf(data, 1024, "%s%s at %s %s on %s\n", type, what, loc, pnp, - parstr); - free(loc, M_BUS); - free(pnp, M_BUS); - devctl_queue_data(data); - return; -bad: - free(pnp, M_BUS); - free(loc, M_BUS); - free(data, M_BUS); - return; -} - -/* - * A device was added to the tree. We are called just after it successfully - * attaches (that is, probe and attach success for this device). No call - * is made if a device is merely parented into the tree. See devnomatch - * if probe fails. If attach fails, no notification is sent (but maybe - * we should have a different message for this). - */ -static void -devadded(device_t dev) -{ - char *pnp = NULL; - char *tmp = NULL; - - pnp = malloc(1024, M_BUS, M_NOWAIT); - if (pnp == NULL) - goto fail; - tmp = malloc(1024, M_BUS, M_NOWAIT); - if (tmp == NULL) - goto fail; - *pnp = '\0'; - bus_child_pnpinfo_str(dev, pnp, 1024); - snprintf(tmp, 1024, "%s %s", device_get_nameunit(dev), pnp); - devaddq("+", tmp, dev); -fail: - if (pnp != NULL) - free(pnp, M_BUS); - if (tmp != NULL) - free(tmp, M_BUS); - return; -} - -/* - * A device was removed from the tree. We are called just before this - * happens. - */ -static void -devremoved(device_t dev) -{ - char *pnp = NULL; - char *tmp = NULL; - - pnp = malloc(1024, M_BUS, M_NOWAIT); - if (pnp == NULL) - goto fail; - tmp = malloc(1024, M_BUS, M_NOWAIT); - if (tmp == NULL) - goto fail; - *pnp = '\0'; - bus_child_pnpinfo_str(dev, pnp, 1024); - snprintf(tmp, 1024, "%s %s", device_get_nameunit(dev), pnp); - devaddq("-", tmp, dev); -fail: - if (pnp != NULL) - free(pnp, M_BUS); - if (tmp != NULL) - free(tmp, M_BUS); - return; -} - -/* - * Called when there's no match for this device. This is only called - * the first time that no match happens, so we don't keep getitng this - * message. Should that prove to be undesirable, we can change it. - * This is called when all drivers that can attach to a given bus - * decline to accept this device. Other errrors may not be detected. - */ -static void -devnomatch(device_t dev) -{ - devaddq("?", "", dev); -} - -static int -sysctl_devctl_disable(SYSCTL_HANDLER_ARGS) -{ - struct dev_event_info *n1; - int dis, error; - - dis = devctl_disable; - error = sysctl_handle_int(oidp, &dis, 0, req); - if (error || !req->newptr) - return (error); - mtx_lock(&devsoftc.mtx); - devctl_disable = dis; - if (dis) { - while (!TAILQ_EMPTY(&devsoftc.devq)) { - n1 = TAILQ_FIRST(&devsoftc.devq); - TAILQ_REMOVE(&devsoftc.devq, n1, dei_link); - free(n1->dei_data, M_BUS); - free(n1, M_BUS); - } - } - mtx_unlock(&devsoftc.mtx); - return (0); -} - -/* End of /dev/devctl code */ - -TAILQ_HEAD(,device) bus_data_devices; -static int bus_data_generation = 1; - -kobj_method_t null_methods[] = { - { 0, 0 } -}; - -DEFINE_CLASS(null, null_methods, 0); - -/* - * Driver compatibility layer implementation - */ - -static driverinfo_list_t driverinfos = TAILQ_HEAD_INITIALIZER(driverinfos); - -/** - * @internal - * - * Is used, when a driver s used in an API function, - * but is not in driverinfos. Eg. may happen, if - * device_set_driver is called by a bus with driver, set - * to kobj_class, which wasn't registered by DRIVER_MODULE(). - */ -static drv_internal_t -driverinfo_create_driver_drv_internal (driver_t *driver){ - drv_internal_t new_drv; - - new_drv = malloc (sizeof(struct drv_internal), M_TEMP, M_NOWAIT|M_ZERO); - if (new_drv){ - new_drv.devops = driver; - new_drv.flags = DR_LOWEST; - } - return (new_drv); -} -/** - * @internal - * @brief Find or add driver compatibility settings - * - * If a driver is already present in compatibility layer - * return it, else, if @p add non-zero, add it. - * - * @param driver the device class and flags - * @param add non-zero to add driver to layer - */ -static driverinfo_t -driverinfo_find_internal (drv_internal_t driver, int add) { - driverinfo_t di; - - PDEBUG(("looking for driver %s to compatibility layer", driver->devops->name)); - if (!driver) - return (NULL); - - TAILQ_FOREACH(di, &driverinfos, link){ - if (driver->devops == di->driver) - break; - } - - if (!di && add){ >>> TRUNCATED FOR MAIL (1000 lines) <<<
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200712061815.lB6IFR7D014250>