xref: /openbsd-src/sys/dev/hotplug.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: hotplug.c,v 1.12 2011/07/02 22:20:07 nicm 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	64
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 	return (0);
101 }
102 
103 int
104 hotplug_get_event(struct hotplug_event *he)
105 {
106 	int s;
107 
108 	if (evqueue_count == 0)
109 		return (1);
110 
111 	s = splbio();
112 	*he = evqueue[evqueue_tail];
113 	evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
114 	evqueue_count--;
115 	splx(s);
116 	return (0);
117 }
118 
119 int
120 hotplugopen(dev_t dev, int flag, int mode, struct proc *p)
121 {
122 	if (minor(dev) != 0)
123 		return (ENXIO);
124 	if ((flag & FWRITE))
125 		return (EPERM);
126 	if (opened)
127 		return (EBUSY);
128 	opened = 1;
129 	return (0);
130 }
131 
132 int
133 hotplugclose(dev_t dev, int flag, int mode, struct proc *p)
134 {
135 	struct hotplug_event he;
136 
137 	while (hotplug_get_event(&he) == 0)
138 		;
139 	opened = 0;
140 	return (0);
141 }
142 
143 int
144 hotplugread(dev_t dev, struct uio *uio, int flags)
145 {
146 	struct hotplug_event he;
147 	int error;
148 
149 	if (uio->uio_resid != sizeof(he))
150 		return (EINVAL);
151 
152 again:
153 	if (hotplug_get_event(&he) == 0)
154 		return (uiomove(&he, sizeof(he), uio));
155 	if (flags & IO_NDELAY)
156 		return (EAGAIN);
157 
158 	error = tsleep(&evqueue, PRIBIO | PCATCH, "htplev", 0);
159 	if (error)
160 		return (error);
161 	goto again;
162 }
163 
164 int
165 hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
166 {
167 	switch (cmd) {
168 	case FIOASYNC:
169 		/* ignore */
170 	case FIONBIO:
171 		/* handled in the upper fs layer */
172 		break;
173 	default:
174 		return (ENOTTY);
175 	}
176 
177 	return (0);
178 }
179 
180 int
181 hotplugpoll(dev_t dev, int events, struct proc *p)
182 {
183 	int revents = 0;
184 
185 	if (events & (POLLIN | POLLRDNORM)) {
186 		if (evqueue_count > 0)
187 			revents |= events & (POLLIN | POLLRDNORM);
188 		else
189 			selrecord(p, &hotplug_sel);
190 	}
191 
192 	return (revents);
193 }
194 
195 int
196 hotplugkqfilter(dev_t dev, struct knote *kn)
197 {
198 	struct klist *klist;
199 	int s;
200 
201 	switch (kn->kn_filter) {
202 	case EVFILT_READ:
203 		klist = &hotplug_sel.si_note;
204 		kn->kn_fop = &hotplugread_filtops;
205 		break;
206 	default:
207 		return (EINVAL);
208 	}
209 
210 	s = splbio();
211 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
212 	splx(s);
213 	return (0);
214 }
215 
216 void
217 filt_hotplugrdetach(struct knote *kn)
218 {
219 	int s;
220 
221 	s = splbio();
222 	SLIST_REMOVE(&hotplug_sel.si_note, kn, knote, kn_selnext);
223 	splx(s);
224 }
225 
226 int
227 filt_hotplugread(struct knote *kn, long hint)
228 {
229 	kn->kn_data = evqueue_count;
230 
231 	return (evqueue_count > 0);
232 }
233