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