xref: /openbsd-src/sys/dev/hotplug.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
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