1*594f789aSthorpej /* $NetBSD: dnkbd.c,v 1.15 2024/01/16 05:48:28 thorpej Exp $ */
2d380e65dStsutsui /* $OpenBSD: dnkbd.c,v 1.17 2009/07/23 21:05:56 blambert Exp $ */
3d380e65dStsutsui
4d380e65dStsutsui /*
5d380e65dStsutsui * Copyright (c) 2005, Miodrag Vallat
6d380e65dStsutsui * Copyright (c) 1997 Michael Smith. All rights reserved.
7d380e65dStsutsui *
8d380e65dStsutsui * Redistribution and use in source and binary forms, with or without
9d380e65dStsutsui * modification, are permitted provided that the following conditions
10d380e65dStsutsui * are met:
11d380e65dStsutsui * 1. Redistributions of source code must retain the above copyright
12d380e65dStsutsui * notice, this list of conditions and the following disclaimer.
13d380e65dStsutsui * 2. Redistributions in binary form must reproduce the above copyright
14d380e65dStsutsui * notice, this list of conditions and the following disclaimer in the
15d380e65dStsutsui * documentation and/or other materials provided with the distribution.
16d380e65dStsutsui *
17d380e65dStsutsui * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18d380e65dStsutsui * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19d380e65dStsutsui * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20d380e65dStsutsui * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21d380e65dStsutsui * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22d380e65dStsutsui * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23d380e65dStsutsui * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24d380e65dStsutsui * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25d380e65dStsutsui * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26d380e65dStsutsui * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27d380e65dStsutsui * SUCH DAMAGE.
28d380e65dStsutsui */
29d380e65dStsutsui
30d380e65dStsutsui /*
31d380e65dStsutsui * Driver for the Apollo Domain keyboard and mouse.
32619ff4fbStsutsui *
33619ff4fbStsutsui * Random notes on the Apollo keyboard :
34619ff4fbStsutsui *
35619ff4fbStsutsui * - Powers up in 'cooked' mode, where the alpha keys generate ascii rather
36619ff4fbStsutsui * than make/break codes. Other keys seem to behave OK though.
37619ff4fbStsutsui *
38619ff4fbStsutsui * - Alt L/R keys generate two-byte sequence :
39619ff4fbStsutsui * make break
40619ff4fbStsutsui * L 0xfe,2 0xfe,3
41619ff4fbStsutsui * R 0xfe,0 0xfe,1
42619ff4fbStsutsui *
43619ff4fbStsutsui * - Mouse activity shows up inline in 4-byte packets introduced with 0xdf.
44619ff4fbStsutsui * Byte 1 is 1MRL0000 where M, R, L are the mouse buttons, and 0 is
45619ff4fbStsutsui * down, 1 is up.
46619ff4fbStsutsui * Byte 2 is 2's complement X movement, +ve to the right.
47619ff4fbStsutsui * Byte 3 is 2's complement Y movement, +ve is up.
48619ff4fbStsutsui *
49619ff4fbStsutsui * - Keyboard recognises commands sent to it, all preceded by 0xff. Commands
50619ff4fbStsutsui * are echoed once sent completely.
51619ff4fbStsutsui *
52619ff4fbStsutsui * 0x00 go to cooked mode.
53619ff4fbStsutsui * 0x01 go to 'raw' (scancode) mode.
54619ff4fbStsutsui * 0x12,0x21 status report as <id1>\r<id2>\r<model>\r followed by 0xff
55619ff4fbStsutsui * and then the cooked/raw status.
56619ff4fbStsutsui * 0x21,0x81 beep on
57619ff4fbStsutsui * 0x21,0x82 beep off
58619ff4fbStsutsui *
59619ff4fbStsutsui * Some version examples :
60619ff4fbStsutsui *
61619ff4fbStsutsui * <3-@> <1-0> <SD-03687-MS> Apollo p/n 007121 REV 00 ('old-style' US layout)
62619ff4fbStsutsui * <3-@> <2-0> <SD-03683-MS> Apollo p/n 007121 REV 01 ('old-style' US layout)
63619ff4fbStsutsui * <3-@> <2-0> <SD-03980-MS> Apollo 3500? keyboard.
64619ff4fbStsutsui * <3-@> <X-X> <RX-60857-HW> HP p/n A1630-82001 R2
65619ff4fbStsutsui * ('new-style' off 425t, US layout),
66619ff4fbStsutsui * also Apollo p/n 014555-002
67619ff4fbStsutsui * ('new-style' off DN5500, US layout).
68d380e65dStsutsui */
69d380e65dStsutsui
70d19119aaStsutsui #include "opt_wsdisplay_compat.h"
71d19119aaStsutsui
72806e09adStsutsui #include "wsdisplay.h"
73806e09adStsutsui #include "wsmouse.h"
74806e09adStsutsui
75d380e65dStsutsui #include <sys/param.h>
76d380e65dStsutsui #include <sys/systm.h>
77d380e65dStsutsui #include <sys/device.h>
78d380e65dStsutsui #include <sys/ioctl.h>
79d380e65dStsutsui #include <sys/kernel.h>
80d380e65dStsutsui #include <sys/callout.h>
81d380e65dStsutsui #include <sys/conf.h>
82d380e65dStsutsui #include <sys/bus.h>
83d380e65dStsutsui #include <sys/cpu.h>
84d380e65dStsutsui
85d380e65dStsutsui #include <machine/autoconf.h>
86d380e65dStsutsui
87d380e65dStsutsui #include <dev/cons.h>
88d380e65dStsutsui
89d380e65dStsutsui #include <dev/wscons/wsconsio.h>
90d380e65dStsutsui #include <dev/wscons/wskbdvar.h>
91d380e65dStsutsui #include <dev/wscons/wsksymdef.h>
92d380e65dStsutsui #include <dev/wscons/wsksymvar.h>
93806e09adStsutsui #if NWSDISPLAY > 0
94806e09adStsutsui #include <dev/wscons/wsdisplayvar.h>
95806e09adStsutsui #endif
96d380e65dStsutsui #if NWSMOUSE > 0
97d380e65dStsutsui #include <dev/wscons/wsmousevar.h>
98d380e65dStsutsui #endif
99d380e65dStsutsui
100d380e65dStsutsui #include <dev/ic/ns16550reg.h>
101d380e65dStsutsui #include <dev/ic/comreg.h>
102d380e65dStsutsui
103d380e65dStsutsui #include <hp300/dev/dnkbdmap.h>
104d380e65dStsutsui #include <hp300/dev/frodoreg.h>
105d380e65dStsutsui #include <hp300/dev/frodovar.h>
106d380e65dStsutsui
107d380e65dStsutsui #include "hilkbd.h"
108d380e65dStsutsui #include "ioconf.h"
109d380e65dStsutsui
110d380e65dStsutsui /*
111d380e65dStsutsui * Keyboard key codes
112d380e65dStsutsui */
113d380e65dStsutsui
114d380e65dStsutsui #define DNKEY_CAPSLOCK 0x7e
115d380e65dStsutsui #define DNKEY_REPEAT 0x7f
116d380e65dStsutsui #define DNKEY_RELEASE 0x80
117d380e65dStsutsui #define DNKEY_CHANNEL 0xff
118d380e65dStsutsui
119d380e65dStsutsui /*
120d380e65dStsutsui * Channels
121d380e65dStsutsui */
122d380e65dStsutsui
123d380e65dStsutsui #define DNCHANNEL_RESET 0x00
124d380e65dStsutsui #define DNCHANNEL_KBD 0x01
125d380e65dStsutsui #define DNCHANNEL_MOUSE 0x02
126d380e65dStsutsui
127d380e65dStsutsui /*
128d380e65dStsutsui * Keyboard modes
129d380e65dStsutsui */
130d380e65dStsutsui
131d380e65dStsutsui #define DNMODE_COOKED 0x00
132d380e65dStsutsui #define DNMODE_RAW 0x01
133d380e65dStsutsui
134d380e65dStsutsui /*
135d380e65dStsutsui * Keyboard commands
136d380e65dStsutsui */
137d380e65dStsutsui
138d380e65dStsutsui #define DNCMD_PREFIX 0xff
139d380e65dStsutsui #define DNCMD_COOKED DNMODE_COOKED
140d380e65dStsutsui #define DNCMD_RAW DNMODE_RAW
141d380e65dStsutsui #define DNCMD_IDENT_1 0x12
142d380e65dStsutsui #define DNCMD_IDENT_2 0x21
143d380e65dStsutsui
144d380e65dStsutsui /*
145d380e65dStsutsui * Bell commands
146d380e65dStsutsui */
147d380e65dStsutsui
148d380e65dStsutsui #define DNCMD_BELL 0x21
149d380e65dStsutsui #define DNCMD_BELL_ON 0x81
150d380e65dStsutsui #define DNCMD_BELL_OFF 0x82
151d380e65dStsutsui
152d380e65dStsutsui /*
153d380e65dStsutsui * Mouse status
154d380e65dStsutsui */
155d380e65dStsutsui
156d380e65dStsutsui #define DNBUTTON_L 0x10
157d380e65dStsutsui #define DNBUTTON_R 0x20
158d380e65dStsutsui #define DNBUTTON_M 0x40
159d380e65dStsutsui
160d380e65dStsutsui struct dnkbd_softc {
161d380e65dStsutsui device_t sc_dev;
162d380e65dStsutsui bus_space_tag_t sc_bst;
163d380e65dStsutsui bus_space_handle_t sc_bsh;
164d380e65dStsutsui
165d380e65dStsutsui int sc_flags;
166d380e65dStsutsui #define SF_ENABLED 0x01 /* keyboard enabled */
167d380e65dStsutsui #define SF_CONSOLE 0x02 /* keyboard is console */
168d380e65dStsutsui #define SF_POLLING 0x04 /* polling mode */
169d380e65dStsutsui #define SF_PLUGGED 0x08 /* keyboard has been seen plugged */
170d380e65dStsutsui #define SF_ATTACHED 0x10 /* subdevices have been attached */
171d380e65dStsutsui #define SF_MOUSE 0x20 /* mouse enabled */
172d380e65dStsutsui #define SF_BELL 0x40 /* bell is active */
173d380e65dStsutsui #define SF_BELL_TMO 0x80 /* bell stop timeout is scheduled */
174d380e65dStsutsui
175d380e65dStsutsui u_int sc_identlen;
176d380e65dStsutsui #define MAX_IDENTLEN 32
177d380e65dStsutsui char sc_ident[MAX_IDENTLEN];
178d380e65dStsutsui kbd_t sc_layout;
179d380e65dStsutsui
180d380e65dStsutsui enum { STATE_KEYBOARD, STATE_MOUSE, STATE_CHANNEL, STATE_ECHO }
181d380e65dStsutsui sc_state, sc_prevstate;
182d380e65dStsutsui u_int sc_echolen;
183d380e65dStsutsui
1845d7f465dStsutsui uint8_t sc_mousepkt[3]; /* mouse packet being constructed */
185d380e65dStsutsui u_int sc_mousepos; /* index in above */
186d380e65dStsutsui
187d380e65dStsutsui struct callout sc_bellstop_tmo;
188d380e65dStsutsui
189d380e65dStsutsui device_t sc_wskbddev;
190d380e65dStsutsui #if NWSMOUSE > 0
191d380e65dStsutsui device_t sc_wsmousedev;
192d380e65dStsutsui #endif
193d380e65dStsutsui
194d380e65dStsutsui #ifdef WSDISPLAY_COMPAT_RAWKBD
195d380e65dStsutsui int sc_rawkbd;
196d380e65dStsutsui #endif
197d380e65dStsutsui };
198d380e65dStsutsui
1995d7f465dStsutsui static int dnkbd_match(device_t, cfdata_t, void *);
2005d7f465dStsutsui static void dnkbd_attach(device_t, device_t, void *);
201d380e65dStsutsui
202d380e65dStsutsui CFATTACH_DECL_NEW(dnkbd, sizeof(struct dnkbd_softc),
203d380e65dStsutsui dnkbd_match, dnkbd_attach, NULL, NULL);
204d380e65dStsutsui
2055d7f465dStsutsui static void dnkbd_init(struct dnkbd_softc *, uint16_t, uint16_t);
2065d7f465dStsutsui static int dnkbd_enable(void *, int);
2075d7f465dStsutsui static void dnkbd_set_leds(void *, int);
2085d7f465dStsutsui static int dnkbd_ioctl(void *, u_long, void *, int, struct lwp *);
209d380e65dStsutsui
2105d7f465dStsutsui static const struct wskbd_accessops dnkbd_accessops = {
211d380e65dStsutsui dnkbd_enable,
212d380e65dStsutsui dnkbd_set_leds,
213d380e65dStsutsui dnkbd_ioctl
214d380e65dStsutsui };
215d380e65dStsutsui
216d380e65dStsutsui #if NWSMOUSE > 0
2175d7f465dStsutsui static int dnmouse_enable(void *);
2185d7f465dStsutsui static int dnmouse_ioctl(void *, u_long, void *, int, struct lwp *);
2195d7f465dStsutsui static void dnmouse_disable(void *);
220d380e65dStsutsui
2215d7f465dStsutsui static const struct wsmouse_accessops dnmouse_accessops = {
222d380e65dStsutsui dnmouse_enable,
223d380e65dStsutsui dnmouse_ioctl,
224d380e65dStsutsui dnmouse_disable
225d380e65dStsutsui };
226d380e65dStsutsui #endif
227d380e65dStsutsui
2285d7f465dStsutsui static void dnkbd_bell(void *, u_int, u_int, u_int);
2295d7f465dStsutsui static void dnkbd_cngetc(void *, u_int *, int *);
2305d7f465dStsutsui static void dnkbd_cnpollc(void *, int);
231d380e65dStsutsui
2325d7f465dStsutsui static const struct wskbd_consops dnkbd_consops = {
233d380e65dStsutsui dnkbd_cngetc,
234d380e65dStsutsui dnkbd_cnpollc,
235d380e65dStsutsui dnkbd_bell
236d380e65dStsutsui };
237d380e65dStsutsui
2385d7f465dStsutsui static struct wskbd_mapdata dnkbd_keymapdata = {
239d380e65dStsutsui dnkbd_keydesctab,
240d380e65dStsutsui #ifdef DNKBD_LAYOUT
241d380e65dStsutsui DNKBD_LAYOUT
242d380e65dStsutsui #else
243d380e65dStsutsui KB_US
244d380e65dStsutsui #endif
245d380e65dStsutsui };
246d380e65dStsutsui
247d380e65dStsutsui typedef enum { EVENT_NONE, EVENT_KEYBOARD, EVENT_MOUSE } dnevent;
248d380e65dStsutsui
249f9e54331Stsutsui #define APCIBRD(x) (500000 / (x))
250f9e54331Stsutsui
2515d7f465dStsutsui static void dnevent_kbd(struct dnkbd_softc *, int);
2525d7f465dStsutsui static void dnevent_kbd_internal(struct dnkbd_softc *, int);
2535d7f465dStsutsui static void dnevent_mouse(struct dnkbd_softc *, uint8_t *);
2545d7f465dStsutsui static void dnkbd_attach_subdevices(struct dnkbd_softc *);
2555d7f465dStsutsui static void dnkbd_bellstop(void *);
2565d7f465dStsutsui static void dnkbd_decode(int, u_int *, int *);
2575d7f465dStsutsui static dnevent dnkbd_input(struct dnkbd_softc *, int);
2585d7f465dStsutsui static int dnkbd_intr(void *);
2595d7f465dStsutsui static int dnkbd_pollin(struct dnkbd_softc *, u_int);
2605d7f465dStsutsui static int dnkbd_pollout(struct dnkbd_softc *, int);
2615d7f465dStsutsui static int dnkbd_probe(struct dnkbd_softc *);
2625d7f465dStsutsui static int dnkbd_send(struct dnkbd_softc *, const uint8_t *, size_t);
2638eb987cfStsutsui static void dnkbd_break(struct dnkbd_softc *, int);
264d380e65dStsutsui
265d380e65dStsutsui int
dnkbd_match(device_t parent,cfdata_t cf,void * aux)266d380e65dStsutsui dnkbd_match(device_t parent, cfdata_t cf, void *aux)
267d380e65dStsutsui {
268d380e65dStsutsui struct frodo_attach_args *fa = aux;
269d380e65dStsutsui
270d380e65dStsutsui if (strcmp(fa->fa_name, dnkbd_cd.cd_name) != 0)
2715d7f465dStsutsui return 0;
272d380e65dStsutsui
273d380e65dStsutsui if (machineid == HP_382) {
274d380e65dStsutsui /* 382 has frodo but no Domain keyboard connector. */
275d380e65dStsutsui return 0;
276d380e65dStsutsui }
277d380e65dStsutsui
278d380e65dStsutsui /* only attach to the first frodo port */
2795d7f465dStsutsui return fa->fa_offset == FRODO_APCI_OFFSET(0);
280d380e65dStsutsui }
281d380e65dStsutsui
282d380e65dStsutsui void
dnkbd_attach(device_t parent,device_t self,void * aux)283d380e65dStsutsui dnkbd_attach(device_t parent, device_t self, void *aux)
284d380e65dStsutsui {
285d380e65dStsutsui struct dnkbd_softc *sc = device_private(self);
286d380e65dStsutsui struct frodo_attach_args *fa = aux;
287d380e65dStsutsui
2882ba8a55cStsutsui aprint_normal(": ");
289d380e65dStsutsui
290d380e65dStsutsui sc->sc_dev = self;
291d380e65dStsutsui sc->sc_bst = fa->fa_bst;
292d380e65dStsutsui if (bus_space_map(sc->sc_bst, fa->fa_base + fa->fa_offset,
293d380e65dStsutsui FRODO_APCISPACE, 0, &sc->sc_bsh) != 0) {
294d380e65dStsutsui aprint_error(": can't map i/o space\n");
295d380e65dStsutsui return;
296d380e65dStsutsui }
297d380e65dStsutsui
298d380e65dStsutsui callout_init(&sc->sc_bellstop_tmo, 0);
299d380e65dStsutsui callout_setfunc(&sc->sc_bellstop_tmo, dnkbd_bellstop, sc);
300d380e65dStsutsui
301d380e65dStsutsui /* reset the port */
302d380e65dStsutsui dnkbd_init(sc, 1200, LCR_8BITS | LCR_PEVEN | LCR_PENAB);
303d380e65dStsutsui
304*594f789aSthorpej frodo_intr_establish(parent, dnkbd_intr, sc, fa->fa_line, ISRPRI_TTY);
305d380e65dStsutsui
3068eb987cfStsutsui /* send break to reset keyboard state */
3078eb987cfStsutsui dnkbd_break(sc, 1);
3088eb987cfStsutsui delay(10 * 1000); /* 10ms for 12 space bits */
3098eb987cfStsutsui dnkbd_break(sc, 0);
3108eb987cfStsutsui delay(10 * 1000);
3118eb987cfStsutsui
312d380e65dStsutsui /* probe for keyboard */
313d380e65dStsutsui if (dnkbd_probe(sc) != 0) {
3142ba8a55cStsutsui aprint_normal("no keyboard\n");
315d380e65dStsutsui return;
316d380e65dStsutsui }
317d380e65dStsutsui
318d380e65dStsutsui dnkbd_attach_subdevices(sc);
319d380e65dStsutsui }
320d380e65dStsutsui
321d380e65dStsutsui void
dnkbd_init(struct dnkbd_softc * sc,uint16_t rate,uint16_t lctl)322d380e65dStsutsui dnkbd_init(struct dnkbd_softc *sc, uint16_t rate, uint16_t lctl)
323d380e65dStsutsui {
324d380e65dStsutsui bus_space_tag_t bst;
325d380e65dStsutsui bus_space_handle_t bsh;
326f9e54331Stsutsui u_int divisor;
327d380e65dStsutsui
328d380e65dStsutsui bst = sc->sc_bst;
329d380e65dStsutsui bsh = sc->sc_bsh;
330d380e65dStsutsui
331f9e54331Stsutsui divisor = APCIBRD(rate);
332d380e65dStsutsui bus_space_write_1(bst, bsh, com_lctl, LCR_DLAB);
333f9e54331Stsutsui bus_space_write_1(bst, bsh, com_dlbl, divisor & 0xff);
334f9e54331Stsutsui bus_space_write_1(bst, bsh, com_dlbh, (divisor >> 8) & 0xff);
335d380e65dStsutsui bus_space_write_1(bst, bsh, com_lctl, lctl);
336d380e65dStsutsui bus_space_write_1(bst, bsh, com_ier, IER_ERXRDY | IER_ETXRDY);
337d380e65dStsutsui bus_space_write_1(bst, bsh, com_fifo,
338d380e65dStsutsui FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1);
339d380e65dStsutsui bus_space_write_1(bst, bsh, com_mcr, MCR_DTR | MCR_RTS);
340d380e65dStsutsui
341d380e65dStsutsui delay(100);
342f9e54331Stsutsui (void)bus_space_read_1(bst, bsh, com_iir);
343d380e65dStsutsui }
344d380e65dStsutsui
345d380e65dStsutsui void
dnkbd_attach_subdevices(struct dnkbd_softc * sc)346d380e65dStsutsui dnkbd_attach_subdevices(struct dnkbd_softc *sc)
347d380e65dStsutsui {
348d380e65dStsutsui struct wskbddev_attach_args ka;
349d380e65dStsutsui #if NWSMOUSE > 0
350d380e65dStsutsui struct wsmousedev_attach_args ma;
351d380e65dStsutsui #endif
352d380e65dStsutsui #if NHILKBD > 0
353d380e65dStsutsui extern int hil_is_console;
354d380e65dStsutsui #endif
355d380e65dStsutsui
356d380e65dStsutsui /*
357d380e65dStsutsui * If both hilkbd and dnkbd are configured, prefer the Domain
358d380e65dStsutsui * keyboard as console (if we are here, we know the keyboard is
359d380e65dStsutsui * plugged), unless the console keyboard has been claimed already
360d380e65dStsutsui * (i.e. late hotplug with hil keyboard plugged first).
361d380e65dStsutsui */
362806e09adStsutsui #if NWSDISPLAY > 0
363806e09adStsutsui if (cn_tab->cn_putc == wsdisplay_cnputc) {
364d380e65dStsutsui #if NHILKBD > 0
365d380e65dStsutsui if (hil_is_console == -1) {
366d380e65dStsutsui ka.console = 1;
367d380e65dStsutsui hil_is_console = 0;
368d380e65dStsutsui } else
369d380e65dStsutsui ka.console = 0;
370d380e65dStsutsui #else
371d380e65dStsutsui ka.console = 1;
372d380e65dStsutsui #endif
373d380e65dStsutsui } else
374806e09adStsutsui #endif
375806e09adStsutsui {
376d380e65dStsutsui ka.console = 0;
377806e09adStsutsui }
378d380e65dStsutsui
379d380e65dStsutsui ka.keymap = &dnkbd_keymapdata;
380d380e65dStsutsui ka.accessops = &dnkbd_accessops;
381d380e65dStsutsui ka.accesscookie = sc;
382d380e65dStsutsui #ifndef DKKBD_LAYOUT
383d380e65dStsutsui dnkbd_keymapdata.layout = sc->sc_layout;
384d380e65dStsutsui #endif
385d380e65dStsutsui
386d380e65dStsutsui if (ka.console) {
387d380e65dStsutsui sc->sc_flags = SF_PLUGGED | SF_CONSOLE | SF_ENABLED;
388d380e65dStsutsui wskbd_cnattach(&dnkbd_consops, sc, &dnkbd_keymapdata);
389d380e65dStsutsui } else {
390d380e65dStsutsui sc->sc_flags = SF_PLUGGED;
391d380e65dStsutsui }
392d380e65dStsutsui
3932685996bSthorpej sc->sc_wskbddev = config_found(sc->sc_dev, &ka, wskbddevprint,
394c7fb772bSthorpej CFARGS(.iattr = "wskbddev"));
395d380e65dStsutsui
396d380e65dStsutsui #if NWSMOUSE > 0
397d380e65dStsutsui ma.accessops = &dnmouse_accessops;
398d380e65dStsutsui ma.accesscookie = sc;
399d380e65dStsutsui
4002685996bSthorpej sc->sc_wsmousedev = config_found(sc->sc_dev, &ma, wsmousedevprint,
401c7fb772bSthorpej CFARGS(.iattr = "wsmousedev"));
402d380e65dStsutsui #endif
403d380e65dStsutsui
404d380e65dStsutsui SET(sc->sc_flags, SF_ATTACHED);
405d380e65dStsutsui }
406d380e65dStsutsui
407d380e65dStsutsui int
dnkbd_probe(struct dnkbd_softc * sc)408d380e65dStsutsui dnkbd_probe(struct dnkbd_softc *sc)
409d380e65dStsutsui {
410d380e65dStsutsui int dat, rc, flags;
4115d7f465dStsutsui uint8_t cmdbuf[2];
412d380e65dStsutsui char rspbuf[MAX_IDENTLEN], *word, *end;
413d380e65dStsutsui u_int i;
414d380e65dStsutsui int s;
415d380e65dStsutsui
416d380e65dStsutsui s = spltty();
417d380e65dStsutsui flags = sc->sc_flags;
418d380e65dStsutsui SET(sc->sc_flags, SF_POLLING);
419d380e65dStsutsui sc->sc_state = STATE_CHANNEL;
420d380e65dStsutsui splx(s);
421d380e65dStsutsui
422d380e65dStsutsui /*
423d380e65dStsutsui * Switch keyboard to raw mode.
424d380e65dStsutsui */
425d380e65dStsutsui cmdbuf[0] = DNCMD_RAW;
426d380e65dStsutsui rc = dnkbd_send(sc, cmdbuf, 1);
427d380e65dStsutsui if (rc != 0)
428d380e65dStsutsui goto out;
429d380e65dStsutsui
430d380e65dStsutsui /*
431d380e65dStsutsui * Send the identify command.
432d380e65dStsutsui */
433d380e65dStsutsui cmdbuf[0] = DNCMD_IDENT_1;
434d380e65dStsutsui cmdbuf[1] = DNCMD_IDENT_2;
435d380e65dStsutsui rc = dnkbd_send(sc, cmdbuf, 2);
436d380e65dStsutsui if (rc != 0)
437d380e65dStsutsui goto out;
438d380e65dStsutsui
439d380e65dStsutsui for (i = 0; ; i++) {
440d380e65dStsutsui dat = dnkbd_pollin(sc, 10000);
441d380e65dStsutsui if (dat == -1)
442d380e65dStsutsui break;
443d380e65dStsutsui
444d380e65dStsutsui if (i < sizeof(rspbuf))
445d380e65dStsutsui rspbuf[i] = dat;
446d380e65dStsutsui }
447d380e65dStsutsui
448d380e65dStsutsui if (i > sizeof(rspbuf) || i == 0) {
4492ba8a55cStsutsui aprint_error_dev(sc->sc_dev,
4502ba8a55cStsutsui "unexpected identify string length %d\n", i);
451d380e65dStsutsui rc = ENXIO;
452d380e65dStsutsui goto out;
453d380e65dStsutsui }
454d380e65dStsutsui
455d380e65dStsutsui /*
456d380e65dStsutsui * Make sure the identification string is NULL terminated
457d380e65dStsutsui * (overwriting the keyboard mode byte if necessary).
458d380e65dStsutsui */
459d380e65dStsutsui i--;
460d380e65dStsutsui if (rspbuf[i] != 0)
461d380e65dStsutsui rspbuf[i] = 0;
462d380e65dStsutsui
463d380e65dStsutsui /*
464d380e65dStsutsui * Now display the identification strings, if they changed.
465d380e65dStsutsui */
466d380e65dStsutsui if (i != sc->sc_identlen || memcmp(rspbuf, sc->sc_ident, i) != 0) {
467d380e65dStsutsui sc->sc_layout = KB_US;
468d380e65dStsutsui sc->sc_identlen = i;
469d380e65dStsutsui memcpy(sc->sc_ident, rspbuf, i);
470d380e65dStsutsui
471d380e65dStsutsui if (cold == 0)
4722ba8a55cStsutsui aprint_normal_dev(sc->sc_dev, "");
4732ba8a55cStsutsui aprint_normal("model ");
474d380e65dStsutsui word = rspbuf;
475d380e65dStsutsui for (i = 0; i < 3; i++) {
476d380e65dStsutsui end = strchr(word, '\r');
477d380e65dStsutsui if (end == NULL)
478d380e65dStsutsui break;
479d380e65dStsutsui *end++ = '\0';
4802ba8a55cStsutsui aprint_normal("<%s> ", word);
481d380e65dStsutsui /*
482d380e65dStsutsui * Parse the layout code if applicable
483d380e65dStsutsui */
484d380e65dStsutsui if (i == 1 && *word++ == '3') {
485d380e65dStsutsui if (*word == '-')
486d380e65dStsutsui word++;
487d380e65dStsutsui switch (*word) {
488d380e65dStsutsui #if 0
489d380e65dStsutsui default:
490d380e65dStsutsui case ' ':
491d380e65dStsutsui sc->sc_layout = KB_US;
492d380e65dStsutsui break;
493d380e65dStsutsui #endif
494d380e65dStsutsui case 'a':
495d380e65dStsutsui sc->sc_layout = KB_DE;
496d380e65dStsutsui break;
497d380e65dStsutsui case 'b':
498d380e65dStsutsui sc->sc_layout = KB_FR;
499d380e65dStsutsui break;
500d380e65dStsutsui case 'c':
501d380e65dStsutsui sc->sc_layout = KB_DK;
502d380e65dStsutsui break;
503d380e65dStsutsui case 'd':
504d380e65dStsutsui sc->sc_layout = KB_SV;
505d380e65dStsutsui break;
506d380e65dStsutsui case 'e':
507d380e65dStsutsui sc->sc_layout = KB_UK;
508d380e65dStsutsui break;
509d380e65dStsutsui case 'f':
510d380e65dStsutsui sc->sc_layout = KB_JP;
511d380e65dStsutsui break;
512d380e65dStsutsui case 'g':
513d380e65dStsutsui sc->sc_layout = KB_SG;
514d380e65dStsutsui break;
515d380e65dStsutsui }
516d380e65dStsutsui }
517d380e65dStsutsui word = end;
518d380e65dStsutsui }
5192ba8a55cStsutsui aprint_normal("\n");
520d380e65dStsutsui }
521d380e65dStsutsui
522d380e65dStsutsui /*
523d380e65dStsutsui * Ready to work, the default channel is the keyboard.
524d380e65dStsutsui */
525d380e65dStsutsui sc->sc_state = STATE_KEYBOARD;
526d380e65dStsutsui
527d380e65dStsutsui out:
528d380e65dStsutsui s = spltty();
529d380e65dStsutsui sc->sc_flags = flags;
530d380e65dStsutsui splx(s);
531d380e65dStsutsui
5325d7f465dStsutsui return rc;
533d380e65dStsutsui }
534d380e65dStsutsui
535d380e65dStsutsui /*
536d380e65dStsutsui * State machine.
537d380e65dStsutsui *
538d380e65dStsutsui * In raw mode, the keyboard may feed us the following sequences:
539d380e65dStsutsui * - on the keyboard channel:
540d380e65dStsutsui * + a raw key code, in the range 0x01-0x7e, or'ed with 0x80 if key release.
541d380e65dStsutsui * + the key repeat sequence 0x7f.
542d380e65dStsutsui * - on the mouse channel:
543d380e65dStsutsui * + a 3 byte mouse sequence (buttons state, dx move, dy move).
544d380e65dStsutsui * - at any time:
545d380e65dStsutsui * + a 2 byte channel sequence (0xff followed by the channel number) telling
546d380e65dStsutsui * us which device the following input will come from.
547d380e65dStsutsui * + if we get 0xff but an invalid channel number, this is a command echo.
548d380e65dStsutsui * Currently we only handle this for bell commands, which size are known.
549d380e65dStsutsui * Other commands are issued through dnkbd_send() which ``eats'' the echo.
550d380e65dStsutsui *
551d380e65dStsutsui * Older keyboards reset the channel to the keyboard (by sending ff 01) after
552d380e65dStsutsui * every mouse packet.
553d380e65dStsutsui */
554d380e65dStsutsui
555d380e65dStsutsui dnevent
dnkbd_input(struct dnkbd_softc * sc,int dat)556d380e65dStsutsui dnkbd_input(struct dnkbd_softc *sc, int dat)
557d380e65dStsutsui {
558d380e65dStsutsui dnevent event = EVENT_NONE;
559d380e65dStsutsui
560d380e65dStsutsui switch (sc->sc_state) {
561d380e65dStsutsui case STATE_KEYBOARD:
562d380e65dStsutsui switch (dat) {
563d380e65dStsutsui case DNKEY_REPEAT:
564d380e65dStsutsui /*
565d380e65dStsutsui * We ignore event repeats, as wskbd does its own
566d380e65dStsutsui * soft repeat processing.
567d380e65dStsutsui */
568d380e65dStsutsui break;
569d380e65dStsutsui case DNKEY_CHANNEL:
570d380e65dStsutsui sc->sc_prevstate = sc->sc_state;
571d380e65dStsutsui sc->sc_state = STATE_CHANNEL;
572d380e65dStsutsui break;
573d380e65dStsutsui default:
574d380e65dStsutsui event = EVENT_KEYBOARD;
575d380e65dStsutsui break;
576d380e65dStsutsui }
577d380e65dStsutsui break;
578d380e65dStsutsui
579d380e65dStsutsui case STATE_MOUSE:
580d380e65dStsutsui if (dat == DNKEY_CHANNEL && sc->sc_mousepos == 0) {
581d380e65dStsutsui sc->sc_prevstate = sc->sc_state;
582d380e65dStsutsui sc->sc_state = STATE_CHANNEL;
583d380e65dStsutsui } else {
584d380e65dStsutsui sc->sc_mousepkt[sc->sc_mousepos++] = dat;
585d380e65dStsutsui if (sc->sc_mousepos == sizeof(sc->sc_mousepkt)) {
586d380e65dStsutsui sc->sc_mousepos = 0;
587d380e65dStsutsui event = EVENT_MOUSE;
588d380e65dStsutsui }
589d380e65dStsutsui }
590d380e65dStsutsui break;
591d380e65dStsutsui
592d380e65dStsutsui case STATE_CHANNEL:
593d380e65dStsutsui switch (dat) {
594d380e65dStsutsui case DNKEY_CHANNEL:
595d380e65dStsutsui /*
596d380e65dStsutsui * During hotplug, we might get spurious 0xff bytes.
597d380e65dStsutsui * Ignore them.
598d380e65dStsutsui */
599d380e65dStsutsui break;
600d380e65dStsutsui case DNCHANNEL_RESET:
601d380e65dStsutsui /*
602d380e65dStsutsui * Identify the keyboard again. This will switch it to
603d380e65dStsutsui * raw mode again. If this fails, we'll consider the
604d380e65dStsutsui * keyboard as unplugged (to ignore further events until
605d380e65dStsutsui * a successful reset).
606d380e65dStsutsui */
607d380e65dStsutsui if (dnkbd_probe(sc) == 0) {
608d380e65dStsutsui /*
609d380e65dStsutsui * We need to attach wskbd and wsmouse children
610d380e65dStsutsui * if this is a live first plug.
611d380e65dStsutsui */
612d380e65dStsutsui if (!ISSET(sc->sc_flags, SF_ATTACHED))
613d380e65dStsutsui dnkbd_attach_subdevices(sc);
614d380e65dStsutsui SET(sc->sc_flags, SF_PLUGGED);
615d380e65dStsutsui } else {
616d380e65dStsutsui CLR(sc->sc_flags, SF_PLUGGED);
617d380e65dStsutsui }
618d380e65dStsutsui
619d380e65dStsutsui sc->sc_state = STATE_KEYBOARD;
620d380e65dStsutsui break;
621d380e65dStsutsui case DNCHANNEL_KBD:
622d380e65dStsutsui sc->sc_state = STATE_KEYBOARD;
623d380e65dStsutsui break;
624d380e65dStsutsui case DNCHANNEL_MOUSE:
625d380e65dStsutsui sc->sc_state = STATE_MOUSE;
626d380e65dStsutsui sc->sc_mousepos = 0; /* just in case */
627d380e65dStsutsui break;
628d380e65dStsutsui case DNCMD_BELL:
629d380e65dStsutsui /*
630d380e65dStsutsui * We are getting a bell command echoed to us.
631d380e65dStsutsui * Ignore it.
632d380e65dStsutsui */
633d380e65dStsutsui sc->sc_state = STATE_ECHO;
634d380e65dStsutsui sc->sc_echolen = 1; /* one byte to follow */
635d380e65dStsutsui break;
636d380e65dStsutsui default:
637d380e65dStsutsui printf("%s: unexpected channel byte %02x\n",
638d380e65dStsutsui device_xname(sc->sc_dev), dat);
639d380e65dStsutsui break;
640d380e65dStsutsui }
641d380e65dStsutsui break;
642d380e65dStsutsui
643d380e65dStsutsui case STATE_ECHO:
644d380e65dStsutsui if (--sc->sc_echolen == 0) {
645d380e65dStsutsui /* get back to the state we were in before the echo */
646d380e65dStsutsui sc->sc_state = sc->sc_prevstate;
647d380e65dStsutsui }
648d380e65dStsutsui break;
649d380e65dStsutsui }
650d380e65dStsutsui
6515d7f465dStsutsui return event;
652d380e65dStsutsui }
653d380e65dStsutsui
654d380e65dStsutsui /*
655d380e65dStsutsui * Event breakers.
656d380e65dStsutsui */
657d380e65dStsutsui
658d380e65dStsutsui void
dnkbd_decode(int keycode,u_int * type,int * key)659d380e65dStsutsui dnkbd_decode(int keycode, u_int *type, int *key)
660d380e65dStsutsui {
661d380e65dStsutsui *type = (keycode & DNKEY_RELEASE) ?
662d380e65dStsutsui WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
663d380e65dStsutsui *key = (keycode & ~DNKEY_RELEASE);
664d380e65dStsutsui }
665d380e65dStsutsui
666d380e65dStsutsui void
dnevent_kbd(struct dnkbd_softc * sc,int dat)667d380e65dStsutsui dnevent_kbd(struct dnkbd_softc *sc, int dat)
668d380e65dStsutsui {
669d380e65dStsutsui if (!ISSET(sc->sc_flags, SF_PLUGGED))
670d380e65dStsutsui return;
671d380e65dStsutsui
672d380e65dStsutsui if (sc->sc_wskbddev == NULL)
673d380e65dStsutsui return;
674d380e65dStsutsui
675d380e65dStsutsui if (!ISSET(sc->sc_flags, SF_ENABLED))
676d380e65dStsutsui return;
677d380e65dStsutsui
678d380e65dStsutsui /*
679d380e65dStsutsui * Even in raw mode, the caps lock key is treated specially:
680d380e65dStsutsui * first key press causes event 0x7e, release causes no event;
681d380e65dStsutsui * then a new key press causes nothing, and release causes
682d380e65dStsutsui * event 0xfe. Moreover, while kept down, it does not produce
683d380e65dStsutsui * repeat events.
684d380e65dStsutsui *
685d380e65dStsutsui * So the best we can do is fake the missed events, but this
686d380e65dStsutsui * will not allow the capslock key to be remapped as a control
687d380e65dStsutsui * key since it will not be possible to chord it with anything.
688d380e65dStsutsui */
689d380e65dStsutsui dnevent_kbd_internal(sc, dat);
690d380e65dStsutsui if ((dat & ~DNKEY_RELEASE) == DNKEY_CAPSLOCK)
691d380e65dStsutsui dnevent_kbd_internal(sc, dat ^ DNKEY_RELEASE);
692d380e65dStsutsui }
693d380e65dStsutsui
694d380e65dStsutsui void
dnevent_kbd_internal(struct dnkbd_softc * sc,int dat)695d380e65dStsutsui dnevent_kbd_internal(struct dnkbd_softc *sc, int dat)
696d380e65dStsutsui {
697d380e65dStsutsui u_int type;
698d380e65dStsutsui int key;
699d380e65dStsutsui int s;
700d380e65dStsutsui
701d380e65dStsutsui dnkbd_decode(dat, &type, &key);
702d380e65dStsutsui
703d380e65dStsutsui #ifdef WSDISPLAY_COMPAT_RAWKBD
704d380e65dStsutsui if (sc->sc_rawkbd) {
705d380e65dStsutsui u_char cbuf[2];
7061e2a3ba7Stsutsui int c, j;
707d380e65dStsutsui
7081e2a3ba7Stsutsui j = 0;
709d380e65dStsutsui c = dnkbd_raw[key];
710d19119aaStsutsui if (c != 0) {
711d380e65dStsutsui /* fake extended scancode if necessary */
712d380e65dStsutsui if (c & 0x80)
713d380e65dStsutsui cbuf[j++] = 0xe0;
714d380e65dStsutsui cbuf[j] = c & 0x7f;
715d380e65dStsutsui if (type == WSCONS_EVENT_KEY_UP)
716d380e65dStsutsui cbuf[j] |= 0x80;
717d380e65dStsutsui j++;
718d380e65dStsutsui }
719d380e65dStsutsui
720d380e65dStsutsui if (j != 0) {
721d380e65dStsutsui s = spltty();
722d380e65dStsutsui wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
723d380e65dStsutsui splx(s);
7248675267dStsutsui }
725d380e65dStsutsui } else
726d380e65dStsutsui #endif
727d380e65dStsutsui {
728d380e65dStsutsui s = spltty();
729d380e65dStsutsui wskbd_input(sc->sc_wskbddev, type, key);
730d380e65dStsutsui splx(s);
731d380e65dStsutsui }
732d380e65dStsutsui }
733d380e65dStsutsui
734d380e65dStsutsui #if NWSMOUSE > 0
735d380e65dStsutsui void
dnevent_mouse(struct dnkbd_softc * sc,uint8_t * dat)7365d7f465dStsutsui dnevent_mouse(struct dnkbd_softc *sc, uint8_t *dat)
737d380e65dStsutsui {
738d380e65dStsutsui if (!ISSET(sc->sc_flags, SF_PLUGGED))
739d380e65dStsutsui return;
740d380e65dStsutsui
741d380e65dStsutsui if (sc->sc_wsmousedev == NULL)
742d380e65dStsutsui return;
743d380e65dStsutsui
744d380e65dStsutsui if (!ISSET(sc->sc_flags, SF_MOUSE))
745d380e65dStsutsui return;
746d380e65dStsutsui
747d380e65dStsutsui /*
748d380e65dStsutsui * First byte is button status. It has the 0x80 bit always set, and
749d380e65dStsutsui * the next 3 bits are *cleared* when the mouse buttons are pressed.
750d380e65dStsutsui */
751d380e65dStsutsui #ifdef DEBUG
752d380e65dStsutsui if (!ISSET(*dat, 0x80)) {
753d380e65dStsutsui printf("%s: incorrect mouse packet %02x %02x %02x\n",
754d380e65dStsutsui device_xname(sc->sc_dev), dat[0], dat[1], dat[2]);
755d380e65dStsutsui return;
756d380e65dStsutsui }
757d380e65dStsutsui #endif
758d380e65dStsutsui
759d380e65dStsutsui wsmouse_input(sc->sc_wsmousedev,
760d380e65dStsutsui (~dat[0] & (DNBUTTON_L | DNBUTTON_M | DNBUTTON_R)) >> 4,
761d380e65dStsutsui (int8_t)dat[1], (int8_t)dat[2], 0, 0, WSMOUSE_INPUT_DELTA);
762d380e65dStsutsui }
763d380e65dStsutsui #endif
764d380e65dStsutsui
765d380e65dStsutsui /*
766d380e65dStsutsui * Low-level communication routines.
767d380e65dStsutsui */
768d380e65dStsutsui
769d380e65dStsutsui int
dnkbd_pollin(struct dnkbd_softc * sc,u_int tries)770d380e65dStsutsui dnkbd_pollin(struct dnkbd_softc *sc, u_int tries)
771d380e65dStsutsui {
772d380e65dStsutsui bus_space_tag_t bst;
773d380e65dStsutsui bus_space_handle_t bsh;
774d380e65dStsutsui u_int cnt;
775d380e65dStsutsui
776d380e65dStsutsui bst = sc->sc_bst;
777d380e65dStsutsui bsh = sc->sc_bsh;
778d380e65dStsutsui
779d380e65dStsutsui for (cnt = tries; cnt != 0; cnt--) {
780d380e65dStsutsui if (bus_space_read_1(bst, bsh, com_lsr) & LSR_RXRDY)
781d380e65dStsutsui break;
782d380e65dStsutsui DELAY(10);
783d380e65dStsutsui }
784d380e65dStsutsui
785d380e65dStsutsui if (cnt == 0)
7865d7f465dStsutsui return -1;
787d380e65dStsutsui else
788d380e65dStsutsui return (int)bus_space_read_1(bst, bsh, com_data);
789d380e65dStsutsui }
790d380e65dStsutsui
791d380e65dStsutsui int
dnkbd_pollout(struct dnkbd_softc * sc,int dat)792d380e65dStsutsui dnkbd_pollout(struct dnkbd_softc *sc, int dat)
793d380e65dStsutsui {
794d380e65dStsutsui bus_space_tag_t bst;
795d380e65dStsutsui bus_space_handle_t bsh;
796d380e65dStsutsui u_int cnt;
797d380e65dStsutsui
798d380e65dStsutsui bst = sc->sc_bst;
799d380e65dStsutsui bsh = sc->sc_bsh;
800d380e65dStsutsui
801d380e65dStsutsui for (cnt = 10000; cnt != 0; cnt--) {
802d380e65dStsutsui if (bus_space_read_1(bst, bsh, com_lsr) & LSR_TXRDY)
803d380e65dStsutsui break;
804d380e65dStsutsui DELAY(10);
805d380e65dStsutsui }
806d380e65dStsutsui if (cnt == 0)
8075d7f465dStsutsui return EBUSY;
808d380e65dStsutsui else {
809d380e65dStsutsui bus_space_write_1(bst, bsh, com_data, dat);
8105d7f465dStsutsui return 0;
811d380e65dStsutsui }
812d380e65dStsutsui }
813d380e65dStsutsui
814d380e65dStsutsui int
dnkbd_send(struct dnkbd_softc * sc,const uint8_t * cmdbuf,size_t cmdlen)8155d7f465dStsutsui dnkbd_send(struct dnkbd_softc *sc, const uint8_t *cmdbuf, size_t cmdlen)
816d380e65dStsutsui {
817d380e65dStsutsui int cnt, rc, dat;
818d380e65dStsutsui u_int cmdpos;
819d380e65dStsutsui
820d380e65dStsutsui /* drain rxfifo */
821d380e65dStsutsui for (cnt = 10; cnt != 0; cnt--) {
822d380e65dStsutsui if (dnkbd_pollin(sc, 10) == -1)
823d380e65dStsutsui break;
824d380e65dStsutsui }
825d380e65dStsutsui if (cnt == 0)
8265d7f465dStsutsui return EBUSY;
827d380e65dStsutsui
828d380e65dStsutsui /* send command escape */
829d380e65dStsutsui if ((rc = dnkbd_pollout(sc, DNCMD_PREFIX)) != 0)
8305d7f465dStsutsui return rc;
831d380e65dStsutsui
832d380e65dStsutsui /* send command buffer */
833d380e65dStsutsui for (cmdpos = 0; cmdpos < cmdlen; cmdpos++) {
834d380e65dStsutsui if ((rc = dnkbd_pollout(sc, cmdbuf[cmdpos])) != 0)
8355d7f465dStsutsui return rc;
836d380e65dStsutsui }
837d380e65dStsutsui
838d380e65dStsutsui /* wait for command echo */
839d380e65dStsutsui do {
840d380e65dStsutsui dat = dnkbd_pollin(sc, 10000);
841d380e65dStsutsui if (dat == -1)
8425d7f465dStsutsui return EIO;
843d380e65dStsutsui } while (dat != DNCMD_PREFIX);
844d380e65dStsutsui
845d380e65dStsutsui for (cmdpos = 0; cmdpos < cmdlen; cmdpos++) {
846d380e65dStsutsui dat = dnkbd_pollin(sc, 10000);
847d380e65dStsutsui if (dat != cmdbuf[cmdpos])
8485d7f465dStsutsui return EIO;
849d380e65dStsutsui }
850d380e65dStsutsui
8515d7f465dStsutsui return 0;
852d380e65dStsutsui }
853d380e65dStsutsui
8548eb987cfStsutsui void
dnkbd_break(struct dnkbd_softc * sc,int onoff)8558eb987cfStsutsui dnkbd_break(struct dnkbd_softc *sc, int onoff)
8568eb987cfStsutsui {
8578eb987cfStsutsui bus_space_tag_t bst;
8588eb987cfStsutsui bus_space_handle_t bsh;
8598eb987cfStsutsui uint8_t reg;
8608eb987cfStsutsui
8618eb987cfStsutsui bst = sc->sc_bst;
8628eb987cfStsutsui bsh = sc->sc_bsh;
8638eb987cfStsutsui
8648eb987cfStsutsui reg = bus_space_read_1(bst, bsh, com_lctl);
8658eb987cfStsutsui if (onoff)
8668eb987cfStsutsui reg |= LCR_SBREAK;
8678eb987cfStsutsui else
8688eb987cfStsutsui reg &= ~LCR_SBREAK;
8698eb987cfStsutsui bus_space_write_1(bst, bsh, com_lctl, reg);
8708eb987cfStsutsui }
8718eb987cfStsutsui
872d380e65dStsutsui int
dnkbd_intr(void * v)873d380e65dStsutsui dnkbd_intr(void *v)
874d380e65dStsutsui {
875d380e65dStsutsui struct dnkbd_softc *sc = v;
876d380e65dStsutsui bus_space_tag_t bst;
877d380e65dStsutsui bus_space_handle_t bsh;
8785d7f465dStsutsui uint8_t iir, lsr, c;
879d380e65dStsutsui int claimed = 0;
880d380e65dStsutsui
881d380e65dStsutsui bst = sc->sc_bst;
882d380e65dStsutsui bsh = sc->sc_bsh;
883d380e65dStsutsui
884d380e65dStsutsui for (;;) {
885d380e65dStsutsui iir = bus_space_read_1(bst, bsh, com_iir);
886d380e65dStsutsui
887d380e65dStsutsui switch (iir & IIR_IMASK) {
888d380e65dStsutsui case IIR_RLS:
889d380e65dStsutsui /*
890d380e65dStsutsui * Line status change. This should never happen,
891d380e65dStsutsui * so silently ack the interrupt.
892d380e65dStsutsui */
893d380e65dStsutsui c = bus_space_read_1(bst, bsh, com_lsr);
894d380e65dStsutsui break;
895d380e65dStsutsui
896d380e65dStsutsui case IIR_RXRDY:
897d380e65dStsutsui case IIR_RXTOUT:
898d380e65dStsutsui /*
899d380e65dStsutsui * Data available. We process it byte by byte,
900d380e65dStsutsui * unless we are doing polling work...
901d380e65dStsutsui */
902d380e65dStsutsui if (ISSET(sc->sc_flags, SF_POLLING)) {
9035d7f465dStsutsui return 1;
904d380e65dStsutsui }
905d380e65dStsutsui
906d380e65dStsutsui for (;;) {
907d380e65dStsutsui c = bus_space_read_1(bst, bsh, com_data);
908d380e65dStsutsui switch (dnkbd_input(sc, c)) {
909d380e65dStsutsui case EVENT_KEYBOARD:
910d380e65dStsutsui dnevent_kbd(sc, c);
911d380e65dStsutsui break;
912d380e65dStsutsui #if NWSMOUSE > 0
913d380e65dStsutsui case EVENT_MOUSE:
914d380e65dStsutsui dnevent_mouse(sc, sc->sc_mousepkt);
915d380e65dStsutsui break;
916d380e65dStsutsui #endif
917d380e65dStsutsui default: /* appease gcc */
918d380e65dStsutsui break;
919d380e65dStsutsui }
920d380e65dStsutsui lsr = bus_space_read_1(bst, bsh, com_lsr) &
921d380e65dStsutsui LSR_RCV_MASK;
922d380e65dStsutsui if (lsr == 0)
923d380e65dStsutsui break;
924d380e65dStsutsui else if (lsr != LSR_RXRDY) {
925d380e65dStsutsui /* ignore error */
926d380e65dStsutsui break;
927d380e65dStsutsui }
928d380e65dStsutsui }
929d380e65dStsutsui break;
930d380e65dStsutsui
931d380e65dStsutsui case IIR_TXRDY:
932d380e65dStsutsui /*
933d380e65dStsutsui * Transmit available. Since we do all our commands
934d380e65dStsutsui * in polling mode, we do not need to do anything here.
935d380e65dStsutsui */
936d380e65dStsutsui break;
937d380e65dStsutsui
938d380e65dStsutsui default:
939d380e65dStsutsui if (iir & IIR_NOPEND)
9405d7f465dStsutsui return claimed;
941d380e65dStsutsui /* FALLTHROUGH */
942d380e65dStsutsui
943d380e65dStsutsui case IIR_MLSC:
944d380e65dStsutsui /*
945d380e65dStsutsui * Modem status change. This should never happen,
946d380e65dStsutsui * so silently ack the interrupt.
947d380e65dStsutsui */
948d380e65dStsutsui c = bus_space_read_1(bst, bsh, com_msr);
949d380e65dStsutsui break;
950d380e65dStsutsui }
951d380e65dStsutsui
952d380e65dStsutsui claimed = 1;
953d380e65dStsutsui }
954d380e65dStsutsui }
955d380e65dStsutsui
956d380e65dStsutsui /*
957d380e65dStsutsui * Wskbd callbacks
958d380e65dStsutsui */
959d380e65dStsutsui
960d380e65dStsutsui int
dnkbd_enable(void * v,int on)961d380e65dStsutsui dnkbd_enable(void *v, int on)
962d380e65dStsutsui {
963d380e65dStsutsui struct dnkbd_softc *sc = v;
964d380e65dStsutsui
965d380e65dStsutsui if (on) {
966d380e65dStsutsui if (ISSET(sc->sc_flags, SF_ENABLED))
9675d7f465dStsutsui return EBUSY;
968d380e65dStsutsui SET(sc->sc_flags, SF_ENABLED);
969d380e65dStsutsui } else {
970d380e65dStsutsui if (ISSET(sc->sc_flags, SF_CONSOLE))
9715d7f465dStsutsui return EBUSY;
972d380e65dStsutsui CLR(sc->sc_flags, SF_ENABLED);
973d380e65dStsutsui }
974d380e65dStsutsui
9755d7f465dStsutsui return 0;
976d380e65dStsutsui }
977d380e65dStsutsui
978d380e65dStsutsui void
dnkbd_set_leds(void * v,int leds)979d380e65dStsutsui dnkbd_set_leds(void *v, int leds)
980d380e65dStsutsui {
981d380e65dStsutsui /*
982d380e65dStsutsui * Not supported. There is only one LED on this keyboard, and
983d380e65dStsutsui * is hardware tied to the caps lock key.
984d380e65dStsutsui */
985d380e65dStsutsui }
986d380e65dStsutsui
987d380e65dStsutsui int
dnkbd_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)988d380e65dStsutsui dnkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
989d380e65dStsutsui {
990d380e65dStsutsui #ifdef WSDISPLAY_COMPAT_RAWKBD
991d380e65dStsutsui struct dnkbd_softc *sc = v;
992d380e65dStsutsui #endif
993d380e65dStsutsui
994d380e65dStsutsui #define WSKBD_TYPE_UNKNOWN 0
995d380e65dStsutsui
996d380e65dStsutsui switch (cmd) {
997d380e65dStsutsui case WSKBDIO_GTYPE:
998d380e65dStsutsui *(int *)data = WSKBD_TYPE_UNKNOWN; /* XXX */
9995d7f465dStsutsui return 0;
1000d380e65dStsutsui case WSKBDIO_SETLEDS:
10015d7f465dStsutsui return ENXIO;
1002d380e65dStsutsui case WSKBDIO_GETLEDS:
1003d380e65dStsutsui *(int *)data = 0;
10045d7f465dStsutsui return 0;
1005d380e65dStsutsui case WSKBDIO_COMPLEXBELL:
1006d380e65dStsutsui #define d ((struct wskbd_bell_data *)data)
1007d380e65dStsutsui dnkbd_bell(v, d->period, d->pitch, d->volume);
1008d380e65dStsutsui #undef d
10095d7f465dStsutsui return 0;
1010d380e65dStsutsui #ifdef WSDISPLAY_COMPAT_RAWKBD
1011d380e65dStsutsui case WSKBDIO_SETMODE:
1012d380e65dStsutsui sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
10135d7f465dStsutsui return 0;
1014d380e65dStsutsui #endif
1015d380e65dStsutsui }
1016d380e65dStsutsui
1017d380e65dStsutsui return EPASSTHROUGH;
1018d380e65dStsutsui }
1019d380e65dStsutsui
1020d380e65dStsutsui #if NWSMOUSE > 0
1021d380e65dStsutsui /*
1022d380e65dStsutsui * Wsmouse callbacks
1023d380e65dStsutsui */
1024d380e65dStsutsui
1025d380e65dStsutsui int
dnmouse_enable(void * v)1026d380e65dStsutsui dnmouse_enable(void *v)
1027d380e65dStsutsui {
1028d380e65dStsutsui struct dnkbd_softc *sc = v;
1029d380e65dStsutsui
1030d380e65dStsutsui if (ISSET(sc->sc_flags, SF_MOUSE))
10315d7f465dStsutsui return EBUSY;
1032d380e65dStsutsui SET(sc->sc_flags, SF_MOUSE);
1033d380e65dStsutsui
10345d7f465dStsutsui return 0;
1035d380e65dStsutsui }
1036d380e65dStsutsui
1037d380e65dStsutsui int
dnmouse_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)1038d380e65dStsutsui dnmouse_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
1039d380e65dStsutsui {
1040d380e65dStsutsui #if 0
1041d380e65dStsutsui struct dnkbd_softc *sc = v;
1042d380e65dStsutsui #endif
1043d380e65dStsutsui
1044d380e65dStsutsui #define WSMOUSE_TYPE_UNKNOWN 0
1045d380e65dStsutsui
1046d380e65dStsutsui switch (cmd) {
1047d380e65dStsutsui case WSMOUSEIO_GTYPE:
1048d380e65dStsutsui *(int *)data = WSMOUSE_TYPE_UNKNOWN; /* XXX */
10495d7f465dStsutsui return 0;
1050d380e65dStsutsui }
1051d380e65dStsutsui
10525d7f465dStsutsui return -1;
1053d380e65dStsutsui }
1054d380e65dStsutsui
1055d380e65dStsutsui void
dnmouse_disable(void * v)1056d380e65dStsutsui dnmouse_disable(void *v)
1057d380e65dStsutsui {
1058d380e65dStsutsui struct dnkbd_softc *sc = v;
1059d380e65dStsutsui
1060d380e65dStsutsui CLR(sc->sc_flags, SF_MOUSE);
1061d380e65dStsutsui }
1062d380e65dStsutsui #endif
1063d380e65dStsutsui
1064d380e65dStsutsui /*
1065d380e65dStsutsui * Console support
1066d380e65dStsutsui */
1067d380e65dStsutsui
1068d380e65dStsutsui void
dnkbd_cngetc(void * v,u_int * type,int * data)1069d380e65dStsutsui dnkbd_cngetc(void *v, u_int *type, int *data)
1070d380e65dStsutsui {
1071d380e65dStsutsui static int lastdat = 0;
1072d380e65dStsutsui struct dnkbd_softc *sc = v;
1073d380e65dStsutsui int s;
1074d380e65dStsutsui int dat;
1075d380e65dStsutsui
1076d380e65dStsutsui /* Take care of caps lock */
1077d380e65dStsutsui if ((lastdat & ~DNKEY_RELEASE) == DNKEY_CAPSLOCK) {
1078d380e65dStsutsui dat = lastdat ^ DNKEY_RELEASE;
1079d380e65dStsutsui lastdat = 0;
1080d380e65dStsutsui } else {
1081d380e65dStsutsui for (;;) {
1082d380e65dStsutsui s = splhigh();
1083d380e65dStsutsui dat = dnkbd_pollin(sc, 10000);
1084d380e65dStsutsui if (dat != -1) {
1085d380e65dStsutsui if (dnkbd_input(sc, dat) == EVENT_KEYBOARD) {
1086d380e65dStsutsui splx(s);
1087d380e65dStsutsui break;
1088d380e65dStsutsui }
1089d380e65dStsutsui }
1090d380e65dStsutsui splx(s);
1091d380e65dStsutsui }
1092d380e65dStsutsui lastdat = dat;
1093d380e65dStsutsui }
1094d380e65dStsutsui
1095d380e65dStsutsui dnkbd_decode(dat, type, data);
1096d380e65dStsutsui }
1097d380e65dStsutsui
1098d380e65dStsutsui void
dnkbd_cnpollc(void * v,int on)1099d380e65dStsutsui dnkbd_cnpollc(void *v, int on)
1100d380e65dStsutsui {
1101d380e65dStsutsui struct dnkbd_softc *sc = v;
1102d380e65dStsutsui
1103d380e65dStsutsui if (on)
1104d380e65dStsutsui SET(sc->sc_flags, SF_POLLING);
1105d380e65dStsutsui else
1106d380e65dStsutsui CLR(sc->sc_flags, SF_POLLING);
1107d380e65dStsutsui }
1108d380e65dStsutsui
1109d380e65dStsutsui /*
1110d380e65dStsutsui * Bell routines.
1111d380e65dStsutsui */
1112d380e65dStsutsui void
dnkbd_bell(void * v,u_int period,u_int pitch,u_int volume)1113d380e65dStsutsui dnkbd_bell(void *v, u_int period, u_int pitch, u_int volume)
1114d380e65dStsutsui {
1115d380e65dStsutsui struct dnkbd_softc *sc = v;
1116d380e65dStsutsui int s;
1117d380e65dStsutsui
1118d380e65dStsutsui s = spltty();
1119d380e65dStsutsui
1120d380e65dStsutsui if (pitch == 0 || period == 0 || volume == 0) {
1121d380e65dStsutsui if (ISSET(sc->sc_flags, SF_BELL_TMO)) {
1122d380e65dStsutsui callout_stop(&sc->sc_bellstop_tmo);
1123d380e65dStsutsui dnkbd_bellstop(v);
1124d380e65dStsutsui }
1125d380e65dStsutsui } else {
1126d380e65dStsutsui
1127d380e65dStsutsui if (!ISSET(sc->sc_flags, SF_BELL)) {
1128d380e65dStsutsui dnkbd_pollout(sc, DNCMD_PREFIX);
1129d380e65dStsutsui dnkbd_pollout(sc, DNCMD_BELL);
1130d380e65dStsutsui dnkbd_pollout(sc, DNCMD_BELL_ON);
1131d380e65dStsutsui SET(sc->sc_flags, SF_BELL);
1132d380e65dStsutsui }
1133d380e65dStsutsui
1134d380e65dStsutsui if (ISSET(sc->sc_flags, SF_BELL_TMO))
1135d380e65dStsutsui callout_stop(&sc->sc_bellstop_tmo);
1136d380e65dStsutsui callout_schedule(&sc->sc_bellstop_tmo, period);
1137d380e65dStsutsui SET(sc->sc_flags, SF_BELL_TMO);
1138d380e65dStsutsui }
1139d380e65dStsutsui
1140d380e65dStsutsui splx(s);
1141d380e65dStsutsui }
1142d380e65dStsutsui
1143d380e65dStsutsui void
dnkbd_bellstop(void * v)1144d380e65dStsutsui dnkbd_bellstop(void *v)
1145d380e65dStsutsui {
1146d380e65dStsutsui struct dnkbd_softc *sc = v;
1147d380e65dStsutsui int s;
1148d380e65dStsutsui
1149d380e65dStsutsui s = spltty();
1150d380e65dStsutsui
1151d380e65dStsutsui dnkbd_pollout(sc, DNCMD_PREFIX);
1152d380e65dStsutsui dnkbd_pollout(sc, DNCMD_BELL);
1153d380e65dStsutsui dnkbd_pollout(sc, DNCMD_BELL_OFF);
1154d380e65dStsutsui CLR(sc->sc_flags, SF_BELL);
1155d380e65dStsutsui CLR(sc->sc_flags, SF_BELL_TMO);
1156d380e65dStsutsui
1157d380e65dStsutsui splx(s);
1158d380e65dStsutsui }
1159