blob: 4bae5d89348d7d798169b73be559e3f0215a6220 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
42MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
43MODULE_DESCRIPTION(DRIVER_DESC);
44MODULE_LICENSE("GPL");
45
46static unsigned char sunkbd_keycode[128] = {
Vojtech Pavlik8d9a9ae2005-09-05 00:12:47 -050047 0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 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
77struct 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 volatile s8 reset;
87 volatile s8 layout;
88};
89
90/*
91 * sunkbd_interrupt() is called by the low level driver when a character
92 * is received.
93 */
94
95static irqreturn_t sunkbd_interrupt(struct serio *serio,
96 unsigned char data, unsigned int flags, struct pt_regs *regs)
97{
98 struct sunkbd* sunkbd = serio_get_drvdata(serio);
99
100 if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
101 sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */
102 wake_up_interruptible(&sunkbd->wait);
103 goto out;
104 }
105
106 if (sunkbd->layout == -1) {
107 sunkbd->layout = data;
108 wake_up_interruptible(&sunkbd->wait);
109 goto out;
110 }
111
112 switch (data) {
113
114 case SUNKBD_RET_RESET:
115 schedule_work(&sunkbd->tq);
116 sunkbd->reset = -1;
117 break;
118
119 case SUNKBD_RET_LAYOUT:
120 sunkbd->layout = -1;
121 break;
122
123 case SUNKBD_RET_ALLUP: /* All keys released */
124 break;
125
126 default:
127 if (sunkbd->keycode[data & SUNKBD_KEY]) {
128 input_regs(&sunkbd->dev, regs);
129 input_report_key(&sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
130 input_sync(&sunkbd->dev);
131 } else {
132 printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
133 data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
134 }
135 }
136out:
137 return IRQ_HANDLED;
138}
139
140/*
141 * sunkbd_event() handles events from the input module.
142 */
143
144static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
145{
146 struct sunkbd *sunkbd = dev->private;
147
148 switch (type) {
149
150 case EV_LED:
151
152 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
153 sunkbd->serio->write(sunkbd->serio,
154 (!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
155 (!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
156 return 0;
157
158 case EV_SND:
159
160 switch (code) {
161
162 case SND_CLICK:
163 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
164 return 0;
165
166 case SND_BELL:
167 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
168 return 0;
169 }
170
171 break;
172 }
173
174 return -1;
175}
176
177/*
178 * sunkbd_initialize() checks for a Sun keyboard attached, and determines
179 * its type.
180 */
181
182static int sunkbd_initialize(struct sunkbd *sunkbd)
183{
184 sunkbd->reset = -2;
185 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
186 wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
187 if (sunkbd->reset <0)
188 return -1;
189
190 sunkbd->type = sunkbd->reset;
191
192 if (sunkbd->type == 4) { /* Type 4 keyboard */
193 sunkbd->layout = -2;
194 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
195 wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4);
196 if (sunkbd->layout < 0) return -1;
197 if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
198 }
199
200 return 0;
201}
202
203/*
204 * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
205 * were in.
206 */
207
208static void sunkbd_reinit(void *data)
209{
210 struct sunkbd *sunkbd = data;
211
212 wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
213
214 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
215 sunkbd->serio->write(sunkbd->serio,
216 (!!test_bit(LED_CAPSL, sunkbd->dev.led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev.led) << 2) |
217 (!!test_bit(LED_COMPOSE, sunkbd->dev.led) << 1) | !!test_bit(LED_NUML, sunkbd->dev.led));
218 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev.snd));
219 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev.snd));
220}
221
222/*
223 * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
224 */
225
226static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
227{
228 struct sunkbd *sunkbd;
229 int i;
230 int err;
231
232 if (!(sunkbd = kmalloc(sizeof(struct sunkbd), GFP_KERNEL)))
233 return -ENOMEM;
234
235 memset(sunkbd, 0, sizeof(struct sunkbd));
236
237 init_input_dev(&sunkbd->dev);
238 init_waitqueue_head(&sunkbd->wait);
239
240 sunkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
241 sunkbd->dev.ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML);
242 sunkbd->dev.sndbit[0] = BIT(SND_CLICK) | BIT(SND_BELL);
243
244 sunkbd->serio = serio;
245
246 INIT_WORK(&sunkbd->tq, sunkbd_reinit, sunkbd);
247
248 sunkbd->dev.keycode = sunkbd->keycode;
249 sunkbd->dev.keycodesize = sizeof(unsigned char);
250 sunkbd->dev.keycodemax = ARRAY_SIZE(sunkbd_keycode);
251
252 sunkbd->dev.event = sunkbd_event;
253 sunkbd->dev.private = sunkbd;
254
255 serio_set_drvdata(serio, sunkbd);
256
257 err = serio_open(serio, drv);
258 if (err) {
259 serio_set_drvdata(serio, NULL);
260 kfree(sunkbd);
261 return err;
262 }
263
264 if (sunkbd_initialize(sunkbd) < 0) {
265 serio_close(serio);
266 serio_set_drvdata(serio, NULL);
267 kfree(sunkbd);
268 return -ENODEV;
269 }
270
271 sprintf(sunkbd->name, "Sun Type %d keyboard", sunkbd->type);
272
273 memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
274 for (i = 0; i < 128; i++)
275 set_bit(sunkbd->keycode[i], sunkbd->dev.keybit);
276 clear_bit(0, sunkbd->dev.keybit);
277
278 sprintf(sunkbd->phys, "%s/input0", serio->phys);
279
280 sunkbd->dev.name = sunkbd->name;
281 sunkbd->dev.phys = sunkbd->phys;
282 sunkbd->dev.id.bustype = BUS_RS232;
283 sunkbd->dev.id.vendor = SERIO_SUNKBD;
284 sunkbd->dev.id.product = sunkbd->type;
285 sunkbd->dev.id.version = 0x0100;
286 sunkbd->dev.dev = &serio->dev;
287
288 input_register_device(&sunkbd->dev);
289
290 printk(KERN_INFO "input: %s on %s\n", sunkbd->name, serio->phys);
291
292 return 0;
293}
294
295/*
296 * sunkbd_disconnect() unregisters and closes behind us.
297 */
298
299static void sunkbd_disconnect(struct serio *serio)
300{
301 struct sunkbd *sunkbd = serio_get_drvdata(serio);
302 input_unregister_device(&sunkbd->dev);
303 serio_close(serio);
304 serio_set_drvdata(serio, NULL);
305 kfree(sunkbd);
306}
307
308static struct serio_device_id sunkbd_serio_ids[] = {
309 {
310 .type = SERIO_RS232,
311 .proto = SERIO_SUNKBD,
312 .id = SERIO_ANY,
313 .extra = SERIO_ANY,
314 },
315 {
316 .type = SERIO_RS232,
317 .proto = SERIO_UNKNOWN, /* sunkbd does probe */
318 .id = SERIO_ANY,
319 .extra = SERIO_ANY,
320 },
321 { 0 }
322};
323
324MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
325
326static struct serio_driver sunkbd_drv = {
327 .driver = {
328 .name = "sunkbd",
329 },
330 .description = DRIVER_DESC,
331 .id_table = sunkbd_serio_ids,
332 .interrupt = sunkbd_interrupt,
333 .connect = sunkbd_connect,
334 .disconnect = sunkbd_disconnect,
335};
336
337/*
338 * The functions for insering/removing us as a module.
339 */
340
341static int __init sunkbd_init(void)
342{
343 serio_register_driver(&sunkbd_drv);
344 return 0;
345}
346
347static void __exit sunkbd_exit(void)
348{
349 serio_unregister_driver(&sunkbd_drv);
350}
351
352module_init(sunkbd_init);
353module_exit(sunkbd_exit);