xref: /dflybsd-src/sys/dev/video/fb/fb.c (revision 0bb7d8c82a64940013681cf515d16f3e62eb7e3c)
1 /*-
2  * (MPSAFE)
3  *
4  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
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 as
12  *    the first lines of this file unmodified.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $FreeBSD: src/sys/dev/fb/fb.c,v 1.11.2.2 2000/08/02 22:35:22 peter Exp $
31  * $DragonFly: src/sys/dev/video/fb/fb.c,v 1.19 2008/01/09 21:29:11 swildner Exp $
32  */
33 
34 #include "opt_fb.h"
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/conf.h>
39 #include <sys/bus.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/uio.h>
43 #include <sys/fbio.h>
44 #include <sys/linker_set.h>
45 #include <sys/device.h>
46 #include <sys/thread2.h>
47 
48 #include <vm/vm.h>
49 #include <vm/pmap.h>
50 
51 #include "fbreg.h"
52 
53 SET_DECLARE(videodriver_set, const video_driver_t);
54 
55 /* local arrays */
56 
57 /*
58  * We need at least one entry each in order to initialize a video card
59  * for the kernel console.  The arrays will be increased dynamically
60  * when necessary.
61  */
62 
63 static int		vid_malloc;
64 static int		adapters = 1;
65 static video_adapter_t	*adp_ini;
66 static video_adapter_t	**adapter = &adp_ini;
67 static video_switch_t	*vidsw_ini;
68        video_switch_t	**vidsw = &vidsw_ini;
69 
70 #ifdef FB_INSTALL_CDEV
71 static cdev_t	vidcdevsw_ini;
72 static cdev_t	*vidcdevsw = &vidcdevsw_ini;
73 #endif
74 
75 #define ARRAY_DELTA	4
76 
77 static int
78 vid_realloc_array(void)
79 {
80 	video_adapter_t **new_adp;
81 	video_switch_t **new_vidsw;
82 #ifdef FB_INSTALL_CDEV
83 	cdev_t *new_cdevsw;
84 #endif
85 	int newsize;
86 
87 	if (!vid_malloc)
88 		return ENOMEM;
89 
90 	crit_enter();
91 	newsize = ((adapters + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA;
92 	new_adp = kmalloc(sizeof(*new_adp)*newsize, M_DEVBUF, M_WAITOK | M_ZERO);
93 	new_vidsw = kmalloc(sizeof(*new_vidsw)*newsize, M_DEVBUF,
94 	    M_WAITOK | M_ZERO);
95 #ifdef FB_INSTALL_CDEV
96 	new_cdevsw = kmalloc(sizeof(*new_cdevsw)*newsize, M_DEVBUF,
97 	    M_WAITOK | M_ZERO);
98 #endif
99 	bcopy(adapter, new_adp, sizeof(*adapter)*adapters);
100 	bcopy(vidsw, new_vidsw, sizeof(*vidsw)*adapters);
101 #ifdef FB_INSTALL_CDEV
102 	bcopy(vidcdevsw, new_cdevsw, sizeof(*vidcdevsw)*adapters);
103 #endif
104 	if (adapters > 1) {
105 		kfree(adapter, M_DEVBUF);
106 		kfree(vidsw, M_DEVBUF);
107 #ifdef FB_INSTALL_CDEV
108 		kfree(vidcdevsw, M_DEVBUF);
109 #endif
110 	}
111 	adapter = new_adp;
112 	vidsw = new_vidsw;
113 #ifdef FB_INSTALL_CDEV
114 	vidcdevsw = new_cdevsw;
115 #endif
116 	adapters = newsize;
117 	crit_exit();
118 
119 	if (bootverbose)
120 		kprintf("fb: new array size %d\n", adapters);
121 
122 	return 0;
123 }
124 
125 static void
126 vid_malloc_init(void *arg)
127 {
128 	vid_malloc = TRUE;
129 }
130 
131 SYSINIT(vid_mem, SI_BOOT1_POST, SI_ORDER_ANY, vid_malloc_init, NULL);
132 
133 /*
134  * Low-level frame buffer driver functions
135  * frame buffer subdrivers, such as the VGA driver, call these functions
136  * to initialize the video_adapter structure and register it to the virtual
137  * frame buffer driver `fb'.
138  */
139 
140 /* initialize the video_adapter_t structure */
141 void
142 vid_init_struct(video_adapter_t *adp, char *name, int type, int unit)
143 {
144 	adp->va_flags = 0;
145 	adp->va_name = name;
146 	adp->va_type = type;
147 	adp->va_unit = unit;
148 }
149 
150 /* Register a video adapter */
151 int
152 vid_register(video_adapter_t *adp)
153 {
154 	const video_driver_t **list;
155 	const video_driver_t *p;
156 	int index;
157 
158 	for (index = 0; index < adapters; ++index) {
159 		if (adapter[index] == NULL)
160 			break;
161 	}
162 	if (index >= adapters) {
163 		if (vid_realloc_array())
164 			return -1;
165 	}
166 
167 	adp->va_index = index;
168 	adp->va_token = NULL;
169 	lwkt_gettoken(&tty_token);
170 	SET_FOREACH(list, videodriver_set) {
171 		p = *list;
172 		if (strcmp(p->name, adp->va_name) == 0) {
173 			adapter[index] = adp;
174 			vidsw[index] = p->vidsw;
175 			lwkt_reltoken(&tty_token);
176 			return index;
177 		}
178 	}
179 
180 	lwkt_reltoken(&tty_token);
181 	return -1;
182 }
183 
184 int
185 vid_unregister(video_adapter_t *adp)
186 {
187 	lwkt_gettoken(&tty_token);
188 	if ((adp->va_index < 0) || (adp->va_index >= adapters)) {
189 		lwkt_reltoken(&tty_token);
190 		return ENOENT;
191 	}
192 	if (adapter[adp->va_index] != adp) {
193 		lwkt_reltoken(&tty_token);
194 		return ENOENT;
195 	}
196 
197 	adapter[adp->va_index] = NULL;
198 	vidsw[adp->va_index] = NULL;
199 	lwkt_reltoken(&tty_token);
200 	return 0;
201 }
202 
203 /* Get video I/O function table */
204 video_switch_t *
205 vid_get_switch(char *name)
206 {
207 	const video_driver_t **list;
208 	const video_driver_t *p;
209 
210 	lwkt_gettoken(&tty_token);
211 	SET_FOREACH(list, videodriver_set) {
212 		p = *list;
213 		if (strcmp(p->name, name) == 0) {
214 			lwkt_reltoken(&tty_token);
215 			return p->vidsw;
216 		}
217 	}
218 
219 	lwkt_reltoken(&tty_token);
220 	return NULL;
221 }
222 
223 /*
224  * Video card client functions
225  * Video card clients, such as the console driver `syscons' and the frame
226  * buffer cdev driver, use these functions to claim and release a card for
227  * exclusive use.
228  */
229 
230 /* find the video card specified by a driver name and a unit number */
231 int
232 vid_find_adapter(char *driver, int unit)
233 {
234 	int i;
235 
236 	for (i = 0; i < adapters; ++i) {
237 		if (adapter[i] == NULL)
238 			continue;
239 		if (strcmp("*", driver) && strcmp(adapter[i]->va_name, driver))
240 			continue;
241 		if ((unit != -1) && (adapter[i]->va_unit != unit))
242 			continue;
243 		return i;
244 	}
245 	return -1;
246 }
247 
248 /* allocate a video card */
249 int
250 vid_allocate(char *driver, int unit, void *id)
251 {
252 	int index;
253 
254 	crit_enter();
255 	index = vid_find_adapter(driver, unit);
256 	if (index >= 0) {
257 		if (adapter[index]->va_token) {
258 			crit_exit();
259 			return -1;
260 		}
261 		adapter[index]->va_token = id;
262 	}
263 	crit_exit();
264 	return index;
265 }
266 
267 int
268 vid_release(video_adapter_t *adp, void *id)
269 {
270 	int error;
271 
272 	crit_enter();
273 	if (adp->va_token == NULL) {
274 		error = EINVAL;
275 	} else if (adp->va_token != id) {
276 		error = EPERM;
277 	} else {
278 		adp->va_token = NULL;
279 		error = 0;
280 	}
281 	crit_exit();
282 	return error;
283 }
284 
285 /* Get a video adapter structure */
286 video_adapter_t *
287 vid_get_adapter(int index)
288 {
289 	if ((index < 0) || (index >= adapters))
290 		return NULL;
291 	return adapter[index];
292 }
293 
294 /* Configure drivers: this is a backdoor for the console driver XXX */
295 int
296 vid_configure(int flags)
297 {
298 	const video_driver_t **list;
299 	const video_driver_t *p;
300 
301 	SET_FOREACH(list, videodriver_set) {
302 		p = *list;
303 		if (p->configure != NULL)
304 			(*p->configure)(flags);
305 	}
306 
307 	return 0;
308 }
309 
310 /*
311  * Virtual frame buffer cdev driver functions
312  * The virtual frame buffer driver dispatches driver functions to
313  * appropriate subdrivers.
314  */
315 
316 #define FB_DRIVER_NAME	"fb"
317 
318 #ifdef FB_INSTALL_CDEV
319 
320 #if experimental
321 
322 static devclass_t	fb_devclass;
323 
324 static int		fbprobe(device_t dev);
325 static int		fbattach(device_t dev);
326 
327 static device_method_t fb_methods[] = {
328 	DEVMETHOD(device_probe,		fbprobe),
329 	DEVMETHOD(device_attach,	fbattach),
330 
331 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
332 	{ 0, 0 }
333 };
334 
335 static driver_t fb_driver = {
336 	FB_DRIVER_NAME,
337 	fb_methods,
338 	0,
339 };
340 
341 static int
342 fbprobe(device_t dev)
343 {
344 	int unit;
345 
346 	unit = device_get_unit(dev);
347 	if (unit >= adapters)
348 		return ENXIO;
349 	if (adapter[unit] == NULL)
350 		return ENXIO;
351 
352 	device_set_desc(dev, "generic frame buffer");
353 	return 0;
354 }
355 
356 static int
357 fbattach(device_t dev)
358 {
359 	kprintf("fbattach: about to attach children\n");
360 	bus_generic_attach(dev);
361 	return 0;
362 }
363 
364 #endif /* experimental */
365 
366 #define FB_UNIT(dev)	minor(dev)
367 #define FB_MKMINOR(unit) (u)
368 
369 #if 0
370 static d_default_t	fboperate;
371 static d_open_t		fbopen;
372 
373 #define CDEV_MAJOR	123	/* XXX */
374 
375 static struct dev_ops fb_ops = {
376 	{ FB_DRIVER_NAME, CDEV_MAJOR, 0 },
377 	.d_default =	fboperate,
378 	.d_open =	fbopen
379 };
380 #endif
381 
382 static void
383 vfbattach(void *arg)
384 {
385 	static int fb_devsw_installed = FALSE;
386 
387 	if (!fb_devsw_installed) {
388 #if 0
389 		dev_ops_add(&fb_ops, 0, 0);
390 #endif
391 		fb_devsw_installed = TRUE;
392 	}
393 }
394 
395 PSEUDO_SET(vfbattach, fb);
396 
397 /*
398  *  Note: dev represents the actual video device, not the frame buffer
399  */
400 int
401 fb_attach(cdev_t dev, video_adapter_t *adp)
402 {
403 	if (adp->va_index >= adapters)
404 		return EINVAL;
405 	if (adapter[adp->va_index] != adp)
406 		return EINVAL;
407 
408 	crit_enter();
409 	reference_dev(dev);
410 	adp->va_minor = minor(dev);
411 	vidcdevsw[adp->va_index] = dev;
412 	crit_exit();
413 
414 	kprintf("fb%d at %s%d\n", adp->va_index, adp->va_name, adp->va_unit);
415 	return 0;
416 }
417 
418 /*
419  *  Note: dev represents the actual video device, not the frame buffer
420  */
421 int
422 fb_detach(cdev_t dev, video_adapter_t *adp)
423 {
424 	if (adp->va_index >= adapters)
425 		return EINVAL;
426 	if (adapter[adp->va_index] != adp)
427 		return EINVAL;
428 	if (vidcdevsw[adp->va_index] != dev)
429 		return EINVAL;
430 
431 	crit_enter();
432 	vidcdevsw[adp->va_index] = NULL;
433 	crit_exit();
434 	release_dev(dev);
435 	return 0;
436 }
437 
438 #if 0
439 static int
440 fbopen(struct dev_open_args *ap)
441 {
442 	cdev_t dev = ap->a_head.a_dev;
443 	int unit;
444 	cdev_t fdev;
445 
446 	unit = FB_UNIT(dev);
447 	if (unit < 0 || unit >= adapters)
448 		return ENXIO;
449 	if ((fdev = vidcdevsw[unit]) == NULL)
450 		return ENXIO;
451 	return dev_dopen(fdev, ap->a_oflags, ap->a_devtype, ap->a_cred);
452 }
453 
454 static int
455 fboperate(struct dev_generic_args *ap)
456 {
457 	cdev_t dev = ap->a_dev;
458 	int unit;
459 	cdev_t fdev;
460 
461 	unit = FB_UNIT(dev);
462 	if ((fdev = vidcdevsw[unit]) == NULL)
463 		return ENXIO;
464 	ap->a_dev = fdev;
465 	return dev_doperate(ap);
466 }
467 #endif
468 
469 /*
470  * Generic frame buffer cdev driver functions
471  * Frame buffer subdrivers may call these functions to implement common
472  * driver functions.
473  */
474 
475 int genfbopen(genfb_softc_t *sc, video_adapter_t *adp, int flag, int mode,
476 	      struct ucred *cred)
477 {
478 	crit_enter();
479 	if (!(sc->gfb_flags & FB_OPEN))
480 		sc->gfb_flags |= FB_OPEN;
481 	crit_exit();
482 	return 0;
483 }
484 
485 int genfbclose(genfb_softc_t *sc, video_adapter_t *adp, int flag, int mode)
486 {
487 	crit_enter();
488 	sc->gfb_flags &= ~FB_OPEN;
489 	crit_exit();
490 	return 0;
491 }
492 
493 int genfbread(genfb_softc_t *sc, video_adapter_t *adp, struct uio *uio,
494 	      int flag)
495 {
496 	int size;
497 	int offset;
498 	int error;
499 	int len;
500 
501 	lwkt_gettoken(&tty_token);
502 	error = 0;
503 	size = adp->va_buffer_size/adp->va_info.vi_planes;
504 	while (uio->uio_resid > 0) {
505 		if (uio->uio_offset >= size)
506 			break;
507 		offset = uio->uio_offset%adp->va_window_size;
508 		len = (int)szmin(uio->uio_resid, size - uio->uio_offset);
509 		len = imin(len, adp->va_window_size - offset);
510 		if (len <= 0)
511 			break;
512 		(*vidsw[adp->va_index]->set_win_org)(adp, uio->uio_offset);
513 		error = uiomove((caddr_t)(adp->va_window + offset),
514 				(size_t)len, uio);
515 		if (error)
516 			break;
517 	}
518 	lwkt_reltoken(&tty_token);
519 	return error;
520 }
521 
522 int genfbwrite(genfb_softc_t *sc, video_adapter_t *adp, struct uio *uio,
523 	       int flag)
524 {
525 	return ENODEV;
526 }
527 
528 int genfbioctl(genfb_softc_t *sc, video_adapter_t *adp, u_long cmd,
529 	       caddr_t arg, int flag, struct ucred *cred)
530 {
531 	int error;
532 
533 	if (adp == NULL)	/* XXX */
534 		return ENXIO;
535 	lwkt_gettoken(&tty_token);
536 	error = (*vidsw[adp->va_index]->ioctl)(adp, cmd, arg);
537 	if (error == ENOIOCTL)
538 		error = ENODEV;
539 	lwkt_reltoken(&tty_token);
540 	return error;
541 }
542 
543 int genfbmmap(genfb_softc_t *sc, video_adapter_t *adp, vm_offset_t offset,
544 	      int prot)
545 {
546 	int error;
547 
548 	lwkt_gettoken(&tty_token);
549 	error = (*vidsw[adp->va_index]->mmap)(adp, offset, prot);
550 	lwkt_reltoken(&tty_token);
551 	return (error);
552 }
553 
554 #endif /* FB_INSTALL_CDEV */
555 
556 static char *
557 adapter_name(int type)
558 {
559     static struct {
560 	int type;
561 	char *name;
562     } names[] = {
563 	{ KD_MONO,	"MDA" },
564 	{ KD_HERCULES,	"Hercules" },
565 	{ KD_CGA,	"CGA" },
566 	{ KD_EGA,	"EGA" },
567 	{ KD_VGA,	"VGA" },
568 	{ KD_TGA,	"TGA" },
569 	{ -1,		"Unknown" },
570     };
571     int i;
572 
573     for (i = 0; names[i].type != -1; ++i)
574 	if (names[i].type == type)
575 	    break;
576     return names[i].name;
577 }
578 
579 /*
580  * Generic low-level frame buffer functions
581  * The low-level functions in the frame buffer subdriver may use these
582  * functions.
583  */
584 
585 void
586 fb_dump_adp_info(char *driver, video_adapter_t *adp, int level)
587 {
588     if (level <= 0)
589 	return;
590 
591     kprintf("%s%d: %s%d, %s, type:%s (%d), flags:0x%x\n",
592 	   FB_DRIVER_NAME, adp->va_index, driver, adp->va_unit, adp->va_name,
593 	   adapter_name(adp->va_type), adp->va_type, adp->va_flags);
594     kprintf("%s%d: port:0x%x-0x%x, mem:0x%x 0x%x\n",
595 	   FB_DRIVER_NAME, adp->va_index,
596 	   adp->va_io_base, adp->va_io_base + adp->va_io_size - 1,
597 	   adp->va_mem_base, adp->va_mem_size);
598     kprintf("%s%d: init mode:%d, bios mode:%d, current mode:%d\n",
599 	   FB_DRIVER_NAME, adp->va_index,
600 	   adp->va_initial_mode, adp->va_initial_bios_mode, adp->va_mode);
601     kprintf("%s%d: window:%p size:%dk gran:%dk, buf:%p size:%dk\n",
602 	   FB_DRIVER_NAME, adp->va_index,
603 	   (void *)adp->va_window, (int)adp->va_window_size/1024,
604 	   (int)adp->va_window_gran/1024, (void *)adp->va_buffer,
605 	   (int)adp->va_buffer_size/1024);
606 }
607 
608 void
609 fb_dump_mode_info(char *driver, video_adapter_t *adp, video_info_t *info,
610 		  int level)
611 {
612     if (level <= 0)
613 	return;
614 
615     kprintf("%s%d: %s, mode:%d, flags:0x%x ",
616 	   driver, adp->va_unit, adp->va_name, info->vi_mode, info->vi_flags);
617     if (info->vi_flags & V_INFO_GRAPHICS)
618 	kprintf("G %dx%dx%d, %d plane(s), font:%dx%d, ",
619 	       info->vi_width, info->vi_height,
620 	       info->vi_depth, info->vi_planes,
621 	       info->vi_cwidth, info->vi_cheight);
622     else
623 	kprintf("T %dx%d, font:%dx%d, ",
624 	       info->vi_width, info->vi_height,
625 	       info->vi_cwidth, info->vi_cheight);
626     kprintf("win:0x%x\n", info->vi_window);
627 }
628 
629 int
630 fb_type(int adp_type)
631 {
632 	static struct {
633 		int	fb_type;
634 		int	va_type;
635 	} types[] = {
636 		{ FBTYPE_MDA,		KD_MONO },
637 		{ FBTYPE_HERCULES,	KD_HERCULES },
638 		{ FBTYPE_CGA,		KD_CGA },
639 		{ FBTYPE_EGA,		KD_EGA },
640 		{ FBTYPE_VGA,		KD_VGA },
641 		{ FBTYPE_TGA,		KD_TGA },
642 	};
643 	int i;
644 
645 	for (i = 0; i < sizeof(types)/sizeof(types[0]); ++i) {
646 		if (types[i].va_type == adp_type)
647 			return types[i].fb_type;
648 	}
649 	return -1;
650 }
651 
652 int
653 fb_commonioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
654 {
655 	int error;
656 
657 	/* assert(adp != NULL) */
658 
659 	error = 0;
660 	crit_enter();
661 
662 	lwkt_gettoken(&tty_token);
663 	switch (cmd) {
664 
665 	case FBIO_ADAPTER:	/* get video adapter index */
666 		*(int *)arg = adp->va_index;
667 		break;
668 
669 	case FBIO_ADPTYPE:	/* get video adapter type */
670 		*(int *)arg = adp->va_type;
671 		break;
672 
673 	case FBIO_ADPINFO:	/* get video adapter info */
674 	        ((video_adapter_info_t *)arg)->va_index = adp->va_index;
675 		((video_adapter_info_t *)arg)->va_type = adp->va_type;
676 		bcopy(adp->va_name, ((video_adapter_info_t *)arg)->va_name,
677 		      imin(strlen(adp->va_name) + 1,
678 			   sizeof(((video_adapter_info_t *)arg)->va_name)));
679 		((video_adapter_info_t *)arg)->va_unit = adp->va_unit;
680 		((video_adapter_info_t *)arg)->va_flags = adp->va_flags;
681 		((video_adapter_info_t *)arg)->va_io_base = adp->va_io_base;
682 		((video_adapter_info_t *)arg)->va_io_size = adp->va_io_size;
683 		((video_adapter_info_t *)arg)->va_mem_base = adp->va_mem_base;
684 		((video_adapter_info_t *)arg)->va_mem_size = adp->va_mem_size;
685 		((video_adapter_info_t *)arg)->va_window
686 #ifdef __i386__
687 			= vtophys(adp->va_window);
688 #else
689 			= adp->va_window;
690 #endif
691 		((video_adapter_info_t *)arg)->va_window_size
692 			= adp->va_window_size;
693 		((video_adapter_info_t *)arg)->va_window_gran
694 			= adp->va_window_gran;
695 		((video_adapter_info_t *)arg)->va_window_orig
696 			= adp->va_window_orig;
697 		((video_adapter_info_t *)arg)->va_unused0
698 #ifdef __i386__
699 			= (adp->va_buffer) ? vtophys(adp->va_buffer) : 0;
700 #else
701 			= adp->va_buffer;
702 #endif
703 		((video_adapter_info_t *)arg)->va_buffer_size
704 			= adp->va_buffer_size;
705 		((video_adapter_info_t *)arg)->va_mode = adp->va_mode;
706 		((video_adapter_info_t *)arg)->va_initial_mode
707 			= adp->va_initial_mode;
708 		((video_adapter_info_t *)arg)->va_initial_bios_mode
709 			= adp->va_initial_bios_mode;
710 		((video_adapter_info_t *)arg)->va_line_width
711 			= adp->va_line_width;
712 		((video_adapter_info_t *)arg)->va_disp_start.x
713 			= adp->va_disp_start.x;
714 		((video_adapter_info_t *)arg)->va_disp_start.y
715 			= adp->va_disp_start.y;
716 		break;
717 
718 	case FBIO_MODEINFO:	/* get mode information */
719 		error = (*vidsw[adp->va_index]->get_info)(adp,
720 				((video_info_t *)arg)->vi_mode,
721 				(video_info_t *)arg);
722 		if (error)
723 			error = ENODEV;
724 		break;
725 
726 	case FBIO_FINDMODE:	/* find a matching video mode */
727 		error = (*vidsw[adp->va_index]->query_mode)(adp,
728 				(video_info_t *)arg);
729 		break;
730 
731 	case FBIO_GETMODE:	/* get video mode */
732 		*(int *)arg = adp->va_mode;
733 		break;
734 
735 	case FBIO_SETMODE:	/* set video mode */
736 		error = (*vidsw[adp->va_index]->set_mode)(adp, *(int *)arg);
737 		if (error)
738 			error = ENODEV;	/* EINVAL? */
739 		break;
740 
741 	case FBIO_GETWINORG:	/* get frame buffer window origin */
742 		*(u_int *)arg = adp->va_window_orig;
743 		break;
744 
745 	case FBIO_GETDISPSTART:	/* get display start address */
746 		((video_display_start_t *)arg)->x = adp->va_disp_start.x;
747 		((video_display_start_t *)arg)->y = adp->va_disp_start.y;
748 		break;
749 
750 	case FBIO_GETLINEWIDTH:	/* get scan line width in bytes */
751 		*(u_int *)arg = adp->va_line_width;
752 		break;
753 
754 	case FBIO_GETPALETTE:	/* get color palette */
755 	case FBIO_SETPALETTE:	/* set color palette */
756 		/* XXX */
757 
758 	case FBIOPUTCMAP:
759 	case FBIOGETCMAP:
760 		/* XXX */
761 
762 	case FBIO_SETWINORG:	/* set frame buffer window origin */
763 	case FBIO_SETDISPSTART:	/* set display start address */
764 	case FBIO_SETLINEWIDTH:	/* set scan line width in pixel */
765 
766 	case FBIOGTYPE:
767 	case FBIOGATTR:
768 	case FBIOSVIDEO:
769 	case FBIOGVIDEO:
770 	case FBIOSCURSOR:
771 	case FBIOGCURSOR:
772 	case FBIOSCURPOS:
773 	case FBIOGCURPOS:
774 	case FBIOGCURMAX:
775 
776 	default:
777 		error = ENODEV;
778 		break;
779 	}
780 
781 	crit_exit();
782 	lwkt_reltoken(&tty_token);
783 	return error;
784 }
785