scoutos / prex-0.9.0 / bsp / drv / dev / input / pckbd.c @ 03e9c04a
History | View | Annotate | Download (6.89 KB)
1 |
/*-
|
---|---|
2 |
* Copyright (c) 2005-2009, Kohsuke Ohtani
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions
|
7 |
* are met:
|
8 |
* 1. Redistributions of source code must retain the above copyright
|
9 |
* notice, this list of conditions and the following disclaimer.
|
10 |
* 2. Redistributions in binary form must reproduce the above copyright
|
11 |
* notice, this list of conditions and the following disclaimer in the
|
12 |
* documentation and/or other materials provided with the distribution.
|
13 |
* 3. Neither the name of the author nor the names of any co-contributors
|
14 |
* may be used to endorse or promote products derived from this software
|
15 |
* without specific prior written permission.
|
16 |
*
|
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
27 |
* SUCH DAMAGE.
|
28 |
*/
|
29 |
|
30 |
/*
|
31 |
* pckbd.c - PC/AT keyboard driver
|
32 |
*/
|
33 |
|
34 |
#include <driver.h> |
35 |
#include <sys/keycode.h> |
36 |
#include <wscons.h> |
37 |
#include <pm.h> |
38 |
|
39 |
#include "i8042.h" |
40 |
|
41 |
/* Parameters */
|
42 |
#define KBD_IRQ 1 |
43 |
|
44 |
struct pckbd_softc {
|
45 |
device_t dev; /* device object */
|
46 |
irq_t irq; /* irq handle */
|
47 |
int polling; /* true if polling mode */ |
48 |
u_char led_sts; /* keyboard LED status */
|
49 |
int shift; /* shift key state */ |
50 |
int alt; /* alt key state */ |
51 |
int ctrl; /* control key state */ |
52 |
int capslk; /* caps lock key staet */ |
53 |
}; |
54 |
|
55 |
static int pckbd_init(struct driver *); |
56 |
static int pckbd_getc(void *); |
57 |
static void pckbd_set_poll(void *, int); |
58 |
|
59 |
struct driver pckbd_driver = {
|
60 |
/* name */ "pckbd", |
61 |
/* devops */ NULL, |
62 |
/* devsz */ sizeof(struct pckbd_softc), |
63 |
/* flags */ 0, |
64 |
/* probe */ NULL, |
65 |
/* init */ pckbd_init,
|
66 |
/* shutdown */ NULL, |
67 |
}; |
68 |
|
69 |
static struct wscons_kbd_ops wscons_pckbd_ops = { |
70 |
/* getc */ pckbd_getc,
|
71 |
/* set_poll */ pckbd_set_poll,
|
72 |
}; |
73 |
|
74 |
/*
|
75 |
* Key map
|
76 |
*/
|
77 |
static const u_char key_map[] = { |
78 |
0, 0x1b, '1', '2', '3', '4', '5', '6', |
79 |
'7', '8', '9', '0', '-', '=', '\b', '\t', |
80 |
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', |
81 |
'o', 'p', '[', ']', '\n', K_CTRL, 'a', 's', |
82 |
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', |
83 |
'\'', '`', K_SHFT, '\\', 'z', 'x', 'c', 'v', |
84 |
'b', 'n', 'm', ',', '.', '/', K_SHFT, '*', |
85 |
K_ALT, ' ', K_CAPS, K_F1, K_F2, K_F3, K_F4, K_F5,
|
86 |
K_F6, K_F7, K_F8, K_F9, K_F10, 0, 0, K_HOME, |
87 |
K_UP, K_PGUP, 0, K_LEFT, 0, K_RGHT, 0, K_END, |
88 |
K_DOWN, K_PGDN, K_INS, 0x7f, K_F11, K_F12
|
89 |
}; |
90 |
|
91 |
#define KEY_MAX (sizeof(key_map) / sizeof(char)) |
92 |
|
93 |
static const u_char shift_map[] = { |
94 |
0, 0x1b, '!', '@', '#', '$', '%', '^', |
95 |
'&', '*', '(', ')', '_', '+', '\b', '\t', |
96 |
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', |
97 |
'O', 'P', '{', '}', '\n', K_CTRL, 'A', 'S', |
98 |
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', |
99 |
'"', '~', 0, '|', 'Z', 'X', 'C', 'V', |
100 |
'B', 'N', 'M', '<', '>', '?', 0, '*', |
101 |
K_ALT, ' ', 0, 0, 0, 0, 0, 0, |
102 |
0, 0, 0, 0, 0, 0, 0, K_HOME, |
103 |
K_UP, K_PGUP, 0, K_LEFT, 0, K_RGHT, 0, K_END, |
104 |
K_DOWN, K_PGDN, K_INS, 0x7f, 0, 0 |
105 |
}; |
106 |
|
107 |
/*
|
108 |
* Send command to keyboard controller
|
109 |
*/
|
110 |
static void |
111 |
kmc_send_cmd(u_char cmd) |
112 |
{ |
113 |
|
114 |
kmc_wait_ibe(); |
115 |
bus_write_8(KMC_CMD, cmd); |
116 |
} |
117 |
|
118 |
/*
|
119 |
* Update LEDs for current modifier state.
|
120 |
*/
|
121 |
static void |
122 |
pckbd_set_leds(struct pckbd_softc *sc)
|
123 |
{ |
124 |
u_char val = 0;
|
125 |
|
126 |
/* Update LEDs */
|
127 |
if (sc->capslk)
|
128 |
val |= 0x04;
|
129 |
if (sc->led_sts != val) {
|
130 |
sc->led_sts = val; |
131 |
bus_write_8(KMC_DATA, 0xed);
|
132 |
while (bus_read_8(KMC_STS) & 2); |
133 |
bus_write_8(KMC_DATA, val); |
134 |
while (bus_read_8(KMC_STS) & 2); |
135 |
} |
136 |
} |
137 |
|
138 |
/*
|
139 |
* Scan key input. Returns ascii code.
|
140 |
*/
|
141 |
static int |
142 |
pckbd_scan_key(struct pckbd_softc *sc)
|
143 |
{ |
144 |
u_char scan, ascii, val; |
145 |
int press;
|
146 |
|
147 |
again:
|
148 |
/* Get scan code */
|
149 |
kmc_wait_obf(); |
150 |
scan = bus_read_8(KMC_DATA); |
151 |
|
152 |
/* Send ack to the controller */
|
153 |
val = bus_read_8(KMC_PORTB); |
154 |
bus_write_8(KMC_PORTB, val | 0x80);
|
155 |
bus_write_8(KMC_PORTB, val); |
156 |
|
157 |
/* Convert scan code to ascii */
|
158 |
press = scan & 0x80 ? 0 : 1; |
159 |
scan = scan & 0x7f;
|
160 |
if (scan >= KEY_MAX)
|
161 |
goto again;
|
162 |
ascii = key_map[scan]; |
163 |
|
164 |
/* Check meta key */
|
165 |
switch (ascii) {
|
166 |
case K_SHFT:
|
167 |
sc->shift = press; |
168 |
return 0; |
169 |
case K_CTRL:
|
170 |
sc->ctrl = press; |
171 |
return 0; |
172 |
case K_ALT:
|
173 |
sc->alt = press; |
174 |
return 0; |
175 |
case K_CAPS:
|
176 |
sc->capslk = !sc->capslk; |
177 |
pckbd_set_leds(sc); |
178 |
return 0; |
179 |
} |
180 |
|
181 |
/* Ignore key release */
|
182 |
if (!press)
|
183 |
return 0; |
184 |
|
185 |
if (ascii >= 0x80) |
186 |
return ascii;
|
187 |
|
188 |
/* Check Alt+Ctrl+Del */
|
189 |
if (sc->alt && sc->ctrl && ascii == 0x7f) { |
190 |
#ifdef CONFIG_PM
|
191 |
pm_set_power(PWR_REBOOT); |
192 |
#else
|
193 |
machine_powerdown(PWR_REBOOT); |
194 |
#endif
|
195 |
} |
196 |
|
197 |
/* Check ctrl & shift state */
|
198 |
if (sc->ctrl) {
|
199 |
if (ascii >= 'a' && ascii <= 'z') |
200 |
ascii = ascii - 'a' + 0x01; |
201 |
else if (ascii == '\\') |
202 |
ascii = 0x1c;
|
203 |
else
|
204 |
ascii = 0;
|
205 |
} else if (sc->shift) |
206 |
ascii = shift_map[scan]; |
207 |
|
208 |
if (ascii == 0) |
209 |
return 0; |
210 |
|
211 |
/* Check caps lock state */
|
212 |
if (sc->capslk) {
|
213 |
if (ascii >= 'A' && ascii <= 'Z') |
214 |
ascii += 'a' - 'A'; |
215 |
else if (ascii >= 'a' && ascii <= 'z') |
216 |
ascii -= 'a' - 'A'; |
217 |
} |
218 |
|
219 |
/* Check alt key */
|
220 |
if (sc->alt)
|
221 |
ascii |= 0x80;
|
222 |
|
223 |
/* Insert key to queue */
|
224 |
return ascii;
|
225 |
} |
226 |
|
227 |
/*
|
228 |
* Interrupt service routine
|
229 |
*/
|
230 |
static int |
231 |
pckbd_isr(void *arg)
|
232 |
{ |
233 |
struct pckbd_softc *sc = arg;
|
234 |
int c;
|
235 |
|
236 |
c = pckbd_scan_key(sc); |
237 |
if (c != 0) |
238 |
wscons_kbd_input(c); |
239 |
return 0; |
240 |
} |
241 |
|
242 |
static int |
243 |
pckbd_getc(void *aux)
|
244 |
{ |
245 |
struct pckbd_softc *sc = aux;
|
246 |
int c;
|
247 |
int s;
|
248 |
|
249 |
sc->alt = 0;
|
250 |
sc->ctrl = 0;
|
251 |
sc->shift = 0;
|
252 |
|
253 |
s = splhigh(); |
254 |
while ((c = pckbd_scan_key(sc)) == 0) ; |
255 |
splx(s); |
256 |
return c;
|
257 |
} |
258 |
|
259 |
static void |
260 |
pckbd_set_poll(void *aux, int on) |
261 |
{ |
262 |
struct pckbd_softc *sc = aux;
|
263 |
|
264 |
sc->polling = on; |
265 |
} |
266 |
|
267 |
static int |
268 |
pckbd_init(struct driver *self)
|
269 |
{ |
270 |
struct pckbd_softc *sc;
|
271 |
device_t dev; |
272 |
|
273 |
dev = device_create(self, "kbd", D_CHR);
|
274 |
|
275 |
sc = device_private(dev); |
276 |
sc->dev = dev; |
277 |
sc->polling = 0;
|
278 |
sc->led_sts = 0;
|
279 |
|
280 |
/* Disable keyboard controller */
|
281 |
kmc_send_cmd(CMD_KBD_DIS); |
282 |
|
283 |
sc->irq = irq_attach(KBD_IRQ, IPL_INPUT, 0, pckbd_isr, IST_NONE, sc);
|
284 |
|
285 |
/* Discard garbage data */
|
286 |
while (bus_read_8(KMC_STS) & STS_OBF)
|
287 |
bus_read_8(KMC_DATA); |
288 |
|
289 |
/* Enable keyboard controller */
|
290 |
kmc_send_cmd(CMD_KBD_EN); |
291 |
|
292 |
wscons_attach_kbd(&wscons_pckbd_ops, sc); |
293 |
return 0; |
294 |
} |