OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [input/] [keyboard/] [sunkbd.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * $Id: sunkbd.c,v 1.14 2001/09/25 10:12:07 vojtech Exp $
3
 *
4
 *  Copyright (c) 1999-2001 Vojtech Pavlik
5
 */
6
 
7
/*
8
 * Sun keyboard driver for Linux
9
 */
10
 
11
/*
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
 *
26
 * Should you need to contact me, the author, you can do so either by
27
 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
28
 * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
29
 */
30
 
31
#include <linux/delay.h>
32
#include <linux/slab.h>
33
#include <linux/module.h>
34
#include <linux/interrupt.h>
35
#include <linux/init.h>
36
#include <linux/input.h>
37
#include <linux/serio.h>
38
#include <linux/workqueue.h>
39
 
40
#define DRIVER_DESC     "Sun keyboard driver"
41
 
42
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
43
MODULE_DESCRIPTION(DRIVER_DESC);
44
MODULE_LICENSE("GPL");
45
 
46
static unsigned char sunkbd_keycode[128] = {
47
          0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
48
         65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106,  1,  2,  3,
49
          4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
50
        116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
51
         26, 27,111,127, 71, 72, 73, 74,134,135,107,  0, 29, 30, 31, 32,
52
         33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
53
        104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
54
         79, 80, 81,  0,  0,  0,138, 58,125, 57,126,109, 86, 78
55
};
56
 
57
#define SUNKBD_CMD_RESET        0x1
58
#define SUNKBD_CMD_BELLON       0x2
59
#define SUNKBD_CMD_BELLOFF      0x3
60
#define SUNKBD_CMD_CLICK        0xa
61
#define SUNKBD_CMD_NOCLICK      0xb
62
#define SUNKBD_CMD_SETLED       0xe
63
#define SUNKBD_CMD_LAYOUT       0xf
64
 
65
#define SUNKBD_RET_RESET        0xff
66
#define SUNKBD_RET_ALLUP        0x7f
67
#define SUNKBD_RET_LAYOUT       0xfe
68
 
69
#define SUNKBD_LAYOUT_5_MASK    0x20
70
#define SUNKBD_RELEASE          0x80
71
#define SUNKBD_KEY              0x7f
72
 
73
/*
74
 * Per-keyboard data.
75
 */
76
 
77
struct sunkbd {
78
        unsigned char keycode[128];
79
        struct input_dev *dev;
80
        struct serio *serio;
81
        struct work_struct tq;
82
        wait_queue_head_t wait;
83
        char name[64];
84
        char phys[32];
85
        char type;
86
        unsigned char enabled;
87
        volatile s8 reset;
88
        volatile s8 layout;
89
};
90
 
91
/*
92
 * sunkbd_interrupt() is called by the low level driver when a character
93
 * is received.
94
 */
95
 
96
static irqreturn_t sunkbd_interrupt(struct serio *serio,
97
                unsigned char data, unsigned int flags)
98
{
99
        struct sunkbd* sunkbd = serio_get_drvdata(serio);
100
 
101
        if (sunkbd->reset <= -1) {              /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
102
                sunkbd->reset = data;           /* The keyboard sends 0xff 0xff 0xID on powerup */
103
                wake_up_interruptible(&sunkbd->wait);
104
                goto out;
105
        }
106
 
107
        if (sunkbd->layout == -1) {
108
                sunkbd->layout = data;
109
                wake_up_interruptible(&sunkbd->wait);
110
                goto out;
111
        }
112
 
113
        switch (data) {
114
 
115
                case SUNKBD_RET_RESET:
116
                        schedule_work(&sunkbd->tq);
117
                        sunkbd->reset = -1;
118
                        break;
119
 
120
                case SUNKBD_RET_LAYOUT:
121
                        sunkbd->layout = -1;
122
                        break;
123
 
124
                case SUNKBD_RET_ALLUP: /* All keys released */
125
                        break;
126
 
127
                default:
128
                        if (!sunkbd->enabled)
129
                                break;
130
 
131
                        if (sunkbd->keycode[data & SUNKBD_KEY]) {
132
                                input_report_key(sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
133
                                input_sync(sunkbd->dev);
134
                        } else {
135
                                printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
136
                                        data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
137
                        }
138
        }
139
out:
140
        return IRQ_HANDLED;
141
}
142
 
143
/*
144
 * sunkbd_event() handles events from the input module.
145
 */
146
 
147
static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
148
{
149
        struct sunkbd *sunkbd = input_get_drvdata(dev);
150
 
151
        switch (type) {
152
 
153
                case EV_LED:
154
 
155
                        sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
156
                        sunkbd->serio->write(sunkbd->serio,
157
                                (!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
158
                                (!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
159
                        return 0;
160
 
161
                case EV_SND:
162
 
163
                        switch (code) {
164
 
165
                                case SND_CLICK:
166
                                        sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
167
                                        return 0;
168
 
169
                                case SND_BELL:
170
                                        sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
171
                                        return 0;
172
                        }
173
 
174
                        break;
175
        }
176
 
177
        return -1;
178
}
179
 
180
/*
181
 * sunkbd_initialize() checks for a Sun keyboard attached, and determines
182
 * its type.
183
 */
184
 
185
static int sunkbd_initialize(struct sunkbd *sunkbd)
186
{
187
        sunkbd->reset = -2;
188
        sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
189
        wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
190
        if (sunkbd->reset < 0)
191
                return -1;
192
 
193
        sunkbd->type = sunkbd->reset;
194
 
195
        if (sunkbd->type == 4) {        /* Type 4 keyboard */
196
                sunkbd->layout = -2;
197
                sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
198
                wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4);
199
                if (sunkbd->layout < 0) return -1;
200
                if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
201
        }
202
 
203
        return 0;
204
}
205
 
206
/*
207
 * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
208
 * were in.
209
 */
210
 
211
static void sunkbd_reinit(struct work_struct *work)
212
{
213
        struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
214
 
215
        wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
216
 
217
        sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
218
        sunkbd->serio->write(sunkbd->serio,
219
                (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
220
                (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) | !!test_bit(LED_NUML, sunkbd->dev->led));
221
        sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
222
        sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
223
}
224
 
225
static void sunkbd_enable(struct sunkbd *sunkbd, int enable)
226
{
227
        serio_pause_rx(sunkbd->serio);
228
        sunkbd->enabled = enable;
229
        serio_continue_rx(sunkbd->serio);
230
}
231
 
232
/*
233
 * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
234
 */
235
 
236
static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
237
{
238
        struct sunkbd *sunkbd;
239
        struct input_dev *input_dev;
240
        int err = -ENOMEM;
241
        int i;
242
 
243
        sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
244
        input_dev = input_allocate_device();
245
        if (!sunkbd || !input_dev)
246
                goto fail1;
247
 
248
        sunkbd->serio = serio;
249
        sunkbd->dev = input_dev;
250
        init_waitqueue_head(&sunkbd->wait);
251
        INIT_WORK(&sunkbd->tq, sunkbd_reinit);
252
        snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
253
 
254
        serio_set_drvdata(serio, sunkbd);
255
 
256
        err = serio_open(serio, drv);
257
        if (err)
258
                goto fail2;
259
 
260
        if (sunkbd_initialize(sunkbd) < 0) {
261
                err = -ENODEV;
262
                goto fail3;
263
        }
264
 
265
        snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
266
        memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
267
 
268
        input_dev->name = sunkbd->name;
269
        input_dev->phys = sunkbd->phys;
270
        input_dev->id.bustype = BUS_RS232;
271
        input_dev->id.vendor  = SERIO_SUNKBD;
272
        input_dev->id.product = sunkbd->type;
273
        input_dev->id.version = 0x0100;
274
        input_dev->dev.parent = &serio->dev;
275
 
276
        input_set_drvdata(input_dev, sunkbd);
277
 
278
        input_dev->event = sunkbd_event;
279
 
280
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
281
                BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
282
        input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
283
                BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
284
        input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
285
 
286
        input_dev->keycode = sunkbd->keycode;
287
        input_dev->keycodesize = sizeof(unsigned char);
288
        input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
289
        for (i = 0; i < 128; i++)
290
                set_bit(sunkbd->keycode[i], input_dev->keybit);
291
        clear_bit(0, input_dev->keybit);
292
 
293
        sunkbd_enable(sunkbd, 1);
294
 
295
        err = input_register_device(sunkbd->dev);
296
        if (err)
297
                goto fail4;
298
 
299
        return 0;
300
 
301
 fail4: sunkbd_enable(sunkbd, 0);
302
 fail3: serio_close(serio);
303
 fail2: serio_set_drvdata(serio, NULL);
304
 fail1: input_free_device(input_dev);
305
        kfree(sunkbd);
306
        return err;
307
}
308
 
309
/*
310
 * sunkbd_disconnect() unregisters and closes behind us.
311
 */
312
 
313
static void sunkbd_disconnect(struct serio *serio)
314
{
315
        struct sunkbd *sunkbd = serio_get_drvdata(serio);
316
 
317
        sunkbd_enable(sunkbd, 0);
318
        input_unregister_device(sunkbd->dev);
319
        serio_close(serio);
320
        serio_set_drvdata(serio, NULL);
321
        kfree(sunkbd);
322
}
323
 
324
static struct serio_device_id sunkbd_serio_ids[] = {
325
        {
326
                .type   = SERIO_RS232,
327
                .proto  = SERIO_SUNKBD,
328
                .id     = SERIO_ANY,
329
                .extra  = SERIO_ANY,
330
        },
331
        {
332
                .type   = SERIO_RS232,
333
                .proto  = SERIO_UNKNOWN, /* sunkbd does probe */
334
                .id     = SERIO_ANY,
335
                .extra  = SERIO_ANY,
336
        },
337
        { 0 }
338
};
339
 
340
MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
341
 
342
static struct serio_driver sunkbd_drv = {
343
        .driver         = {
344
                .name   = "sunkbd",
345
        },
346
        .description    = DRIVER_DESC,
347
        .id_table       = sunkbd_serio_ids,
348
        .interrupt      = sunkbd_interrupt,
349
        .connect        = sunkbd_connect,
350
        .disconnect     = sunkbd_disconnect,
351
};
352
 
353
/*
354
 * The functions for insering/removing us as a module.
355
 */
356
 
357
static int __init sunkbd_init(void)
358
{
359
        return serio_register_driver(&sunkbd_drv);
360
}
361
 
362
static void __exit sunkbd_exit(void)
363
{
364
        serio_unregister_driver(&sunkbd_drv);
365
}
366
 
367
module_init(sunkbd_init);
368
module_exit(sunkbd_exit);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.