WorkStruct: make allyesconfig
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / input / keyboard / sunkbd.c
CommitLineData
1da177e4
LT
1/*
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] = {
8d9a9ae3 47 0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
1da177e4
LT
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
77struct sunkbd {
78 unsigned char keycode[128];
3c42f0c3 79 struct input_dev *dev;
1da177e4
LT
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;
3c42f0c3 86 unsigned char enabled;
1da177e4
LT
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
96static irqreturn_t sunkbd_interrupt(struct serio *serio,
7d12e780 97 unsigned char data, unsigned int flags)
1da177e4
LT
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:
3c42f0c3
DT
128 if (!sunkbd->enabled)
129 break;
130
1da177e4 131 if (sunkbd->keycode[data & SUNKBD_KEY]) {
3c42f0c3
DT
132 input_report_key(sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
133 input_sync(sunkbd->dev);
1da177e4
LT
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 }
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);
3c42f0c3 190 if (sunkbd->reset < 0)
1da177e4
LT
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
c4028958 211static void sunkbd_reinit(struct work_struct *work)
1da177e4 212{
c4028958 213 struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
1da177e4
LT
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,
3c42f0c3
DT
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
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);
1da177e4
LT
230}
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;
3c42f0c3
DT
239 struct input_dev *input_dev;
240 int err = -ENOMEM;
1da177e4 241 int i;
1da177e4 242
3c42f0c3
DT
243 sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
244 input_dev = input_allocate_device();
245 if (!sunkbd || !input_dev)
246 goto fail;
1da177e4
LT
247
248 sunkbd->serio = serio;
3c42f0c3
DT
249 sunkbd->dev = input_dev;
250 init_waitqueue_head(&sunkbd->wait);
c4028958 251 INIT_WORK(&sunkbd->tq, sunkbd_reinit);
3c42f0c3 252 snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
1da177e4
LT
253
254 serio_set_drvdata(serio, sunkbd);
255
256 err = serio_open(serio, drv);
3c42f0c3
DT
257 if (err)
258 goto fail;
1da177e4
LT
259
260 if (sunkbd_initialize(sunkbd) < 0) {
261 serio_close(serio);
3c42f0c3 262 goto fail;
1da177e4
LT
263 }
264
ea08c6fa 265 snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
1da177e4 266 memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
1da177e4 267
3c42f0c3
DT
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);
285 for (i = 0; i < 128; i++)
286 set_bit(sunkbd->keycode[i], input_dev->keybit);
287 clear_bit(0, input_dev->keybit);
1da177e4 288
3c42f0c3
DT
289 sunkbd_enable(sunkbd, 1);
290 input_register_device(sunkbd->dev);
1da177e4 291 return 0;
3c42f0c3
DT
292
293 fail: serio_set_drvdata(serio, NULL);
294 input_free_device(input_dev);
295 kfree(sunkbd);
296 return err;
1da177e4
LT
297}
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);
3c42f0c3
DT
306
307 sunkbd_enable(sunkbd, 0);
308 input_unregister_device(sunkbd->dev);
1da177e4
LT
309 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);