Device layer rollup commit.
[dragonfly.git] / sys / kern / kern_device.c
CommitLineData
335dda38
MD
1/*
2 * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com> All rights reserved.
3 * cdevsw from kern/kern_conf.c Copyright (c) 1995 Terrence R. Lambert
4 * cdevsw from kern/kern_conf.c Copyright (c) 1995 Julian R. Elishcer,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
e4c9c0c8 28 * $DragonFly: src/sys/kern/kern_device.c,v 1.11 2004/05/19 22:52:58 dillon Exp $
335dda38
MD
29 */
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/sysctl.h>
33#include <sys/systm.h>
34#include <sys/module.h>
35#include <sys/malloc.h>
36#include <sys/conf.h>
37#include <sys/vnode.h>
38#include <sys/queue.h>
39#include <sys/msgport.h>
40#include <sys/device.h>
41#include <machine/stdarg.h>
42#include <sys/proc.h>
43#include <sys/thread2.h>
44#include <sys/msgport2.h>
45
e4c9c0c8 46static struct cdevlink *cdevbase[NUMCDEVSW];
335dda38
MD
47
48static int cdevsw_putport(lwkt_port_t port, lwkt_msg_t msg);
49
e4c9c0c8
MD
50struct cdevsw dead_cdevsw;
51
335dda38
MD
52/*
53 * Initialize a message port to serve as the default message-handling port
54 * for device operations. This message port provides compatibility with
455fcd7e 55 * traditional cdevsw dispatch functions by running them synchronously.
b44419cb
MD
56 *
57 * YYY NOTE: ms_cmd can now hold a function pointer, should this code be
58 * converted from an integer op to a function pointer with a flag to
59 * indicate legacy operation?
335dda38
MD
60 */
61static void
62init_default_cdevsw_port(lwkt_port_t port)
63{
c95cd171 64 lwkt_initport(port, NULL);
df2244e3 65 port->mp_putport = cdevsw_putport;
335dda38
MD
66}
67
68static
69int
70cdevsw_putport(lwkt_port_t port, lwkt_msg_t lmsg)
71{
72 cdevallmsg_t msg = (cdevallmsg_t)lmsg;
e4c9c0c8 73 struct cdevsw *devsw = msg->am_msg.dev->si_devsw;
335dda38
MD
74 int error;
75
335dda38
MD
76 /*
77 * Run the device switch function synchronously in the context of the
4fd10eb6 78 * caller and return a synchronous error code (anything not EASYNC).
335dda38 79 */
b44419cb 80 switch(msg->am_lmsg.ms_cmd.cm_op) {
335dda38 81 case CDEV_CMD_OPEN:
e4c9c0c8 82 error = devsw->old_open(
335dda38
MD
83 msg->am_open.msg.dev,
84 msg->am_open.oflags,
85 msg->am_open.devtype,
86 msg->am_open.td);
87 break;
88 case CDEV_CMD_CLOSE:
e4c9c0c8 89 error = devsw->old_close(
335dda38
MD
90 msg->am_close.msg.dev,
91 msg->am_close.fflag,
92 msg->am_close.devtype,
93 msg->am_close.td);
94 break;
95 case CDEV_CMD_STRATEGY:
e4c9c0c8 96 devsw->old_strategy(msg->am_strategy.bp);
335dda38
MD
97 error = 0;
98 break;
99 case CDEV_CMD_IOCTL:
e4c9c0c8 100 error = devsw->old_ioctl(
335dda38
MD
101 msg->am_ioctl.msg.dev,
102 msg->am_ioctl.cmd,
103 msg->am_ioctl.data,
104 msg->am_ioctl.fflag,
105 msg->am_ioctl.td);
106 break;
107 case CDEV_CMD_DUMP:
e4c9c0c8
MD
108 error = devsw->old_dump(
109 msg->am_dump.msg.dev,
110 msg->am_dump.count,
111 msg->am_dump.blkno,
112 msg->am_dump.secsize);
335dda38
MD
113 break;
114 case CDEV_CMD_PSIZE:
e4c9c0c8 115 msg->am_psize.result = devsw->old_psize(msg->am_psize.msg.dev);
335dda38
MD
116 error = 0; /* XXX */
117 break;
118 case CDEV_CMD_READ:
e4c9c0c8 119 error = devsw->old_read(
335dda38
MD
120 msg->am_read.msg.dev,
121 msg->am_read.uio,
122 msg->am_read.ioflag);
123 break;
124 case CDEV_CMD_WRITE:
e4c9c0c8 125 error = devsw->old_write(
335dda38
MD
126 msg->am_read.msg.dev,
127 msg->am_read.uio,
128 msg->am_read.ioflag);
129 break;
130 case CDEV_CMD_POLL:
e4c9c0c8 131 msg->am_poll.events = devsw->old_poll(
335dda38
MD
132 msg->am_poll.msg.dev,
133 msg->am_poll.events,
134 msg->am_poll.td);
135 error = 0;
136 break;
137 case CDEV_CMD_KQFILTER:
e4c9c0c8 138 msg->am_kqfilter.result = devsw->old_kqfilter(
335dda38
MD
139 msg->am_kqfilter.msg.dev,
140 msg->am_kqfilter.kn);
141 error = 0;
142 break;
143 case CDEV_CMD_MMAP:
e4c9c0c8 144 msg->am_mmap.result = devsw->old_mmap(
335dda38
MD
145 msg->am_mmap.msg.dev,
146 msg->am_mmap.offset,
147 msg->am_mmap.nprot);
148 error = 0; /* XXX */
149 break;
150 default:
151 error = ENOSYS;
152 break;
153 }
4fd10eb6 154 KKASSERT(error != EASYNC);
335dda38
MD
155 return(error);
156}
157
335dda38
MD
158static __inline
159lwkt_port_t
160_init_cdevmsg(dev_t dev, cdevmsg_t msg, int cmd)
161{
b44419cb 162 lwkt_initmsg_simple(&msg->msg, cmd);
335dda38 163 msg->dev = dev;
e4c9c0c8 164 return(dev->si_port);
335dda38
MD
165}
166
167int
168dev_dopen(dev_t dev, int oflags, int devtype, thread_t td)
169{
170 struct cdevmsg_open msg;
171 lwkt_port_t port;
172
173 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN);
174 if (port == NULL)
175 return(ENXIO);
176 msg.oflags = oflags;
177 msg.devtype = devtype;
178 msg.td = td;
179 return(lwkt_domsg(port, &msg.msg.msg));
180}
181
182int
183dev_dclose(dev_t dev, int fflag, int devtype, thread_t td)
184{
185 struct cdevmsg_close msg;
186 lwkt_port_t port;
187
188 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE);
189 if (port == NULL)
190 return(ENXIO);
191 msg.fflag = fflag;
192 msg.devtype = devtype;
193 msg.td = td;
194 return(lwkt_domsg(port, &msg.msg.msg));
195}
196
197void
198dev_dstrategy(dev_t dev, struct buf *bp)
199{
200 struct cdevmsg_strategy msg;
201 lwkt_port_t port;
202
203 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY);
204 KKASSERT(port); /* 'nostrategy' function is NULL YYY */
205 msg.bp = bp;
206 lwkt_domsg(port, &msg.msg.msg);
207}
208
209int
210dev_dioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td)
211{
212 struct cdevmsg_ioctl msg;
213 lwkt_port_t port;
214
215 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL);
216 if (port == NULL)
217 return(ENXIO);
218 msg.cmd = cmd;
219 msg.data = data;
220 msg.fflag = fflag;
221 msg.td = td;
222 return(lwkt_domsg(port, &msg.msg.msg));
223}
224
e4c9c0c8
MD
225/*
226 * note: the disk layer is expected to set count, blkno, and secsize before
227 * forwarding the message.
228 */
335dda38
MD
229int
230dev_ddump(dev_t dev)
231{
232 struct cdevmsg_dump msg;
233 lwkt_port_t port;
234
235 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP);
236 if (port == NULL)
237 return(ENXIO);
e4c9c0c8
MD
238 msg.count = 0;
239 msg.blkno = 0;
240 msg.secsize = 0;
335dda38
MD
241 return(lwkt_domsg(port, &msg.msg.msg));
242}
243
244int
245dev_dpsize(dev_t dev)
246{
247 struct cdevmsg_psize msg;
248 lwkt_port_t port;
249 int error;
250
251 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE);
252 if (port == NULL)
253 return(-1);
254 error = lwkt_domsg(port, &msg.msg.msg);
255 if (error == 0)
256 return(msg.result);
257 return(-1);
258}
259
260int
261dev_dread(dev_t dev, struct uio *uio, int ioflag)
262{
263 struct cdevmsg_read msg;
264 lwkt_port_t port;
265
266 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ);
267 if (port == NULL)
268 return(ENXIO);
269 msg.uio = uio;
270 msg.ioflag = ioflag;
271 return(lwkt_domsg(port, &msg.msg.msg));
272}
273
274int
275dev_dwrite(dev_t dev, struct uio *uio, int ioflag)
276{
277 struct cdevmsg_write msg;
278 lwkt_port_t port;
279
280 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE);
281 if (port == NULL)
282 return(ENXIO);
283 msg.uio = uio;
284 msg.ioflag = ioflag;
285 return(lwkt_domsg(port, &msg.msg.msg));
286}
287
288int
289dev_dpoll(dev_t dev, int events, thread_t td)
290{
291 struct cdevmsg_poll msg;
292 lwkt_port_t port;
293 int error;
294
295 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL);
296 if (port == NULL)
297 return(ENXIO);
298 msg.events = events;
299 msg.td = td;
300 error = lwkt_domsg(port, &msg.msg.msg);
301 if (error == 0)
302 return(msg.events);
303 return(seltrue(dev, msg.events, td));
304}
305
306int
307dev_dkqfilter(dev_t dev, struct knote *kn)
308{
309 struct cdevmsg_kqfilter msg;
310 lwkt_port_t port;
311 int error;
312
313 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER);
314 if (port == NULL)
315 return(ENXIO);
316 msg.kn = kn;
317 error = lwkt_domsg(port, &msg.msg.msg);
318 if (error == 0)
319 return(msg.result);
320 return(ENODEV);
321}
322
323int
324dev_dmmap(dev_t dev, vm_offset_t offset, int nprot)
325{
326 struct cdevmsg_mmap msg;
327 lwkt_port_t port;
328 int error;
329
330 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP);
331 if (port == NULL)
332 return(-1);
333 msg.offset = offset;
334 msg.nprot = nprot;
335 error = lwkt_domsg(port, &msg.msg.msg);
336 if (error == 0)
337 return(msg.result);
338 return(-1);
339}
340
335dda38
MD
341const char *
342dev_dname(dev_t dev)
343{
e4c9c0c8 344 return(dev->si_devsw->d_name);
335dda38
MD
345}
346
347int
348dev_dflags(dev_t dev)
349{
e4c9c0c8 350 return(dev->si_devsw->d_flags);
335dda38
MD
351}
352
353int
354dev_dmaj(dev_t dev)
355{
e4c9c0c8 356 return(dev->si_devsw->d_maj);
335dda38
MD
357}
358
359lwkt_port_t
360dev_dport(dev_t dev)
361{
e4c9c0c8 362 return(dev->si_port);
335dda38
MD
363}
364
335dda38
MD
365/*
366 * Convert a cdevsw template into the real thing, filling in fields the
367 * device left empty with appropriate defaults.
368 */
369void
370compile_devsw(struct cdevsw *devsw)
371{
372 static lwkt_port devsw_compat_port;
373
df2244e3 374 if (devsw_compat_port.mp_putport == NULL)
335dda38
MD
375 init_default_cdevsw_port(&devsw_compat_port);
376
377 if (devsw->old_open == NULL)
378 devsw->old_open = noopen;
379 if (devsw->old_close == NULL)
380 devsw->old_close = noclose;
381 if (devsw->old_read == NULL)
382 devsw->old_read = noread;
383 if (devsw->old_write == NULL)
384 devsw->old_write = nowrite;
385 if (devsw->old_ioctl == NULL)
386 devsw->old_ioctl = noioctl;
387 if (devsw->old_poll == NULL)
388 devsw->old_poll = nopoll;
389 if (devsw->old_mmap == NULL)
390 devsw->old_mmap = nommap;
391 if (devsw->old_strategy == NULL)
392 devsw->old_strategy = nostrategy;
393 if (devsw->old_dump == NULL)
394 devsw->old_dump = nodump;
395 if (devsw->old_psize == NULL)
396 devsw->old_psize = nopsize;
397 if (devsw->old_kqfilter == NULL)
398 devsw->old_kqfilter = nokqfilter;
399
400 if (devsw->d_port == NULL)
401 devsw->d_port = &devsw_compat_port;
e4c9c0c8
MD
402 if (devsw->d_clone == NULL)
403 devsw->d_clone = noclone;
335dda38
MD
404}
405
406/*
e4c9c0c8
MD
407 * This makes a cdevsw entry visible to userland (e.g /dev/<blah>).
408 *
409 * The kernel can overload a major number with multiple cdevsw's but only
410 * the one installed in cdevbase[] is visible to userland. make_dev() does
411 * not automatically call cdevsw_add() (nor do we want it to, since
412 * partition-managed disk devices are overloaded on top of the raw device).
335dda38
MD
413 */
414int
e4c9c0c8 415cdevsw_add(struct cdevsw *devsw, u_int mask, u_int match)
335dda38 416{
e4c9c0c8
MD
417 int maj;
418 struct cdevlink *link;
419
420 compile_devsw(devsw);
421 maj = devsw->d_maj;
422 if (maj < 0 || maj >= NUMCDEVSW) {
335dda38 423 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
e4c9c0c8 424 devsw->d_name, maj);
335dda38
MD
425 return (EINVAL);
426 }
e4c9c0c8
MD
427 for (link = cdevbase[maj]; link; link = link->next) {
428 /*
429 * If we get an exact match we usurp the target, but we only print
430 * a warning message if a different device switch is installed.
431 */
432 if (link->mask == mask && link->match == match) {
433 if (link->devsw != devsw) {
434 printf("WARNING: \"%s\" (%p) is usurping \"%s\"'s (%p)"
435 " cdevsw[]\n",
436 devsw->d_name, devsw,
437 link->devsw->d_name, link->devsw);
438 link->devsw = devsw;
439 ++devsw->d_refs;
440 }
441 return(0);
442 }
443 /*
444 * XXX add additional warnings for overlaps
445 */
335dda38 446 }
e4c9c0c8
MD
447
448 link = malloc(sizeof(struct cdevlink), M_DEVBUF, M_INTWAIT|M_ZERO);
449 link->mask = mask;
450 link->match = match;
451 link->devsw = devsw;
452 link->next = cdevbase[maj];
453 cdevbase[maj] = link;
454 ++devsw->d_refs;
455 return(0);
335dda38
MD
456}
457
458/*
e4c9c0c8
MD
459 * Should only be used by udev2dev().
460 *
461 * If the minor number is -1, we match the first cdevsw we find for this
462 * major.
463 *
464 * Note that this function will return NULL if the minor number is not within
465 * the bounds of the installed mask(s).
335dda38 466 */
e4c9c0c8
MD
467struct cdevsw *
468cdevsw_get(int x, int y)
335dda38 469{
e4c9c0c8 470 struct cdevlink *link;
335dda38 471
e4c9c0c8
MD
472 if (x < 0 || x >= NUMCDEVSW)
473 return(NULL);
474 for (link = cdevbase[x]; link; link = link->next) {
475 if (y == -1 || (link->mask & y) == link->match)
476 return(link->devsw);
477 }
478 return(NULL);
479}
480
481/*
482 * Use the passed cdevsw as a template to create our intercept cdevsw,
483 * and install and return ours.
484 */
485struct cdevsw *
486cdevsw_add_override(dev_t backing_dev, u_int mask, u_int match)
487{
488 struct cdevsw *devsw;
489 struct cdevsw *bsw = backing_dev->si_devsw;
490
491 devsw = malloc(sizeof(struct cdevsw), M_DEVBUF, M_INTWAIT|M_ZERO);
492 devsw->d_name = bsw->d_name;
493 devsw->d_maj = bsw->d_maj;
494 devsw->d_flags = bsw->d_flags;
495 compile_devsw(devsw);
496 cdevsw_add(devsw, mask, match);
497
498 return(devsw);
335dda38
MD
499}
500
e4c9c0c8
MD
501/*
502 * Override a device's port, returning the previously installed port. This
503 * is XXX very dangerous.
504 */
335dda38
MD
505lwkt_port_t
506cdevsw_dev_override(dev_t dev, lwkt_port_t port)
507{
e4c9c0c8 508 lwkt_port_t oport;
335dda38 509
e4c9c0c8
MD
510 oport = dev->si_port;
511 dev->si_port = port;
512 return(oport);
335dda38
MD
513}
514
515/*
e4c9c0c8
MD
516 * Remove a cdevsw entry from the cdevbase[] major array so no new user opens
517 * can be performed, and destroy all devices installed in the hash table
518 * which are associated with this cdevsw. (see destroy_all_dev()).
335dda38
MD
519 */
520int
e4c9c0c8 521cdevsw_remove(struct cdevsw *devsw, u_int mask, u_int match)
335dda38 522{
e4c9c0c8
MD
523 int maj = devsw->d_maj;
524 struct cdevlink *link;
525 struct cdevlink **plink;
526
527 if (maj < 0 || maj >= NUMCDEVSW) {
335dda38 528 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
e4c9c0c8 529 devsw->d_name, maj);
335dda38
MD
530 return EINVAL;
531 }
e4c9c0c8
MD
532 if (devsw != &dead_cdevsw)
533 destroy_all_dev(devsw, mask, match);
534 for (plink = &cdevbase[maj]; (link = *plink) != NULL; plink = &link->next) {
535 if (link->mask == mask && link->match == match) {
536 if (link->devsw == devsw)
537 break;
538 printf("%s: ERROR: cannot remove from cdevsw[], its major"
539 " number %d was stolen by %s\n",
540 devsw->d_name, maj,
541 link->devsw->d_name
542 );
543 }
544 }
545 if (link == NULL) {
546 printf("%s(%d): WARNING: cdevsw removed multiple times!\n",
547 devsw->d_name, maj);
548 } else {
549 *plink = link->next;
550 --devsw->d_refs; /* XXX cdevsw_release() / record refs */
551 free(link, M_DEVBUF);
552 }
553 if (devsw->d_refs != 0) {
554 printf("%s: Warning: cdevsw_remove() called while %d device refs"
555 " still exist! (major %d)\n",
556 devsw->d_name,
557 devsw->d_refs,
558 maj);
559 } else {
560 printf("%s: cdevsw removed\n", devsw->d_name);
561 }
335dda38
MD
562 return 0;
563}
564
e4c9c0c8
MD
565/*
566 * Release a cdevsw entry. When the ref count reaches zero, recurse
567 * through the stack.
568 */
569void
570cdevsw_release(struct cdevsw *devsw)
571{
572 --devsw->d_refs;
573 if (devsw->d_refs == 0) {
574 /* XXX */
575 }
576}
577