scoutos / prex-0.9.0 / bsp / drv / dev / input / pckbd.c @ 03e9c04a
History | View | Annotate | Download (6.89 KB)
1 | 03e9c04a | Brad Neuman | /*-
|
---|---|---|---|
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 | } |