blob: cac4781103c3ed1cb45a31d22bc0486aa5492e02 [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,
David Howells7d12e782006-10-05 14:55:46 +010097 unsigned char data, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098{
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_report_key(sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
133 input_sync(sunkbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 } else {
135 printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
136 data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
137 }
138 }
139out:
140 return IRQ_HANDLED;
141}
142
143/*
144 * sunkbd_event() handles events from the input module.
145 */
146
147static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
148{
149 struct sunkbd *sunkbd = dev->private;
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
185static 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);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500190 if (sunkbd->reset < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 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
211static void sunkbd_reinit(void *data)
212{
213 struct sunkbd *sunkbd = data;
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,
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500219 (!!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
225static void sunkbd_enable(struct sunkbd *sunkbd, int enable)
226{
227 serio_pause_rx(sunkbd->serio);
228 sunkbd->enabled = 1;
229 serio_continue_rx(sunkbd->serio);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230}
231
232/*
233 * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
234 */
235
236static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
237{
238 struct sunkbd *sunkbd;
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500239 struct input_dev *input_dev;
240 int err = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500243 sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
244 input_dev = input_allocate_device();
245 if (!sunkbd || !input_dev)
246 goto fail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
248 sunkbd->serio = serio;
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500249 sunkbd->dev = input_dev;
250 init_waitqueue_head(&sunkbd->wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 INIT_WORK(&sunkbd->tq, sunkbd_reinit, sunkbd);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500252 snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
254 serio_set_drvdata(serio, sunkbd);
255
256 err = serio_open(serio, drv);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500257 if (err)
258 goto fail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
260 if (sunkbd_initialize(sunkbd) < 0) {
261 serio_close(serio);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500262 goto fail;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 }
264
Dmitry Torokhovea08c6f2006-06-26 01:46:17 -0400265 snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500267
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->cdev.dev = &serio->dev;
275 input_dev->private = sunkbd;
276 input_dev->event = sunkbd_event;
277
278 input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
279 input_dev->ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML);
280 input_dev->sndbit[0] = BIT(SND_CLICK) | BIT(SND_BELL);
281
282 input_dev->keycode = sunkbd->keycode;
283 input_dev->keycodesize = sizeof(unsigned char);
284 input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 for (i = 0; i < 128; i++)
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500286 set_bit(sunkbd->keycode[i], input_dev->keybit);
287 clear_bit(0, input_dev->keybit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500289 sunkbd_enable(sunkbd, 1);
290 input_register_device(sunkbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 return 0;
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500292
293 fail: serio_set_drvdata(serio, NULL);
294 input_free_device(input_dev);
295 kfree(sunkbd);
296 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297}
298
299/*
300 * sunkbd_disconnect() unregisters and closes behind us.
301 */
302
303static void sunkbd_disconnect(struct serio *serio)
304{
305 struct sunkbd *sunkbd = serio_get_drvdata(serio);
Dmitry Torokhov3c42f0c2005-09-15 02:01:45 -0500306
307 sunkbd_enable(sunkbd, 0);
308 input_unregister_device(sunkbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 serio_close(serio);
310 serio_set_drvdata(serio, NULL);
311 kfree(sunkbd);
312}
313
314static struct serio_device_id sunkbd_serio_ids[] = {
315 {
316 .type = SERIO_RS232,
317 .proto = SERIO_SUNKBD,
318 .id = SERIO_ANY,
319 .extra = SERIO_ANY,
320 },
321 {
322 .type = SERIO_RS232,
323 .proto = SERIO_UNKNOWN, /* sunkbd does probe */
324 .id = SERIO_ANY,
325 .extra = SERIO_ANY,
326 },
327 { 0 }
328};
329
330MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
331
332static struct serio_driver sunkbd_drv = {
333 .driver = {
334 .name = "sunkbd",
335 },
336 .description = DRIVER_DESC,
337 .id_table = sunkbd_serio_ids,
338 .interrupt = sunkbd_interrupt,
339 .connect = sunkbd_connect,
340 .disconnect = sunkbd_disconnect,
341};
342
343/*
344 * The functions for insering/removing us as a module.
345 */
346
347static int __init sunkbd_init(void)
348{
349 serio_register_driver(&sunkbd_drv);
350 return 0;
351}
352
353static void __exit sunkbd_exit(void)
354{
355 serio_unregister_driver(&sunkbd_drv);
356}
357
358module_init(sunkbd_init);
359module_exit(sunkbd_exit);