xref: /qemu/hw/input/pckbd.c (revision abcacb20)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU PC keyboard emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2003 Fabrice Bellard
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
2471e8a915SMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
26d1e45668SVolker Rümelin #include "qemu/error-report.h"
27c2e846bbSPhilippe Mathieu-Daudé #include "qemu/log.h"
28d1e45668SVolker Rümelin #include "qemu/timer.h"
29b86ce7a6SBernhard Beschow #include "qapi/error.h"
3049ab747fSPaolo Bonzini #include "hw/isa/isa.h"
31d6454270SMarkus Armbruster #include "migration/vmstate.h"
3268f01317SIgor Mammedov #include "hw/acpi/acpi_aml_interface.h"
3349ab747fSPaolo Bonzini #include "hw/input/ps2.h"
3464552b6bSMarkus Armbruster #include "hw/irq.h"
3547973a2dSPhilippe Mathieu-Daudé #include "hw/input/i8042.h"
36ff6e1624SVolker Rümelin #include "hw/qdev-properties.h"
3771e8a915SMarkus Armbruster #include "sysemu/reset.h"
3854d31236SMarkus Armbruster #include "sysemu/runstate.h"
3949ab747fSPaolo Bonzini 
4065b182c3SDr. David Alan Gilbert #include "trace.h"
4149ab747fSPaolo Bonzini 
4249ab747fSPaolo Bonzini /* Keyboard Controller Commands */
4332be0157SMark Cave-Ayland 
4432be0157SMark Cave-Ayland /* Read mode bits */
4532be0157SMark Cave-Ayland #define KBD_CCMD_READ_MODE         0x20
4632be0157SMark Cave-Ayland /* Write mode bits */
4732be0157SMark Cave-Ayland #define KBD_CCMD_WRITE_MODE        0x60
4832be0157SMark Cave-Ayland /* Get controller version */
4932be0157SMark Cave-Ayland #define KBD_CCMD_GET_VERSION       0xA1
5032be0157SMark Cave-Ayland /* Disable mouse interface */
5132be0157SMark Cave-Ayland #define KBD_CCMD_MOUSE_DISABLE     0xA7
5232be0157SMark Cave-Ayland /* Enable mouse interface */
5332be0157SMark Cave-Ayland #define KBD_CCMD_MOUSE_ENABLE      0xA8
5432be0157SMark Cave-Ayland /* Mouse interface test */
5532be0157SMark Cave-Ayland #define KBD_CCMD_TEST_MOUSE        0xA9
5632be0157SMark Cave-Ayland /* Controller self test */
5732be0157SMark Cave-Ayland #define KBD_CCMD_SELF_TEST         0xAA
5832be0157SMark Cave-Ayland /* Keyboard interface test */
5932be0157SMark Cave-Ayland #define KBD_CCMD_KBD_TEST          0xAB
6032be0157SMark Cave-Ayland /* Keyboard interface disable */
6132be0157SMark Cave-Ayland #define KBD_CCMD_KBD_DISABLE       0xAD
6232be0157SMark Cave-Ayland /* Keyboard interface enable */
6332be0157SMark Cave-Ayland #define KBD_CCMD_KBD_ENABLE        0xAE
6432be0157SMark Cave-Ayland /* read input port */
6532be0157SMark Cave-Ayland #define KBD_CCMD_READ_INPORT       0xC0
6632be0157SMark Cave-Ayland /* read output port */
6732be0157SMark Cave-Ayland #define KBD_CCMD_READ_OUTPORT      0xD0
6832be0157SMark Cave-Ayland /* write output port */
6932be0157SMark Cave-Ayland #define KBD_CCMD_WRITE_OUTPORT     0xD1
7049ab747fSPaolo Bonzini #define KBD_CCMD_WRITE_OBUF        0xD2
7132be0157SMark Cave-Ayland /* Write to output buffer as if initiated by the auxiliary device */
7232be0157SMark Cave-Ayland #define KBD_CCMD_WRITE_AUX_OBUF    0xD3
7332be0157SMark Cave-Ayland /* Write the following byte to the mouse */
7432be0157SMark Cave-Ayland #define KBD_CCMD_WRITE_MOUSE       0xD4
7532be0157SMark Cave-Ayland /* HP vectra only ? */
7632be0157SMark Cave-Ayland #define KBD_CCMD_DISABLE_A20       0xDD
7732be0157SMark Cave-Ayland /* HP vectra only ? */
7832be0157SMark Cave-Ayland #define KBD_CCMD_ENABLE_A20        0xDF
7932be0157SMark Cave-Ayland /* Pulse bits 3-0 of the output port P2. */
8032be0157SMark Cave-Ayland #define KBD_CCMD_PULSE_BITS_3_0    0xF0
8132be0157SMark Cave-Ayland /* Pulse bit 0 of the output port P2 = CPU reset. */
8232be0157SMark Cave-Ayland #define KBD_CCMD_RESET             0xFE
8332be0157SMark Cave-Ayland /* Pulse no bits of the output port P2. */
8432be0157SMark Cave-Ayland #define KBD_CCMD_NO_OP             0xFF
8549ab747fSPaolo Bonzini 
8649ab747fSPaolo Bonzini /* Status Register Bits */
8732be0157SMark Cave-Ayland 
8832be0157SMark Cave-Ayland /* Keyboard output buffer full */
8932be0157SMark Cave-Ayland #define KBD_STAT_OBF           0x01
9032be0157SMark Cave-Ayland /* Keyboard input buffer full */
9132be0157SMark Cave-Ayland #define KBD_STAT_IBF           0x02
9232be0157SMark Cave-Ayland /* Self test successful */
9332be0157SMark Cave-Ayland #define KBD_STAT_SELFTEST      0x04
9432be0157SMark Cave-Ayland /* Last write was a command write (0=data) */
9532be0157SMark Cave-Ayland #define KBD_STAT_CMD           0x08
9632be0157SMark Cave-Ayland /* Zero if keyboard locked */
9732be0157SMark Cave-Ayland #define KBD_STAT_UNLOCKED      0x10
9832be0157SMark Cave-Ayland /* Mouse output buffer full */
9932be0157SMark Cave-Ayland #define KBD_STAT_MOUSE_OBF     0x20
10032be0157SMark Cave-Ayland /* General receive/xmit timeout */
10132be0157SMark Cave-Ayland #define KBD_STAT_GTO           0x40
10232be0157SMark Cave-Ayland /* Parity error */
10332be0157SMark Cave-Ayland #define KBD_STAT_PERR          0x80
10449ab747fSPaolo Bonzini 
10549ab747fSPaolo Bonzini /* Controller Mode Register Bits */
10632be0157SMark Cave-Ayland 
10732be0157SMark Cave-Ayland /* Keyboard data generate IRQ1 */
10832be0157SMark Cave-Ayland #define KBD_MODE_KBD_INT       0x01
10932be0157SMark Cave-Ayland /* Mouse data generate IRQ12 */
11032be0157SMark Cave-Ayland #define KBD_MODE_MOUSE_INT     0x02
11132be0157SMark Cave-Ayland /* The system flag (?) */
11232be0157SMark Cave-Ayland #define KBD_MODE_SYS           0x04
11332be0157SMark Cave-Ayland /* The keylock doesn't affect the keyboard if set */
11432be0157SMark Cave-Ayland #define KBD_MODE_NO_KEYLOCK    0x08
11532be0157SMark Cave-Ayland /* Disable keyboard interface */
11632be0157SMark Cave-Ayland #define KBD_MODE_DISABLE_KBD   0x10
11732be0157SMark Cave-Ayland /* Disable mouse interface */
11832be0157SMark Cave-Ayland #define KBD_MODE_DISABLE_MOUSE 0x20
11932be0157SMark Cave-Ayland /* Scan code conversion to PC format */
12032be0157SMark Cave-Ayland #define KBD_MODE_KCC           0x40
12149ab747fSPaolo Bonzini #define KBD_MODE_RFU           0x80
12249ab747fSPaolo Bonzini 
12349ab747fSPaolo Bonzini /* Output Port Bits */
12449ab747fSPaolo Bonzini #define KBD_OUT_RESET           0x01    /* 1=normal mode, 0=reset */
12549ab747fSPaolo Bonzini #define KBD_OUT_A20             0x02    /* x86 only */
12649ab747fSPaolo Bonzini #define KBD_OUT_OBF             0x10    /* Keyboard output buffer full */
12749ab747fSPaolo Bonzini #define KBD_OUT_MOUSE_OBF       0x20    /* Mouse output buffer full */
12849ab747fSPaolo Bonzini 
12932be0157SMark Cave-Ayland /*
13032be0157SMark Cave-Ayland  * OSes typically write 0xdd/0xdf to turn the A20 line off and on.
131d13c0404SPaolo Bonzini  * We make the default value of the outport include these four bits,
132d13c0404SPaolo Bonzini  * so that the subsection is rarely necessary.
133d13c0404SPaolo Bonzini  */
134d13c0404SPaolo Bonzini #define KBD_OUT_ONES            0xcc
135d13c0404SPaolo Bonzini 
136f6f57a82SVolker Rümelin #define KBD_PENDING_KBD_COMPAT  0x01
137f6f57a82SVolker Rümelin #define KBD_PENDING_AUX_COMPAT  0x02
138aa67a42fSVolker Rümelin #define KBD_PENDING_CTRL_KBD    0x04
139aa67a42fSVolker Rümelin #define KBD_PENDING_CTRL_AUX    0x08
140f6f57a82SVolker Rümelin #define KBD_PENDING_KBD         KBD_MODE_DISABLE_KBD    /* 0x10 */
141f6f57a82SVolker Rümelin #define KBD_PENDING_AUX         KBD_MODE_DISABLE_MOUSE  /* 0x20 */
14249ab747fSPaolo Bonzini 
143d1e45668SVolker Rümelin #define KBD_MIGR_TIMER_PENDING  0x1
144d1e45668SVolker Rümelin 
145ac9192bdSVolker Rümelin #define KBD_OBSRC_KBD           0x01
146ac9192bdSVolker Rümelin #define KBD_OBSRC_MOUSE         0x02
147aa67a42fSVolker Rümelin #define KBD_OBSRC_CTRL          0x04
148ac9192bdSVolker Rümelin 
14949ab747fSPaolo Bonzini 
15032be0157SMark Cave-Ayland /*
15132be0157SMark Cave-Ayland  * XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
15232be0157SMark Cave-Ayland  * incorrect, but it avoids having to simulate exact delays
15332be0157SMark Cave-Ayland  */
154c3c4a961SVolker Rümelin static void kbd_update_irq_lines(KBDState *s)
15549ab747fSPaolo Bonzini {
15649ab747fSPaolo Bonzini     int irq_kbd_level, irq_mouse_level;
15749ab747fSPaolo Bonzini 
15849ab747fSPaolo Bonzini     irq_kbd_level = 0;
15949ab747fSPaolo Bonzini     irq_mouse_level = 0;
160c3c4a961SVolker Rümelin 
161c3c4a961SVolker Rümelin     if (s->status & KBD_STAT_OBF) {
162c3c4a961SVolker Rümelin         if (s->status & KBD_STAT_MOUSE_OBF) {
163c3c4a961SVolker Rümelin             if (s->mode & KBD_MODE_MOUSE_INT) {
164c3c4a961SVolker Rümelin                 irq_mouse_level = 1;
165c3c4a961SVolker Rümelin             }
166c3c4a961SVolker Rümelin         } else {
167c3c4a961SVolker Rümelin             if ((s->mode & KBD_MODE_KBD_INT) &&
168c3c4a961SVolker Rümelin                 !(s->mode & KBD_MODE_DISABLE_KBD)) {
169c3c4a961SVolker Rümelin                 irq_kbd_level = 1;
170c3c4a961SVolker Rümelin             }
171c3c4a961SVolker Rümelin         }
172c3c4a961SVolker Rümelin     }
173c2b17479SMark Cave-Ayland     qemu_set_irq(s->irqs[I8042_KBD_IRQ], irq_kbd_level);
174c2b17479SMark Cave-Ayland     qemu_set_irq(s->irqs[I8042_MOUSE_IRQ], irq_mouse_level);
175c3c4a961SVolker Rümelin }
176c3c4a961SVolker Rümelin 
177ff6e1624SVolker Rümelin static void kbd_deassert_irq(KBDState *s)
178ff6e1624SVolker Rümelin {
179ff6e1624SVolker Rümelin     s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
180ff6e1624SVolker Rümelin     s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
181ff6e1624SVolker Rümelin     kbd_update_irq_lines(s);
182ff6e1624SVolker Rümelin }
183ff6e1624SVolker Rümelin 
184e4697fabSVolker Rümelin static uint8_t kbd_pending(KBDState *s)
185e4697fabSVolker Rümelin {
186f6f57a82SVolker Rümelin     if (s->extended_state) {
187f6f57a82SVolker Rümelin         return s->pending & (~s->mode | ~(KBD_PENDING_KBD | KBD_PENDING_AUX));
188f6f57a82SVolker Rümelin     } else {
189e4697fabSVolker Rümelin         return s->pending;
190e4697fabSVolker Rümelin     }
191f6f57a82SVolker Rümelin }
192e4697fabSVolker Rümelin 
193c3c4a961SVolker Rümelin /* update irq and KBD_STAT_[MOUSE_]OBF */
194c3c4a961SVolker Rümelin static void kbd_update_irq(KBDState *s)
195c3c4a961SVolker Rümelin {
196e4697fabSVolker Rümelin     uint8_t pending = kbd_pending(s);
197e4697fabSVolker Rümelin 
19849ab747fSPaolo Bonzini     s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
19949ab747fSPaolo Bonzini     s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
200e4697fabSVolker Rümelin     if (pending) {
20149ab747fSPaolo Bonzini         s->status |= KBD_STAT_OBF;
20249ab747fSPaolo Bonzini         s->outport |= KBD_OUT_OBF;
203e4697fabSVolker Rümelin         if (pending & KBD_PENDING_CTRL_KBD) {
204aa67a42fSVolker Rümelin             s->obsrc = KBD_OBSRC_CTRL;
205e4697fabSVolker Rümelin         } else if (pending & KBD_PENDING_CTRL_AUX) {
206aa67a42fSVolker Rümelin             s->status |= KBD_STAT_MOUSE_OBF;
207aa67a42fSVolker Rümelin             s->outport |= KBD_OUT_MOUSE_OBF;
208aa67a42fSVolker Rümelin             s->obsrc = KBD_OBSRC_CTRL;
209e4697fabSVolker Rümelin         } else if (pending & KBD_PENDING_KBD) {
210aa67a42fSVolker Rümelin             s->obsrc = KBD_OBSRC_KBD;
211aa67a42fSVolker Rümelin         } else {
21249ab747fSPaolo Bonzini             s->status |= KBD_STAT_MOUSE_OBF;
21349ab747fSPaolo Bonzini             s->outport |= KBD_OUT_MOUSE_OBF;
214ac9192bdSVolker Rümelin             s->obsrc = KBD_OBSRC_MOUSE;
21549ab747fSPaolo Bonzini         }
21649ab747fSPaolo Bonzini     }
217c3c4a961SVolker Rümelin     kbd_update_irq_lines(s);
21849ab747fSPaolo Bonzini }
21949ab747fSPaolo Bonzini 
220ff6e1624SVolker Rümelin static void kbd_safe_update_irq(KBDState *s)
221ff6e1624SVolker Rümelin {
222ff6e1624SVolker Rümelin     /*
223ff6e1624SVolker Rümelin      * with KBD_STAT_OBF set, a call to kbd_read_data() will eventually call
224ff6e1624SVolker Rümelin      * kbd_update_irq()
225ff6e1624SVolker Rümelin      */
226ff6e1624SVolker Rümelin     if (s->status & KBD_STAT_OBF) {
227ff6e1624SVolker Rümelin         return;
228ff6e1624SVolker Rümelin     }
229d1e45668SVolker Rümelin     /* the throttle timer is pending and will call kbd_update_irq() */
230d1e45668SVolker Rümelin     if (s->throttle_timer && timer_pending(s->throttle_timer)) {
231d1e45668SVolker Rümelin         return;
232d1e45668SVolker Rümelin     }
233e4697fabSVolker Rümelin     if (kbd_pending(s)) {
234ff6e1624SVolker Rümelin         kbd_update_irq(s);
235ff6e1624SVolker Rümelin     }
236ff6e1624SVolker Rümelin }
237ff6e1624SVolker Rümelin 
23849ab747fSPaolo Bonzini static void kbd_update_kbd_irq(void *opaque, int level)
23949ab747fSPaolo Bonzini {
240ff6e1624SVolker Rümelin     KBDState *s = opaque;
24149ab747fSPaolo Bonzini 
242ff6e1624SVolker Rümelin     if (level) {
24349ab747fSPaolo Bonzini         s->pending |= KBD_PENDING_KBD;
244ff6e1624SVolker Rümelin     } else {
24549ab747fSPaolo Bonzini         s->pending &= ~KBD_PENDING_KBD;
246ff6e1624SVolker Rümelin     }
247ff6e1624SVolker Rümelin     kbd_safe_update_irq(s);
24849ab747fSPaolo Bonzini }
24949ab747fSPaolo Bonzini 
25049ab747fSPaolo Bonzini static void kbd_update_aux_irq(void *opaque, int level)
25149ab747fSPaolo Bonzini {
252ff6e1624SVolker Rümelin     KBDState *s = opaque;
25349ab747fSPaolo Bonzini 
254ff6e1624SVolker Rümelin     if (level) {
25549ab747fSPaolo Bonzini         s->pending |= KBD_PENDING_AUX;
256ff6e1624SVolker Rümelin     } else {
25749ab747fSPaolo Bonzini         s->pending &= ~KBD_PENDING_AUX;
258ff6e1624SVolker Rümelin     }
259ff6e1624SVolker Rümelin     kbd_safe_update_irq(s);
26049ab747fSPaolo Bonzini }
26149ab747fSPaolo Bonzini 
262d1e45668SVolker Rümelin static void kbd_throttle_timeout(void *opaque)
263d1e45668SVolker Rümelin {
264d1e45668SVolker Rümelin     KBDState *s = opaque;
265d1e45668SVolker Rümelin 
266e4697fabSVolker Rümelin     if (kbd_pending(s)) {
267d1e45668SVolker Rümelin         kbd_update_irq(s);
268d1e45668SVolker Rümelin     }
269d1e45668SVolker Rümelin }
270d1e45668SVolker Rümelin 
27149ab747fSPaolo Bonzini static uint64_t kbd_read_status(void *opaque, hwaddr addr,
27249ab747fSPaolo Bonzini                                 unsigned size)
27349ab747fSPaolo Bonzini {
27449ab747fSPaolo Bonzini     KBDState *s = opaque;
27549ab747fSPaolo Bonzini     int val;
27649ab747fSPaolo Bonzini     val = s->status;
27765b182c3SDr. David Alan Gilbert     trace_pckbd_kbd_read_status(val);
27849ab747fSPaolo Bonzini     return val;
27949ab747fSPaolo Bonzini }
28049ab747fSPaolo Bonzini 
28149ab747fSPaolo Bonzini static void kbd_queue(KBDState *s, int b, int aux)
28249ab747fSPaolo Bonzini {
283aa67a42fSVolker Rümelin     if (s->extended_state) {
284aa67a42fSVolker Rümelin         s->cbdata = b;
285aa67a42fSVolker Rümelin         s->pending &= ~KBD_PENDING_CTRL_KBD & ~KBD_PENDING_CTRL_AUX;
286aa67a42fSVolker Rümelin         s->pending |= aux ? KBD_PENDING_CTRL_AUX : KBD_PENDING_CTRL_KBD;
287aa67a42fSVolker Rümelin         kbd_safe_update_irq(s);
288aa67a42fSVolker Rümelin     } else {
289aa67a42fSVolker Rümelin         ps2_queue(aux ? s->mouse : s->kbd, b);
290aa67a42fSVolker Rümelin     }
291aa67a42fSVolker Rümelin }
292aa67a42fSVolker Rümelin 
293aa67a42fSVolker Rümelin static uint8_t kbd_dequeue(KBDState *s)
294aa67a42fSVolker Rümelin {
295aa67a42fSVolker Rümelin     uint8_t b = s->cbdata;
296aa67a42fSVolker Rümelin 
297aa67a42fSVolker Rümelin     s->pending &= ~KBD_PENDING_CTRL_KBD & ~KBD_PENDING_CTRL_AUX;
298e4697fabSVolker Rümelin     if (kbd_pending(s)) {
299aa67a42fSVolker Rümelin         kbd_update_irq(s);
300aa67a42fSVolker Rümelin     }
301aa67a42fSVolker Rümelin     return b;
30249ab747fSPaolo Bonzini }
30349ab747fSPaolo Bonzini 
30449ab747fSPaolo Bonzini static void outport_write(KBDState *s, uint32_t val)
30549ab747fSPaolo Bonzini {
30665b182c3SDr. David Alan Gilbert     trace_pckbd_outport_write(val);
30749ab747fSPaolo Bonzini     s->outport = val;
3083115b9e2SEfimov Vasily     qemu_set_irq(s->a20_out, (val >> 1) & 1);
30949ab747fSPaolo Bonzini     if (!(val & 1)) {
310cf83f140SEric Blake         qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
31149ab747fSPaolo Bonzini     }
31249ab747fSPaolo Bonzini }
31349ab747fSPaolo Bonzini 
31449ab747fSPaolo Bonzini static void kbd_write_command(void *opaque, hwaddr addr,
31549ab747fSPaolo Bonzini                               uint64_t val, unsigned size)
31649ab747fSPaolo Bonzini {
31749ab747fSPaolo Bonzini     KBDState *s = opaque;
31849ab747fSPaolo Bonzini 
31965b182c3SDr. David Alan Gilbert     trace_pckbd_kbd_write_command(val);
32049ab747fSPaolo Bonzini 
32132be0157SMark Cave-Ayland     /*
32232be0157SMark Cave-Ayland      * Bits 3-0 of the output port P2 of the keyboard controller may be pulsed
32349ab747fSPaolo Bonzini      * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE
32449ab747fSPaolo Bonzini      * command specify the output port bits to be pulsed.
32549ab747fSPaolo Bonzini      * 0: Bit should be pulsed. 1: Bit should not be modified.
32649ab747fSPaolo Bonzini      * The only useful version of this command is pulsing bit 0,
32749ab747fSPaolo Bonzini      * which does a CPU reset.
32849ab747fSPaolo Bonzini      */
32949ab747fSPaolo Bonzini     if ((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
33032be0157SMark Cave-Ayland         if (!(val & 1)) {
33149ab747fSPaolo Bonzini             val = KBD_CCMD_RESET;
33232be0157SMark Cave-Ayland         } else {
33349ab747fSPaolo Bonzini             val = KBD_CCMD_NO_OP;
33449ab747fSPaolo Bonzini         }
33532be0157SMark Cave-Ayland     }
33649ab747fSPaolo Bonzini 
33749ab747fSPaolo Bonzini     switch (val) {
33849ab747fSPaolo Bonzini     case KBD_CCMD_READ_MODE:
33949ab747fSPaolo Bonzini         kbd_queue(s, s->mode, 0);
34049ab747fSPaolo Bonzini         break;
34149ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_MODE:
34249ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_OBUF:
34349ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_AUX_OBUF:
34449ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_MOUSE:
34549ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_OUTPORT:
34649ab747fSPaolo Bonzini         s->write_cmd = val;
34749ab747fSPaolo Bonzini         break;
34849ab747fSPaolo Bonzini     case KBD_CCMD_MOUSE_DISABLE:
34949ab747fSPaolo Bonzini         s->mode |= KBD_MODE_DISABLE_MOUSE;
35049ab747fSPaolo Bonzini         break;
35149ab747fSPaolo Bonzini     case KBD_CCMD_MOUSE_ENABLE:
35249ab747fSPaolo Bonzini         s->mode &= ~KBD_MODE_DISABLE_MOUSE;
353f6f57a82SVolker Rümelin         kbd_safe_update_irq(s);
35449ab747fSPaolo Bonzini         break;
35549ab747fSPaolo Bonzini     case KBD_CCMD_TEST_MOUSE:
35649ab747fSPaolo Bonzini         kbd_queue(s, 0x00, 0);
35749ab747fSPaolo Bonzini         break;
35849ab747fSPaolo Bonzini     case KBD_CCMD_SELF_TEST:
35949ab747fSPaolo Bonzini         s->status |= KBD_STAT_SELFTEST;
36049ab747fSPaolo Bonzini         kbd_queue(s, 0x55, 0);
36149ab747fSPaolo Bonzini         break;
36249ab747fSPaolo Bonzini     case KBD_CCMD_KBD_TEST:
36349ab747fSPaolo Bonzini         kbd_queue(s, 0x00, 0);
36449ab747fSPaolo Bonzini         break;
36549ab747fSPaolo Bonzini     case KBD_CCMD_KBD_DISABLE:
36649ab747fSPaolo Bonzini         s->mode |= KBD_MODE_DISABLE_KBD;
36749ab747fSPaolo Bonzini         break;
36849ab747fSPaolo Bonzini     case KBD_CCMD_KBD_ENABLE:
36949ab747fSPaolo Bonzini         s->mode &= ~KBD_MODE_DISABLE_KBD;
370ff6e1624SVolker Rümelin         kbd_safe_update_irq(s);
37149ab747fSPaolo Bonzini         break;
37249ab747fSPaolo Bonzini     case KBD_CCMD_READ_INPORT:
373f1b7e0e4SHervé Poussineau         kbd_queue(s, 0x80, 0);
37449ab747fSPaolo Bonzini         break;
37549ab747fSPaolo Bonzini     case KBD_CCMD_READ_OUTPORT:
37649ab747fSPaolo Bonzini         kbd_queue(s, s->outport, 0);
37749ab747fSPaolo Bonzini         break;
37849ab747fSPaolo Bonzini     case KBD_CCMD_ENABLE_A20:
3793115b9e2SEfimov Vasily         qemu_irq_raise(s->a20_out);
38049ab747fSPaolo Bonzini         s->outport |= KBD_OUT_A20;
38149ab747fSPaolo Bonzini         break;
38249ab747fSPaolo Bonzini     case KBD_CCMD_DISABLE_A20:
3833115b9e2SEfimov Vasily         qemu_irq_lower(s->a20_out);
38449ab747fSPaolo Bonzini         s->outport &= ~KBD_OUT_A20;
38549ab747fSPaolo Bonzini         break;
38649ab747fSPaolo Bonzini     case KBD_CCMD_RESET:
387cf83f140SEric Blake         qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
38849ab747fSPaolo Bonzini         break;
38949ab747fSPaolo Bonzini     case KBD_CCMD_NO_OP:
39049ab747fSPaolo Bonzini         /* ignore that */
39149ab747fSPaolo Bonzini         break;
39249ab747fSPaolo Bonzini     default:
393c2e846bbSPhilippe Mathieu-Daudé         qemu_log_mask(LOG_GUEST_ERROR,
394c2e846bbSPhilippe Mathieu-Daudé                       "unsupported keyboard cmd=0x%02" PRIx64 "\n", val);
39549ab747fSPaolo Bonzini         break;
39649ab747fSPaolo Bonzini     }
39749ab747fSPaolo Bonzini }
39849ab747fSPaolo Bonzini 
39949ab747fSPaolo Bonzini static uint64_t kbd_read_data(void *opaque, hwaddr addr,
40049ab747fSPaolo Bonzini                               unsigned size)
40149ab747fSPaolo Bonzini {
40249ab747fSPaolo Bonzini     KBDState *s = opaque;
40349ab747fSPaolo Bonzini 
404ac9192bdSVolker Rümelin     if (s->status & KBD_STAT_OBF) {
405ff6e1624SVolker Rümelin         kbd_deassert_irq(s);
406ac9192bdSVolker Rümelin         if (s->obsrc & KBD_OBSRC_KBD) {
407d1e45668SVolker Rümelin             if (s->throttle_timer) {
408d1e45668SVolker Rümelin                 timer_mod(s->throttle_timer,
409d1e45668SVolker Rümelin                           qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 1000);
410d1e45668SVolker Rümelin             }
411ff6e1624SVolker Rümelin             s->obdata = ps2_read_data(s->kbd);
412ac9192bdSVolker Rümelin         } else if (s->obsrc & KBD_OBSRC_MOUSE) {
413ac9192bdSVolker Rümelin             s->obdata = ps2_read_data(s->mouse);
414aa67a42fSVolker Rümelin         } else if (s->obsrc & KBD_OBSRC_CTRL) {
415aa67a42fSVolker Rümelin             s->obdata = kbd_dequeue(s);
416ff6e1624SVolker Rümelin         }
417ff6e1624SVolker Rümelin     }
41849ab747fSPaolo Bonzini 
419ff6e1624SVolker Rümelin     trace_pckbd_kbd_read_data(s->obdata);
420ff6e1624SVolker Rümelin     return s->obdata;
42149ab747fSPaolo Bonzini }
42249ab747fSPaolo Bonzini 
42349ab747fSPaolo Bonzini static void kbd_write_data(void *opaque, hwaddr addr,
42449ab747fSPaolo Bonzini                            uint64_t val, unsigned size)
42549ab747fSPaolo Bonzini {
42649ab747fSPaolo Bonzini     KBDState *s = opaque;
42749ab747fSPaolo Bonzini 
42865b182c3SDr. David Alan Gilbert     trace_pckbd_kbd_write_data(val);
42949ab747fSPaolo Bonzini 
43049ab747fSPaolo Bonzini     switch (s->write_cmd) {
43149ab747fSPaolo Bonzini     case 0:
43249ab747fSPaolo Bonzini         ps2_write_keyboard(s->kbd, val);
433f6f57a82SVolker Rümelin         /* sending data to the keyboard reenables PS/2 communication */
434f6f57a82SVolker Rümelin         s->mode &= ~KBD_MODE_DISABLE_KBD;
435f6f57a82SVolker Rümelin         kbd_safe_update_irq(s);
43649ab747fSPaolo Bonzini         break;
43749ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_MODE:
43849ab747fSPaolo Bonzini         s->mode = val;
43949ab747fSPaolo Bonzini         ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
440ff6e1624SVolker Rümelin         /*
441ff6e1624SVolker Rümelin          * a write to the mode byte interrupt enable flags directly updates
442ff6e1624SVolker Rümelin          * the irq lines
443ff6e1624SVolker Rümelin          */
444ff6e1624SVolker Rümelin         kbd_update_irq_lines(s);
445ff6e1624SVolker Rümelin         /*
446ff6e1624SVolker Rümelin          * a write to the mode byte disable interface flags may raise
447ff6e1624SVolker Rümelin          * an irq if there is pending data in the PS/2 queues.
448ff6e1624SVolker Rümelin          */
449ff6e1624SVolker Rümelin         kbd_safe_update_irq(s);
45049ab747fSPaolo Bonzini         break;
45149ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_OBUF:
45249ab747fSPaolo Bonzini         kbd_queue(s, val, 0);
45349ab747fSPaolo Bonzini         break;
45449ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_AUX_OBUF:
45549ab747fSPaolo Bonzini         kbd_queue(s, val, 1);
45649ab747fSPaolo Bonzini         break;
45749ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_OUTPORT:
45849ab747fSPaolo Bonzini         outport_write(s, val);
45949ab747fSPaolo Bonzini         break;
46049ab747fSPaolo Bonzini     case KBD_CCMD_WRITE_MOUSE:
46149ab747fSPaolo Bonzini         ps2_write_mouse(s->mouse, val);
462f6f57a82SVolker Rümelin         /* sending data to the mouse reenables PS/2 communication */
463f6f57a82SVolker Rümelin         s->mode &= ~KBD_MODE_DISABLE_MOUSE;
464f6f57a82SVolker Rümelin         kbd_safe_update_irq(s);
46549ab747fSPaolo Bonzini         break;
46649ab747fSPaolo Bonzini     default:
46749ab747fSPaolo Bonzini         break;
46849ab747fSPaolo Bonzini     }
46949ab747fSPaolo Bonzini     s->write_cmd = 0;
47049ab747fSPaolo Bonzini }
47149ab747fSPaolo Bonzini 
47249ab747fSPaolo Bonzini static void kbd_reset(void *opaque)
47349ab747fSPaolo Bonzini {
47449ab747fSPaolo Bonzini     KBDState *s = opaque;
47549ab747fSPaolo Bonzini 
47649ab747fSPaolo Bonzini     s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
47749ab747fSPaolo Bonzini     s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
478d13c0404SPaolo Bonzini     s->outport = KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES;
479ff6e1624SVolker Rümelin     s->pending = 0;
480ff6e1624SVolker Rümelin     kbd_deassert_irq(s);
481d1e45668SVolker Rümelin     if (s->throttle_timer) {
482d1e45668SVolker Rümelin         timer_del(s->throttle_timer);
483d1e45668SVolker Rümelin     }
484a28fe7e3SPavel Dovgalyuk }
485a28fe7e3SPavel Dovgalyuk 
486a28fe7e3SPavel Dovgalyuk static uint8_t kbd_outport_default(KBDState *s)
487a28fe7e3SPavel Dovgalyuk {
488d13c0404SPaolo Bonzini     return KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES
489a28fe7e3SPavel Dovgalyuk            | (s->status & KBD_STAT_OBF ? KBD_OUT_OBF : 0)
490a28fe7e3SPavel Dovgalyuk            | (s->status & KBD_STAT_MOUSE_OBF ? KBD_OUT_MOUSE_OBF : 0);
491a28fe7e3SPavel Dovgalyuk }
492a28fe7e3SPavel Dovgalyuk 
493a28fe7e3SPavel Dovgalyuk static int kbd_outport_post_load(void *opaque, int version_id)
494a28fe7e3SPavel Dovgalyuk {
495a28fe7e3SPavel Dovgalyuk     KBDState *s = opaque;
496a28fe7e3SPavel Dovgalyuk     s->outport_present = true;
497a28fe7e3SPavel Dovgalyuk     return 0;
498a28fe7e3SPavel Dovgalyuk }
499a28fe7e3SPavel Dovgalyuk 
500a28fe7e3SPavel Dovgalyuk static bool kbd_outport_needed(void *opaque)
501a28fe7e3SPavel Dovgalyuk {
502a28fe7e3SPavel Dovgalyuk     KBDState *s = opaque;
503a28fe7e3SPavel Dovgalyuk     return s->outport != kbd_outport_default(s);
504a28fe7e3SPavel Dovgalyuk }
505a28fe7e3SPavel Dovgalyuk 
5065cd8cadaSJuan Quintela static const VMStateDescription vmstate_kbd_outport = {
5075cd8cadaSJuan Quintela     .name = "pckbd_outport",
5085cd8cadaSJuan Quintela     .version_id = 1,
5095cd8cadaSJuan Quintela     .minimum_version_id = 1,
5105cd8cadaSJuan Quintela     .post_load = kbd_outport_post_load,
5115cd8cadaSJuan Quintela     .needed = kbd_outport_needed,
5125cd8cadaSJuan Quintela     .fields = (VMStateField[]) {
5135cd8cadaSJuan Quintela         VMSTATE_UINT8(outport, KBDState),
5145cd8cadaSJuan Quintela         VMSTATE_END_OF_LIST()
5155cd8cadaSJuan Quintela     }
5165cd8cadaSJuan Quintela };
5175cd8cadaSJuan Quintela 
518d1e45668SVolker Rümelin static int kbd_extended_state_pre_save(void *opaque)
519d1e45668SVolker Rümelin {
520d1e45668SVolker Rümelin     KBDState *s = opaque;
521d1e45668SVolker Rümelin 
522d1e45668SVolker Rümelin     s->migration_flags = 0;
523d1e45668SVolker Rümelin     if (s->throttle_timer && timer_pending(s->throttle_timer)) {
524d1e45668SVolker Rümelin         s->migration_flags |= KBD_MIGR_TIMER_PENDING;
525d1e45668SVolker Rümelin     }
526d1e45668SVolker Rümelin 
527d1e45668SVolker Rümelin     return 0;
528d1e45668SVolker Rümelin }
529d1e45668SVolker Rümelin 
530d1e45668SVolker Rümelin static int kbd_extended_state_post_load(void *opaque, int version_id)
531d1e45668SVolker Rümelin {
532d1e45668SVolker Rümelin     KBDState *s = opaque;
533d1e45668SVolker Rümelin 
534d1e45668SVolker Rümelin     if (s->migration_flags & KBD_MIGR_TIMER_PENDING) {
535d1e45668SVolker Rümelin         kbd_throttle_timeout(s);
536d1e45668SVolker Rümelin     }
537ac9192bdSVolker Rümelin     s->extended_state_loaded = true;
538d1e45668SVolker Rümelin 
539d1e45668SVolker Rümelin     return 0;
540d1e45668SVolker Rümelin }
541d1e45668SVolker Rümelin 
542ff6e1624SVolker Rümelin static bool kbd_extended_state_needed(void *opaque)
543ff6e1624SVolker Rümelin {
544ff6e1624SVolker Rümelin     KBDState *s = opaque;
545ff6e1624SVolker Rümelin 
546ff6e1624SVolker Rümelin     return s->extended_state;
547ff6e1624SVolker Rümelin }
548ff6e1624SVolker Rümelin 
549ff6e1624SVolker Rümelin static const VMStateDescription vmstate_kbd_extended_state = {
550ff6e1624SVolker Rümelin     .name = "pckbd/extended_state",
551d1e45668SVolker Rümelin     .post_load = kbd_extended_state_post_load,
552d1e45668SVolker Rümelin     .pre_save = kbd_extended_state_pre_save,
553ff6e1624SVolker Rümelin     .needed = kbd_extended_state_needed,
554ff6e1624SVolker Rümelin     .fields = (VMStateField[]) {
555d1e45668SVolker Rümelin         VMSTATE_UINT32(migration_flags, KBDState),
556ac9192bdSVolker Rümelin         VMSTATE_UINT32(obsrc, KBDState),
557ff6e1624SVolker Rümelin         VMSTATE_UINT8(obdata, KBDState),
558aa67a42fSVolker Rümelin         VMSTATE_UINT8(cbdata, KBDState),
559ff6e1624SVolker Rümelin         VMSTATE_END_OF_LIST()
560ff6e1624SVolker Rümelin     }
561ff6e1624SVolker Rümelin };
562ff6e1624SVolker Rümelin 
563f6f57a82SVolker Rümelin static int kbd_pre_save(void *opaque)
564f6f57a82SVolker Rümelin {
565f6f57a82SVolker Rümelin     KBDState *s = opaque;
566f6f57a82SVolker Rümelin 
567f6f57a82SVolker Rümelin     if (s->extended_state) {
568f6f57a82SVolker Rümelin         s->pending_tmp = s->pending;
569f6f57a82SVolker Rümelin     } else {
570f6f57a82SVolker Rümelin         s->pending_tmp = 0;
571f6f57a82SVolker Rümelin         if (s->pending & KBD_PENDING_KBD) {
572f6f57a82SVolker Rümelin             s->pending_tmp |= KBD_PENDING_KBD_COMPAT;
573f6f57a82SVolker Rümelin         }
574f6f57a82SVolker Rümelin         if (s->pending & KBD_PENDING_AUX) {
575f6f57a82SVolker Rümelin             s->pending_tmp |= KBD_PENDING_AUX_COMPAT;
576f6f57a82SVolker Rümelin         }
577f6f57a82SVolker Rümelin     }
578f6f57a82SVolker Rümelin     return 0;
579f6f57a82SVolker Rümelin }
580f6f57a82SVolker Rümelin 
581ac9192bdSVolker Rümelin static int kbd_pre_load(void *opaque)
582ac9192bdSVolker Rümelin {
583ac9192bdSVolker Rümelin     KBDState *s = opaque;
584ac9192bdSVolker Rümelin 
5859d74e6c3SVolker Rümelin     s->outport_present = false;
586ac9192bdSVolker Rümelin     s->extended_state_loaded = false;
587ac9192bdSVolker Rümelin     return 0;
588ac9192bdSVolker Rümelin }
589ac9192bdSVolker Rümelin 
590a28fe7e3SPavel Dovgalyuk static int kbd_post_load(void *opaque, int version_id)
591a28fe7e3SPavel Dovgalyuk {
592a28fe7e3SPavel Dovgalyuk     KBDState *s = opaque;
593a28fe7e3SPavel Dovgalyuk     if (!s->outport_present) {
594a28fe7e3SPavel Dovgalyuk         s->outport = kbd_outport_default(s);
595a28fe7e3SPavel Dovgalyuk     }
596f6f57a82SVolker Rümelin     s->pending = s->pending_tmp;
597ac9192bdSVolker Rümelin     if (!s->extended_state_loaded) {
598ac9192bdSVolker Rümelin         s->obsrc = s->status & KBD_STAT_OBF ?
599ac9192bdSVolker Rümelin             (s->status & KBD_STAT_MOUSE_OBF ? KBD_OBSRC_MOUSE : KBD_OBSRC_KBD) :
600ac9192bdSVolker Rümelin             0;
601f6f57a82SVolker Rümelin         if (s->pending & KBD_PENDING_KBD_COMPAT) {
602f6f57a82SVolker Rümelin             s->pending |= KBD_PENDING_KBD;
603ac9192bdSVolker Rümelin         }
604f6f57a82SVolker Rümelin         if (s->pending & KBD_PENDING_AUX_COMPAT) {
605f6f57a82SVolker Rümelin             s->pending |= KBD_PENDING_AUX;
606f6f57a82SVolker Rümelin         }
607f6f57a82SVolker Rümelin     }
608f6f57a82SVolker Rümelin     /* clear all unused flags */
609f6f57a82SVolker Rümelin     s->pending &= KBD_PENDING_CTRL_KBD | KBD_PENDING_CTRL_AUX |
610f6f57a82SVolker Rümelin                   KBD_PENDING_KBD | KBD_PENDING_AUX;
611a28fe7e3SPavel Dovgalyuk     return 0;
61249ab747fSPaolo Bonzini }
61349ab747fSPaolo Bonzini 
61449ab747fSPaolo Bonzini static const VMStateDescription vmstate_kbd = {
61549ab747fSPaolo Bonzini     .name = "pckbd",
61649ab747fSPaolo Bonzini     .version_id = 3,
61749ab747fSPaolo Bonzini     .minimum_version_id = 3,
618ac9192bdSVolker Rümelin     .pre_load = kbd_pre_load,
619a28fe7e3SPavel Dovgalyuk     .post_load = kbd_post_load,
620f6f57a82SVolker Rümelin     .pre_save = kbd_pre_save,
62149ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
62249ab747fSPaolo Bonzini         VMSTATE_UINT8(write_cmd, KBDState),
62349ab747fSPaolo Bonzini         VMSTATE_UINT8(status, KBDState),
62449ab747fSPaolo Bonzini         VMSTATE_UINT8(mode, KBDState),
625f6f57a82SVolker Rümelin         VMSTATE_UINT8(pending_tmp, KBDState),
62649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
627a28fe7e3SPavel Dovgalyuk     },
6285cd8cadaSJuan Quintela     .subsections = (const VMStateDescription * []) {
6295cd8cadaSJuan Quintela         &vmstate_kbd_outport,
630ff6e1624SVolker Rümelin         &vmstate_kbd_extended_state,
6315cd8cadaSJuan Quintela         NULL
63249ab747fSPaolo Bonzini     }
63349ab747fSPaolo Bonzini };
63449ab747fSPaolo Bonzini 
63549ab747fSPaolo Bonzini /* Memory mapped interface */
6365876503cSPeter Maydell static uint64_t kbd_mm_readfn(void *opaque, hwaddr addr, unsigned size)
63749ab747fSPaolo Bonzini {
63849ab747fSPaolo Bonzini     KBDState *s = opaque;
63949ab747fSPaolo Bonzini 
64032be0157SMark Cave-Ayland     if (addr & s->mask) {
64149ab747fSPaolo Bonzini         return kbd_read_status(s, 0, 1) & 0xff;
64232be0157SMark Cave-Ayland     } else {
64349ab747fSPaolo Bonzini         return kbd_read_data(s, 0, 1) & 0xff;
64449ab747fSPaolo Bonzini     }
64532be0157SMark Cave-Ayland }
64649ab747fSPaolo Bonzini 
6475876503cSPeter Maydell static void kbd_mm_writefn(void *opaque, hwaddr addr,
6485876503cSPeter Maydell                            uint64_t value, unsigned size)
64949ab747fSPaolo Bonzini {
65049ab747fSPaolo Bonzini     KBDState *s = opaque;
65149ab747fSPaolo Bonzini 
65232be0157SMark Cave-Ayland     if (addr & s->mask) {
65349ab747fSPaolo Bonzini         kbd_write_command(s, 0, value & 0xff, 1);
65432be0157SMark Cave-Ayland     } else {
65549ab747fSPaolo Bonzini         kbd_write_data(s, 0, value & 0xff, 1);
65649ab747fSPaolo Bonzini     }
65732be0157SMark Cave-Ayland }
65849ab747fSPaolo Bonzini 
6595876503cSPeter Maydell 
66049ab747fSPaolo Bonzini static const MemoryRegionOps i8042_mmio_ops = {
6615876503cSPeter Maydell     .read = kbd_mm_readfn,
6625876503cSPeter Maydell     .write = kbd_mm_writefn,
6635876503cSPeter Maydell     .valid.min_access_size = 1,
6645876503cSPeter Maydell     .valid.max_access_size = 4,
66549ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
66649ab747fSPaolo Bonzini };
66749ab747fSPaolo Bonzini 
668cb663a81SMark Cave-Ayland static void i8042_mmio_set_kbd_irq(void *opaque, int n, int level)
669cb663a81SMark Cave-Ayland {
670cb663a81SMark Cave-Ayland     MMIOKBDState *s = I8042_MMIO(opaque);
671cb663a81SMark Cave-Ayland     KBDState *ks = &s->kbd;
672cb663a81SMark Cave-Ayland 
673cb663a81SMark Cave-Ayland     kbd_update_kbd_irq(ks, level);
674cb663a81SMark Cave-Ayland }
675cb663a81SMark Cave-Ayland 
676cb663a81SMark Cave-Ayland static void i8042_mmio_set_mouse_irq(void *opaque, int n, int level)
677cb663a81SMark Cave-Ayland {
678cb663a81SMark Cave-Ayland     MMIOKBDState *s = I8042_MMIO(opaque);
679cb663a81SMark Cave-Ayland     KBDState *ks = &s->kbd;
680cb663a81SMark Cave-Ayland 
681cb663a81SMark Cave-Ayland     kbd_update_aux_irq(ks, level);
682cb663a81SMark Cave-Ayland }
683cb663a81SMark Cave-Ayland 
68457f6c3aaSMark Cave-Ayland static void i8042_mmio_reset(DeviceState *dev)
68557f6c3aaSMark Cave-Ayland {
68657f6c3aaSMark Cave-Ayland     MMIOKBDState *s = I8042_MMIO(dev);
68757f6c3aaSMark Cave-Ayland     KBDState *ks = &s->kbd;
68857f6c3aaSMark Cave-Ayland 
68957f6c3aaSMark Cave-Ayland     kbd_reset(ks);
69057f6c3aaSMark Cave-Ayland }
69157f6c3aaSMark Cave-Ayland 
692f4de68d1SMark Cave-Ayland static void i8042_mmio_realize(DeviceState *dev, Error **errp)
693f4de68d1SMark Cave-Ayland {
694f4de68d1SMark Cave-Ayland     MMIOKBDState *s = I8042_MMIO(dev);
695f4de68d1SMark Cave-Ayland     KBDState *ks = &s->kbd;
696f4de68d1SMark Cave-Ayland 
697f4de68d1SMark Cave-Ayland     memory_region_init_io(&s->region, OBJECT(dev), &i8042_mmio_ops, ks,
698f4de68d1SMark Cave-Ayland                           "i8042", s->size);
699f4de68d1SMark Cave-Ayland 
700f4de68d1SMark Cave-Ayland     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->region);
70175877e93SMark Cave-Ayland 
7027227de94SMark Cave-Ayland     ks->kbd = ps2_kbd_init();
703cb663a81SMark Cave-Ayland     qdev_connect_gpio_out(DEVICE(ks->kbd), PS2_DEVICE_IRQ,
704cb663a81SMark Cave-Ayland                           qdev_get_gpio_in_named(dev, "ps2-kbd-input-irq",
705cb663a81SMark Cave-Ayland                                                  0));
7067227de94SMark Cave-Ayland     ks->mouse = ps2_mouse_init();
707cb663a81SMark Cave-Ayland     qdev_connect_gpio_out(DEVICE(ks->mouse), PS2_DEVICE_IRQ,
708cb663a81SMark Cave-Ayland                           qdev_get_gpio_in_named(dev, "ps2-mouse-input-irq",
709cb663a81SMark Cave-Ayland                                                  0));
710f4de68d1SMark Cave-Ayland }
711f4de68d1SMark Cave-Ayland 
71247fc7415SMark Cave-Ayland static void i8042_mmio_init(Object *obj)
71347fc7415SMark Cave-Ayland {
71447fc7415SMark Cave-Ayland     MMIOKBDState *s = I8042_MMIO(obj);
71547fc7415SMark Cave-Ayland     KBDState *ks = &s->kbd;
71647fc7415SMark Cave-Ayland 
71747fc7415SMark Cave-Ayland     ks->extended_state = true;
718cb663a81SMark Cave-Ayland 
719cb663a81SMark Cave-Ayland     qdev_init_gpio_out(DEVICE(obj), ks->irqs, 2);
720cb663a81SMark Cave-Ayland     qdev_init_gpio_in_named(DEVICE(obj), i8042_mmio_set_kbd_irq,
721cb663a81SMark Cave-Ayland                             "ps2-kbd-input-irq", 1);
722cb663a81SMark Cave-Ayland     qdev_init_gpio_in_named(DEVICE(obj), i8042_mmio_set_mouse_irq,
723cb663a81SMark Cave-Ayland                             "ps2-mouse-input-irq", 1);
72447fc7415SMark Cave-Ayland }
72547fc7415SMark Cave-Ayland 
726d4f5b4d8SMark Cave-Ayland static Property i8042_mmio_properties[] = {
727d4f5b4d8SMark Cave-Ayland     DEFINE_PROP_UINT64("mask", MMIOKBDState, kbd.mask, UINT64_MAX),
7287b9fff29SMark Cave-Ayland     DEFINE_PROP_UINT32("size", MMIOKBDState, size, -1),
729d4f5b4d8SMark Cave-Ayland     DEFINE_PROP_END_OF_LIST(),
730d4f5b4d8SMark Cave-Ayland };
731d4f5b4d8SMark Cave-Ayland 
732*abcacb20SMark Cave-Ayland static const VMStateDescription vmstate_kbd_mmio = {
733*abcacb20SMark Cave-Ayland     .name = "pckbd-mmio",
734*abcacb20SMark Cave-Ayland     .version_id = 1,
735*abcacb20SMark Cave-Ayland     .minimum_version_id = 1,
736*abcacb20SMark Cave-Ayland     .fields = (VMStateField[]) {
737*abcacb20SMark Cave-Ayland         VMSTATE_STRUCT(kbd, MMIOKBDState, 0, vmstate_kbd, KBDState),
738*abcacb20SMark Cave-Ayland         VMSTATE_END_OF_LIST()
739*abcacb20SMark Cave-Ayland     }
740*abcacb20SMark Cave-Ayland };
741*abcacb20SMark Cave-Ayland 
742150ee013SMark Cave-Ayland static void i8042_mmio_class_init(ObjectClass *klass, void *data)
743150ee013SMark Cave-Ayland {
744150ee013SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
745150ee013SMark Cave-Ayland 
746f4de68d1SMark Cave-Ayland     dc->realize = i8042_mmio_realize;
74757f6c3aaSMark Cave-Ayland     dc->reset = i8042_mmio_reset;
748*abcacb20SMark Cave-Ayland     dc->vmsd = &vmstate_kbd_mmio;
749d4f5b4d8SMark Cave-Ayland     device_class_set_props(dc, i8042_mmio_properties);
750150ee013SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
751150ee013SMark Cave-Ayland }
752150ee013SMark Cave-Ayland 
753903dd0e4SMark Cave-Ayland MMIOKBDState *i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
75401d924dcSMark Cave-Ayland                             ram_addr_t size, hwaddr mask)
75549ab747fSPaolo Bonzini {
756150ee013SMark Cave-Ayland     DeviceState *dev;
757150ee013SMark Cave-Ayland 
758150ee013SMark Cave-Ayland     dev = qdev_new(TYPE_I8042_MMIO);
759d4f5b4d8SMark Cave-Ayland     qdev_prop_set_uint64(dev, "mask", mask);
7607b9fff29SMark Cave-Ayland     qdev_prop_set_uint32(dev, "size", size);
761150ee013SMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
76249ab747fSPaolo Bonzini 
763cb663a81SMark Cave-Ayland     qdev_connect_gpio_out(dev, I8042_KBD_IRQ, kbd_irq);
764cb663a81SMark Cave-Ayland     qdev_connect_gpio_out(dev, I8042_MOUSE_IRQ, mouse_irq);
76549ab747fSPaolo Bonzini 
766903dd0e4SMark Cave-Ayland     return I8042_MMIO(dev);
76749ab747fSPaolo Bonzini }
76849ab747fSPaolo Bonzini 
769150ee013SMark Cave-Ayland static const TypeInfo i8042_mmio_info = {
770150ee013SMark Cave-Ayland     .name          = TYPE_I8042_MMIO,
771150ee013SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
77247fc7415SMark Cave-Ayland     .instance_init = i8042_mmio_init,
773150ee013SMark Cave-Ayland     .instance_size = sizeof(MMIOKBDState),
774150ee013SMark Cave-Ayland     .class_init    = i8042_mmio_class_init
775150ee013SMark Cave-Ayland };
776150ee013SMark Cave-Ayland 
7770fe4bb32SMarc-André Lureau void i8042_isa_mouse_fake_event(ISAKBDState *isa)
77849ab747fSPaolo Bonzini {
779a2e0b863SAndreas Färber     KBDState *s = &isa->kbd;
78049ab747fSPaolo Bonzini 
78149ab747fSPaolo Bonzini     ps2_mouse_fake_event(s->mouse);
78249ab747fSPaolo Bonzini }
78349ab747fSPaolo Bonzini 
784d80fe99dSMarc-André Lureau void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out)
78549ab747fSPaolo Bonzini {
786d80fe99dSMarc-André Lureau     qdev_connect_gpio_out_named(DEVICE(dev), I8042_A20_LINE, 0, a20_out);
78749ab747fSPaolo Bonzini }
78849ab747fSPaolo Bonzini 
78949ab747fSPaolo Bonzini static const VMStateDescription vmstate_kbd_isa = {
79049ab747fSPaolo Bonzini     .name = "pckbd",
79149ab747fSPaolo Bonzini     .version_id = 3,
79249ab747fSPaolo Bonzini     .minimum_version_id = 3,
79349ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
79449ab747fSPaolo Bonzini         VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState),
79549ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
79649ab747fSPaolo Bonzini     }
79749ab747fSPaolo Bonzini };
79849ab747fSPaolo Bonzini 
79949ab747fSPaolo Bonzini static const MemoryRegionOps i8042_data_ops = {
80049ab747fSPaolo Bonzini     .read = kbd_read_data,
80149ab747fSPaolo Bonzini     .write = kbd_write_data,
80249ab747fSPaolo Bonzini     .impl = {
80349ab747fSPaolo Bonzini         .min_access_size = 1,
80449ab747fSPaolo Bonzini         .max_access_size = 1,
80549ab747fSPaolo Bonzini     },
80649ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
80749ab747fSPaolo Bonzini };
80849ab747fSPaolo Bonzini 
80949ab747fSPaolo Bonzini static const MemoryRegionOps i8042_cmd_ops = {
81049ab747fSPaolo Bonzini     .read = kbd_read_status,
81149ab747fSPaolo Bonzini     .write = kbd_write_command,
81249ab747fSPaolo Bonzini     .impl = {
81349ab747fSPaolo Bonzini         .min_access_size = 1,
81449ab747fSPaolo Bonzini         .max_access_size = 1,
81549ab747fSPaolo Bonzini     },
81649ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
81749ab747fSPaolo Bonzini };
81849ab747fSPaolo Bonzini 
8196eb252d5SMark Cave-Ayland static void i8042_set_kbd_irq(void *opaque, int n, int level)
8206eb252d5SMark Cave-Ayland {
8216eb252d5SMark Cave-Ayland     ISAKBDState *s = I8042(opaque);
8226eb252d5SMark Cave-Ayland     KBDState *ks = &s->kbd;
8236eb252d5SMark Cave-Ayland 
8246eb252d5SMark Cave-Ayland     kbd_update_kbd_irq(ks, level);
8256eb252d5SMark Cave-Ayland }
8266eb252d5SMark Cave-Ayland 
8276eb252d5SMark Cave-Ayland static void i8042_set_mouse_irq(void *opaque, int n, int level)
8286eb252d5SMark Cave-Ayland {
8296eb252d5SMark Cave-Ayland     ISAKBDState *s = I8042(opaque);
8306eb252d5SMark Cave-Ayland     KBDState *ks = &s->kbd;
8316eb252d5SMark Cave-Ayland 
8326eb252d5SMark Cave-Ayland     kbd_update_aux_irq(ks, level);
8336eb252d5SMark Cave-Ayland }
8346eb252d5SMark Cave-Ayland 
8356eb252d5SMark Cave-Ayland 
83655870d6fSMark Cave-Ayland static void i8042_reset(DeviceState *dev)
83755870d6fSMark Cave-Ayland {
83855870d6fSMark Cave-Ayland     ISAKBDState *s = I8042(dev);
83955870d6fSMark Cave-Ayland     KBDState *ks = &s->kbd;
84055870d6fSMark Cave-Ayland 
84155870d6fSMark Cave-Ayland     kbd_reset(ks);
84255870d6fSMark Cave-Ayland }
84355870d6fSMark Cave-Ayland 
844db895a1eSAndreas Färber static void i8042_initfn(Object *obj)
84549ab747fSPaolo Bonzini {
846db895a1eSAndreas Färber     ISAKBDState *isa_s = I8042(obj);
847db895a1eSAndreas Färber     KBDState *s = &isa_s->kbd;
848db895a1eSAndreas Färber 
8491437c94bSPaolo Bonzini     memory_region_init_io(isa_s->io + 0, obj, &i8042_data_ops, s,
8501437c94bSPaolo Bonzini                           "i8042-data", 1);
8511437c94bSPaolo Bonzini     memory_region_init_io(isa_s->io + 1, obj, &i8042_cmd_ops, s,
8521437c94bSPaolo Bonzini                           "i8042-cmd", 1);
8533115b9e2SEfimov Vasily 
8543115b9e2SEfimov Vasily     qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, I8042_A20_LINE, 1);
8556eb252d5SMark Cave-Ayland 
8566eb252d5SMark Cave-Ayland     qdev_init_gpio_out(DEVICE(obj), s->irqs, 2);
8576eb252d5SMark Cave-Ayland     qdev_init_gpio_in_named(DEVICE(obj), i8042_set_kbd_irq,
8586eb252d5SMark Cave-Ayland                             "ps2-kbd-input-irq", 1);
8596eb252d5SMark Cave-Ayland     qdev_init_gpio_in_named(DEVICE(obj), i8042_set_mouse_irq,
8606eb252d5SMark Cave-Ayland                             "ps2-mouse-input-irq", 1);
861db895a1eSAndreas Färber }
862db895a1eSAndreas Färber 
863db895a1eSAndreas Färber static void i8042_realizefn(DeviceState *dev, Error **errp)
864db895a1eSAndreas Färber {
865db895a1eSAndreas Färber     ISADevice *isadev = ISA_DEVICE(dev);
866a2e0b863SAndreas Färber     ISAKBDState *isa_s = I8042(dev);
86749ab747fSPaolo Bonzini     KBDState *s = &isa_s->kbd;
86849ab747fSPaolo Bonzini 
869b86ce7a6SBernhard Beschow     if (isa_s->kbd_irq >= ISA_NUM_IRQS) {
870b86ce7a6SBernhard Beschow         error_setg(errp, "Maximum value for \"kbd-irq\" is: %u",
871b86ce7a6SBernhard Beschow                    ISA_NUM_IRQS - 1);
872b86ce7a6SBernhard Beschow         return;
873b86ce7a6SBernhard Beschow     }
874b86ce7a6SBernhard Beschow 
875b86ce7a6SBernhard Beschow     if (isa_s->mouse_irq >= ISA_NUM_IRQS) {
876b86ce7a6SBernhard Beschow         error_setg(errp, "Maximum value for \"mouse-irq\" is: %u",
877b86ce7a6SBernhard Beschow                    ISA_NUM_IRQS - 1);
878b86ce7a6SBernhard Beschow         return;
879b86ce7a6SBernhard Beschow     }
880b86ce7a6SBernhard Beschow 
8816eb252d5SMark Cave-Ayland     isa_connect_gpio_out(isadev, I8042_KBD_IRQ, isa_s->kbd_irq);
8826eb252d5SMark Cave-Ayland     isa_connect_gpio_out(isadev, I8042_MOUSE_IRQ, isa_s->mouse_irq);
88349ab747fSPaolo Bonzini 
884db895a1eSAndreas Färber     isa_register_ioport(isadev, isa_s->io + 0, 0x60);
885db895a1eSAndreas Färber     isa_register_ioport(isadev, isa_s->io + 1, 0x64);
88649ab747fSPaolo Bonzini 
8877227de94SMark Cave-Ayland     s->kbd = ps2_kbd_init();
8886eb252d5SMark Cave-Ayland     qdev_connect_gpio_out(DEVICE(s->kbd), PS2_DEVICE_IRQ,
8896eb252d5SMark Cave-Ayland                           qdev_get_gpio_in_named(dev, "ps2-kbd-input-irq",
8906eb252d5SMark Cave-Ayland                                                  0));
8917227de94SMark Cave-Ayland     s->mouse = ps2_mouse_init();
8926eb252d5SMark Cave-Ayland     qdev_connect_gpio_out(DEVICE(s->mouse), PS2_DEVICE_IRQ,
8936eb252d5SMark Cave-Ayland                           qdev_get_gpio_in_named(dev, "ps2-mouse-input-irq",
8946eb252d5SMark Cave-Ayland                                                  0));
895d1e45668SVolker Rümelin     if (isa_s->kbd_throttle && !isa_s->kbd.extended_state) {
896d1e45668SVolker Rümelin         warn_report(TYPE_I8042 ": can't enable kbd-throttle without"
897d1e45668SVolker Rümelin                     " extended-state, disabling kbd-throttle");
898d1e45668SVolker Rümelin     } else if (isa_s->kbd_throttle) {
899d1e45668SVolker Rümelin         s->throttle_timer = timer_new_us(QEMU_CLOCK_VIRTUAL,
900d1e45668SVolker Rümelin                                          kbd_throttle_timeout, s);
901d1e45668SVolker Rümelin     }
90249ab747fSPaolo Bonzini }
90349ab747fSPaolo Bonzini 
90468f01317SIgor Mammedov static void i8042_build_aml(AcpiDevAmlIf *adev, Aml *scope)
905df0f3d13SGerd Hoffmann {
90668f01317SIgor Mammedov     ISAKBDState *isa_s = I8042(adev);
907df0f3d13SGerd Hoffmann     Aml *kbd;
908df0f3d13SGerd Hoffmann     Aml *mou;
909df0f3d13SGerd Hoffmann     Aml *crs;
910df0f3d13SGerd Hoffmann 
911df0f3d13SGerd Hoffmann     crs = aml_resource_template();
912df0f3d13SGerd Hoffmann     aml_append(crs, aml_io(AML_DECODE16, 0x0060, 0x0060, 0x01, 0x01));
913df0f3d13SGerd Hoffmann     aml_append(crs, aml_io(AML_DECODE16, 0x0064, 0x0064, 0x01, 0x01));
914b86ce7a6SBernhard Beschow     aml_append(crs, aml_irq_no_flags(isa_s->kbd_irq));
915df0f3d13SGerd Hoffmann 
916df0f3d13SGerd Hoffmann     kbd = aml_device("KBD");
917df0f3d13SGerd Hoffmann     aml_append(kbd, aml_name_decl("_HID", aml_eisaid("PNP0303")));
918df0f3d13SGerd Hoffmann     aml_append(kbd, aml_name_decl("_STA", aml_int(0xf)));
919df0f3d13SGerd Hoffmann     aml_append(kbd, aml_name_decl("_CRS", crs));
920df0f3d13SGerd Hoffmann 
921df0f3d13SGerd Hoffmann     crs = aml_resource_template();
922b86ce7a6SBernhard Beschow     aml_append(crs, aml_irq_no_flags(isa_s->mouse_irq));
923df0f3d13SGerd Hoffmann 
924df0f3d13SGerd Hoffmann     mou = aml_device("MOU");
925df0f3d13SGerd Hoffmann     aml_append(mou, aml_name_decl("_HID", aml_eisaid("PNP0F13")));
926df0f3d13SGerd Hoffmann     aml_append(mou, aml_name_decl("_STA", aml_int(0xf)));
927df0f3d13SGerd Hoffmann     aml_append(mou, aml_name_decl("_CRS", crs));
928df0f3d13SGerd Hoffmann 
929df0f3d13SGerd Hoffmann     aml_append(scope, kbd);
930df0f3d13SGerd Hoffmann     aml_append(scope, mou);
931df0f3d13SGerd Hoffmann }
932df0f3d13SGerd Hoffmann 
933ff6e1624SVolker Rümelin static Property i8042_properties[] = {
934ff6e1624SVolker Rümelin     DEFINE_PROP_BOOL("extended-state", ISAKBDState, kbd.extended_state, true),
935d1e45668SVolker Rümelin     DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false),
936b86ce7a6SBernhard Beschow     DEFINE_PROP_UINT8("kbd-irq", ISAKBDState, kbd_irq, 1),
937b86ce7a6SBernhard Beschow     DEFINE_PROP_UINT8("mouse-irq", ISAKBDState, mouse_irq, 12),
938ff6e1624SVolker Rümelin     DEFINE_PROP_END_OF_LIST(),
939ff6e1624SVolker Rümelin };
940ff6e1624SVolker Rümelin 
94149ab747fSPaolo Bonzini static void i8042_class_initfn(ObjectClass *klass, void *data)
94249ab747fSPaolo Bonzini {
94349ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
94468f01317SIgor Mammedov     AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
945db895a1eSAndreas Färber 
946ff6e1624SVolker Rümelin     device_class_set_props(dc, i8042_properties);
94755870d6fSMark Cave-Ayland     dc->reset = i8042_reset;
948db895a1eSAndreas Färber     dc->realize = i8042_realizefn;
94949ab747fSPaolo Bonzini     dc->vmsd = &vmstate_kbd_isa;
95068f01317SIgor Mammedov     adevc->build_dev_aml = i8042_build_aml;
951cbe9ed73Skumar sourav     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
95249ab747fSPaolo Bonzini }
95349ab747fSPaolo Bonzini 
95449ab747fSPaolo Bonzini static const TypeInfo i8042_info = {
955a2e0b863SAndreas Färber     .name          = TYPE_I8042,
95649ab747fSPaolo Bonzini     .parent        = TYPE_ISA_DEVICE,
95749ab747fSPaolo Bonzini     .instance_size = sizeof(ISAKBDState),
958db895a1eSAndreas Färber     .instance_init = i8042_initfn,
95949ab747fSPaolo Bonzini     .class_init    = i8042_class_initfn,
96068f01317SIgor Mammedov     .interfaces = (InterfaceInfo[]) {
96168f01317SIgor Mammedov         { TYPE_ACPI_DEV_AML_IF },
96268f01317SIgor Mammedov         { },
96368f01317SIgor Mammedov     },
96449ab747fSPaolo Bonzini };
96549ab747fSPaolo Bonzini 
96649ab747fSPaolo Bonzini static void i8042_register_types(void)
96749ab747fSPaolo Bonzini {
96849ab747fSPaolo Bonzini     type_register_static(&i8042_info);
969150ee013SMark Cave-Ayland     type_register_static(&i8042_mmio_info);
97049ab747fSPaolo Bonzini }
97149ab747fSPaolo Bonzini 
97249ab747fSPaolo Bonzini type_init(i8042_register_types)
973