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