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