xref: /qemu/hw/input/pckbd.c (revision 150ee013)
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     }
173c3c4a961SVolker Rümelin     qemu_set_irq(s->irq_kbd, irq_kbd_level);
174c3c4a961SVolker Rümelin     qemu_set_irq(s->irq_mouse, 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 
668*150ee013SMark Cave-Ayland static void i8042_mmio_class_init(ObjectClass *klass, void *data)
669*150ee013SMark Cave-Ayland {
670*150ee013SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
671*150ee013SMark Cave-Ayland 
672*150ee013SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
673*150ee013SMark Cave-Ayland }
674*150ee013SMark Cave-Ayland 
67549ab747fSPaolo Bonzini void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
67649ab747fSPaolo Bonzini                    MemoryRegion *region, ram_addr_t size,
67749ab747fSPaolo Bonzini                    hwaddr mask)
67849ab747fSPaolo Bonzini {
679*150ee013SMark Cave-Ayland     DeviceState *dev;
680*150ee013SMark Cave-Ayland     KBDState *s;
681*150ee013SMark Cave-Ayland 
682*150ee013SMark Cave-Ayland     dev = qdev_new(TYPE_I8042_MMIO);
683*150ee013SMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
684*150ee013SMark Cave-Ayland     s = &I8042_MMIO(dev)->kbd;
68549ab747fSPaolo Bonzini 
68649ab747fSPaolo Bonzini     s->irq_kbd = kbd_irq;
68749ab747fSPaolo Bonzini     s->irq_mouse = mouse_irq;
68849ab747fSPaolo Bonzini     s->mask = mask;
68949ab747fSPaolo Bonzini 
690ff6e1624SVolker Rümelin     s->extended_state = true;
691ff6e1624SVolker Rümelin 
69249ab747fSPaolo Bonzini     vmstate_register(NULL, 0, &vmstate_kbd, s);
69349ab747fSPaolo Bonzini 
6942c9b15caSPaolo Bonzini     memory_region_init_io(region, NULL, &i8042_mmio_ops, s, "i8042", size);
69549ab747fSPaolo Bonzini 
69649ab747fSPaolo Bonzini     s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
69749ab747fSPaolo Bonzini     s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
69849ab747fSPaolo Bonzini     qemu_register_reset(kbd_reset, s);
69949ab747fSPaolo Bonzini }
70049ab747fSPaolo Bonzini 
701*150ee013SMark Cave-Ayland static const TypeInfo i8042_mmio_info = {
702*150ee013SMark Cave-Ayland     .name          = TYPE_I8042_MMIO,
703*150ee013SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
704*150ee013SMark Cave-Ayland     .instance_size = sizeof(MMIOKBDState),
705*150ee013SMark Cave-Ayland     .class_init    = i8042_mmio_class_init
706*150ee013SMark Cave-Ayland };
707*150ee013SMark Cave-Ayland 
7080fe4bb32SMarc-André Lureau void i8042_isa_mouse_fake_event(ISAKBDState *isa)
70949ab747fSPaolo Bonzini {
710a2e0b863SAndreas Färber     KBDState *s = &isa->kbd;
71149ab747fSPaolo Bonzini 
71249ab747fSPaolo Bonzini     ps2_mouse_fake_event(s->mouse);
71349ab747fSPaolo Bonzini }
71449ab747fSPaolo Bonzini 
715d80fe99dSMarc-André Lureau void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out)
71649ab747fSPaolo Bonzini {
717d80fe99dSMarc-André Lureau     qdev_connect_gpio_out_named(DEVICE(dev), I8042_A20_LINE, 0, a20_out);
71849ab747fSPaolo Bonzini }
71949ab747fSPaolo Bonzini 
72049ab747fSPaolo Bonzini static const VMStateDescription vmstate_kbd_isa = {
72149ab747fSPaolo Bonzini     .name = "pckbd",
72249ab747fSPaolo Bonzini     .version_id = 3,
72349ab747fSPaolo Bonzini     .minimum_version_id = 3,
72449ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
72549ab747fSPaolo Bonzini         VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState),
72649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
72749ab747fSPaolo Bonzini     }
72849ab747fSPaolo Bonzini };
72949ab747fSPaolo Bonzini 
73049ab747fSPaolo Bonzini static const MemoryRegionOps i8042_data_ops = {
73149ab747fSPaolo Bonzini     .read = kbd_read_data,
73249ab747fSPaolo Bonzini     .write = kbd_write_data,
73349ab747fSPaolo Bonzini     .impl = {
73449ab747fSPaolo Bonzini         .min_access_size = 1,
73549ab747fSPaolo Bonzini         .max_access_size = 1,
73649ab747fSPaolo Bonzini     },
73749ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
73849ab747fSPaolo Bonzini };
73949ab747fSPaolo Bonzini 
74049ab747fSPaolo Bonzini static const MemoryRegionOps i8042_cmd_ops = {
74149ab747fSPaolo Bonzini     .read = kbd_read_status,
74249ab747fSPaolo Bonzini     .write = kbd_write_command,
74349ab747fSPaolo Bonzini     .impl = {
74449ab747fSPaolo Bonzini         .min_access_size = 1,
74549ab747fSPaolo Bonzini         .max_access_size = 1,
74649ab747fSPaolo Bonzini     },
74749ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
74849ab747fSPaolo Bonzini };
74949ab747fSPaolo Bonzini 
750db895a1eSAndreas Färber static void i8042_initfn(Object *obj)
75149ab747fSPaolo Bonzini {
752db895a1eSAndreas Färber     ISAKBDState *isa_s = I8042(obj);
753db895a1eSAndreas Färber     KBDState *s = &isa_s->kbd;
754db895a1eSAndreas Färber 
7551437c94bSPaolo Bonzini     memory_region_init_io(isa_s->io + 0, obj, &i8042_data_ops, s,
7561437c94bSPaolo Bonzini                           "i8042-data", 1);
7571437c94bSPaolo Bonzini     memory_region_init_io(isa_s->io + 1, obj, &i8042_cmd_ops, s,
7581437c94bSPaolo Bonzini                           "i8042-cmd", 1);
7593115b9e2SEfimov Vasily 
7603115b9e2SEfimov Vasily     qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, I8042_A20_LINE, 1);
761db895a1eSAndreas Färber }
762db895a1eSAndreas Färber 
763db895a1eSAndreas Färber static void i8042_realizefn(DeviceState *dev, Error **errp)
764db895a1eSAndreas Färber {
765db895a1eSAndreas Färber     ISADevice *isadev = ISA_DEVICE(dev);
766a2e0b863SAndreas Färber     ISAKBDState *isa_s = I8042(dev);
76749ab747fSPaolo Bonzini     KBDState *s = &isa_s->kbd;
76849ab747fSPaolo Bonzini 
769b86ce7a6SBernhard Beschow     if (isa_s->kbd_irq >= ISA_NUM_IRQS) {
770b86ce7a6SBernhard Beschow         error_setg(errp, "Maximum value for \"kbd-irq\" is: %u",
771b86ce7a6SBernhard Beschow                    ISA_NUM_IRQS - 1);
772b86ce7a6SBernhard Beschow         return;
773b86ce7a6SBernhard Beschow     }
774b86ce7a6SBernhard Beschow 
775b86ce7a6SBernhard Beschow     if (isa_s->mouse_irq >= ISA_NUM_IRQS) {
776b86ce7a6SBernhard Beschow         error_setg(errp, "Maximum value for \"mouse-irq\" is: %u",
777b86ce7a6SBernhard Beschow                    ISA_NUM_IRQS - 1);
778b86ce7a6SBernhard Beschow         return;
779b86ce7a6SBernhard Beschow     }
780b86ce7a6SBernhard Beschow 
781215caca6SBernhard Beschow     s->irq_kbd = isa_get_irq(isadev, isa_s->kbd_irq);
782215caca6SBernhard Beschow     s->irq_mouse = isa_get_irq(isadev, isa_s->mouse_irq);
78349ab747fSPaolo Bonzini 
784db895a1eSAndreas Färber     isa_register_ioport(isadev, isa_s->io + 0, 0x60);
785db895a1eSAndreas Färber     isa_register_ioport(isadev, isa_s->io + 1, 0x64);
78649ab747fSPaolo Bonzini 
78749ab747fSPaolo Bonzini     s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
78849ab747fSPaolo Bonzini     s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
789d1e45668SVolker Rümelin     if (isa_s->kbd_throttle && !isa_s->kbd.extended_state) {
790d1e45668SVolker Rümelin         warn_report(TYPE_I8042 ": can't enable kbd-throttle without"
791d1e45668SVolker Rümelin                     " extended-state, disabling kbd-throttle");
792d1e45668SVolker Rümelin     } else if (isa_s->kbd_throttle) {
793d1e45668SVolker Rümelin         s->throttle_timer = timer_new_us(QEMU_CLOCK_VIRTUAL,
794d1e45668SVolker Rümelin                                          kbd_throttle_timeout, s);
795d1e45668SVolker Rümelin     }
79649ab747fSPaolo Bonzini     qemu_register_reset(kbd_reset, s);
79749ab747fSPaolo Bonzini }
79849ab747fSPaolo Bonzini 
79968f01317SIgor Mammedov static void i8042_build_aml(AcpiDevAmlIf *adev, Aml *scope)
800df0f3d13SGerd Hoffmann {
80168f01317SIgor Mammedov     ISAKBDState *isa_s = I8042(adev);
802df0f3d13SGerd Hoffmann     Aml *kbd;
803df0f3d13SGerd Hoffmann     Aml *mou;
804df0f3d13SGerd Hoffmann     Aml *crs;
805df0f3d13SGerd Hoffmann 
806df0f3d13SGerd Hoffmann     crs = aml_resource_template();
807df0f3d13SGerd Hoffmann     aml_append(crs, aml_io(AML_DECODE16, 0x0060, 0x0060, 0x01, 0x01));
808df0f3d13SGerd Hoffmann     aml_append(crs, aml_io(AML_DECODE16, 0x0064, 0x0064, 0x01, 0x01));
809b86ce7a6SBernhard Beschow     aml_append(crs, aml_irq_no_flags(isa_s->kbd_irq));
810df0f3d13SGerd Hoffmann 
811df0f3d13SGerd Hoffmann     kbd = aml_device("KBD");
812df0f3d13SGerd Hoffmann     aml_append(kbd, aml_name_decl("_HID", aml_eisaid("PNP0303")));
813df0f3d13SGerd Hoffmann     aml_append(kbd, aml_name_decl("_STA", aml_int(0xf)));
814df0f3d13SGerd Hoffmann     aml_append(kbd, aml_name_decl("_CRS", crs));
815df0f3d13SGerd Hoffmann 
816df0f3d13SGerd Hoffmann     crs = aml_resource_template();
817b86ce7a6SBernhard Beschow     aml_append(crs, aml_irq_no_flags(isa_s->mouse_irq));
818df0f3d13SGerd Hoffmann 
819df0f3d13SGerd Hoffmann     mou = aml_device("MOU");
820df0f3d13SGerd Hoffmann     aml_append(mou, aml_name_decl("_HID", aml_eisaid("PNP0F13")));
821df0f3d13SGerd Hoffmann     aml_append(mou, aml_name_decl("_STA", aml_int(0xf)));
822df0f3d13SGerd Hoffmann     aml_append(mou, aml_name_decl("_CRS", crs));
823df0f3d13SGerd Hoffmann 
824df0f3d13SGerd Hoffmann     aml_append(scope, kbd);
825df0f3d13SGerd Hoffmann     aml_append(scope, mou);
826df0f3d13SGerd Hoffmann }
827df0f3d13SGerd Hoffmann 
828ff6e1624SVolker Rümelin static Property i8042_properties[] = {
829ff6e1624SVolker Rümelin     DEFINE_PROP_BOOL("extended-state", ISAKBDState, kbd.extended_state, true),
830d1e45668SVolker Rümelin     DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false),
831b86ce7a6SBernhard Beschow     DEFINE_PROP_UINT8("kbd-irq", ISAKBDState, kbd_irq, 1),
832b86ce7a6SBernhard Beschow     DEFINE_PROP_UINT8("mouse-irq", ISAKBDState, mouse_irq, 12),
833ff6e1624SVolker Rümelin     DEFINE_PROP_END_OF_LIST(),
834ff6e1624SVolker Rümelin };
835ff6e1624SVolker Rümelin 
83649ab747fSPaolo Bonzini static void i8042_class_initfn(ObjectClass *klass, void *data)
83749ab747fSPaolo Bonzini {
83849ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
83968f01317SIgor Mammedov     AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
840db895a1eSAndreas Färber 
841ff6e1624SVolker Rümelin     device_class_set_props(dc, i8042_properties);
842db895a1eSAndreas Färber     dc->realize = i8042_realizefn;
84349ab747fSPaolo Bonzini     dc->vmsd = &vmstate_kbd_isa;
84468f01317SIgor Mammedov     adevc->build_dev_aml = i8042_build_aml;
845cbe9ed73Skumar sourav     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
84649ab747fSPaolo Bonzini }
84749ab747fSPaolo Bonzini 
84849ab747fSPaolo Bonzini static const TypeInfo i8042_info = {
849a2e0b863SAndreas Färber     .name          = TYPE_I8042,
85049ab747fSPaolo Bonzini     .parent        = TYPE_ISA_DEVICE,
85149ab747fSPaolo Bonzini     .instance_size = sizeof(ISAKBDState),
852db895a1eSAndreas Färber     .instance_init = i8042_initfn,
85349ab747fSPaolo Bonzini     .class_init    = i8042_class_initfn,
85468f01317SIgor Mammedov     .interfaces = (InterfaceInfo[]) {
85568f01317SIgor Mammedov         { TYPE_ACPI_DEV_AML_IF },
85668f01317SIgor Mammedov         { },
85768f01317SIgor Mammedov     },
85849ab747fSPaolo Bonzini };
85949ab747fSPaolo Bonzini 
86049ab747fSPaolo Bonzini static void i8042_register_types(void)
86149ab747fSPaolo Bonzini {
86249ab747fSPaolo Bonzini     type_register_static(&i8042_info);
863*150ee013SMark Cave-Ayland     type_register_static(&i8042_mmio_info);
86449ab747fSPaolo Bonzini }
86549ab747fSPaolo Bonzini 
86649ab747fSPaolo Bonzini type_init(i8042_register_types)
867