xref: /netbsd-src/sys/arch/hp300/dev/dnkbd.c (revision 594f789a5c7bef0390e4223293d2f6fcada88e9b)
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