1 /* $OpenBSD: hotplug.c,v 1.21 2020/12/25 12:59:52 visa 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/fcntl.h> 26 #include <sys/hotplug.h> 27 #include <sys/ioctl.h> 28 #include <sys/poll.h> 29 #include <sys/vnode.h> 30 31 #define HOTPLUG_MAXEVENTS 64 32 33 static int opened; 34 static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS]; 35 static int evqueue_head, evqueue_tail, evqueue_count; 36 static struct selinfo hotplug_sel; 37 38 void filt_hotplugrdetach(struct knote *); 39 int filt_hotplugread(struct knote *, long); 40 41 const struct filterops hotplugread_filtops = { 42 .f_flags = FILTEROP_ISFD, 43 .f_attach = NULL, 44 .f_detach = filt_hotplugrdetach, 45 .f_event = filt_hotplugread, 46 }; 47 48 #define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1) 49 50 51 int hotplug_put_event(struct hotplug_event *); 52 int hotplug_get_event(struct hotplug_event *); 53 54 void hotplugattach(int); 55 56 void 57 hotplugattach(int count) 58 { 59 opened = 0; 60 evqueue_head = 0; 61 evqueue_tail = 0; 62 evqueue_count = 0; 63 } 64 65 void 66 hotplug_device_attach(enum devclass class, char *name) 67 { 68 struct hotplug_event he; 69 70 he.he_type = HOTPLUG_DEVAT; 71 he.he_devclass = class; 72 strlcpy(he.he_devname, name, sizeof(he.he_devname)); 73 hotplug_put_event(&he); 74 } 75 76 void 77 hotplug_device_detach(enum devclass class, char *name) 78 { 79 struct hotplug_event he; 80 81 he.he_type = HOTPLUG_DEVDT; 82 he.he_devclass = class; 83 strlcpy(he.he_devname, name, sizeof(he.he_devname)); 84 hotplug_put_event(&he); 85 } 86 87 int 88 hotplug_put_event(struct hotplug_event *he) 89 { 90 if (evqueue_count == HOTPLUG_MAXEVENTS && opened) { 91 printf("hotplug: event lost, queue full\n"); 92 return (1); 93 } 94 95 evqueue[evqueue_head] = *he; 96 evqueue_head = EVQUEUE_NEXT(evqueue_head); 97 if (evqueue_count == HOTPLUG_MAXEVENTS) 98 evqueue_tail = EVQUEUE_NEXT(evqueue_tail); 99 else 100 evqueue_count++; 101 wakeup(&evqueue); 102 selwakeup(&hotplug_sel); 103 return (0); 104 } 105 106 int 107 hotplug_get_event(struct hotplug_event *he) 108 { 109 int s; 110 111 if (evqueue_count == 0) 112 return (1); 113 114 s = splbio(); 115 *he = evqueue[evqueue_tail]; 116 evqueue_tail = EVQUEUE_NEXT(evqueue_tail); 117 evqueue_count--; 118 splx(s); 119 return (0); 120 } 121 122 int 123 hotplugopen(dev_t dev, int flag, int mode, struct proc *p) 124 { 125 if (minor(dev) != 0) 126 return (ENXIO); 127 if ((flag & FWRITE)) 128 return (EPERM); 129 if (opened) 130 return (EBUSY); 131 opened = 1; 132 return (0); 133 } 134 135 int 136 hotplugclose(dev_t dev, int flag, int mode, struct proc *p) 137 { 138 struct hotplug_event he; 139 140 while (hotplug_get_event(&he) == 0) 141 continue; 142 opened = 0; 143 return (0); 144 } 145 146 int 147 hotplugread(dev_t dev, struct uio *uio, int flags) 148 { 149 struct hotplug_event he; 150 int error; 151 152 if (uio->uio_resid != sizeof(he)) 153 return (EINVAL); 154 155 again: 156 if (hotplug_get_event(&he) == 0) 157 return (uiomove(&he, sizeof(he), uio)); 158 if (flags & IO_NDELAY) 159 return (EAGAIN); 160 161 error = tsleep_nsec(&evqueue, PRIBIO | PCATCH, "htplev", INFSLP); 162 if (error) 163 return (error); 164 goto again; 165 } 166 167 int 168 hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 169 { 170 switch (cmd) { 171 case FIOASYNC: 172 /* ignore */ 173 case FIONBIO: 174 /* handled in the upper fs layer */ 175 break; 176 default: 177 return (ENOTTY); 178 } 179 180 return (0); 181 } 182 183 int 184 hotplugpoll(dev_t dev, int events, struct proc *p) 185 { 186 int revents = 0; 187 188 if (events & (POLLIN | POLLRDNORM)) { 189 if (evqueue_count > 0) 190 revents |= events & (POLLIN | POLLRDNORM); 191 else 192 selrecord(p, &hotplug_sel); 193 } 194 195 return (revents); 196 } 197 198 int 199 hotplugkqfilter(dev_t dev, struct knote *kn) 200 { 201 struct klist *klist; 202 int s; 203 204 switch (kn->kn_filter) { 205 case EVFILT_READ: 206 klist = &hotplug_sel.si_note; 207 kn->kn_fop = &hotplugread_filtops; 208 break; 209 default: 210 return (EINVAL); 211 } 212 213 s = splbio(); 214 klist_insert_locked(klist, kn); 215 splx(s); 216 return (0); 217 } 218 219 void 220 filt_hotplugrdetach(struct knote *kn) 221 { 222 int s; 223 224 s = splbio(); 225 klist_remove_locked(&hotplug_sel.si_note, kn); 226 splx(s); 227 } 228 229 int 230 filt_hotplugread(struct knote *kn, long hint) 231 { 232 kn->kn_data = evqueue_count; 233 234 return (evqueue_count > 0); 235 } 236