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,
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
28 * $DragonFly: src/sys/kern/kern_device.c,v 1.17 2006/04/30 17:22:17 dillon Exp $
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>
39 #include <sys/vnode.h>
40 #include <sys/queue.h>
41 #include <sys/msgport.h>
42 #include <sys/device.h>
43 #include <machine/stdarg.h>
45 #include <sys/thread2.h>
46 #include <sys/msgport2.h>
48 static struct cdevlink *cdevbase[NUMCDEVSW];
50 static int cdevsw_putport(lwkt_port_t port, lwkt_msg_t msg);
52 struct cdevsw dead_cdevsw;
55 * Initialize a message port to serve as the default message-handling port
56 * for device operations. This message port provides compatibility with
57 * traditional cdevsw dispatch functions by running them synchronously.
59 * YYY NOTE: ms_cmd can now hold a function pointer, should this code be
60 * converted from an integer op to a function pointer with a flag to
61 * indicate legacy operation?
64 init_default_cdevsw_port(lwkt_port_t port)
66 lwkt_initport(port, NULL);
67 port->mp_putport = cdevsw_putport;
72 cdevsw_putport(lwkt_port_t port, lwkt_msg_t lmsg)
74 cdevallmsg_t msg = (cdevallmsg_t)lmsg;
75 struct cdevsw *devsw = msg->am_msg.dev->si_devsw;
79 * Run the device switch function synchronously in the context of the
80 * caller and return a synchronous error code (anything not EASYNC).
82 switch(msg->am_lmsg.ms_cmd.cm_op) {
84 error = devsw->old_open(
91 error = devsw->old_close(
92 msg->am_close.msg.dev,
94 msg->am_close.devtype,
97 case CDEV_CMD_STRATEGY:
98 devsw->old_strategy(msg->am_strategy.msg.dev, msg->am_strategy.bio);
102 error = devsw->old_ioctl(
103 msg->am_ioctl.msg.dev,
110 error = devsw->old_dump(
111 msg->am_dump.msg.dev,
114 msg->am_dump.secsize);
117 msg->am_psize.result = devsw->old_psize(msg->am_psize.msg.dev);
121 error = devsw->old_read(
122 msg->am_read.msg.dev,
124 msg->am_read.ioflag);
127 error = devsw->old_write(
128 msg->am_read.msg.dev,
130 msg->am_read.ioflag);
133 msg->am_poll.events = devsw->old_poll(
134 msg->am_poll.msg.dev,
139 case CDEV_CMD_KQFILTER:
140 msg->am_kqfilter.result = devsw->old_kqfilter(
141 msg->am_kqfilter.msg.dev,
142 msg->am_kqfilter.kn);
146 msg->am_mmap.result = devsw->old_mmap(
147 msg->am_mmap.msg.dev,
156 KKASSERT(error != EASYNC);
162 _init_cdevmsg(dev_t dev, cdevmsg_t msg, int cmd)
164 lwkt_initmsg_simple(&msg->msg, cmd);
166 return(dev->si_port);
170 dev_dopen(dev_t dev, int oflags, int devtype, thread_t td)
172 struct cdevmsg_open msg;
175 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN);
179 msg.devtype = devtype;
181 return(lwkt_domsg(port, &msg.msg.msg));
185 dev_dclose(dev_t dev, int fflag, int devtype, thread_t td)
187 struct cdevmsg_close msg;
190 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE);
194 msg.devtype = devtype;
196 return(lwkt_domsg(port, &msg.msg.msg));
200 * Core device strategy call, used to issue I/O on a device. There are
201 * two versions, a non-chained version and a chained version. The chained
202 * version reuses a BIO set up by vn_strategy(). The only difference is
203 * that, for now, we do not push a new tracking structure when chaining
204 * from vn_strategy. XXX this will ultimately have to change.
207 dev_dstrategy(dev_t dev, struct bio *bio)
209 struct cdevmsg_strategy msg;
210 struct bio_track *track;
213 KKASSERT(bio->bio_track == NULL);
214 KKASSERT(bio->bio_buf->b_cmd != BUF_CMD_DONE);
215 if (bio->bio_buf->b_cmd == BUF_CMD_READ)
216 track = &dev->si_track_read;
218 track = &dev->si_track_write;
219 atomic_add_int(&track->bk_active, 1);
220 bio->bio_track = track;
222 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY);
223 KKASSERT(port); /* 'nostrategy' function is NULL YYY */
225 lwkt_domsg(port, &msg.msg.msg);
229 dev_dstrategy_chain(dev_t dev, struct bio *bio)
231 struct cdevmsg_strategy msg;
234 KKASSERT(bio->bio_track != NULL);
235 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY);
236 KKASSERT(port); /* 'nostrategy' function is NULL YYY */
238 lwkt_domsg(port, &msg.msg.msg);
242 dev_dioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td)
244 struct cdevmsg_ioctl msg;
247 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL);
254 return(lwkt_domsg(port, &msg.msg.msg));
258 * note: the disk layer is expected to set count, blkno, and secsize before
259 * forwarding the message.
264 struct cdevmsg_dump msg;
267 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP);
273 return(lwkt_domsg(port, &msg.msg.msg));
277 dev_dpsize(dev_t dev)
279 struct cdevmsg_psize msg;
283 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE);
286 error = lwkt_domsg(port, &msg.msg.msg);
293 dev_dread(dev_t dev, struct uio *uio, int ioflag)
295 struct cdevmsg_read msg;
299 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ);
304 error = lwkt_domsg(port, &msg.msg.msg);
306 dev->si_lastread = time_second;
311 dev_dwrite(dev_t dev, struct uio *uio, int ioflag)
313 struct cdevmsg_write msg;
316 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE);
319 dev->si_lastwrite = time_second;
322 return(lwkt_domsg(port, &msg.msg.msg));
326 dev_dpoll(dev_t dev, int events, thread_t td)
328 struct cdevmsg_poll msg;
332 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL);
337 error = lwkt_domsg(port, &msg.msg.msg);
340 return(seltrue(dev, msg.events, td));
344 dev_dkqfilter(dev_t dev, struct knote *kn)
346 struct cdevmsg_kqfilter msg;
350 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER);
354 error = lwkt_domsg(port, &msg.msg.msg);
361 dev_dmmap(dev_t dev, vm_offset_t offset, int nprot)
363 struct cdevmsg_mmap msg;
367 port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP);
372 error = lwkt_domsg(port, &msg.msg.msg);
381 return(dev->si_devsw->d_name);
385 dev_dflags(dev_t dev)
387 return(dev->si_devsw->d_flags);
393 return(dev->si_devsw->d_maj);
399 return(dev->si_port);
403 * Convert a cdevsw template into the real thing, filling in fields the
404 * device left empty with appropriate defaults.
407 compile_devsw(struct cdevsw *devsw)
409 static lwkt_port devsw_compat_port;
411 if (devsw_compat_port.mp_putport == NULL)
412 init_default_cdevsw_port(&devsw_compat_port);
414 if (devsw->old_open == NULL)
415 devsw->old_open = noopen;
416 if (devsw->old_close == NULL)
417 devsw->old_close = noclose;
418 if (devsw->old_read == NULL)
419 devsw->old_read = noread;
420 if (devsw->old_write == NULL)
421 devsw->old_write = nowrite;
422 if (devsw->old_ioctl == NULL)
423 devsw->old_ioctl = noioctl;
424 if (devsw->old_poll == NULL)
425 devsw->old_poll = nopoll;
426 if (devsw->old_mmap == NULL)
427 devsw->old_mmap = nommap;
428 if (devsw->old_strategy == NULL)
429 devsw->old_strategy = nostrategy;
430 if (devsw->old_dump == NULL)
431 devsw->old_dump = nodump;
432 if (devsw->old_psize == NULL)
433 devsw->old_psize = nopsize;
434 if (devsw->old_kqfilter == NULL)
435 devsw->old_kqfilter = nokqfilter;
437 if (devsw->d_port == NULL)
438 devsw->d_port = &devsw_compat_port;
439 if (devsw->d_clone == NULL)
440 devsw->d_clone = noclone;
444 * This makes a cdevsw entry visible to userland (e.g /dev/<blah>).
446 * The kernel can overload a major number by making multiple cdevsw_add()
447 * calls, but only the most recent one (the first one in the cdevbase[] list
448 * matching the mask/match) will be visible to userland. make_dev() does
449 * not automatically call cdevsw_add() (nor do we want it to, since
450 * partition-managed disk devices are overloaded on top of the raw device).
452 * Disk devices typically register their major, e.g. 'ad0', and then call
453 * into the disk label management code which overloads its own onto e.g. 'ad0'
454 * to support all the various slice and partition combinations.
456 * The mask/match supplied in this call are a full 32 bits and the same
457 * mask and match must be specified in a later cdevsw_remove() call to
458 * match this add. However, the match value for the minor number should never
459 * have any bits set in the major number's bit range (8-15). The mask value
460 * may be conveniently specified as -1 without creating any major number
464 cdevsw_add(struct cdevsw *devsw, u_int mask, u_int match)
467 struct cdevlink *link;
469 compile_devsw(devsw);
471 if (maj < 0 || maj >= NUMCDEVSW) {
472 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
476 for (link = cdevbase[maj]; link; link = link->next) {
478 * If we get an exact match we usurp the target, but we only print
479 * a warning message if a different device switch is installed.
481 if (link->mask == mask && link->match == match) {
482 if (link->devsw != devsw) {
483 printf("WARNING: \"%s\" (%p) is usurping \"%s\"'s (%p)"
485 devsw->d_name, devsw,
486 link->devsw->d_name, link->devsw);
493 * XXX add additional warnings for overlaps
497 link = malloc(sizeof(struct cdevlink), M_DEVBUF, M_INTWAIT|M_ZERO);
501 link->next = cdevbase[maj];
502 cdevbase[maj] = link;
508 * Should only be used by udev2dev().
510 * If the minor number is -1, we match the first cdevsw we find for this
511 * major. If the mask is not -1 then multiple minor numbers can match
514 * Note that this function will return NULL if the minor number is not within
515 * the bounds of the installed mask(s).
517 * The specified minor number should NOT include any major bits.
520 cdevsw_get(int x, int y)
522 struct cdevlink *link;
524 if (x < 0 || x >= NUMCDEVSW)
526 for (link = cdevbase[x]; link; link = link->next) {
527 if (y == -1 || (link->mask & y) == link->match)
534 * Use the passed cdevsw as a template to create our intercept cdevsw,
535 * and install and return ours.
538 cdevsw_add_override(dev_t backing_dev, u_int mask, u_int match)
540 struct cdevsw *devsw;
541 struct cdevsw *bsw = backing_dev->si_devsw;
543 devsw = malloc(sizeof(struct cdevsw), M_DEVBUF, M_INTWAIT|M_ZERO);
544 devsw->d_name = bsw->d_name;
545 devsw->d_maj = bsw->d_maj;
546 devsw->d_flags = bsw->d_flags;
547 compile_devsw(devsw);
548 cdevsw_add(devsw, mask, match);
554 * Override a device's port, returning the previously installed port. This
555 * is XXX very dangerous.
558 cdevsw_dev_override(dev_t dev, lwkt_port_t port)
562 oport = dev->si_port;
568 * Remove a cdevsw entry from the cdevbase[] major array so no new user opens
569 * can be performed, and destroy all devices installed in the hash table
570 * which are associated with this cdevsw. (see destroy_all_dev()).
573 cdevsw_remove(struct cdevsw *devsw, u_int mask, u_int match)
575 int maj = devsw->d_maj;
576 struct cdevlink *link;
577 struct cdevlink **plink;
579 if (maj < 0 || maj >= NUMCDEVSW) {
580 printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
584 if (devsw != &dead_cdevsw)
585 destroy_all_dev(devsw, mask, match);
586 for (plink = &cdevbase[maj]; (link = *plink) != NULL; plink = &link->next) {
587 if (link->mask == mask && link->match == match) {
588 if (link->devsw == devsw)
590 printf("%s: ERROR: cannot remove from cdevsw[], its major"
591 " number %d was stolen by %s\n",
598 printf("%s(%d)[%08x/%08x]: WARNING: cdevsw removed multiple times!\n",
599 devsw->d_name, maj, mask, match);
602 --devsw->d_refs; /* XXX cdevsw_release() / record refs */
603 free(link, M_DEVBUF);
605 if (cdevbase[maj] == NULL && devsw->d_refs != 0) {
606 printf("%s(%d)[%08x/%08x]: Warning: cdevsw_remove() called while "
607 "%d device refs still exist!\n",
608 devsw->d_name, maj, mask, match, devsw->d_refs);
610 printf("%s: cdevsw removed\n", devsw->d_name);
616 * Release a cdevsw entry. When the ref count reaches zero, recurse
620 cdevsw_release(struct cdevsw *devsw)
623 if (devsw->d_refs == 0) {