xref: /dragonfly/sys/kern/kern_device.c (revision cdecd76a)
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  *
28  * $DragonFly: src/sys/kern/kern_device.c,v 1.3 2003/07/24 23:52:38 dillon Exp $
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 
46 static struct cdevsw 	*cdevsw[NUMCDEVSW];
47 static struct lwkt_port	*cdevport[NUMCDEVSW];
48 
49 static int cdevsw_putport(lwkt_port_t port, lwkt_msg_t msg);
50 
51 /*
52  * Initialize a message port to serve as the default message-handling port
53  * for device operations.  This message port provides compatibility with
54  * traditional cdevsw dispatch functions.  There are two primary modes:
55  *
56  * mp_td is NULL:  The d_autoq mask is ignored and all messages are translated
57  * 		   into directly, synchronous cdevsw calls.
58  *
59  * mp_td not NULL: The d_autoq mask is used to determine which messages should
60  *		   be queued and which should be handled synchronously.
61  *
62  * Don't worry too much about optimizing this code, the critical devices
63  * will implement their own port messaging functions directly.
64  */
65 static void
66 init_default_cdevsw_port(lwkt_port_t port)
67 {
68     lwkt_init_port(port, NULL);
69     port->mp_beginmsg = cdevsw_putport;
70 }
71 
72 static
73 int
74 cdevsw_putport(lwkt_port_t port, lwkt_msg_t lmsg)
75 {
76     cdevallmsg_t msg = (cdevallmsg_t)lmsg;
77     struct cdevsw *csw = msg->am_msg.csw;
78     int error;
79 
80     /*
81      * If queueable then officially queue the message
82      */
83     if (port->mp_td) {
84 	int mask = (1 << (msg->am_lmsg.ms_cmd & MSG_SUBCMD_MASK));
85 	if (csw->d_autoq & mask)
86 	    return(lwkt_putport(port, &msg->am_lmsg));
87     }
88 
89     /*
90      * Run the device switch function synchronously in the context of the
91      * caller and return a synchronous error code (anything not EASYNC).
92      */
93     switch(msg->am_lmsg.ms_cmd) {
94     case CDEV_CMD_OPEN:
95 	error = csw->old_open(
96 		    msg->am_open.msg.dev,
97 		    msg->am_open.oflags,
98 		    msg->am_open.devtype,
99 		    msg->am_open.td);
100 	break;
101     case CDEV_CMD_CLOSE:
102 	error = csw->old_close(
103 		    msg->am_close.msg.dev,
104 		    msg->am_close.fflag,
105 		    msg->am_close.devtype,
106 		    msg->am_close.td);
107 	break;
108     case CDEV_CMD_STRATEGY:
109 	csw->old_strategy(msg->am_strategy.bp);
110 	error = 0;
111 	break;
112     case CDEV_CMD_IOCTL:
113 	error = csw->old_ioctl(
114 		    msg->am_ioctl.msg.dev,
115 		    msg->am_ioctl.cmd,
116 		    msg->am_ioctl.data,
117 		    msg->am_ioctl.fflag,
118 		    msg->am_ioctl.td);
119 	break;
120     case CDEV_CMD_DUMP:
121 	error = csw->old_dump(msg->am_ioctl.msg.dev);
122 	break;
123     case CDEV_CMD_PSIZE:
124 	msg->am_psize.result = csw->old_psize(msg->am_psize.msg.dev);
125 	error = 0;	/* XXX */
126 	break;
127     case CDEV_CMD_READ:
128 	error = csw->old_read(
129 		    msg->am_read.msg.dev,
130 		    msg->am_read.uio,
131 		    msg->am_read.ioflag);
132 	break;
133     case CDEV_CMD_WRITE:
134 	error = csw->old_write(
135 		    msg->am_read.msg.dev,
136 		    msg->am_read.uio,
137 		    msg->am_read.ioflag);
138 	break;
139     case CDEV_CMD_POLL:
140 	msg->am_poll.events = csw->old_poll(
141 				msg->am_poll.msg.dev,
142 				msg->am_poll.events,
143 				msg->am_poll.td);
144 	error = 0;
145 	break;
146     case CDEV_CMD_KQFILTER:
147 	msg->am_kqfilter.result = csw->old_kqfilter(
148 				msg->am_kqfilter.msg.dev,
149 				msg->am_kqfilter.kn);
150 	error = 0;
151 	break;
152     case CDEV_CMD_MMAP:
153 	msg->am_mmap.result = csw->old_mmap(
154 		    msg->am_mmap.msg.dev,
155 		    msg->am_mmap.offset,
156 		    msg->am_mmap.nprot);
157 	error = 0;	/* XXX */
158 	break;
159     default:
160 	error = ENOSYS;
161 	break;
162     }
163     KKASSERT(error != EASYNC);
164     return(error);
165 }
166 
167 /*
168  * These device dispatch functions provide convenient entry points for
169  * any code wishing to make a dev call.
170  *
171  * YYY we ought to be able to optimize the port lookup by caching it in
172  * the dev_t structure itself.
173  */
174 static __inline
175 struct cdevsw *
176 _devsw(dev_t dev)
177 {
178     if (dev->si_devsw)
179 	return (dev->si_devsw);
180     return(cdevsw[major(dev)]);
181 }
182 
183 static __inline
184 lwkt_port_t
185 _init_cdevmsg(dev_t dev, cdevmsg_t msg, int cmd)
186 {
187     struct cdevsw *csw;
188 
189     lwkt_initmsg(&msg->msg, cmd);
190     msg->dev = dev;
191     msg->csw = csw = _devsw(dev);
192     if (csw != NULL) {			/* YYY too hackish */
193 	KKASSERT(csw->d_port);		/* YYY too hackish */
194 	if (cdevport[major(dev)])	/* YYY too hackish */
195 	    return(cdevport[major(dev)]);
196 	return(csw->d_port);
197     }
198     return(NULL);
199 }
200 
201 int
202 dev_dopen(dev_t dev, int oflags, int devtype, thread_t td)
203 {
204     struct cdevmsg_open	msg;
205     lwkt_port_t port;
206 
207     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN);
208     if (port == NULL)
209 	return(ENXIO);
210     msg.oflags = oflags;
211     msg.devtype = devtype;
212     msg.td = td;
213     return(lwkt_domsg(port, &msg.msg.msg));
214 }
215 
216 int
217 dev_dclose(dev_t dev, int fflag, int devtype, thread_t td)
218 {
219     struct cdevmsg_close msg;
220     lwkt_port_t port;
221 
222     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE);
223     if (port == NULL)
224 	return(ENXIO);
225     msg.fflag = fflag;
226     msg.devtype = devtype;
227     msg.td = td;
228     return(lwkt_domsg(port, &msg.msg.msg));
229 }
230 
231 void
232 dev_dstrategy(dev_t dev, struct buf *bp)
233 {
234     struct cdevmsg_strategy msg;
235     lwkt_port_t port;
236 
237     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY);
238     KKASSERT(port);	/* 'nostrategy' function is NULL YYY */
239     msg.bp = bp;
240     lwkt_domsg(port, &msg.msg.msg);
241 }
242 
243 int
244 dev_dioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td)
245 {
246     struct cdevmsg_ioctl msg;
247     lwkt_port_t port;
248 
249     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL);
250     if (port == NULL)
251 	return(ENXIO);
252     msg.cmd = cmd;
253     msg.data = data;
254     msg.fflag = fflag;
255     msg.td = td;
256     return(lwkt_domsg(port, &msg.msg.msg));
257 }
258 
259 int
260 dev_ddump(dev_t dev)
261 {
262     struct cdevmsg_dump	msg;
263     lwkt_port_t port;
264 
265     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP);
266     if (port == NULL)
267 	return(ENXIO);
268     return(lwkt_domsg(port, &msg.msg.msg));
269 }
270 
271 int
272 dev_dpsize(dev_t dev)
273 {
274     struct cdevmsg_psize msg;
275     lwkt_port_t port;
276     int error;
277 
278     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE);
279     if (port == NULL)
280 	return(-1);
281     error = lwkt_domsg(port, &msg.msg.msg);
282     if (error == 0)
283 	return(msg.result);
284     return(-1);
285 }
286 
287 int
288 dev_dread(dev_t dev, struct uio *uio, int ioflag)
289 {
290     struct cdevmsg_read msg;
291     lwkt_port_t port;
292 
293     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ);
294     if (port == NULL)
295 	return(ENXIO);
296     msg.uio = uio;
297     msg.ioflag = ioflag;
298     return(lwkt_domsg(port, &msg.msg.msg));
299 }
300 
301 int
302 dev_dwrite(dev_t dev, struct uio *uio, int ioflag)
303 {
304     struct cdevmsg_write msg;
305     lwkt_port_t port;
306 
307     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE);
308     if (port == NULL)
309 	return(ENXIO);
310     msg.uio = uio;
311     msg.ioflag = ioflag;
312     return(lwkt_domsg(port, &msg.msg.msg));
313 }
314 
315 int
316 dev_dpoll(dev_t dev, int events, thread_t td)
317 {
318     struct cdevmsg_poll msg;
319     lwkt_port_t port;
320     int error;
321 
322     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL);
323     if (port == NULL)
324 	return(ENXIO);
325     msg.events = events;
326     msg.td = td;
327     error = lwkt_domsg(port, &msg.msg.msg);
328     if (error == 0)
329 	return(msg.events);
330     return(seltrue(dev, msg.events, td));
331 }
332 
333 int
334 dev_dkqfilter(dev_t dev, struct knote *kn)
335 {
336     struct cdevmsg_kqfilter msg;
337     lwkt_port_t port;
338     int error;
339 
340     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER);
341     if (port == NULL)
342 	return(ENXIO);
343     msg.kn = kn;
344     error = lwkt_domsg(port, &msg.msg.msg);
345     if (error == 0)
346 	return(msg.result);
347     return(ENODEV);
348 }
349 
350 int
351 dev_dmmap(dev_t dev, vm_offset_t offset, int nprot)
352 {
353     struct cdevmsg_mmap msg;
354     lwkt_port_t port;
355     int error;
356 
357     port = _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP);
358     if (port == NULL)
359 	return(-1);
360     msg.offset = offset;
361     msg.nprot = nprot;
362     error = lwkt_domsg(port, &msg.msg.msg);
363     if (error == 0)
364 	return(msg.result);
365     return(-1);
366 }
367 
368 int
369 dev_port_dopen(lwkt_port_t port, dev_t dev, int oflags, int devtype, thread_t td)
370 {
371     struct cdevmsg_open	msg;
372 
373     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_OPEN);
374     if (port == NULL)
375 	return(ENXIO);
376     msg.oflags = oflags;
377     msg.devtype = devtype;
378     msg.td = td;
379     return(lwkt_domsg(port, &msg.msg.msg));
380 }
381 
382 int
383 dev_port_dclose(lwkt_port_t port, dev_t dev, int fflag, int devtype, thread_t td)
384 {
385     struct cdevmsg_close msg;
386 
387     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_CLOSE);
388     if (port == NULL)
389 	return(ENXIO);
390     msg.fflag = fflag;
391     msg.devtype = devtype;
392     msg.td = td;
393     return(lwkt_domsg(port, &msg.msg.msg));
394 }
395 
396 void
397 dev_port_dstrategy(lwkt_port_t port, dev_t dev, struct buf *bp)
398 {
399     struct cdevmsg_strategy msg;
400 
401     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_STRATEGY);
402     KKASSERT(port);	/* 'nostrategy' function is NULL YYY */
403     msg.bp = bp;
404     lwkt_domsg(port, &msg.msg.msg);
405 }
406 
407 int
408 dev_port_dioctl(lwkt_port_t port, dev_t dev, u_long cmd, caddr_t data, int fflag, thread_t td)
409 {
410     struct cdevmsg_ioctl msg;
411 
412     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_IOCTL);
413     if (port == NULL)
414 	return(ENXIO);
415     msg.cmd = cmd;
416     msg.data = data;
417     msg.fflag = fflag;
418     msg.td = td;
419     return(lwkt_domsg(port, &msg.msg.msg));
420 }
421 
422 int
423 dev_port_ddump(lwkt_port_t port, dev_t dev)
424 {
425     struct cdevmsg_dump	msg;
426 
427     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_DUMP);
428     if (port == NULL)
429 	return(ENXIO);
430     return(lwkt_domsg(port, &msg.msg.msg));
431 }
432 
433 int
434 dev_port_dpsize(lwkt_port_t port, dev_t dev)
435 {
436     struct cdevmsg_psize msg;
437     int error;
438 
439     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_PSIZE);
440     if (port == NULL)
441 	return(-1);
442     error = lwkt_domsg(port, &msg.msg.msg);
443     if (error == 0)
444 	return(msg.result);
445     return(-1);
446 }
447 
448 int
449 dev_port_dread(lwkt_port_t port, dev_t dev, struct uio *uio, int ioflag)
450 {
451     struct cdevmsg_read msg;
452 
453     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_READ);
454     if (port == NULL)
455 	return(ENXIO);
456     msg.uio = uio;
457     msg.ioflag = ioflag;
458     return(lwkt_domsg(port, &msg.msg.msg));
459 }
460 
461 int
462 dev_port_dwrite(lwkt_port_t port, dev_t dev, struct uio *uio, int ioflag)
463 {
464     struct cdevmsg_write msg;
465 
466     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_WRITE);
467     if (port == NULL)
468 	return(ENXIO);
469     msg.uio = uio;
470     msg.ioflag = ioflag;
471     return(lwkt_domsg(port, &msg.msg.msg));
472 }
473 
474 int
475 dev_port_dpoll(lwkt_port_t port, dev_t dev, int events, thread_t td)
476 {
477     struct cdevmsg_poll msg;
478     int error;
479 
480     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_POLL);
481     if (port == NULL)
482 	return(ENXIO);
483     msg.events = events;
484     msg.td = td;
485     error = lwkt_domsg(port, &msg.msg.msg);
486     if (error == 0)
487 	return(msg.events);
488     return(seltrue(dev, msg.events, td));
489 }
490 
491 int
492 dev_port_dkqfilter(lwkt_port_t port, dev_t dev, struct knote *kn)
493 {
494     struct cdevmsg_kqfilter msg;
495     int error;
496 
497     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_KQFILTER);
498     if (port == NULL)
499 	return(ENXIO);
500     msg.kn = kn;
501     error = lwkt_domsg(port, &msg.msg.msg);
502     if (error == 0)
503 	return(msg.result);
504     return(ENODEV);
505 }
506 
507 int
508 dev_port_dmmap(lwkt_port_t port, dev_t dev, vm_offset_t offset, int nprot)
509 {
510     struct cdevmsg_mmap msg;
511     int error;
512 
513     _init_cdevmsg(dev, &msg.msg, CDEV_CMD_MMAP);
514     if (port == NULL)
515 	return(-1);
516     msg.offset = offset;
517     msg.nprot = nprot;
518     error = lwkt_domsg(port, &msg.msg.msg);
519     if (error == 0)
520 	return(msg.result);
521     return(-1);
522 }
523 
524 const char *
525 dev_dname(dev_t dev)
526 {
527     struct cdevsw *csw;
528 
529     if ((csw = _devsw(dev)) != NULL)
530 	return(csw->d_name);
531     return(NULL);
532 }
533 
534 int
535 dev_dflags(dev_t dev)
536 {
537     struct cdevsw *csw;
538 
539     if ((csw = _devsw(dev)) != NULL)
540 	return(csw->d_flags);
541     return(0);
542 }
543 
544 int
545 dev_dmaj(dev_t dev)
546 {
547     struct cdevsw *csw;
548 
549     if ((csw = _devsw(dev)) != NULL)
550 	return(csw->d_maj);
551     return(0);
552 }
553 
554 lwkt_port_t
555 dev_dport(dev_t dev)
556 {
557     struct cdevsw *csw;
558 
559     if ((csw = _devsw(dev)) != NULL) {
560 	if (cdevport[major(dev)])	/* YYY too hackish */
561 	    return(cdevport[major(dev)]);
562 	return(csw->d_port);
563     }
564     return(NULL);
565 }
566 
567 #if 0
568 /*
569  * cdevsw[] array functions, moved from kern/kern_conf.c
570  */
571 struct cdevsw *
572 devsw(dev_t dev)
573 {
574     return(_devsw(dev));
575 }
576 #endif
577 
578 /*
579  * Convert a cdevsw template into the real thing, filling in fields the
580  * device left empty with appropriate defaults.
581  */
582 void
583 compile_devsw(struct cdevsw *devsw)
584 {
585     static lwkt_port devsw_compat_port;
586 
587     if (devsw_compat_port.mp_beginmsg == NULL)
588 	init_default_cdevsw_port(&devsw_compat_port);
589 
590     if (devsw->old_open == NULL)
591 	devsw->old_open = noopen;
592     if (devsw->old_close == NULL)
593 	devsw->old_close = noclose;
594     if (devsw->old_read == NULL)
595 	devsw->old_read = noread;
596     if (devsw->old_write == NULL)
597 	devsw->old_write = nowrite;
598     if (devsw->old_ioctl == NULL)
599 	devsw->old_ioctl = noioctl;
600     if (devsw->old_poll == NULL)
601 	devsw->old_poll = nopoll;
602     if (devsw->old_mmap == NULL)
603 	devsw->old_mmap = nommap;
604     if (devsw->old_strategy == NULL)
605 	devsw->old_strategy = nostrategy;
606     if (devsw->old_dump == NULL)
607 	devsw->old_dump = nodump;
608     if (devsw->old_psize == NULL)
609 	devsw->old_psize = nopsize;
610     if (devsw->old_kqfilter == NULL)
611 	devsw->old_kqfilter = nokqfilter;
612 
613     if (devsw->d_port == NULL)
614 	devsw->d_port = &devsw_compat_port;
615 }
616 
617 /*
618  * Add a cdevsw entry
619  */
620 int
621 cdevsw_add(struct cdevsw *newentry)
622 {
623     compile_devsw(newentry);
624     if (newentry->d_maj < 0 || newentry->d_maj >= NUMCDEVSW) {
625 	printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
626 	    newentry->d_name, newentry->d_maj);
627 	return (EINVAL);
628     }
629     if (cdevsw[newentry->d_maj]) {
630 	printf("WARNING: \"%s\" is usurping \"%s\"'s cdevsw[]\n",
631 	    newentry->d_name, cdevsw[newentry->d_maj]->d_name);
632     }
633     cdevsw[newentry->d_maj] = newentry;
634     return (0);
635 }
636 
637 /*
638  * Add a cdevsw entry and override the port.
639  */
640 lwkt_port_t
641 cdevsw_add_override(struct cdevsw *newentry, lwkt_port_t port)
642 {
643     int error;
644 
645     if ((error = cdevsw_add(newentry)) == 0)
646 	cdevport[newentry->d_maj] = port;
647     return(newentry->d_port);
648 }
649 
650 lwkt_port_t
651 cdevsw_dev_override(dev_t dev, lwkt_port_t port)
652 {
653     struct cdevsw *csw;
654 
655     KKASSERT(major(dev) >= 0 && major(dev) < NUMCDEVSW);
656     if ((csw = _devsw(dev)) != NULL) {
657 	cdevport[major(dev)] = port;
658 	return(csw->d_port);
659     }
660     return(NULL);
661 }
662 
663 /*
664  *  Remove a cdevsw entry
665  */
666 int
667 cdevsw_remove(struct cdevsw *oldentry)
668 {
669     if (oldentry->d_maj < 0 || oldentry->d_maj >= NUMCDEVSW) {
670 	printf("%s: ERROR: driver has bogus cdevsw->d_maj = %d\n",
671 	    oldentry->d_name, oldentry->d_maj);
672 	return EINVAL;
673     }
674     cdevsw[oldentry->d_maj] = NULL;
675     cdevport[oldentry->d_maj] = NULL;
676     return 0;
677 }
678 
679