Statistics
| Branch: | Revision:

root / 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
}