1 /* $OpenBSD: hotplug.c,v 1.25 2024/12/30 02:46:00 guenther Exp $ */ 2 /* 3 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* 19 * Device attachment and detachment notifications. 20 */ 21 22 #include <sys/param.h> 23 #include <sys/systm.h> 24 #include <sys/device.h> 25 #include <sys/event.h> 26 #include <sys/fcntl.h> 27 #include <sys/hotplug.h> 28 #include <sys/ioctl.h> 29 #include <sys/mutex.h> 30 #include <sys/vnode.h> 31 32 #define HOTPLUG_MAXEVENTS 64 33 34 /* 35 * Locks used to protect struct members and global data 36 * M hotplug_mtx 37 */ 38 39 static struct mutex hotplug_mtx = MUTEX_INITIALIZER(IPL_MPFLOOR); 40 41 static int opened; 42 static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS]; 43 static int evqueue_head, evqueue_tail, evqueue_count; /* [M] */ 44 static struct klist hotplug_klist; /* [M] */ 45 46 void filt_hotplugrdetach(struct knote *); 47 int filt_hotplugread(struct knote *, long); 48 int filt_hotplugmodify(struct kevent *, struct knote *); 49 int filt_hotplugprocess(struct knote *, struct kevent *); 50 51 const struct filterops hotplugread_filtops = { 52 .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE, 53 .f_attach = NULL, 54 .f_detach = filt_hotplugrdetach, 55 .f_event = filt_hotplugread, 56 .f_modify = filt_hotplugmodify, 57 .f_process = filt_hotplugprocess, 58 }; 59 60 #define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1) 61 62 63 int hotplug_put_event(struct hotplug_event *); 64 int hotplug_get_event(struct hotplug_event *); 65 66 void hotplugattach(int); 67 68 void 69 hotplugattach(int count) 70 { 71 opened = 0; 72 evqueue_head = 0; 73 evqueue_tail = 0; 74 evqueue_count = 0; 75 76 klist_init_mutex(&hotplug_klist, &hotplug_mtx); 77 } 78 79 void 80 hotplug_device_attach(enum devclass class, char *name) 81 { 82 struct hotplug_event he; 83 84 he.he_type = HOTPLUG_DEVAT; 85 he.he_devclass = class; 86 strlcpy(he.he_devname, name, sizeof(he.he_devname)); 87 hotplug_put_event(&he); 88 } 89 90 void 91 hotplug_device_detach(enum devclass class, char *name) 92 { 93 struct hotplug_event he; 94 95 he.he_type = HOTPLUG_DEVDT; 96 he.he_devclass = class; 97 strlcpy(he.he_devname, name, sizeof(he.he_devname)); 98 hotplug_put_event(&he); 99 } 100 101 int 102 hotplug_put_event(struct hotplug_event *he) 103 { 104 mtx_enter(&hotplug_mtx); 105 if (evqueue_count == HOTPLUG_MAXEVENTS && opened) { 106 mtx_leave(&hotplug_mtx); 107 printf("hotplug: event lost, queue full\n"); 108 return (1); 109 } 110 111 evqueue[evqueue_head] = *he; 112 evqueue_head = EVQUEUE_NEXT(evqueue_head); 113 if (evqueue_count == HOTPLUG_MAXEVENTS) 114 evqueue_tail = EVQUEUE_NEXT(evqueue_tail); 115 else 116 evqueue_count++; 117 knote_locked(&hotplug_klist, 0); 118 wakeup(&evqueue); 119 mtx_leave(&hotplug_mtx); 120 return (0); 121 } 122 123 int 124 hotplug_get_event(struct hotplug_event *he) 125 { 126 if (evqueue_count == 0) 127 return (1); 128 129 *he = evqueue[evqueue_tail]; 130 evqueue_tail = EVQUEUE_NEXT(evqueue_tail); 131 evqueue_count--; 132 return (0); 133 } 134 135 int 136 hotplugopen(dev_t dev, int flag, int mode, struct proc *p) 137 { 138 if (minor(dev) != 0) 139 return (ENXIO); 140 if ((flag & FWRITE)) 141 return (EPERM); 142 if (opened) 143 return (EBUSY); 144 opened = 1; 145 return (0); 146 } 147 148 int 149 hotplugclose(dev_t dev, int flag, int mode, struct proc *p) 150 { 151 struct hotplug_event he; 152 153 mtx_enter(&hotplug_mtx); 154 while (hotplug_get_event(&he) == 0) 155 continue; 156 mtx_leave(&hotplug_mtx); 157 klist_invalidate(&hotplug_klist); 158 opened = 0; 159 return (0); 160 } 161 162 int 163 hotplugread(dev_t dev, struct uio *uio, int flags) 164 { 165 struct hotplug_event he; 166 int error; 167 168 if (uio->uio_resid != sizeof(he)) 169 return (EINVAL); 170 171 mtx_enter(&hotplug_mtx); 172 while (hotplug_get_event(&he)) { 173 if (flags & IO_NDELAY) { 174 mtx_leave(&hotplug_mtx); 175 return (EAGAIN); 176 } 177 178 error = msleep_nsec(&evqueue, &hotplug_mtx, PRIBIO | PCATCH, 179 "htplev", INFSLP); 180 if (error) { 181 mtx_leave(&hotplug_mtx); 182 return (error); 183 } 184 } 185 mtx_leave(&hotplug_mtx); 186 187 return (uiomove(&he, sizeof(he), uio)); 188 } 189 190 int 191 hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 192 { 193 switch (cmd) { 194 case FIOASYNC: 195 /* ignore */ 196 default: 197 return (ENOTTY); 198 } 199 200 return (0); 201 } 202 203 int 204 hotplugkqfilter(dev_t dev, struct knote *kn) 205 { 206 switch (kn->kn_filter) { 207 case EVFILT_READ: 208 kn->kn_fop = &hotplugread_filtops; 209 break; 210 default: 211 return (EINVAL); 212 } 213 214 klist_insert(&hotplug_klist, kn); 215 return (0); 216 } 217 218 void 219 filt_hotplugrdetach(struct knote *kn) 220 { 221 klist_remove(&hotplug_klist, kn); 222 } 223 224 int 225 filt_hotplugread(struct knote *kn, long hint) 226 { 227 kn->kn_data = evqueue_count; 228 229 return (evqueue_count > 0); 230 } 231 232 int 233 filt_hotplugmodify(struct kevent *kev, struct knote *kn) 234 { 235 int active; 236 237 mtx_enter(&hotplug_mtx); 238 active = knote_modify(kev, kn); 239 mtx_leave(&hotplug_mtx); 240 241 return (active); 242 } 243 244 int 245 filt_hotplugprocess(struct knote *kn, struct kevent *kev) 246 { 247 int active; 248 249 mtx_enter(&hotplug_mtx); 250 active = knote_process(kn, kev); 251 mtx_leave(&hotplug_mtx); 252 253 return (active); 254 } 255