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