vncfb.c revision 1.9 1 /* $NetBSD: vncfb.c,v 1.9 2011/12/30 19:32:32 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) invisible.ca>
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 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Jared D. McNeill.
18 * 4. Neither the name of The NetBSD Foundation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include "opt_wsemul.h"
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: vncfb.c,v 1.9 2011/12/30 19:32:32 jmcneill Exp $");
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/device.h>
44 #include <sys/kmem.h>
45
46 #include <uvm/uvm_extern.h>
47
48 #include <dev/wscons/wsconsio.h>
49
50 #include <dev/wscons/wsdisplayvar.h>
51 #include <dev/wsfont/wsfont.h>
52 #include <dev/rasops/rasops.h>
53 #include <dev/wscons/wsdisplay_vconsvar.h>
54
55 #include <dev/wscons/wskbdvar.h>
56 #include <dev/wscons/wsksymdef.h>
57 #include <dev/wscons/wsksymvar.h>
58
59 #include <machine/mainbus.h>
60 #include <machine/thunk.h>
61
62 #define VNCFB_REFRESH_INTERVAL 33 /* fb refresh interval when mapped */
63
64 struct vncfb_fbops {
65 void (*copycols)(void *, int, int, int, int);
66 void (*erasecols)(void *, int, int, int, long);
67 void (*copyrows)(void *, int, int, int);
68 void (*eraserows)(void *, int, int, long);
69 void (*putchar)(void *, int, int, u_int, long);
70 void (*cursor)(void *, int, int, int);
71 };
72
73 struct vncfb_softc {
74 device_t sc_dev;
75 device_t sc_wskbddev;
76 thunk_rfb_t sc_rfb;
77 unsigned int sc_width;
78 unsigned int sc_height;
79 unsigned int sc_depth;
80 int sc_mode;
81 uint8_t * sc_mem;
82 size_t sc_memsize;
83 uint8_t * sc_framebuf;
84 size_t sc_framebufsize;
85 struct vcons_data sc_vd;
86 struct vncfb_fbops sc_ops;
87
88 int sc_kbd_enable;
89
90 void *sc_ih;
91 void *sc_sih;
92
93 callout_t sc_callout;
94 void *sc_refresh_sih;
95 };
96
97 static int vncfb_match(device_t, cfdata_t, void *);
98 static void vncfb_attach(device_t, device_t, void *);
99
100 CFATTACH_DECL_NEW(vncfb, sizeof(struct vncfb_softc),
101 vncfb_match, vncfb_attach, NULL, NULL);
102
103 static void vncfb_putchar(void *, int, int, u_int, long);
104 static void vncfb_copycols(void *, int, int, int, int);
105 static void vncfb_erasecols(void *, int, int, int, long);
106 static void vncfb_copyrows(void *, int, int, int);
107 static void vncfb_eraserows(void *, int, int, long);
108 static void vncfb_cursor(void *, int, int, int);
109
110 static int vncfb_ioctl(void *, void *, u_long, void *, int, lwp_t *);
111 static paddr_t vncfb_mmap(void *, void *, off_t, int);
112
113 static void vncfb_init_screen(void *, struct vcons_screen *, int, long *);
114
115 static void vncfb_update(struct vncfb_softc *, int, int, int, int);
116 static void vncfb_copyrect(struct vncfb_softc *, int, int, int, int, int, int);
117 static void vncfb_fillrect(struct vncfb_softc *, int, int, int, int, uint32_t);
118 static int vncfb_intr(void *);
119 static void vncfb_softintr(void *);
120 static void vncfb_refresh(void *);
121 static void vncfb_softrefresh(void *);
122
123 static int vncfb_kbd_enable(void *, int);
124 static void vncfb_kbd_set_leds(void *, int);
125 static int vncfb_kbd_ioctl(void *, u_long, void *, int, lwp_t *);
126
127 static void vncfb_kbd_cngetc(void *, u_int *, int *);
128 static void vncfb_kbd_cnpollc(void *, int);
129 static void vncfb_kbd_bell(void *, u_int, u_int, u_int);
130
131 static struct vcons_screen vncfb_console_screen;
132
133 static struct wsscreen_descr vncfb_defaultscreen = {
134 .name = "default",
135 .fontwidth = 8,
136 .fontheight = 16,
137 .capabilities = WSSCREEN_WSCOLORS | WSSCREEN_HILIT,
138 };
139
140 static const struct wsscreen_descr *vncfb_screens[] = {
141 &vncfb_defaultscreen,
142 };
143
144 static struct wsscreen_list vncfb_screenlist = {
145 .screens = vncfb_screens,
146 .nscreens = __arraycount(vncfb_screens),
147 };
148
149 static struct wsdisplay_accessops vncfb_accessops = {
150 .ioctl = vncfb_ioctl,
151 .mmap = vncfb_mmap,
152 };
153
154 extern const struct wscons_keydesc vnckbd_keydesctab[];
155
156 static const struct wskbd_mapdata vncfb_keymapdata = {
157 vnckbd_keydesctab,
158 KB_US,
159 };
160
161 static struct wskbd_accessops vncfb_kbd_accessops = {
162 vncfb_kbd_enable,
163 vncfb_kbd_set_leds,
164 vncfb_kbd_ioctl,
165 };
166
167 static const struct wskbd_consops vncfb_kbd_consops = {
168 vncfb_kbd_cngetc,
169 vncfb_kbd_cnpollc,
170 vncfb_kbd_bell,
171 };
172
173 static int
174 vncfb_match(device_t parent, cfdata_t match, void *priv)
175 {
176 struct thunkbus_attach_args *taa = priv;
177
178 return taa->taa_type == THUNKBUS_TYPE_VNCFB;
179 }
180
181 static void
182 vncfb_attach(device_t parent, device_t self, void *priv)
183 {
184 struct vncfb_softc *sc = device_private(self);
185 struct thunkbus_attach_args *taa = priv;
186 struct wsemuldisplaydev_attach_args waa;
187 struct wskbddev_attach_args kaa;
188 struct rasops_info *ri;
189 unsigned long defattr;
190
191 sc->sc_dev = self;
192 sc->sc_width = taa->u.vnc.width;
193 sc->sc_height = taa->u.vnc.height;
194 sc->sc_depth = 32;
195 sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
196 #if notyet
197 sc->sc_sockfd = thunk_vnc_open_socket(taa->u.vnc.port);
198 if (sc->sc_sockfd == -1)
199 panic("couldn't open VNC socket");
200 #endif
201
202 sc->sc_framebufsize = sc->sc_width * sc->sc_height * (sc->sc_depth / 8);
203 sc->sc_memsize = sc->sc_framebufsize + PAGE_SIZE;
204
205 sc->sc_mem = kmem_zalloc(sc->sc_memsize, KM_SLEEP);
206 sc->sc_framebuf = (void *)round_page((vaddr_t)sc->sc_mem);
207
208 aprint_naive("\n");
209 aprint_normal(": %ux%u %ubpp (port %u)\n",
210 sc->sc_width, sc->sc_height, sc->sc_depth, taa->u.vnc.port);
211 aprint_normal_dev(self, "mem @ %p\n", sc->sc_mem);
212 aprint_normal_dev(self, "fb @ %p\n", sc->sc_framebuf);
213
214 sc->sc_rfb.width = sc->sc_width;
215 sc->sc_rfb.height = sc->sc_height;
216 sc->sc_rfb.depth = sc->sc_depth;
217 sc->sc_rfb.framebuf = sc->sc_framebuf;
218 snprintf(sc->sc_rfb.name, sizeof(sc->sc_rfb.name),
219 "NetBSD/usermode %d.%d.%d",
220 __NetBSD_Version__ / 100000000,
221 (__NetBSD_Version__ / 1000000) % 100,
222 (__NetBSD_Version__ / 100) % 100);
223 if (thunk_rfb_open(&sc->sc_rfb, taa->u.vnc.port) != 0)
224 panic("couldn't open rfb server");
225
226 sc->sc_sih = softint_establish(SOFTINT_SERIAL, vncfb_softintr, sc);
227 sc->sc_ih = sigio_intr_establish(vncfb_intr, sc);
228
229 sc->sc_refresh_sih = softint_establish(SOFTINT_SERIAL,
230 vncfb_softrefresh, sc);
231
232 callout_init(&sc->sc_callout, 0);
233 callout_setfunc(&sc->sc_callout, vncfb_refresh, sc);
234
235 vcons_init(&sc->sc_vd, sc, &vncfb_defaultscreen, &vncfb_accessops);
236 sc->sc_vd.init_screen = vncfb_init_screen;
237
238 ri = &vncfb_console_screen.scr_ri;
239 vcons_init_screen(&sc->sc_vd, &vncfb_console_screen, 1, &defattr);
240 vncfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
241 vncfb_defaultscreen.textops = &ri->ri_ops;
242 vncfb_defaultscreen.capabilities = ri->ri_caps;
243 vncfb_defaultscreen.nrows = ri->ri_rows;
244 vncfb_defaultscreen.ncols = ri->ri_cols;
245 wsdisplay_cnattach(&vncfb_defaultscreen, ri, 0, 0, defattr);
246
247 vcons_replay_msgbuf(&vncfb_console_screen);
248
249 waa.console = true;
250 waa.scrdata = &vncfb_screenlist;
251 waa.accessops = &vncfb_accessops;
252 waa.accesscookie = &sc->sc_vd;
253
254 config_found(self, &waa, wsemuldisplaydevprint);
255
256 wskbd_cnattach(&vncfb_kbd_consops, sc, &vncfb_keymapdata);
257
258 kaa.console = true;
259 kaa.keymap = &vncfb_keymapdata;
260 kaa.accessops = &vncfb_kbd_accessops;
261 kaa.accesscookie = sc;
262
263 sc->sc_wskbddev = config_found_ia(self, "wskbddev", &kaa,
264 wskbddevprint);
265 }
266
267 static void
268 vncfb_init_screen(void *priv, struct vcons_screen *scr, int existing,
269 long *defattr)
270 {
271 struct vncfb_softc *sc = priv;
272 struct vncfb_fbops *ops = &sc->sc_ops;
273 struct rasops_info *ri = &scr->scr_ri;
274
275 ri->ri_width = sc->sc_width;
276 ri->ri_height = sc->sc_height;
277 ri->ri_depth = sc->sc_depth;
278 ri->ri_stride = sc->sc_width * ri->ri_depth / 8;
279 ri->ri_bits = sc->sc_framebuf;
280 ri->ri_flg = RI_CENTER | RI_FULLCLEAR;
281 if (existing)
282 ri->ri_flg |= RI_CLEAR;
283
284 rasops_init(ri, sc->sc_height / 8, sc->sc_width / 8);
285 ri->ri_caps = WSSCREEN_WSCOLORS;
286 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
287 sc->sc_width / ri->ri_font->fontwidth);
288
289 ri->ri_hw = scr;
290
291 ops->putchar = ri->ri_ops.putchar;
292 ops->copyrows = ri->ri_ops.copyrows;
293 ops->eraserows = ri->ri_ops.eraserows;
294 ops->copycols = ri->ri_ops.copycols;
295 ops->erasecols = ri->ri_ops.erasecols;
296 ops->cursor = ri->ri_ops.cursor;
297
298 ri->ri_ops.copyrows = vncfb_copyrows;
299 ri->ri_ops.copycols = vncfb_copycols;
300 ri->ri_ops.eraserows = vncfb_eraserows;
301 ri->ri_ops.erasecols = vncfb_erasecols;
302 ri->ri_ops.putchar = vncfb_putchar;
303 ri->ri_ops.cursor = vncfb_cursor;
304 }
305
306 static void
307 vncfb_putchar(void *priv, int row, int col, u_int c, long attr)
308 {
309 struct rasops_info *ri = priv;
310 struct vcons_screen *scr = ri->ri_hw;
311 struct vncfb_softc *sc = scr->scr_cookie;
312 struct vncfb_fbops *ops = &sc->sc_ops;
313 int x, y, w, h;
314
315 ops->putchar(ri, row, col, c, attr);
316
317 x = ri->ri_xorigin + (col * ri->ri_font->fontwidth);
318 y = ri->ri_yorigin + (row * ri->ri_font->fontheight);
319 w = ri->ri_font->fontwidth;
320 h = ri->ri_font->fontheight;
321
322 vncfb_update(sc, x, y, w, h);
323 }
324
325 static void
326 vncfb_copycols(void *priv, int row, int srccol, int dstcol, int ncols)
327 {
328 struct rasops_info *ri = priv;
329 struct vcons_screen *scr = ri->ri_hw;
330 struct vncfb_softc *sc = scr->scr_cookie;
331 struct vncfb_fbops *ops = &sc->sc_ops;
332 int x, y, w, h;
333
334 ops->copycols(ri, row, srccol, dstcol, ncols);
335
336 y = ri->ri_yorigin + (row * ri->ri_font->fontheight);
337 h = ri->ri_font->fontheight;
338 if (srccol < dstcol) {
339 x = ri->ri_xorigin + (srccol * ri->ri_font->fontwidth);
340 w = (dstcol - srccol) * ri->ri_font->fontwidth;
341
342 } else {
343 x = ri->ri_xorigin + (dstcol * ri->ri_font->fontwidth);
344 w = (srccol - dstcol) * ri->ri_font->fontwidth;
345 }
346
347 vncfb_update(sc, x, y, w, h);
348 }
349
350 static void
351 vncfb_erasecols(void *priv, int row, int startcol, int ncols, long fillattr)
352 {
353 struct rasops_info *ri = priv;
354 struct vcons_screen *scr = ri->ri_hw;
355 struct vncfb_softc *sc = scr->scr_cookie;
356 struct vncfb_fbops *ops = &sc->sc_ops;
357 int x, y, w, h, c;
358
359 ops->erasecols(ri, row, startcol, ncols, fillattr);
360
361 y = ri->ri_yorigin + (row * ri->ri_font->fontheight);
362 h = ri->ri_font->fontheight;
363 x = ri->ri_xorigin + (startcol * ri->ri_font->fontwidth);
364 w = ncols * ri->ri_font->fontwidth;
365 c = ri->ri_devcmap[(fillattr >> 16) & 0xf] & 0xffffff;
366
367 vncfb_fillrect(sc, x, y, w, h, c);
368 }
369
370 static void
371 vncfb_copyrows(void *priv, int srcrow, int dstrow, int nrows)
372 {
373 struct rasops_info *ri = priv;
374 struct vcons_screen *scr = ri->ri_hw;
375 struct vncfb_softc *sc = scr->scr_cookie;
376 struct vncfb_fbops *ops = &sc->sc_ops;
377 int x, y, w, h, srcx, srcy;
378 int fontheight;
379
380 /* barrier */
381 while (sc->sc_rfb.nupdates > 0)
382 if (thunk_rfb_poll(&sc->sc_rfb, NULL) == -1)
383 break;
384
385 ops->copyrows(ri, srcrow, dstrow, nrows);
386
387 fontheight = ri->ri_font->fontheight;
388 x = ri->ri_xorigin;
389 y = ri->ri_yorigin + dstrow * fontheight;
390 w = ri->ri_width;
391 h = nrows * fontheight;
392
393 srcx = ri->ri_xorigin;
394 srcy = ri->ri_yorigin + srcrow * fontheight;
395
396 vncfb_copyrect(sc, x, y, w, h, srcx, srcy);
397 }
398
399 static void
400 vncfb_eraserows(void *priv, int row, int nrows, long fillattr)
401 {
402 struct rasops_info *ri = priv;
403 struct vcons_screen *scr = ri->ri_hw;
404 struct vncfb_softc *sc = scr->scr_cookie;
405 struct vncfb_fbops *ops = &sc->sc_ops;
406 int x, y, w, h, c;
407
408 ops->eraserows(ri, row, nrows, fillattr);
409
410 y = ri->ri_yorigin + (row * ri->ri_font->fontheight);
411 h = nrows * ri->ri_font->fontheight;
412 x = ri->ri_xorigin;
413 w = ri->ri_width;
414 c = ri->ri_devcmap[(fillattr >> 16) & 0xf] & 0xffffff;
415
416 vncfb_fillrect(sc, x, y, w, h, c);
417 }
418
419 static void
420 vncfb_cursor(void *priv, int on, int row, int col)
421 {
422 struct rasops_info *ri = priv;
423 struct vcons_screen *scr = ri->ri_hw;
424 struct vncfb_softc *sc = scr->scr_cookie;
425 struct vncfb_fbops *ops = &sc->sc_ops;
426 int ox, oy, x, y, w, h;
427
428 w = ri->ri_font->fontwidth;
429 h = ri->ri_font->fontheight;
430
431 ox = ri->ri_ccol * w + ri->ri_xorigin;
432 oy = ri->ri_crow * h + ri->ri_yorigin;
433
434 ops->cursor(ri, on, row, col);
435
436 x = ri->ri_ccol * w + ri->ri_xorigin;
437 y = ri->ri_crow * h + ri->ri_yorigin;
438
439 vncfb_update(sc, ox, oy, w, h);
440 vncfb_update(sc, x, y, w, h);
441 }
442
443 static int
444 vncfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l)
445 {
446 struct vcons_data *vd = v;
447 struct vncfb_softc *sc = vd->cookie;
448 struct wsdisplay_fbinfo *wdf;
449 struct vcons_screen *ms = vd->active;
450 int new_mode;
451
452 switch (cmd) {
453 case WSDISPLAYIO_GTYPE:
454 *(u_int *)data = WSDISPLAY_TYPE_VNC;
455 return 0;
456 case WSDISPLAYIO_GINFO:
457 wdf = data;
458 wdf->height = ms->scr_ri.ri_height;
459 wdf->width = ms->scr_ri.ri_width;
460 wdf->depth = ms->scr_ri.ri_depth;
461 wdf->cmsize = 256;
462 return 0;
463 case WSDISPLAYIO_LINEBYTES:
464 *(u_int *)data = sc->sc_width * (sc->sc_depth / 8);
465 return 0;
466 case WSDISPLAYIO_SMODE:
467 new_mode = *(int *)data;
468 if (sc->sc_mode != new_mode) {
469 sc->sc_mode = new_mode;
470 if (new_mode == WSDISPLAYIO_MODE_EMUL) {
471 callout_halt(&sc->sc_callout, NULL);
472 vcons_redraw_screen(ms);
473 } else {
474 callout_schedule(&sc->sc_callout, 1);
475 }
476 }
477 return 0;
478 default:
479 return EPASSTHROUGH;
480 }
481 }
482
483 static paddr_t
484 vncfb_mmap(void *v, void *vs, off_t offset, int prot)
485 {
486 struct vcons_data *vd = v;
487 struct vncfb_softc *sc = vd->cookie;
488 paddr_t pa;
489 vaddr_t va;
490
491 if (offset < 0 || offset + PAGE_SIZE > sc->sc_framebufsize) {
492 device_printf(sc->sc_dev, "mmap: offset 0x%x, fbsize 0x%x"
493 " out of range!\n",
494 (unsigned int)offset, (unsigned int)sc->sc_framebufsize);
495 return -1;
496 }
497
498 va = trunc_page((vaddr_t)sc->sc_framebuf + offset);
499
500 if (pmap_extract(pmap_kernel(), va, &pa) == false) {
501 device_printf(sc->sc_dev, "mmap: pmap_extract failed!\n");
502 return -1;
503 }
504
505 return atop(pa);
506 }
507
508 static void
509 vncfb_update(struct vncfb_softc *sc, int x, int y, int w, int h)
510 {
511 thunk_rfb_update(&sc->sc_rfb, x, y, w, h);
512 softint_schedule(sc->sc_sih);
513 }
514
515 static void
516 vncfb_copyrect(struct vncfb_softc *sc, int x, int y, int w, int h,
517 int srcx, int srcy)
518 {
519 thunk_rfb_copyrect(&sc->sc_rfb, x, y, w, h, srcx, srcy);
520 softint_schedule(sc->sc_sih);
521 }
522
523 static void
524 vncfb_fillrect(struct vncfb_softc *sc, int x, int y, int w, int h, uint32_t c)
525 {
526
527 thunk_rfb_fillrect(&sc->sc_rfb, x, y, w, h, (uint8_t *)&c);
528 softint_schedule(sc->sc_sih);
529 }
530
531 static int
532 vncfb_intr(void *priv)
533 {
534 struct vncfb_softc *sc = priv;
535
536 softint_schedule(sc->sc_sih);
537
538 return 0;
539 }
540
541 static void
542 vncfb_softintr(void *priv)
543 {
544 struct vncfb_softc *sc = priv;
545 thunk_rfb_event_t event;
546 int s;
547
548 while (thunk_rfb_poll(&sc->sc_rfb, &event) > 0) {
549 switch (event.message_type) {
550 case THUNK_RFB_KEY_EVENT:
551 s = spltty();
552 wskbd_input(sc->sc_wskbddev,
553 event.data.key_event.down_flag ?
554 WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP,
555 event.data.key_event.keysym & 0xfff);
556 splx(s);
557 break;
558 default:
559 break;
560 }
561 }
562 }
563
564 static void
565 vncfb_refresh(void *priv)
566 {
567 struct vncfb_softc *sc = priv;
568
569 softint_schedule(sc->sc_refresh_sih);
570 }
571
572 static void
573 vncfb_softrefresh(void *priv)
574 {
575 struct vncfb_softc *sc = priv;
576
577 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)
578 return;
579
580 /* update the screen */
581 vncfb_update(sc, 0, 0, sc->sc_width, sc->sc_height);
582
583 /* flush the pending drawing op */
584 while (thunk_rfb_poll(&sc->sc_rfb, NULL) > 0)
585 ;
586
587 callout_schedule(&sc->sc_callout, mstohz(VNCFB_REFRESH_INTERVAL));
588 }
589
590 static int
591 vncfb_kbd_enable(void *priv, int on)
592 {
593 struct vncfb_softc *sc = priv;
594
595 sc->sc_kbd_enable = on;
596
597 return 0;
598 }
599
600 static void
601 vncfb_kbd_set_leds(void *priv, int leds)
602 {
603 }
604
605 static int
606 vncfb_kbd_ioctl(void *priv, u_long cmd, void *data, int flag, lwp_t *l)
607 {
608 struct wskbd_bell_data *bd;
609
610 switch (cmd) {
611 case WSKBDIO_GTYPE:
612 *(int *)data = WSKBD_TYPE_RFB;
613 return 0;
614 case WSKBDIO_COMPLEXBELL:
615 bd = data;
616 vncfb_kbd_bell(priv, bd->pitch, bd->period, bd->volume);
617 return 0;
618 default:
619 return EPASSTHROUGH;
620 }
621 }
622
623 static void
624 vncfb_kbd_cngetc(void *priv, u_int *type, int *data)
625 {
626 }
627
628 static void
629 vncfb_kbd_cnpollc(void *priv, int on)
630 {
631 }
632
633 static void
634 vncfb_kbd_bell(void *priv, u_int pitch, u_int period, u_int volume)
635 {
636 struct vncfb_softc *sc = priv;
637
638 thunk_rfb_bell(&sc->sc_rfb);
639 softint_schedule(sc->sc_sih);
640 }
641