blob: 9dbd7b85686d52af9eafaaca6ca35f54b6b935cb [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];
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -050079 struct input_dev *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 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;
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -050086 unsigned char enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 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
96static irqreturn_t sunkbd_interrupt(struct serio *serio,
97 unsigned char data, unsigned int flags, struct pt_regs *regs)
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:
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500128 if (!sunkbd->enabled)
129 break;
130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 if (sunkbd->keycode[data & SUNKBD_KEY]) {
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500132 input_regs(sunkbd->dev, regs);
133 input_report_key(sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
134 input_sync(sunkbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 } else {
136 printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
137 data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
138 }
139 }
140out:
141 return IRQ_HANDLED;
142}
143
144/*
145 * sunkbd_event() handles events from the input module.
146 */
147
148static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
149{
150 struct sunkbd *sunkbd = dev->private;
151
152 switch (type) {
153
154 case EV_LED:
155
156 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
157 sunkbd->serio->write(sunkbd->serio,
158 (!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
159 (!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
160 return 0;
161
162 case EV_SND:
163
164 switch (code) {
165
166 case SND_CLICK:
167 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
168 return 0;
169
170 case SND_BELL:
171 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
172 return 0;
173 }
174
175 break;
176 }
177
178 return -1;
179}
180
181/*
182 * sunkbd_initialize() checks for a Sun keyboard attached, and determines
183 * its type.
184 */
185
186static int sunkbd_initialize(struct sunkbd *sunkbd)
187{
188 sunkbd->reset = -2;
189 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
190 wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500191 if (sunkbd->reset < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 return -1;
193
194 sunkbd->type = sunkbd->reset;
195
196 if (sunkbd->type == 4) { /* Type 4 keyboard */
197 sunkbd->layout = -2;
198 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
199 wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4);
200 if (sunkbd->layout < 0) return -1;
201 if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
202 }
203
204 return 0;
205}
206
207/*
208 * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
209 * were in.
210 */
211
212static void sunkbd_reinit(void *data)
213{
214 struct sunkbd *sunkbd = data;
215
216 wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
217
218 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
219 sunkbd->serio->write(sunkbd->serio,
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500220 (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
221 (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) | !!test_bit(LED_NUML, sunkbd->dev->led));
222 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
223 sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
224}
225
226static void sunkbd_enable(struct sunkbd *sunkbd, int enable)
227{
228 serio_pause_rx(sunkbd->serio);
229 sunkbd->enabled = 1;
230 serio_continue_rx(sunkbd->serio);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231}
232
233/*
234 * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
235 */
236
237static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
238{
239 struct sunkbd *sunkbd;
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500240 struct input_dev *input_dev;
241 int err = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500244 sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
245 input_dev = input_allocate_device();
246 if (!sunkbd || !input_dev)
247 goto fail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249 sunkbd->serio = serio;
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500250 sunkbd->dev = input_dev;
251 init_waitqueue_head(&sunkbd->wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 INIT_WORK(&sunkbd->tq, sunkbd_reinit, sunkbd);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500253 snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
255 serio_set_drvdata(serio, sunkbd);
256
257 err = serio_open(serio, drv);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500258 if (err)
259 goto fail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
261 if (sunkbd_initialize(sunkbd) < 0) {
262 serio_close(serio);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500263 goto fail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 }
265
Dmitry Torokhovea08c6f2006-06-26 01:46:17 -0400266 snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500268
269 input_dev->name = sunkbd->name;
270 input_dev->phys = sunkbd->phys;
271 input_dev->id.bustype = BUS_RS232;
272 input_dev->id.vendor = SERIO_SUNKBD;
273 input_dev->id.product = sunkbd->type;
274 input_dev->id.version = 0x0100;
275 input_dev->cdev.dev = &serio->dev;
276 input_dev->private = sunkbd;
277 input_dev->event = sunkbd_event;
278
279 input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
280 input_dev->ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML);
281 input_dev->sndbit[0] = BIT(SND_CLICK) | BIT(SND_BELL);
282
283 input_dev->keycode = sunkbd->keycode;
284 input_dev->keycodesize = sizeof(unsigned char);
285 input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 for (i = 0; i < 128; i++)
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500287 set_bit(sunkbd->keycode[i], input_dev->keybit);
288 clear_bit(0, input_dev->keybit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500290 sunkbd_enable(sunkbd, 1);
291 input_register_device(sunkbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 return 0;
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500293
294 fail: serio_set_drvdata(serio, NULL);
295 input_free_device(input_dev);
296 kfree(sunkbd);
297 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298}
299
300/*
301 * sunkbd_disconnect() unregisters and closes behind us.
302 */
303
304static void sunkbd_disconnect(struct serio *serio)
305{
306 struct sunkbd *sunkbd = serio_get_drvdata(serio);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500307
308 sunkbd_enable(sunkbd, 0);
309 input_unregister_device(sunkbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 serio_close(serio);
311 serio_set_drvdata(serio, NULL);
312 kfree(sunkbd);
313}
314
315static struct serio_device_id sunkbd_serio_ids[] = {
316 {
317 .type = SERIO_RS232,
318 .proto = SERIO_SUNKBD,
319 .id = SERIO_ANY,
320 .extra = SERIO_ANY,
321 },
322 {
323 .type = SERIO_RS232,
324 .proto = SERIO_UNKNOWN, /* sunkbd does probe */
325 .id = SERIO_ANY,
326 .extra = SERIO_ANY,
327 },
328 { 0 }
329};
330
331MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
332
333static struct serio_driver sunkbd_drv = {
334 .driver = {
335 .name = "sunkbd",
336 },
337 .description = DRIVER_DESC,
338 .id_table = sunkbd_serio_ids,
339 .interrupt = sunkbd_interrupt,
340 .connect = sunkbd_connect,
341 .disconnect = sunkbd_disconnect,
342};
343
344/*
345 * The functions for insering/removing us as a module.
346 */
347
348static int __init sunkbd_init(void)
349{
350 serio_register_driver(&sunkbd_drv);
351 return 0;
352}
353
354static void __exit sunkbd_exit(void)
355{
356 serio_unregister_driver(&sunkbd_drv);
357}
358
359module_init(sunkbd_init);
360module_exit(sunkbd_exit);