1*b9ae17a0Sguenther /* $OpenBSD: hotplug.c,v 1.25 2024/12/30 02:46:00 guenther Exp $ */ 2cfd0487fSgrange /* 3cfd0487fSgrange * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> 4cfd0487fSgrange * 5cfd0487fSgrange * Permission to use, copy, modify, and distribute this software for any 6cfd0487fSgrange * purpose with or without fee is hereby granted, provided that the above 7cfd0487fSgrange * copyright notice and this permission notice appear in all copies. 8cfd0487fSgrange * 9cfd0487fSgrange * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10cfd0487fSgrange * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11cfd0487fSgrange * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12cfd0487fSgrange * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13cfd0487fSgrange * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14cfd0487fSgrange * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15cfd0487fSgrange * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16cfd0487fSgrange */ 17cfd0487fSgrange 18cfd0487fSgrange /* 19cfd0487fSgrange * Device attachment and detachment notifications. 20cfd0487fSgrange */ 21cfd0487fSgrange 22cfd0487fSgrange #include <sys/param.h> 23cfd0487fSgrange #include <sys/systm.h> 24cfd0487fSgrange #include <sys/device.h> 259e667ebcSmvs #include <sys/event.h> 26cfd0487fSgrange #include <sys/fcntl.h> 27cfd0487fSgrange #include <sys/hotplug.h> 28cfd0487fSgrange #include <sys/ioctl.h> 299e667ebcSmvs #include <sys/mutex.h> 30cfd0487fSgrange #include <sys/vnode.h> 31cfd0487fSgrange 32ccee5369Stedu #define HOTPLUG_MAXEVENTS 64 33cfd0487fSgrange 349e667ebcSmvs /* 359e667ebcSmvs * Locks used to protect struct members and global data 369e667ebcSmvs * M hotplug_mtx 379e667ebcSmvs */ 389e667ebcSmvs 399e667ebcSmvs static struct mutex hotplug_mtx = MUTEX_INITIALIZER(IPL_MPFLOOR); 409e667ebcSmvs 41cfd0487fSgrange static int opened; 42d14ac25bStedu static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS]; 439e667ebcSmvs static int evqueue_head, evqueue_tail, evqueue_count; /* [M] */ 449e667ebcSmvs static struct klist hotplug_klist; /* [M] */ 45cfd0487fSgrange 46cfd0487fSgrange void filt_hotplugrdetach(struct knote *); 47cfd0487fSgrange int filt_hotplugread(struct knote *, long); 489e667ebcSmvs int filt_hotplugmodify(struct kevent *, struct knote *); 499e667ebcSmvs int filt_hotplugprocess(struct knote *, struct kevent *); 50cfd0487fSgrange 5194321eb4Svisa const struct filterops hotplugread_filtops = { 529e667ebcSmvs .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE, 5394321eb4Svisa .f_attach = NULL, 5494321eb4Svisa .f_detach = filt_hotplugrdetach, 5594321eb4Svisa .f_event = filt_hotplugread, 569e667ebcSmvs .f_modify = filt_hotplugmodify, 579e667ebcSmvs .f_process = filt_hotplugprocess, 5894321eb4Svisa }; 59cfd0487fSgrange 60cfd0487fSgrange #define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1) 61cfd0487fSgrange 62cfd0487fSgrange 63cfd0487fSgrange int hotplug_put_event(struct hotplug_event *); 64cfd0487fSgrange int hotplug_get_event(struct hotplug_event *); 65cfd0487fSgrange 66cfd0487fSgrange void hotplugattach(int); 67cfd0487fSgrange 68cfd0487fSgrange void 69cfd0487fSgrange hotplugattach(int count) 70cfd0487fSgrange { 71cfd0487fSgrange opened = 0; 72cfd0487fSgrange evqueue_head = 0; 73cfd0487fSgrange evqueue_tail = 0; 74cfd0487fSgrange evqueue_count = 0; 759e667ebcSmvs 769e667ebcSmvs klist_init_mutex(&hotplug_klist, &hotplug_mtx); 77cfd0487fSgrange } 78cfd0487fSgrange 79cfd0487fSgrange void 807c80f029Smk hotplug_device_attach(enum devclass class, char *name) 81cfd0487fSgrange { 82cfd0487fSgrange struct hotplug_event he; 83cfd0487fSgrange 84cfd0487fSgrange he.he_type = HOTPLUG_DEVAT; 85cfd0487fSgrange he.he_devclass = class; 86cfd0487fSgrange strlcpy(he.he_devname, name, sizeof(he.he_devname)); 87cfd0487fSgrange hotplug_put_event(&he); 88cfd0487fSgrange } 89cfd0487fSgrange 90cfd0487fSgrange void 917c80f029Smk hotplug_device_detach(enum devclass class, char *name) 92cfd0487fSgrange { 93cfd0487fSgrange struct hotplug_event he; 94cfd0487fSgrange 95cfd0487fSgrange he.he_type = HOTPLUG_DEVDT; 96cfd0487fSgrange he.he_devclass = class; 97cfd0487fSgrange strlcpy(he.he_devname, name, sizeof(he.he_devname)); 98cfd0487fSgrange hotplug_put_event(&he); 99cfd0487fSgrange } 100cfd0487fSgrange 101cfd0487fSgrange int 102cfd0487fSgrange hotplug_put_event(struct hotplug_event *he) 103cfd0487fSgrange { 1049e667ebcSmvs mtx_enter(&hotplug_mtx); 105d6be6782Sgrange if (evqueue_count == HOTPLUG_MAXEVENTS && opened) { 1069e667ebcSmvs mtx_leave(&hotplug_mtx); 107cfd0487fSgrange printf("hotplug: event lost, queue full\n"); 108cfd0487fSgrange return (1); 109cfd0487fSgrange } 110cfd0487fSgrange 111cfd0487fSgrange evqueue[evqueue_head] = *he; 112cfd0487fSgrange evqueue_head = EVQUEUE_NEXT(evqueue_head); 1138928c497Sdrahn if (evqueue_count == HOTPLUG_MAXEVENTS) 1148928c497Sdrahn evqueue_tail = EVQUEUE_NEXT(evqueue_tail); 1158928c497Sdrahn else 116cfd0487fSgrange evqueue_count++; 1179e667ebcSmvs knote_locked(&hotplug_klist, 0); 118cfd0487fSgrange wakeup(&evqueue); 1199e667ebcSmvs mtx_leave(&hotplug_mtx); 120cfd0487fSgrange return (0); 121cfd0487fSgrange } 122cfd0487fSgrange 123cfd0487fSgrange int 124cfd0487fSgrange hotplug_get_event(struct hotplug_event *he) 125cfd0487fSgrange { 126cfd0487fSgrange if (evqueue_count == 0) 127cfd0487fSgrange return (1); 128cfd0487fSgrange 129cfd0487fSgrange *he = evqueue[evqueue_tail]; 130cfd0487fSgrange evqueue_tail = EVQUEUE_NEXT(evqueue_tail); 131cfd0487fSgrange evqueue_count--; 132cfd0487fSgrange return (0); 133cfd0487fSgrange } 134cfd0487fSgrange 135cfd0487fSgrange int 136cfd0487fSgrange hotplugopen(dev_t dev, int flag, int mode, struct proc *p) 137cfd0487fSgrange { 138cfd0487fSgrange if (minor(dev) != 0) 139cfd0487fSgrange return (ENXIO); 140cfd0487fSgrange if ((flag & FWRITE)) 141cfd0487fSgrange return (EPERM); 142cfd0487fSgrange if (opened) 143cfd0487fSgrange return (EBUSY); 144cfd0487fSgrange opened = 1; 145cfd0487fSgrange return (0); 146cfd0487fSgrange } 147cfd0487fSgrange 148cfd0487fSgrange int 149cfd0487fSgrange hotplugclose(dev_t dev, int flag, int mode, struct proc *p) 150cfd0487fSgrange { 15108fbe7e7Sderaadt struct hotplug_event he; 15208fbe7e7Sderaadt 1539e667ebcSmvs mtx_enter(&hotplug_mtx); 154b6a3a3ddSmiod while (hotplug_get_event(&he) == 0) 15542fa0292Stedu continue; 1569e667ebcSmvs mtx_leave(&hotplug_mtx); 1579e667ebcSmvs klist_invalidate(&hotplug_klist); 158cfd0487fSgrange opened = 0; 159cfd0487fSgrange return (0); 160cfd0487fSgrange } 161cfd0487fSgrange 162cfd0487fSgrange int 163cfd0487fSgrange hotplugread(dev_t dev, struct uio *uio, int flags) 164cfd0487fSgrange { 165cfd0487fSgrange struct hotplug_event he; 166cfd0487fSgrange int error; 167cfd0487fSgrange 168cfd0487fSgrange if (uio->uio_resid != sizeof(he)) 169cfd0487fSgrange return (EINVAL); 170cfd0487fSgrange 1719e667ebcSmvs mtx_enter(&hotplug_mtx); 1729e667ebcSmvs while (hotplug_get_event(&he)) { 1739e667ebcSmvs if (flags & IO_NDELAY) { 1749e667ebcSmvs mtx_leave(&hotplug_mtx); 175cfd0487fSgrange return (EAGAIN); 1769e667ebcSmvs } 177cfd0487fSgrange 1789e667ebcSmvs error = msleep_nsec(&evqueue, &hotplug_mtx, PRIBIO | PCATCH, 1799e667ebcSmvs "htplev", INFSLP); 1809e667ebcSmvs if (error) { 1819e667ebcSmvs mtx_leave(&hotplug_mtx); 182cfd0487fSgrange return (error); 1839e667ebcSmvs } 1849e667ebcSmvs } 1859e667ebcSmvs mtx_leave(&hotplug_mtx); 1869e667ebcSmvs 1879e667ebcSmvs return (uiomove(&he, sizeof(he), uio)); 188cfd0487fSgrange } 189cfd0487fSgrange 190cfd0487fSgrange int 191cfd0487fSgrange hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 192cfd0487fSgrange { 193cfd0487fSgrange switch (cmd) { 194cfd0487fSgrange case FIOASYNC: 195cfd0487fSgrange /* ignore */ 196cfd0487fSgrange default: 197cfd0487fSgrange return (ENOTTY); 198cfd0487fSgrange } 199cfd0487fSgrange 200cfd0487fSgrange return (0); 201cfd0487fSgrange } 202cfd0487fSgrange 203cfd0487fSgrange int 204cfd0487fSgrange hotplugkqfilter(dev_t dev, struct knote *kn) 205cfd0487fSgrange { 206cfd0487fSgrange switch (kn->kn_filter) { 207cfd0487fSgrange case EVFILT_READ: 208cfd0487fSgrange kn->kn_fop = &hotplugread_filtops; 209cfd0487fSgrange break; 210cfd0487fSgrange default: 211b8d5a5fbSnicm return (EINVAL); 212cfd0487fSgrange } 213cfd0487fSgrange 2149e667ebcSmvs klist_insert(&hotplug_klist, kn); 215cfd0487fSgrange return (0); 216cfd0487fSgrange } 217cfd0487fSgrange 218cfd0487fSgrange void 219cfd0487fSgrange filt_hotplugrdetach(struct knote *kn) 220cfd0487fSgrange { 2219e667ebcSmvs klist_remove(&hotplug_klist, kn); 222cfd0487fSgrange } 223cfd0487fSgrange 224cfd0487fSgrange int 225cfd0487fSgrange filt_hotplugread(struct knote *kn, long hint) 226cfd0487fSgrange { 227cfd0487fSgrange kn->kn_data = evqueue_count; 228cfd0487fSgrange 229cfd0487fSgrange return (evqueue_count > 0); 230cfd0487fSgrange } 2319e667ebcSmvs 2329e667ebcSmvs int 2339e667ebcSmvs filt_hotplugmodify(struct kevent *kev, struct knote *kn) 2349e667ebcSmvs { 2359e667ebcSmvs int active; 2369e667ebcSmvs 2379e667ebcSmvs mtx_enter(&hotplug_mtx); 2389e667ebcSmvs active = knote_modify(kev, kn); 2399e667ebcSmvs mtx_leave(&hotplug_mtx); 2409e667ebcSmvs 2419e667ebcSmvs return (active); 2429e667ebcSmvs } 2439e667ebcSmvs 2449e667ebcSmvs int 2459e667ebcSmvs filt_hotplugprocess(struct knote *kn, struct kevent *kev) 2469e667ebcSmvs { 2479e667ebcSmvs int active; 2489e667ebcSmvs 2499e667ebcSmvs mtx_enter(&hotplug_mtx); 2509e667ebcSmvs active = knote_process(kn, kev); 2519e667ebcSmvs mtx_leave(&hotplug_mtx); 2529e667ebcSmvs 2539e667ebcSmvs return (active); 2549e667ebcSmvs } 255