aboutsummaryrefslogtreecommitdiff
path: root/mubios/drivers/kbd.c
blob: 4de0a9631a75ac99973ac6d9b854f2719bfacbc4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 RCC-Project

 Copyright (C) 2026 Richard Qin

 This file is part of RCC-Project.

 RCC-Project is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
 Software Foundation; either version 3 of the License, or (at your option)
 any later version.

 RCC-Project is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.
 */

#include "bios.h"
#include "io.h"

#define KBD_STATUS_PORT 0x0064u
#define KBD_DATA_PORT 0x0060u

#define KBD_STATUS_OBF 0x01u
#define KBD_STATUS_IBF 0x02u
#define KBD_STATUS_AUX 0x20u

#define KBD_CMD_READ_CONFIG 0x20u
#define KBD_CMD_WRITE_CONFIG 0x60u
#define KBD_CMD_DISABLE_KBD 0xADu
#define KBD_CMD_ENABLE_KBD 0xAEu

/*
 * 8042 command byte bits.
 * Bit 0 enables the keyboard IRQ, bit 1 the auxiliary port IRQ, bit 4 and bit 5
 * hold the keyboard and auxiliary clock, and bit 6 selects set 1 scan codes on
 * the keyboard interface.
 */
#define KBD_CONFIG_IRQ1 0x01u
#define KBD_CONFIG_IRQ12 0x02u
#define KBD_CONFIG_DISABLE_KBD_CLOCK 0x10u
#define KBD_CONFIG_DISABLE_AUX_CLOCK 0x20u
#define KBD_CONFIG_TRANSLATE 0x40u

#define KBD_DEVICE_ENABLE_SCAN 0xF4u

#define KBD_WAIT_LIMIT 0x10000u

static int
wait_input_clear (void)
{
  u32 count;

  for (count = 0; count < KBD_WAIT_LIMIT; count++)
    {
      if ((io_in8 (KBD_STATUS_PORT) & KBD_STATUS_IBF) == 0u)
        return 0;
    }

  return -1;
}

static int
wait_output_full (void)
{
  u32 count;

  for (count = 0; count < KBD_WAIT_LIMIT; count++)
    {
      if ((io_in8 (KBD_STATUS_PORT) & KBD_STATUS_OBF) != 0u)
        return 0;
    }

  return -1;
}

static int
kbd_write_command (u8 command)
{
  if (wait_input_clear () != 0)
    return -1;

  io_out8 (KBD_STATUS_PORT, command);
  return 0;
}

static int
kbd_write_data (u8 value)
{
  if (wait_input_clear () != 0)
    return -1;

  io_out8 (KBD_DATA_PORT, value);
  return 0;
}

void
kbd_init (void)
{
  u8 config;

  /* Disable both ports before the controller is reconfigured. */
  kbd_write_command (KBD_CMD_DISABLE_KBD);

  /* Drain the self-test and any pending scan code left in the output buffer. */
  while ((io_in8 (KBD_STATUS_PORT) & KBD_STATUS_OBF) != 0u)
    (void)io_in8 (KBD_DATA_PORT);

  if (kbd_write_command (KBD_CMD_READ_CONFIG) != 0)
    return;

  if (wait_output_full () != 0)
    return;

  config = io_in8 (KBD_DATA_PORT);

  config &= (u8)~(KBD_CONFIG_DISABLE_KBD_CLOCK | KBD_CONFIG_DISABLE_AUX_CLOCK);
  config |= KBD_CONFIG_IRQ1 | KBD_CONFIG_IRQ12 | KBD_CONFIG_TRANSLATE;

  if (kbd_write_command (KBD_CMD_WRITE_CONFIG) != 0)
    return;

  kbd_write_data (config);

  kbd_write_command (KBD_CMD_ENABLE_KBD);

  /* Ask the keyboard to start scanning and consume its acknowledge byte. */
  kbd_write_data (KBD_DEVICE_ENABLE_SCAN);

  if (wait_output_full () == 0)
    (void)io_in8 (KBD_DATA_PORT);
}