1264916Stychon/*-
2264916Stychon * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3302332Sgrehan * Copyright (c) 2015 Nahanni Systems Inc.
4264916Stychon * All rights reserved.
5264916Stychon *
6264916Stychon * Redistribution and use in source and binary forms, with or without
7264916Stychon * modification, are permitted provided that the following conditions
8264916Stychon * are met:
9264916Stychon * 1. Redistributions of source code must retain the above copyright
10264916Stychon *    notice, this list of conditions and the following disclaimer.
11264916Stychon * 2. Redistributions in binary form must reproduce the above copyright
12264916Stychon *    notice, this list of conditions and the following disclaimer in the
13264916Stychon *    documentation and/or other materials provided with the distribution.
14264916Stychon *
15264916Stychon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
16264916Stychon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17264916Stychon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18264916Stychon * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19264916Stychon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20264916Stychon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21264916Stychon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22264916Stychon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23264916Stychon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24264916Stychon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25264916Stychon * SUCH DAMAGE.
26264916Stychon */
27264916Stychon
28264916Stychon#include <sys/cdefs.h>
29264916Stychon__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/atkbdc.c 302332 2016-07-04 03:19:06Z grehan $");
30264916Stychon
31264916Stychon#include <sys/types.h>
32264916Stychon
33264916Stychon#include <machine/vmm.h>
34264916Stychon
35269094Sneel#include <vmmapi.h>
36269094Sneel
37269094Sneel#include <assert.h>
38269094Sneel#include <errno.h>
39302332Sgrehan#include <stdbool.h>
40302332Sgrehan#include <stdlib.h>
41264916Stychon#include <stdio.h>
42302332Sgrehan#include <string.h>
43302332Sgrehan#include <unistd.h>
44302332Sgrehan#include <pthread.h>
45302332Sgrehan#include <pthread_np.h>
46264916Stychon
47302332Sgrehan#include "acpi.h"
48264916Stychon#include "inout.h"
49302332Sgrehan#include "pci_emul.h"
50302332Sgrehan#include "pci_irq.h"
51264916Stychon#include "pci_lpc.h"
52302332Sgrehan#include "ps2kbd.h"
53302332Sgrehan#include "ps2mouse.h"
54264916Stychon
55264916Stychon#define	KBD_DATA_PORT		0x60
56264916Stychon
57264916Stychon#define	KBD_STS_CTL_PORT	0x64
58264916Stychon
59264916Stychon#define	KBDC_RESET		0xfe
60264916Stychon
61302332Sgrehan#define	KBD_DEV_IRQ		1
62302332Sgrehan#define	AUX_DEV_IRQ		12
63302332Sgrehan
64302332Sgrehan/* controller commands */
65302332Sgrehan#define	KBDC_SET_COMMAND_BYTE	0x60
66302332Sgrehan#define	KBDC_GET_COMMAND_BYTE	0x20
67302332Sgrehan#define	KBDC_DISABLE_AUX_PORT	0xa7
68302332Sgrehan#define	KBDC_ENABLE_AUX_PORT	0xa8
69302332Sgrehan#define	KBDC_TEST_AUX_PORT	0xa9
70302332Sgrehan#define	KBDC_TEST_CTRL		0xaa
71302332Sgrehan#define	KBDC_TEST_KBD_PORT	0xab
72302332Sgrehan#define	KBDC_DISABLE_KBD_PORT	0xad
73302332Sgrehan#define	KBDC_ENABLE_KBD_PORT	0xae
74302332Sgrehan#define	KBDC_READ_INPORT	0xc0
75302332Sgrehan#define	KBDC_READ_OUTPORT	0xd0
76302332Sgrehan#define	KBDC_WRITE_OUTPORT	0xd1
77302332Sgrehan#define	KBDC_WRITE_KBD_OUTBUF	0xd2
78302332Sgrehan#define	KBDC_WRITE_AUX_OUTBUF	0xd3
79302332Sgrehan#define	KBDC_WRITE_TO_AUX	0xd4
80302332Sgrehan
81302332Sgrehan/* controller command byte (set by KBDC_SET_COMMAND_BYTE) */
82302332Sgrehan#define	KBD_TRANSLATION		0x40
83302332Sgrehan#define	KBD_SYS_FLAG_BIT	0x04
84302332Sgrehan#define	KBD_DISABLE_KBD_PORT	0x10
85302332Sgrehan#define	KBD_DISABLE_AUX_PORT	0x20
86302332Sgrehan#define	KBD_ENABLE_AUX_INT	0x02
87302332Sgrehan#define	KBD_ENABLE_KBD_INT	0x01
88302332Sgrehan#define	KBD_KBD_CONTROL_BITS	(KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT)
89302332Sgrehan#define	KBD_AUX_CONTROL_BITS	(KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT)
90302332Sgrehan
91302332Sgrehan/* controller status bits */
92302332Sgrehan#define	KBDS_KBD_BUFFER_FULL	0x01
93302332Sgrehan#define KBDS_SYS_FLAG		0x04
94302332Sgrehan#define KBDS_CTRL_FLAG		0x08
95302332Sgrehan#define	KBDS_AUX_BUFFER_FULL	0x20
96302332Sgrehan
97302332Sgrehan/* controller output port */
98302332Sgrehan#define	KBDO_KBD_OUTFULL	0x10
99302332Sgrehan#define	KBDO_AUX_OUTFULL	0x20
100302332Sgrehan
101302332Sgrehan#define	RAMSZ			32
102302332Sgrehan#define	FIFOSZ			15
103302332Sgrehan#define	CTRL_CMD_FLAG		0x8000
104302332Sgrehan
105302332Sgrehanstruct kbd_dev {
106302332Sgrehan	bool	irq_active;
107302332Sgrehan	int	irq;
108302332Sgrehan
109302332Sgrehan	uint8_t	buffer[FIFOSZ];
110302332Sgrehan	int	brd, bwr;
111302332Sgrehan	int	bcnt;
112302332Sgrehan};
113302332Sgrehan
114302332Sgrehanstruct aux_dev {
115302332Sgrehan	bool	irq_active;
116302332Sgrehan	int	irq;
117302332Sgrehan};
118302332Sgrehan
119302332Sgrehanstruct atkbdc_softc {
120302332Sgrehan	struct vmctx *ctx;
121302332Sgrehan	pthread_mutex_t mtx;
122302332Sgrehan
123302332Sgrehan	struct ps2kbd_softc	*ps2kbd_sc;
124302332Sgrehan	struct ps2mouse_softc	*ps2mouse_sc;
125302332Sgrehan
126302332Sgrehan	uint8_t	status;		/* status register */
127302332Sgrehan	uint8_t	outport;	/* controller output port */
128302332Sgrehan	uint8_t	ram[RAMSZ];	/* byte0 = controller config */
129302332Sgrehan
130302332Sgrehan	uint32_t curcmd;	/* current command for next byte */
131302332Sgrehan	uint32_t  ctrlbyte;
132302332Sgrehan
133302332Sgrehan	struct kbd_dev kbd;
134302332Sgrehan	struct aux_dev aux;
135302332Sgrehan};
136302332Sgrehan
137302332Sgrehanstatic void
138302332Sgrehanatkbdc_assert_kbd_intr(struct atkbdc_softc *sc)
139302332Sgrehan{
140302332Sgrehan	if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
141302332Sgrehan		sc->kbd.irq_active = true;
142302332Sgrehan		vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
143302332Sgrehan	}
144302332Sgrehan}
145302332Sgrehan
146302332Sgrehanstatic void
147302332Sgrehanatkbdc_assert_aux_intr(struct atkbdc_softc *sc)
148302332Sgrehan{
149302332Sgrehan	if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
150302332Sgrehan		sc->aux.irq_active = true;
151302332Sgrehan		vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
152302332Sgrehan	}
153302332Sgrehan}
154302332Sgrehan
155264916Stychonstatic int
156302332Sgrehanatkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val)
157302332Sgrehan{
158302332Sgrehan	assert(pthread_mutex_isowned_np(&sc->mtx));
159302332Sgrehan
160302332Sgrehan	if (sc->kbd.bcnt < FIFOSZ) {
161302332Sgrehan		sc->kbd.buffer[sc->kbd.bwr] = val;
162302332Sgrehan		sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ;
163302332Sgrehan		sc->kbd.bcnt++;
164302332Sgrehan		sc->status |= KBDS_KBD_BUFFER_FULL;
165302332Sgrehan		sc->outport |= KBDO_KBD_OUTFULL;
166302332Sgrehan	} else {
167302332Sgrehan		printf("atkbd data buffer full\n");
168302332Sgrehan	}
169302332Sgrehan
170302332Sgrehan	return (sc->kbd.bcnt < FIFOSZ);
171302332Sgrehan}
172302332Sgrehan
173302332Sgrehanstatic void
174302332Sgrehanatkbdc_kbd_read(struct atkbdc_softc *sc)
175302332Sgrehan{
176302332Sgrehan	const uint8_t translation[256] = {
177302332Sgrehan		0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
178302332Sgrehan		0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
179302332Sgrehan		0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
180302332Sgrehan		0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
181302332Sgrehan		0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
182302332Sgrehan		0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
183302332Sgrehan		0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
184302332Sgrehan		0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
185302332Sgrehan		0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
186302332Sgrehan		0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
187302332Sgrehan		0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
188302332Sgrehan		0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
189302332Sgrehan		0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
190302332Sgrehan		0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
191302332Sgrehan		0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
192302332Sgrehan		0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
193302332Sgrehan		0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
194302332Sgrehan		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
195302332Sgrehan		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
196302332Sgrehan		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
197302332Sgrehan		0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
198302332Sgrehan		0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
199302332Sgrehan		0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
200302332Sgrehan		0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
201302332Sgrehan		0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
202302332Sgrehan		0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
203302332Sgrehan		0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
204302332Sgrehan		0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
205302332Sgrehan		0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
206302332Sgrehan		0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
207302332Sgrehan		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
208302332Sgrehan		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
209302332Sgrehan	};
210302332Sgrehan	uint8_t val;
211302332Sgrehan	uint8_t release = 0;
212302332Sgrehan
213302332Sgrehan	assert(pthread_mutex_isowned_np(&sc->mtx));
214302332Sgrehan
215302332Sgrehan	if (sc->ram[0] & KBD_TRANSLATION) {
216302332Sgrehan		while (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) {
217302332Sgrehan			if (val == 0xf0) {
218302332Sgrehan				release = 0x80;
219302332Sgrehan				continue;
220302332Sgrehan			} else {
221302332Sgrehan				val = translation[val] | release;
222302332Sgrehan			}
223302332Sgrehan			atkbdc_kbd_queue_data(sc, val);
224302332Sgrehan			break;
225302332Sgrehan		}
226302332Sgrehan	} else {
227302332Sgrehan		while (sc->kbd.bcnt < FIFOSZ) {
228302332Sgrehan			if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
229302332Sgrehan				atkbdc_kbd_queue_data(sc, val);
230302332Sgrehan			else
231302332Sgrehan				break;
232302332Sgrehan		}
233302332Sgrehan	}
234302332Sgrehan
235302332Sgrehan	if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) ||
236302332Sgrehan	    ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0)
237302332Sgrehan		atkbdc_assert_kbd_intr(sc);
238302332Sgrehan}
239302332Sgrehan
240302332Sgrehanstatic void
241302332Sgrehanatkbdc_aux_poll(struct atkbdc_softc *sc)
242302332Sgrehan{
243302332Sgrehan	if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) {
244302332Sgrehan		sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
245302332Sgrehan		sc->outport |= KBDO_AUX_OUTFULL;
246302332Sgrehan		atkbdc_assert_aux_intr(sc);
247302332Sgrehan	}
248302332Sgrehan}
249302332Sgrehan
250302332Sgrehanstatic void
251302332Sgrehanatkbdc_kbd_poll(struct atkbdc_softc *sc)
252302332Sgrehan{
253302332Sgrehan	assert(pthread_mutex_isowned_np(&sc->mtx));
254302332Sgrehan
255302332Sgrehan	atkbdc_kbd_read(sc);
256302332Sgrehan}
257302332Sgrehan
258302332Sgrehanstatic void
259302332Sgrehanatkbdc_poll(struct atkbdc_softc *sc)
260302332Sgrehan{
261302332Sgrehan	atkbdc_aux_poll(sc);
262302332Sgrehan	atkbdc_kbd_poll(sc);
263302332Sgrehan}
264302332Sgrehan
265302332Sgrehanstatic void
266302332Sgrehanatkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf)
267302332Sgrehan{
268302332Sgrehan	assert(pthread_mutex_isowned_np(&sc->mtx));
269302332Sgrehan
270302332Sgrehan	if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) {
271302332Sgrehan		if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) {
272302332Sgrehan			if (sc->kbd.bcnt == 0)
273302332Sgrehan				sc->status &= ~(KBDS_AUX_BUFFER_FULL |
274302332Sgrehan				                KBDS_KBD_BUFFER_FULL);
275302332Sgrehan			else
276302332Sgrehan				sc->status &= ~(KBDS_AUX_BUFFER_FULL);
277302332Sgrehan			sc->outport &= ~KBDO_AUX_OUTFULL;
278302332Sgrehan		}
279302332Sgrehan
280302332Sgrehan		atkbdc_poll(sc);
281302332Sgrehan		return;
282302332Sgrehan	}
283302332Sgrehan
284302332Sgrehan	if (sc->kbd.bcnt > 0) {
285302332Sgrehan		*buf = sc->kbd.buffer[sc->kbd.brd];
286302332Sgrehan		sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ;
287302332Sgrehan		sc->kbd.bcnt--;
288302332Sgrehan		if (sc->kbd.bcnt == 0) {
289302332Sgrehan			sc->status &= ~KBDS_KBD_BUFFER_FULL;
290302332Sgrehan			sc->outport &= ~KBDO_KBD_OUTFULL;
291302332Sgrehan		}
292302332Sgrehan
293302332Sgrehan		atkbdc_poll(sc);
294302332Sgrehan	}
295302332Sgrehan
296302332Sgrehan	if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) {
297302332Sgrehan		sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
298302332Sgrehan	}
299302332Sgrehan}
300302332Sgrehan
301302332Sgrehanstatic int
302264916Stychonatkbdc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
303264916Stychon    uint32_t *eax, void *arg)
304264916Stychon{
305302332Sgrehan	struct atkbdc_softc *sc;
306302332Sgrehan	uint8_t buf;
307302332Sgrehan	int retval;
308302332Sgrehan
309264916Stychon	if (bytes != 1)
310269094Sneel		return (-1);
311302332Sgrehan	sc = arg;
312302332Sgrehan	retval = 0;
313264916Stychon
314302332Sgrehan	pthread_mutex_lock(&sc->mtx);
315302332Sgrehan	if (in) {
316302332Sgrehan		sc->curcmd = 0;
317302332Sgrehan		if (sc->ctrlbyte != 0) {
318302332Sgrehan			*eax = sc->ctrlbyte & 0xff;
319302332Sgrehan			sc->ctrlbyte = 0;
320302332Sgrehan		} else {
321302332Sgrehan			/* read device buffer; includes kbd cmd responses */
322302332Sgrehan			atkbdc_dequeue_data(sc, &buf);
323302332Sgrehan			*eax = buf;
324302332Sgrehan		}
325264916Stychon
326302332Sgrehan		sc->status &= ~KBDS_CTRL_FLAG;
327302332Sgrehan		pthread_mutex_unlock(&sc->mtx);
328302332Sgrehan		return (retval);
329302332Sgrehan	}
330302332Sgrehan
331302332Sgrehan	if (sc->status & KBDS_CTRL_FLAG) {
332302332Sgrehan		/*
333302332Sgrehan		 * Command byte for the controller.
334302332Sgrehan		 */
335302332Sgrehan		switch (sc->curcmd) {
336302332Sgrehan		case KBDC_SET_COMMAND_BYTE:
337302332Sgrehan			sc->ram[0] = *eax;
338302332Sgrehan			if (sc->ram[0] & KBD_SYS_FLAG_BIT)
339302332Sgrehan				sc->status |= KBDS_SYS_FLAG;
340302332Sgrehan			else
341302332Sgrehan				sc->status &= ~KBDS_SYS_FLAG;
342302332Sgrehan			break;
343302332Sgrehan		case KBDC_WRITE_OUTPORT:
344302332Sgrehan			sc->outport = *eax;
345302332Sgrehan			break;
346302332Sgrehan		case KBDC_WRITE_TO_AUX:
347302332Sgrehan			ps2mouse_write(sc->ps2mouse_sc, *eax, 0);
348302332Sgrehan			atkbdc_poll(sc);
349302332Sgrehan			break;
350302332Sgrehan		case KBDC_WRITE_KBD_OUTBUF:
351302332Sgrehan			atkbdc_kbd_queue_data(sc, *eax);
352302332Sgrehan			break;
353302332Sgrehan		case KBDC_WRITE_AUX_OUTBUF:
354302332Sgrehan			ps2mouse_write(sc->ps2mouse_sc, *eax, 1);
355302332Sgrehan			sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
356302332Sgrehan			atkbdc_aux_poll(sc);
357302332Sgrehan			break;
358302332Sgrehan		default:
359302332Sgrehan			/* write to particular RAM byte */
360302332Sgrehan			if (sc->curcmd >= 0x61 && sc->curcmd <= 0x7f) {
361302332Sgrehan				int byten;
362302332Sgrehan
363302332Sgrehan				byten = (sc->curcmd - 0x60) & 0x1f;
364302332Sgrehan				sc->ram[byten] = *eax & 0xff;
365302332Sgrehan			}
366302332Sgrehan			break;
367302332Sgrehan		}
368302332Sgrehan
369302332Sgrehan		sc->curcmd = 0;
370302332Sgrehan		sc->status &= ~KBDS_CTRL_FLAG;
371302332Sgrehan
372302332Sgrehan		pthread_mutex_unlock(&sc->mtx);
373302332Sgrehan		return (retval);
374302332Sgrehan	}
375302332Sgrehan
376302332Sgrehan	/*
377302332Sgrehan	 * Data byte for the device.
378302332Sgrehan	 */
379302332Sgrehan	ps2kbd_write(sc->ps2kbd_sc, *eax);
380302332Sgrehan	atkbdc_poll(sc);
381302332Sgrehan
382302332Sgrehan	pthread_mutex_unlock(&sc->mtx);
383302332Sgrehan
384302332Sgrehan	return (retval);
385264916Stychon}
386264916Stychon
387264916Stychonstatic int
388264916Stychonatkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
389264916Stychon    int bytes, uint32_t *eax, void *arg)
390264916Stychon{
391302332Sgrehan	struct atkbdc_softc *sc;
392302332Sgrehan	int	error, retval;
393264916Stychon
394264916Stychon	if (bytes != 1)
395269094Sneel		return (-1);
396264916Stychon
397302332Sgrehan	sc = arg;
398269094Sneel	retval = 0;
399302332Sgrehan
400302332Sgrehan	pthread_mutex_lock(&sc->mtx);
401302332Sgrehan
402264916Stychon	if (in) {
403302332Sgrehan		/* read status register */
404302332Sgrehan		*eax = sc->status;
405302332Sgrehan		pthread_mutex_unlock(&sc->mtx);
406302332Sgrehan		return (retval);
407302332Sgrehan	}
408302332Sgrehan
409302332Sgrehan
410302332Sgrehan	sc->curcmd = 0;
411302332Sgrehan	sc->status |= KBDS_CTRL_FLAG;
412302332Sgrehan	sc->ctrlbyte = 0;
413302332Sgrehan
414302332Sgrehan	switch (*eax) {
415302332Sgrehan	case KBDC_GET_COMMAND_BYTE:
416302332Sgrehan		sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0];
417302332Sgrehan		break;
418302332Sgrehan	case KBDC_TEST_CTRL:
419302332Sgrehan		sc->ctrlbyte = CTRL_CMD_FLAG | 0x55;
420302332Sgrehan		break;
421302332Sgrehan	case KBDC_TEST_AUX_PORT:
422302332Sgrehan	case KBDC_TEST_KBD_PORT:
423302332Sgrehan		sc->ctrlbyte = CTRL_CMD_FLAG | 0;
424302332Sgrehan		break;
425302332Sgrehan	case KBDC_READ_INPORT:
426302332Sgrehan		sc->ctrlbyte = CTRL_CMD_FLAG | 0;
427302332Sgrehan		break;
428302332Sgrehan	case KBDC_READ_OUTPORT:
429302332Sgrehan		sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport;
430302332Sgrehan		break;
431302332Sgrehan	case KBDC_SET_COMMAND_BYTE:
432302332Sgrehan	case KBDC_WRITE_OUTPORT:
433302332Sgrehan	case KBDC_WRITE_KBD_OUTBUF:
434302332Sgrehan	case KBDC_WRITE_AUX_OUTBUF:
435302332Sgrehan		sc->curcmd = *eax;
436302332Sgrehan		break;
437302332Sgrehan	case KBDC_DISABLE_KBD_PORT:
438302332Sgrehan		sc->ram[0] |= KBD_DISABLE_KBD_PORT;
439302332Sgrehan		break;
440302332Sgrehan	case KBDC_ENABLE_KBD_PORT:
441302332Sgrehan		sc->ram[0] &= ~KBD_DISABLE_KBD_PORT;
442302332Sgrehan		if (sc->kbd.bcnt > 0)
443302332Sgrehan			sc->status |= KBDS_KBD_BUFFER_FULL;
444302332Sgrehan		atkbdc_poll(sc);
445302332Sgrehan		break;
446302332Sgrehan	case KBDC_WRITE_TO_AUX:
447302332Sgrehan		sc->curcmd = *eax;
448302332Sgrehan		break;
449302332Sgrehan	case KBDC_DISABLE_AUX_PORT:
450302332Sgrehan		sc->ram[0] |= KBD_DISABLE_AUX_PORT;
451302332Sgrehan		ps2mouse_toggle(sc->ps2mouse_sc, 0);
452302332Sgrehan		sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
453302332Sgrehan		sc->outport &= ~KBDS_AUX_BUFFER_FULL;
454302332Sgrehan		break;
455302332Sgrehan	case KBDC_ENABLE_AUX_PORT:
456302332Sgrehan		sc->ram[0] &= ~KBD_DISABLE_AUX_PORT;
457302332Sgrehan		ps2mouse_toggle(sc->ps2mouse_sc, 1);
458302332Sgrehan		if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0)
459302332Sgrehan			sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
460302332Sgrehan		break;
461302332Sgrehan	case KBDC_RESET:		/* Pulse "reset" line */
462302332Sgrehan		error = vm_suspend(ctx, VM_SUSPEND_RESET);
463302332Sgrehan		assert(error == 0 || errno == EALREADY);
464302332Sgrehan		break;
465302332Sgrehan	default:
466302332Sgrehan		if (*eax >= 0x21 && *eax <= 0x3f) {
467302332Sgrehan			/* read "byte N" from RAM */
468302332Sgrehan			int	byten;
469302332Sgrehan
470302332Sgrehan			byten = (*eax - 0x20) & 0x1f;
471302332Sgrehan			sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten];
472264916Stychon		}
473302332Sgrehan		break;
474264916Stychon	}
475264916Stychon
476302332Sgrehan	pthread_mutex_unlock(&sc->mtx);
477302332Sgrehan
478302332Sgrehan	if (sc->ctrlbyte != 0) {
479302332Sgrehan		sc->status |= KBDS_KBD_BUFFER_FULL;
480302332Sgrehan		sc->status &= ~KBDS_AUX_BUFFER_FULL;
481302332Sgrehan		atkbdc_assert_kbd_intr(sc);
482302332Sgrehan	} else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 &&
483302332Sgrehan	           (sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
484302332Sgrehan		sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
485302332Sgrehan		atkbdc_assert_aux_intr(sc);
486302332Sgrehan	} else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) {
487302332Sgrehan		sc->status |= KBDS_KBD_BUFFER_FULL;
488302332Sgrehan		atkbdc_assert_kbd_intr(sc);
489302332Sgrehan	}
490302332Sgrehan
491264916Stychon	return (retval);
492264916Stychon}
493264916Stychon
494302332Sgrehanvoid
495302332Sgrehanatkbdc_event(struct atkbdc_softc *sc, int iskbd)
496302332Sgrehan{
497302332Sgrehan	pthread_mutex_lock(&sc->mtx);
498302332Sgrehan
499302332Sgrehan	if (iskbd)
500302332Sgrehan		atkbdc_kbd_poll(sc);
501302332Sgrehan	else
502302332Sgrehan		atkbdc_aux_poll(sc);
503302332Sgrehan	pthread_mutex_unlock(&sc->mtx);
504302332Sgrehan}
505302332Sgrehan
506302332Sgrehanvoid
507302332Sgrehanatkbdc_init(struct vmctx *ctx)
508302332Sgrehan{
509302332Sgrehan	struct inout_port iop;
510302332Sgrehan	struct atkbdc_softc *sc;
511302332Sgrehan	int error;
512302332Sgrehan
513302332Sgrehan	sc = calloc(1, sizeof(struct atkbdc_softc));
514302332Sgrehan	sc->ctx = ctx;
515302332Sgrehan
516302332Sgrehan	pthread_mutex_init(&sc->mtx, NULL);
517302332Sgrehan
518302332Sgrehan	bzero(&iop, sizeof(struct inout_port));
519302332Sgrehan	iop.name = "atkdbc";
520302332Sgrehan	iop.port = KBD_STS_CTL_PORT;
521302332Sgrehan	iop.size = 1;
522302332Sgrehan	iop.flags = IOPORT_F_INOUT;
523302332Sgrehan	iop.handler = atkbdc_sts_ctl_handler;
524302332Sgrehan	iop.arg = sc;
525302332Sgrehan
526302332Sgrehan	error = register_inout(&iop);
527302332Sgrehan	assert(error == 0);
528302332Sgrehan
529302332Sgrehan	bzero(&iop, sizeof(struct inout_port));
530302332Sgrehan	iop.name = "atkdbc";
531302332Sgrehan	iop.port = KBD_DATA_PORT;
532302332Sgrehan	iop.size = 1;
533302332Sgrehan	iop.flags = IOPORT_F_INOUT;
534302332Sgrehan	iop.handler = atkbdc_data_handler;
535302332Sgrehan	iop.arg = sc;
536302332Sgrehan
537302332Sgrehan	error = register_inout(&iop);
538302332Sgrehan	assert(error == 0);
539302332Sgrehan
540302332Sgrehan	pci_irq_reserve(KBD_DEV_IRQ);
541302332Sgrehan	sc->kbd.irq = KBD_DEV_IRQ;
542302332Sgrehan
543302332Sgrehan	pci_irq_reserve(AUX_DEV_IRQ);
544302332Sgrehan	sc->aux.irq = AUX_DEV_IRQ;
545302332Sgrehan
546302332Sgrehan	sc->ps2kbd_sc = ps2kbd_init(sc);
547302332Sgrehan	sc->ps2mouse_sc = ps2mouse_init(sc);
548302332Sgrehan}
549302332Sgrehan
550302332Sgrehanstatic void
551302332Sgrehanatkbdc_dsdt(void)
552302332Sgrehan{
553302332Sgrehan
554302332Sgrehan	dsdt_line("");
555302332Sgrehan	dsdt_line("Device (KBD)");
556302332Sgrehan	dsdt_line("{");
557302332Sgrehan	dsdt_line("  Name (_HID, EisaId (\"PNP0303\"))");
558302332Sgrehan	dsdt_line("  Name (_CRS, ResourceTemplate ()");
559302332Sgrehan	dsdt_line("  {");
560302332Sgrehan	dsdt_indent(2);
561302332Sgrehan	dsdt_fixed_ioport(KBD_DATA_PORT, 1);
562302332Sgrehan	dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
563302332Sgrehan	dsdt_fixed_irq(1);
564302332Sgrehan	dsdt_unindent(2);
565302332Sgrehan	dsdt_line("  })");
566302332Sgrehan	dsdt_line("}");
567302332Sgrehan
568302332Sgrehan	dsdt_line("");
569302332Sgrehan	dsdt_line("Device (MOU)");
570302332Sgrehan	dsdt_line("{");
571302332Sgrehan	dsdt_line("  Name (_HID, EisaId (\"PNP0F13\"))");
572302332Sgrehan	dsdt_line("  Name (_CRS, ResourceTemplate ()");
573302332Sgrehan	dsdt_line("  {");
574302332Sgrehan	dsdt_indent(2);
575302332Sgrehan	dsdt_fixed_ioport(KBD_DATA_PORT, 1);
576302332Sgrehan	dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
577302332Sgrehan	dsdt_fixed_irq(12);
578302332Sgrehan	dsdt_unindent(2);
579302332Sgrehan	dsdt_line("  })");
580302332Sgrehan	dsdt_line("}");
581302332Sgrehan}
582302332SgrehanLPC_DSDT(atkbdc_dsdt);
583302332Sgrehan
584