1102e6817SAlexander V. Chernikov /*- 2102e6817SAlexander V. Chernikov * SPDX-License-Identifier: BSD-2-Clause 3102e6817SAlexander V. Chernikov * 4102e6817SAlexander V. Chernikov * Copyright (c) 2002-2020 M. Warner Losh <imp@FreeBSD.org> 5102e6817SAlexander V. Chernikov * All rights reserved. 6102e6817SAlexander V. Chernikov * 7102e6817SAlexander V. Chernikov * Redistribution and use in source and binary forms, with or without 8102e6817SAlexander V. Chernikov * modification, are permitted provided that the following conditions 9102e6817SAlexander V. Chernikov * are met: 10102e6817SAlexander V. Chernikov * 1. Redistributions of source code must retain the above copyright 11102e6817SAlexander V. Chernikov * notice, this list of conditions and the following disclaimer. 12102e6817SAlexander V. Chernikov * 2. Redistributions in binary form must reproduce the above copyright 13102e6817SAlexander V. Chernikov * notice, this list of conditions and the following disclaimer in the 14102e6817SAlexander V. Chernikov * documentation and/or other materials provided with the distribution. 15102e6817SAlexander V. Chernikov * 16102e6817SAlexander V. Chernikov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17102e6817SAlexander V. Chernikov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18102e6817SAlexander V. Chernikov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19102e6817SAlexander V. Chernikov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20102e6817SAlexander V. Chernikov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21102e6817SAlexander V. Chernikov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22102e6817SAlexander V. Chernikov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23102e6817SAlexander V. Chernikov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24102e6817SAlexander V. Chernikov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25102e6817SAlexander V. Chernikov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26102e6817SAlexander V. Chernikov * SUCH DAMAGE. 27102e6817SAlexander V. Chernikov */ 28102e6817SAlexander V. Chernikov #include <sys/cdefs.h> 29102e6817SAlexander V. Chernikov #include "opt_bus.h" 30102e6817SAlexander V. Chernikov #include "opt_ddb.h" 31102e6817SAlexander V. Chernikov 32102e6817SAlexander V. Chernikov #include <sys/param.h> 33102e6817SAlexander V. Chernikov #include <sys/conf.h> 34102e6817SAlexander V. Chernikov #include <sys/eventhandler.h> 35102e6817SAlexander V. Chernikov #include <sys/filio.h> 36102e6817SAlexander V. Chernikov #include <sys/lock.h> 37102e6817SAlexander V. Chernikov #include <sys/kernel.h> 38102e6817SAlexander V. Chernikov #include <sys/malloc.h> 39102e6817SAlexander V. Chernikov #include <sys/mutex.h> 40102e6817SAlexander V. Chernikov #include <sys/poll.h> 41102e6817SAlexander V. Chernikov #include <sys/priv.h> 42102e6817SAlexander V. Chernikov #include <sys/proc.h> 43102e6817SAlexander V. Chernikov #include <sys/condvar.h> 44102e6817SAlexander V. Chernikov #include <sys/queue.h> 45102e6817SAlexander V. Chernikov #include <machine/bus.h> 46102e6817SAlexander V. Chernikov #include <sys/sbuf.h> 47102e6817SAlexander V. Chernikov #include <sys/selinfo.h> 48102e6817SAlexander V. Chernikov #include <sys/smp.h> 49102e6817SAlexander V. Chernikov #include <sys/sysctl.h> 50102e6817SAlexander V. Chernikov #include <sys/systm.h> 51102e6817SAlexander V. Chernikov #include <sys/uio.h> 52102e6817SAlexander V. Chernikov #include <sys/bus.h> 53102e6817SAlexander V. Chernikov 54102e6817SAlexander V. Chernikov #include <machine/cpu.h> 55102e6817SAlexander V. Chernikov #include <machine/stdarg.h> 56102e6817SAlexander V. Chernikov 57102e6817SAlexander V. Chernikov #include <vm/uma.h> 58102e6817SAlexander V. Chernikov #include <vm/vm.h> 59102e6817SAlexander V. Chernikov 60102e6817SAlexander V. Chernikov #include <ddb/ddb.h> 61102e6817SAlexander V. Chernikov 62102e6817SAlexander V. Chernikov STAILQ_HEAD(devq, dev_event_info); 63102e6817SAlexander V. Chernikov 64102e6817SAlexander V. Chernikov static struct dev_softc { 65102e6817SAlexander V. Chernikov int inuse; 66102e6817SAlexander V. Chernikov int nonblock; 67102e6817SAlexander V. Chernikov int queued; 68102e6817SAlexander V. Chernikov int async; 69102e6817SAlexander V. Chernikov struct mtx mtx; 70102e6817SAlexander V. Chernikov struct cv cv; 71102e6817SAlexander V. Chernikov struct selinfo sel; 72102e6817SAlexander V. Chernikov struct devq devq; 73102e6817SAlexander V. Chernikov struct sigio *sigio; 74102e6817SAlexander V. Chernikov uma_zone_t zone; 75102e6817SAlexander V. Chernikov } devsoftc; 76102e6817SAlexander V. Chernikov 77102e6817SAlexander V. Chernikov /* 78102e6817SAlexander V. Chernikov * This design allows only one reader for /dev/devctl. This is not desirable 79102e6817SAlexander V. Chernikov * in the long run, but will get a lot of hair out of this implementation. 80102e6817SAlexander V. Chernikov * Maybe we should make this device a clonable device. 81102e6817SAlexander V. Chernikov * 82102e6817SAlexander V. Chernikov * Also note: we specifically do not attach a device to the device_t tree 83102e6817SAlexander V. Chernikov * to avoid potential chicken and egg problems. One could argue that all 84102e6817SAlexander V. Chernikov * of this belongs to the root node. 85102e6817SAlexander V. Chernikov */ 86102e6817SAlexander V. Chernikov 87102e6817SAlexander V. Chernikov #define DEVCTL_DEFAULT_QUEUE_LEN 1000 88102e6817SAlexander V. Chernikov static int sysctl_devctl_queue(SYSCTL_HANDLER_ARGS); 89102e6817SAlexander V. Chernikov static int devctl_queue_length = DEVCTL_DEFAULT_QUEUE_LEN; 90102e6817SAlexander V. Chernikov SYSCTL_PROC(_hw_bus, OID_AUTO, devctl_queue, CTLTYPE_INT | CTLFLAG_RWTUN | 91102e6817SAlexander V. Chernikov CTLFLAG_MPSAFE, NULL, 0, sysctl_devctl_queue, "I", "devctl queue length"); 9297aedd33SWarner Losh static bool nomatch_enabled = true; 936437872cSIsaac Cilia Attard SYSCTL_BOOL(_hw_bus, OID_AUTO, devctl_nomatch_enabled, CTLFLAG_RWTUN, 946437872cSIsaac Cilia Attard &nomatch_enabled, 0, "enable nomatch events"); 95102e6817SAlexander V. Chernikov 96102e6817SAlexander V. Chernikov static void devctl_attach_handler(void *arg __unused, device_t dev); 97102e6817SAlexander V. Chernikov static void devctl_detach_handler(void *arg __unused, device_t dev, 98102e6817SAlexander V. Chernikov enum evhdev_detach state); 99102e6817SAlexander V. Chernikov static void devctl_nomatch_handler(void *arg __unused, device_t dev); 100102e6817SAlexander V. Chernikov 101102e6817SAlexander V. Chernikov static d_open_t devopen; 102102e6817SAlexander V. Chernikov static d_close_t devclose; 103102e6817SAlexander V. Chernikov static d_read_t devread; 104102e6817SAlexander V. Chernikov static d_ioctl_t devioctl; 105102e6817SAlexander V. Chernikov static d_poll_t devpoll; 106102e6817SAlexander V. Chernikov static d_kqfilter_t devkqfilter; 107102e6817SAlexander V. Chernikov 108102e6817SAlexander V. Chernikov #define DEVCTL_BUFFER (1024 - sizeof(void *)) 109102e6817SAlexander V. Chernikov struct dev_event_info { 110102e6817SAlexander V. Chernikov STAILQ_ENTRY(dev_event_info) dei_link; 111102e6817SAlexander V. Chernikov char dei_data[DEVCTL_BUFFER]; 112102e6817SAlexander V. Chernikov }; 113102e6817SAlexander V. Chernikov 114102e6817SAlexander V. Chernikov 115102e6817SAlexander V. Chernikov static struct cdevsw dev_cdevsw = { 116102e6817SAlexander V. Chernikov .d_version = D_VERSION, 117102e6817SAlexander V. Chernikov .d_open = devopen, 118102e6817SAlexander V. Chernikov .d_close = devclose, 119102e6817SAlexander V. Chernikov .d_read = devread, 120102e6817SAlexander V. Chernikov .d_ioctl = devioctl, 121102e6817SAlexander V. Chernikov .d_poll = devpoll, 122102e6817SAlexander V. Chernikov .d_kqfilter = devkqfilter, 123102e6817SAlexander V. Chernikov .d_name = "devctl", 124102e6817SAlexander V. Chernikov }; 125102e6817SAlexander V. Chernikov 126102e6817SAlexander V. Chernikov static void filt_devctl_detach(struct knote *kn); 127102e6817SAlexander V. Chernikov static int filt_devctl_read(struct knote *kn, long hint); 128102e6817SAlexander V. Chernikov 129*ef9ffb85SMark Johnston static const struct filterops devctl_rfiltops = { 130102e6817SAlexander V. Chernikov .f_isfd = 1, 131102e6817SAlexander V. Chernikov .f_detach = filt_devctl_detach, 132102e6817SAlexander V. Chernikov .f_event = filt_devctl_read, 133102e6817SAlexander V. Chernikov }; 134102e6817SAlexander V. Chernikov 135102e6817SAlexander V. Chernikov static struct cdev *devctl_dev; 136102e6817SAlexander V. Chernikov static void devaddq(const char *type, const char *what, device_t dev); 137102e6817SAlexander V. Chernikov 138afbb26b5SBaptiste Daroussin static struct devctlbridge { 139afbb26b5SBaptiste Daroussin send_event_f *send_f; 140afbb26b5SBaptiste Daroussin } devctl_notify_hook = { .send_f = NULL }; 141afbb26b5SBaptiste Daroussin 142102e6817SAlexander V. Chernikov static void 143102e6817SAlexander V. Chernikov devctl_init(void) 144102e6817SAlexander V. Chernikov { 145102e6817SAlexander V. Chernikov int reserve; 146102e6817SAlexander V. Chernikov uma_zone_t z; 147102e6817SAlexander V. Chernikov 148102e6817SAlexander V. Chernikov devctl_dev = make_dev_credf(MAKEDEV_ETERNAL, &dev_cdevsw, 0, NULL, 149102e6817SAlexander V. Chernikov UID_ROOT, GID_WHEEL, 0600, "devctl"); 150102e6817SAlexander V. Chernikov mtx_init(&devsoftc.mtx, "dev mtx", "devd", MTX_DEF); 151102e6817SAlexander V. Chernikov cv_init(&devsoftc.cv, "dev cv"); 152102e6817SAlexander V. Chernikov STAILQ_INIT(&devsoftc.devq); 153102e6817SAlexander V. Chernikov knlist_init_mtx(&devsoftc.sel.si_note, &devsoftc.mtx); 154102e6817SAlexander V. Chernikov if (devctl_queue_length > 0) { 155102e6817SAlexander V. Chernikov /* 156102e6817SAlexander V. Chernikov * Allocate a zone for the messages. Preallocate 2% of these for 157102e6817SAlexander V. Chernikov * a reserve. Allow only devctl_queue_length slabs to cap memory 158102e6817SAlexander V. Chernikov * usage. The reserve usually allows coverage of surges of 159102e6817SAlexander V. Chernikov * events during memory shortages. Normally we won't have to 160102e6817SAlexander V. Chernikov * re-use events from the queue, but will in extreme shortages. 161102e6817SAlexander V. Chernikov */ 162102e6817SAlexander V. Chernikov z = devsoftc.zone = uma_zcreate("DEVCTL", 163102e6817SAlexander V. Chernikov sizeof(struct dev_event_info), NULL, NULL, NULL, NULL, 164102e6817SAlexander V. Chernikov UMA_ALIGN_PTR, 0); 165102e6817SAlexander V. Chernikov reserve = max(devctl_queue_length / 50, 100); /* 2% reserve */ 166102e6817SAlexander V. Chernikov uma_zone_set_max(z, devctl_queue_length); 167102e6817SAlexander V. Chernikov uma_zone_set_maxcache(z, 0); 168102e6817SAlexander V. Chernikov uma_zone_reserve(z, reserve); 169102e6817SAlexander V. Chernikov uma_prealloc(z, reserve); 170102e6817SAlexander V. Chernikov } 171102e6817SAlexander V. Chernikov EVENTHANDLER_REGISTER(device_attach, devctl_attach_handler, 172102e6817SAlexander V. Chernikov NULL, EVENTHANDLER_PRI_LAST); 173102e6817SAlexander V. Chernikov EVENTHANDLER_REGISTER(device_detach, devctl_detach_handler, 174102e6817SAlexander V. Chernikov NULL, EVENTHANDLER_PRI_LAST); 175102e6817SAlexander V. Chernikov EVENTHANDLER_REGISTER(device_nomatch, devctl_nomatch_handler, 176102e6817SAlexander V. Chernikov NULL, EVENTHANDLER_PRI_LAST); 177102e6817SAlexander V. Chernikov } 178102e6817SAlexander V. Chernikov SYSINIT(devctl_init, SI_SUB_DRIVERS, SI_ORDER_SECOND, devctl_init, NULL); 179102e6817SAlexander V. Chernikov 180102e6817SAlexander V. Chernikov /* 181102e6817SAlexander V. Chernikov * A device was added to the tree. We are called just after it successfully 182102e6817SAlexander V. Chernikov * attaches (that is, probe and attach success for this device). No call 183102e6817SAlexander V. Chernikov * is made if a device is merely parented into the tree. See devnomatch 184102e6817SAlexander V. Chernikov * if probe fails. If attach fails, no notification is sent (but maybe 185102e6817SAlexander V. Chernikov * we should have a different message for this). 186102e6817SAlexander V. Chernikov */ 187102e6817SAlexander V. Chernikov static void 188102e6817SAlexander V. Chernikov devctl_attach_handler(void *arg __unused, device_t dev) 189102e6817SAlexander V. Chernikov { 190102e6817SAlexander V. Chernikov devaddq("+", device_get_nameunit(dev), dev); 191102e6817SAlexander V. Chernikov } 192102e6817SAlexander V. Chernikov 193102e6817SAlexander V. Chernikov /* 194102e6817SAlexander V. Chernikov * A device was removed from the tree. We are called just before this 195102e6817SAlexander V. Chernikov * happens. 196102e6817SAlexander V. Chernikov */ 197102e6817SAlexander V. Chernikov static void 198102e6817SAlexander V. Chernikov devctl_detach_handler(void *arg __unused, device_t dev, enum evhdev_detach state) 199102e6817SAlexander V. Chernikov { 200102e6817SAlexander V. Chernikov if (state == EVHDEV_DETACH_COMPLETE) 201102e6817SAlexander V. Chernikov devaddq("-", device_get_nameunit(dev), dev); 202102e6817SAlexander V. Chernikov } 203102e6817SAlexander V. Chernikov 204102e6817SAlexander V. Chernikov /* 205102e6817SAlexander V. Chernikov * Called when there's no match for this device. This is only called 206102e6817SAlexander V. Chernikov * the first time that no match happens, so we don't keep getting this 207102e6817SAlexander V. Chernikov * message. Should that prove to be undesirable, we can change it. 208102e6817SAlexander V. Chernikov * This is called when all drivers that can attach to a given bus 209102e6817SAlexander V. Chernikov * decline to accept this device. Other errors may not be detected. 210102e6817SAlexander V. Chernikov */ 211102e6817SAlexander V. Chernikov static void 212102e6817SAlexander V. Chernikov devctl_nomatch_handler(void *arg __unused, device_t dev) 213102e6817SAlexander V. Chernikov { 2146437872cSIsaac Cilia Attard if (nomatch_enabled) 215102e6817SAlexander V. Chernikov devaddq("?", "", dev); 216102e6817SAlexander V. Chernikov } 217102e6817SAlexander V. Chernikov 218102e6817SAlexander V. Chernikov static int 219102e6817SAlexander V. Chernikov devopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 220102e6817SAlexander V. Chernikov { 221102e6817SAlexander V. Chernikov mtx_lock(&devsoftc.mtx); 222102e6817SAlexander V. Chernikov if (devsoftc.inuse) { 223102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 224102e6817SAlexander V. Chernikov return (EBUSY); 225102e6817SAlexander V. Chernikov } 226102e6817SAlexander V. Chernikov /* move to init */ 227102e6817SAlexander V. Chernikov devsoftc.inuse = 1; 228102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 229102e6817SAlexander V. Chernikov return (0); 230102e6817SAlexander V. Chernikov } 231102e6817SAlexander V. Chernikov 232102e6817SAlexander V. Chernikov static int 233102e6817SAlexander V. Chernikov devclose(struct cdev *dev, int fflag, int devtype, struct thread *td) 234102e6817SAlexander V. Chernikov { 235102e6817SAlexander V. Chernikov mtx_lock(&devsoftc.mtx); 236102e6817SAlexander V. Chernikov devsoftc.inuse = 0; 237102e6817SAlexander V. Chernikov devsoftc.nonblock = 0; 238102e6817SAlexander V. Chernikov devsoftc.async = 0; 239102e6817SAlexander V. Chernikov cv_broadcast(&devsoftc.cv); 240102e6817SAlexander V. Chernikov funsetown(&devsoftc.sigio); 241102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 242102e6817SAlexander V. Chernikov return (0); 243102e6817SAlexander V. Chernikov } 244102e6817SAlexander V. Chernikov 245102e6817SAlexander V. Chernikov /* 246102e6817SAlexander V. Chernikov * The read channel for this device is used to report changes to 247102e6817SAlexander V. Chernikov * userland in realtime. We are required to free the data as well as 248102e6817SAlexander V. Chernikov * the n1 object because we allocate them separately. Also note that 249102e6817SAlexander V. Chernikov * we return one record at a time. If you try to read this device a 250102e6817SAlexander V. Chernikov * character at a time, you will lose the rest of the data. Listening 251102e6817SAlexander V. Chernikov * programs are expected to cope. 252102e6817SAlexander V. Chernikov */ 253102e6817SAlexander V. Chernikov static int 254102e6817SAlexander V. Chernikov devread(struct cdev *dev, struct uio *uio, int ioflag) 255102e6817SAlexander V. Chernikov { 256102e6817SAlexander V. Chernikov struct dev_event_info *n1; 257102e6817SAlexander V. Chernikov int rv; 258102e6817SAlexander V. Chernikov 259102e6817SAlexander V. Chernikov mtx_lock(&devsoftc.mtx); 260102e6817SAlexander V. Chernikov while (STAILQ_EMPTY(&devsoftc.devq)) { 261102e6817SAlexander V. Chernikov if (devsoftc.nonblock) { 262102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 263102e6817SAlexander V. Chernikov return (EAGAIN); 264102e6817SAlexander V. Chernikov } 265102e6817SAlexander V. Chernikov rv = cv_wait_sig(&devsoftc.cv, &devsoftc.mtx); 266102e6817SAlexander V. Chernikov if (rv) { 267102e6817SAlexander V. Chernikov /* 268102e6817SAlexander V. Chernikov * Need to translate ERESTART to EINTR here? -- jake 269102e6817SAlexander V. Chernikov */ 270102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 271102e6817SAlexander V. Chernikov return (rv); 272102e6817SAlexander V. Chernikov } 273102e6817SAlexander V. Chernikov } 274102e6817SAlexander V. Chernikov n1 = STAILQ_FIRST(&devsoftc.devq); 275102e6817SAlexander V. Chernikov STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); 276102e6817SAlexander V. Chernikov devsoftc.queued--; 277102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 278102e6817SAlexander V. Chernikov rv = uiomove(n1->dei_data, strlen(n1->dei_data), uio); 279102e6817SAlexander V. Chernikov uma_zfree(devsoftc.zone, n1); 280102e6817SAlexander V. Chernikov return (rv); 281102e6817SAlexander V. Chernikov } 282102e6817SAlexander V. Chernikov 283102e6817SAlexander V. Chernikov static int 284102e6817SAlexander V. Chernikov devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 285102e6817SAlexander V. Chernikov { 286102e6817SAlexander V. Chernikov switch (cmd) { 287102e6817SAlexander V. Chernikov case FIONBIO: 288102e6817SAlexander V. Chernikov if (*(int*)data) 289102e6817SAlexander V. Chernikov devsoftc.nonblock = 1; 290102e6817SAlexander V. Chernikov else 291102e6817SAlexander V. Chernikov devsoftc.nonblock = 0; 292102e6817SAlexander V. Chernikov return (0); 293102e6817SAlexander V. Chernikov case FIOASYNC: 294102e6817SAlexander V. Chernikov if (*(int*)data) 295102e6817SAlexander V. Chernikov devsoftc.async = 1; 296102e6817SAlexander V. Chernikov else 297102e6817SAlexander V. Chernikov devsoftc.async = 0; 298102e6817SAlexander V. Chernikov return (0); 299102e6817SAlexander V. Chernikov case FIOSETOWN: 300102e6817SAlexander V. Chernikov return fsetown(*(int *)data, &devsoftc.sigio); 301102e6817SAlexander V. Chernikov case FIOGETOWN: 302102e6817SAlexander V. Chernikov *(int *)data = fgetown(&devsoftc.sigio); 303102e6817SAlexander V. Chernikov return (0); 304102e6817SAlexander V. Chernikov 305102e6817SAlexander V. Chernikov /* (un)Support for other fcntl() calls. */ 306102e6817SAlexander V. Chernikov case FIOCLEX: 307102e6817SAlexander V. Chernikov case FIONCLEX: 308102e6817SAlexander V. Chernikov case FIONREAD: 309102e6817SAlexander V. Chernikov default: 310102e6817SAlexander V. Chernikov break; 311102e6817SAlexander V. Chernikov } 312102e6817SAlexander V. Chernikov return (ENOTTY); 313102e6817SAlexander V. Chernikov } 314102e6817SAlexander V. Chernikov 315102e6817SAlexander V. Chernikov static int 316102e6817SAlexander V. Chernikov devpoll(struct cdev *dev, int events, struct thread *td) 317102e6817SAlexander V. Chernikov { 318102e6817SAlexander V. Chernikov int revents = 0; 319102e6817SAlexander V. Chernikov 320102e6817SAlexander V. Chernikov mtx_lock(&devsoftc.mtx); 321102e6817SAlexander V. Chernikov if (events & (POLLIN | POLLRDNORM)) { 322102e6817SAlexander V. Chernikov if (!STAILQ_EMPTY(&devsoftc.devq)) 323102e6817SAlexander V. Chernikov revents = events & (POLLIN | POLLRDNORM); 324102e6817SAlexander V. Chernikov else 325102e6817SAlexander V. Chernikov selrecord(td, &devsoftc.sel); 326102e6817SAlexander V. Chernikov } 327102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 328102e6817SAlexander V. Chernikov 329102e6817SAlexander V. Chernikov return (revents); 330102e6817SAlexander V. Chernikov } 331102e6817SAlexander V. Chernikov 332102e6817SAlexander V. Chernikov static int 333102e6817SAlexander V. Chernikov devkqfilter(struct cdev *dev, struct knote *kn) 334102e6817SAlexander V. Chernikov { 335102e6817SAlexander V. Chernikov int error; 336102e6817SAlexander V. Chernikov 337102e6817SAlexander V. Chernikov if (kn->kn_filter == EVFILT_READ) { 338102e6817SAlexander V. Chernikov kn->kn_fop = &devctl_rfiltops; 339102e6817SAlexander V. Chernikov knlist_add(&devsoftc.sel.si_note, kn, 0); 340102e6817SAlexander V. Chernikov error = 0; 341102e6817SAlexander V. Chernikov } else 342102e6817SAlexander V. Chernikov error = EINVAL; 343102e6817SAlexander V. Chernikov return (error); 344102e6817SAlexander V. Chernikov } 345102e6817SAlexander V. Chernikov 346102e6817SAlexander V. Chernikov static void 347102e6817SAlexander V. Chernikov filt_devctl_detach(struct knote *kn) 348102e6817SAlexander V. Chernikov { 349102e6817SAlexander V. Chernikov knlist_remove(&devsoftc.sel.si_note, kn, 0); 350102e6817SAlexander V. Chernikov } 351102e6817SAlexander V. Chernikov 352102e6817SAlexander V. Chernikov static int 353102e6817SAlexander V. Chernikov filt_devctl_read(struct knote *kn, long hint) 354102e6817SAlexander V. Chernikov { 355102e6817SAlexander V. Chernikov kn->kn_data = devsoftc.queued; 356102e6817SAlexander V. Chernikov return (kn->kn_data != 0); 357102e6817SAlexander V. Chernikov } 358102e6817SAlexander V. Chernikov 359102e6817SAlexander V. Chernikov /** 360102e6817SAlexander V. Chernikov * @brief Return whether the userland process is running 361102e6817SAlexander V. Chernikov */ 362102e6817SAlexander V. Chernikov bool 363102e6817SAlexander V. Chernikov devctl_process_running(void) 364102e6817SAlexander V. Chernikov { 365102e6817SAlexander V. Chernikov return (devsoftc.inuse == 1); 366102e6817SAlexander V. Chernikov } 367102e6817SAlexander V. Chernikov 368102e6817SAlexander V. Chernikov static struct dev_event_info * 369102e6817SAlexander V. Chernikov devctl_alloc_dei(void) 370102e6817SAlexander V. Chernikov { 371102e6817SAlexander V. Chernikov struct dev_event_info *dei = NULL; 372102e6817SAlexander V. Chernikov 373102e6817SAlexander V. Chernikov mtx_lock(&devsoftc.mtx); 374102e6817SAlexander V. Chernikov if (devctl_queue_length == 0) 375102e6817SAlexander V. Chernikov goto out; 376102e6817SAlexander V. Chernikov dei = uma_zalloc(devsoftc.zone, M_NOWAIT); 377102e6817SAlexander V. Chernikov if (dei == NULL) 378102e6817SAlexander V. Chernikov dei = uma_zalloc(devsoftc.zone, M_NOWAIT | M_USE_RESERVE); 379102e6817SAlexander V. Chernikov if (dei == NULL) { 380102e6817SAlexander V. Chernikov /* 381102e6817SAlexander V. Chernikov * Guard against no items in the queue. Normally, this won't 382102e6817SAlexander V. Chernikov * happen, but if lots of events happen all at once and there's 383102e6817SAlexander V. Chernikov * a chance we're out of allocated space but none have yet been 384102e6817SAlexander V. Chernikov * queued when we get here, leaving nothing to steal. This can 385102e6817SAlexander V. Chernikov * also happen with error injection. Fail safe by returning 386102e6817SAlexander V. Chernikov * NULL in that case.. 387102e6817SAlexander V. Chernikov */ 388102e6817SAlexander V. Chernikov if (devsoftc.queued == 0) 389102e6817SAlexander V. Chernikov goto out; 390102e6817SAlexander V. Chernikov dei = STAILQ_FIRST(&devsoftc.devq); 391102e6817SAlexander V. Chernikov STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); 392102e6817SAlexander V. Chernikov devsoftc.queued--; 393102e6817SAlexander V. Chernikov } 394102e6817SAlexander V. Chernikov MPASS(dei != NULL); 395102e6817SAlexander V. Chernikov *dei->dei_data = '\0'; 396102e6817SAlexander V. Chernikov out: 397102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 398102e6817SAlexander V. Chernikov return (dei); 399102e6817SAlexander V. Chernikov } 400102e6817SAlexander V. Chernikov 401102e6817SAlexander V. Chernikov static struct dev_event_info * 402102e6817SAlexander V. Chernikov devctl_alloc_dei_sb(struct sbuf *sb) 403102e6817SAlexander V. Chernikov { 404102e6817SAlexander V. Chernikov struct dev_event_info *dei; 405102e6817SAlexander V. Chernikov 406102e6817SAlexander V. Chernikov dei = devctl_alloc_dei(); 407102e6817SAlexander V. Chernikov if (dei != NULL) 408102e6817SAlexander V. Chernikov sbuf_new(sb, dei->dei_data, sizeof(dei->dei_data), SBUF_FIXEDLEN); 409102e6817SAlexander V. Chernikov return (dei); 410102e6817SAlexander V. Chernikov } 411102e6817SAlexander V. Chernikov 412102e6817SAlexander V. Chernikov static void 413102e6817SAlexander V. Chernikov devctl_free_dei(struct dev_event_info *dei) 414102e6817SAlexander V. Chernikov { 415102e6817SAlexander V. Chernikov uma_zfree(devsoftc.zone, dei); 416102e6817SAlexander V. Chernikov } 417102e6817SAlexander V. Chernikov 418102e6817SAlexander V. Chernikov static void 419102e6817SAlexander V. Chernikov devctl_queue(struct dev_event_info *dei) 420102e6817SAlexander V. Chernikov { 421102e6817SAlexander V. Chernikov mtx_lock(&devsoftc.mtx); 422102e6817SAlexander V. Chernikov STAILQ_INSERT_TAIL(&devsoftc.devq, dei, dei_link); 423102e6817SAlexander V. Chernikov devsoftc.queued++; 424102e6817SAlexander V. Chernikov cv_broadcast(&devsoftc.cv); 425102e6817SAlexander V. Chernikov KNOTE_LOCKED(&devsoftc.sel.si_note, 0); 426102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 427102e6817SAlexander V. Chernikov selwakeup(&devsoftc.sel); 428102e6817SAlexander V. Chernikov if (devsoftc.async && devsoftc.sigio != NULL) 429102e6817SAlexander V. Chernikov pgsigio(&devsoftc.sigio, SIGIO, 0); 430102e6817SAlexander V. Chernikov } 431102e6817SAlexander V. Chernikov 432102e6817SAlexander V. Chernikov /** 433102e6817SAlexander V. Chernikov * @brief Send a 'notification' to userland, using standard ways 434102e6817SAlexander V. Chernikov */ 435102e6817SAlexander V. Chernikov void 436102e6817SAlexander V. Chernikov devctl_notify(const char *system, const char *subsystem, const char *type, 437102e6817SAlexander V. Chernikov const char *data) 438102e6817SAlexander V. Chernikov { 439102e6817SAlexander V. Chernikov struct dev_event_info *dei; 440102e6817SAlexander V. Chernikov struct sbuf sb; 441102e6817SAlexander V. Chernikov 442102e6817SAlexander V. Chernikov if (system == NULL || subsystem == NULL || type == NULL) 443102e6817SAlexander V. Chernikov return; 444afbb26b5SBaptiste Daroussin if (devctl_notify_hook.send_f != NULL) 445afbb26b5SBaptiste Daroussin devctl_notify_hook.send_f(system, subsystem, type, data); 446102e6817SAlexander V. Chernikov dei = devctl_alloc_dei_sb(&sb); 447102e6817SAlexander V. Chernikov if (dei == NULL) 448102e6817SAlexander V. Chernikov return; 449102e6817SAlexander V. Chernikov sbuf_cpy(&sb, "!system="); 450102e6817SAlexander V. Chernikov sbuf_cat(&sb, system); 451102e6817SAlexander V. Chernikov sbuf_cat(&sb, " subsystem="); 452102e6817SAlexander V. Chernikov sbuf_cat(&sb, subsystem); 453102e6817SAlexander V. Chernikov sbuf_cat(&sb, " type="); 454102e6817SAlexander V. Chernikov sbuf_cat(&sb, type); 455102e6817SAlexander V. Chernikov if (data != NULL) { 456102e6817SAlexander V. Chernikov sbuf_putc(&sb, ' '); 457102e6817SAlexander V. Chernikov sbuf_cat(&sb, data); 458102e6817SAlexander V. Chernikov } 459102e6817SAlexander V. Chernikov sbuf_putc(&sb, '\n'); 460102e6817SAlexander V. Chernikov if (sbuf_finish(&sb) != 0) 461102e6817SAlexander V. Chernikov devctl_free_dei(dei); /* overflow -> drop it */ 462102e6817SAlexander V. Chernikov else 463102e6817SAlexander V. Chernikov devctl_queue(dei); 464102e6817SAlexander V. Chernikov } 465102e6817SAlexander V. Chernikov 466102e6817SAlexander V. Chernikov /* 467102e6817SAlexander V. Chernikov * Common routine that tries to make sending messages as easy as possible. 468102e6817SAlexander V. Chernikov * We allocate memory for the data, copy strings into that, but do not 469102e6817SAlexander V. Chernikov * free it unless there's an error. The dequeue part of the driver should 470102e6817SAlexander V. Chernikov * free the data. We don't send data when the device is disabled. We do 471102e6817SAlexander V. Chernikov * send data, even when we have no listeners, because we wish to avoid 472102e6817SAlexander V. Chernikov * races relating to startup and restart of listening applications. 473102e6817SAlexander V. Chernikov * 474102e6817SAlexander V. Chernikov * devaddq is designed to string together the type of event, with the 475102e6817SAlexander V. Chernikov * object of that event, plus the plug and play info and location info 476102e6817SAlexander V. Chernikov * for that event. This is likely most useful for devices, but less 477102e6817SAlexander V. Chernikov * useful for other consumers of this interface. Those should use 478102e6817SAlexander V. Chernikov * the devctl_notify() interface instead. 479102e6817SAlexander V. Chernikov * 480102e6817SAlexander V. Chernikov * Output: 481102e6817SAlexander V. Chernikov * ${type}${what} at $(location dev) $(pnp-info dev) on $(parent dev) 482102e6817SAlexander V. Chernikov */ 483102e6817SAlexander V. Chernikov static void 484102e6817SAlexander V. Chernikov devaddq(const char *type, const char *what, device_t dev) 485102e6817SAlexander V. Chernikov { 486102e6817SAlexander V. Chernikov struct dev_event_info *dei; 487102e6817SAlexander V. Chernikov const char *parstr; 488102e6817SAlexander V. Chernikov struct sbuf sb; 489afbb26b5SBaptiste Daroussin size_t beginlen; 490102e6817SAlexander V. Chernikov 491102e6817SAlexander V. Chernikov dei = devctl_alloc_dei_sb(&sb); 492102e6817SAlexander V. Chernikov if (dei == NULL) 493102e6817SAlexander V. Chernikov return; 494102e6817SAlexander V. Chernikov sbuf_cpy(&sb, type); 495102e6817SAlexander V. Chernikov sbuf_cat(&sb, what); 496102e6817SAlexander V. Chernikov sbuf_cat(&sb, " at "); 497afbb26b5SBaptiste Daroussin beginlen = sbuf_len(&sb); 498102e6817SAlexander V. Chernikov 499102e6817SAlexander V. Chernikov /* Add in the location */ 500102e6817SAlexander V. Chernikov bus_child_location(dev, &sb); 501102e6817SAlexander V. Chernikov sbuf_putc(&sb, ' '); 502102e6817SAlexander V. Chernikov 503102e6817SAlexander V. Chernikov /* Add in pnpinfo */ 504102e6817SAlexander V. Chernikov bus_child_pnpinfo(dev, &sb); 505102e6817SAlexander V. Chernikov 506102e6817SAlexander V. Chernikov /* Get the parent of this device, or / if high enough in the tree. */ 507102e6817SAlexander V. Chernikov if (device_get_parent(dev) == NULL) 508102e6817SAlexander V. Chernikov parstr = "."; /* Or '/' ? */ 509102e6817SAlexander V. Chernikov else 510102e6817SAlexander V. Chernikov parstr = device_get_nameunit(device_get_parent(dev)); 511102e6817SAlexander V. Chernikov sbuf_cat(&sb, " on "); 512102e6817SAlexander V. Chernikov sbuf_cat(&sb, parstr); 513102e6817SAlexander V. Chernikov sbuf_putc(&sb, '\n'); 514102e6817SAlexander V. Chernikov if (sbuf_finish(&sb) != 0) 515102e6817SAlexander V. Chernikov goto bad; 516afbb26b5SBaptiste Daroussin if (devctl_notify_hook.send_f != NULL) { 517afbb26b5SBaptiste Daroussin const char *t; 518afbb26b5SBaptiste Daroussin 519afbb26b5SBaptiste Daroussin switch (*type) { 520afbb26b5SBaptiste Daroussin case '+': 521afbb26b5SBaptiste Daroussin t = "ATTACH"; 522afbb26b5SBaptiste Daroussin break; 523afbb26b5SBaptiste Daroussin case '-': 524afbb26b5SBaptiste Daroussin t = "DETACH"; 525afbb26b5SBaptiste Daroussin break; 526afbb26b5SBaptiste Daroussin default: 527afbb26b5SBaptiste Daroussin t = "NOMATCH"; 528afbb26b5SBaptiste Daroussin break; 529afbb26b5SBaptiste Daroussin } 530afbb26b5SBaptiste Daroussin devctl_notify_hook.send_f("device", 531afbb26b5SBaptiste Daroussin what, t, sbuf_data(&sb) + beginlen); 532afbb26b5SBaptiste Daroussin } 533102e6817SAlexander V. Chernikov devctl_queue(dei); 534102e6817SAlexander V. Chernikov return; 535102e6817SAlexander V. Chernikov bad: 536102e6817SAlexander V. Chernikov devctl_free_dei(dei); 537102e6817SAlexander V. Chernikov } 538102e6817SAlexander V. Chernikov 539102e6817SAlexander V. Chernikov static int 540102e6817SAlexander V. Chernikov sysctl_devctl_queue(SYSCTL_HANDLER_ARGS) 541102e6817SAlexander V. Chernikov { 542102e6817SAlexander V. Chernikov int q, error; 543102e6817SAlexander V. Chernikov 544102e6817SAlexander V. Chernikov q = devctl_queue_length; 545102e6817SAlexander V. Chernikov error = sysctl_handle_int(oidp, &q, 0, req); 546102e6817SAlexander V. Chernikov if (error || !req->newptr) 547102e6817SAlexander V. Chernikov return (error); 548102e6817SAlexander V. Chernikov if (q < 0) 549102e6817SAlexander V. Chernikov return (EINVAL); 550102e6817SAlexander V. Chernikov 551102e6817SAlexander V. Chernikov /* 552102e6817SAlexander V. Chernikov * When set as a tunable, we've not yet initialized the mutex. 553102e6817SAlexander V. Chernikov * It is safe to just assign to devctl_queue_length and return 554102e6817SAlexander V. Chernikov * as we're racing no one. We'll use whatever value set in 555102e6817SAlexander V. Chernikov * devinit. 556102e6817SAlexander V. Chernikov */ 557102e6817SAlexander V. Chernikov if (!mtx_initialized(&devsoftc.mtx)) { 558102e6817SAlexander V. Chernikov devctl_queue_length = q; 559102e6817SAlexander V. Chernikov return (0); 560102e6817SAlexander V. Chernikov } 561102e6817SAlexander V. Chernikov 562102e6817SAlexander V. Chernikov /* 563102e6817SAlexander V. Chernikov * XXX It's hard to grow or shrink the UMA zone. Only allow 564102e6817SAlexander V. Chernikov * disabling the queue size for the moment until underlying 565102e6817SAlexander V. Chernikov * UMA issues can be sorted out. 566102e6817SAlexander V. Chernikov */ 567102e6817SAlexander V. Chernikov if (q != 0) 568102e6817SAlexander V. Chernikov return (EINVAL); 569102e6817SAlexander V. Chernikov if (q == devctl_queue_length) 570102e6817SAlexander V. Chernikov return (0); 571102e6817SAlexander V. Chernikov mtx_lock(&devsoftc.mtx); 572102e6817SAlexander V. Chernikov devctl_queue_length = 0; 573102e6817SAlexander V. Chernikov uma_zdestroy(devsoftc.zone); 574102e6817SAlexander V. Chernikov devsoftc.zone = 0; 575102e6817SAlexander V. Chernikov mtx_unlock(&devsoftc.mtx); 576102e6817SAlexander V. Chernikov return (0); 577102e6817SAlexander V. Chernikov } 578102e6817SAlexander V. Chernikov 579102e6817SAlexander V. Chernikov /** 580102e6817SAlexander V. Chernikov * @brief safely quotes strings that might have double quotes in them. 581102e6817SAlexander V. Chernikov * 582102e6817SAlexander V. Chernikov * The devctl protocol relies on quoted strings having matching quotes. 583102e6817SAlexander V. Chernikov * This routine quotes any internal quotes so the resulting string 584102e6817SAlexander V. Chernikov * is safe to pass to snprintf to construct, for example pnp info strings. 585102e6817SAlexander V. Chernikov * 586102e6817SAlexander V. Chernikov * @param sb sbuf to place the characters into 587102e6817SAlexander V. Chernikov * @param src Original buffer. 588102e6817SAlexander V. Chernikov */ 589102e6817SAlexander V. Chernikov void 590102e6817SAlexander V. Chernikov devctl_safe_quote_sb(struct sbuf *sb, const char *src) 591102e6817SAlexander V. Chernikov { 592102e6817SAlexander V. Chernikov while (*src != '\0') { 593102e6817SAlexander V. Chernikov if (*src == '"' || *src == '\\') 594102e6817SAlexander V. Chernikov sbuf_putc(sb, '\\'); 595102e6817SAlexander V. Chernikov sbuf_putc(sb, *src++); 596102e6817SAlexander V. Chernikov } 597102e6817SAlexander V. Chernikov } 598afbb26b5SBaptiste Daroussin 599afbb26b5SBaptiste Daroussin void 600afbb26b5SBaptiste Daroussin devctl_set_notify_hook(send_event_f *hook) 601afbb26b5SBaptiste Daroussin { 602afbb26b5SBaptiste Daroussin devctl_notify_hook.send_f = hook; 603afbb26b5SBaptiste Daroussin } 604afbb26b5SBaptiste Daroussin 605afbb26b5SBaptiste Daroussin void 606afbb26b5SBaptiste Daroussin devctl_unset_notify_hook(void) 607afbb26b5SBaptiste Daroussin { 608afbb26b5SBaptiste Daroussin devctl_notify_hook.send_f = NULL; 609afbb26b5SBaptiste Daroussin } 610afbb26b5SBaptiste Daroussin 611