diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/input/keyboard/lkkbd.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/input/keyboard/lkkbd.c')
-rw-r--r-- | drivers/input/keyboard/lkkbd.c | 752 |
1 files changed, 752 insertions, 0 deletions
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c new file mode 100644 index 000000000000..2694ff2b5beb --- /dev/null +++ b/drivers/input/keyboard/lkkbd.c | |||
@@ -0,0 +1,752 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de> | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | * LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations | ||
11 | * and VAXstations, but can also be used on any standard RS232 with an | ||
12 | * adaptor). | ||
13 | * | ||
14 | * DISCLAIMER: This works for _me_. If you break anything by using the | ||
15 | * information given below, I will _not_ be liable! | ||
16 | * | ||
17 | * RJ10 pinout: To DE9: Or DB25: | ||
18 | * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD) | ||
19 | * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND) | ||
20 | * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD) | ||
21 | * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!! | ||
22 | * | ||
23 | * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For | ||
24 | * RJ10, it's like this: | ||
25 | * | ||
26 | * __=__ Hold the plug in front of you, cable downwards, | ||
27 | * /___/| nose is hidden behind the plug. Now, pin 1 is at | ||
28 | * |1234|| the left side, pin 4 at the right and 2 and 3 are | ||
29 | * |IIII|| in between, of course:) | ||
30 | * | || | ||
31 | * |____|/ | ||
32 | * || So the adaptor consists of three connected cables | ||
33 | * || for data transmission (RxD and TxD) and signal ground. | ||
34 | * Additionally, you have to get +12V from somewhere. | ||
35 | * Most easily, you'll get that from a floppy or HDD power connector. | ||
36 | * It's the yellow cable there (black is ground and red is +5V). | ||
37 | * | ||
38 | * The keyboard and all the commands it understands are documented in | ||
39 | * "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This | ||
40 | * document is LK201 specific, but LK401 is mostly compatible. It comes | ||
41 | * up in LK201 mode and doesn't report any of the additional keys it | ||
42 | * has. These need to be switched on with the LK_CMD_ENABLE_LK401 | ||
43 | * command. You'll find this document (scanned .pdf file) on MANX, | ||
44 | * a search engine specific to DEC documentation. Try | ||
45 | * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1 | ||
46 | */ | ||
47 | |||
48 | /* | ||
49 | * This program is free software; you can redistribute it and/or modify | ||
50 | * it under the terms of the GNU General Public License as published by | ||
51 | * the Free Software Foundation; either version 2 of the License, or | ||
52 | * (at your option) any later version. | ||
53 | * | ||
54 | * This program is distributed in the hope that it will be useful, | ||
55 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
56 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
57 | * GNU General Public License for more details. | ||
58 | * | ||
59 | * You should have received a copy of the GNU General Public License | ||
60 | * along with this program; if not, write to the Free Software | ||
61 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
62 | * | ||
63 | * Should you need to contact me, the author, you can do so either by | ||
64 | * email or by paper mail: | ||
65 | * Jan-Benedict Glaw, Lilienstraße 16, 33790 Hörste (near Halle/Westf.), | ||
66 | * Germany. | ||
67 | */ | ||
68 | |||
69 | #include <linux/delay.h> | ||
70 | #include <linux/slab.h> | ||
71 | #include <linux/module.h> | ||
72 | #include <linux/moduleparam.h> | ||
73 | #include <linux/interrupt.h> | ||
74 | #include <linux/init.h> | ||
75 | #include <linux/input.h> | ||
76 | #include <linux/serio.h> | ||
77 | #include <linux/workqueue.h> | ||
78 | |||
79 | #define DRIVER_DESC "LK keyboard driver" | ||
80 | |||
81 | MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); | ||
82 | MODULE_DESCRIPTION (DRIVER_DESC); | ||
83 | MODULE_LICENSE ("GPL"); | ||
84 | |||
85 | /* | ||
86 | * Known parameters: | ||
87 | * bell_volume | ||
88 | * keyclick_volume | ||
89 | * ctrlclick_volume | ||
90 | * | ||
91 | * Please notice that there's not yet an API to set these at runtime. | ||
92 | */ | ||
93 | static int bell_volume = 100; /* % */ | ||
94 | module_param (bell_volume, int, 0); | ||
95 | MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%"); | ||
96 | |||
97 | static int keyclick_volume = 100; /* % */ | ||
98 | module_param (keyclick_volume, int, 0); | ||
99 | MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%"); | ||
100 | |||
101 | static int ctrlclick_volume = 100; /* % */ | ||
102 | module_param (ctrlclick_volume, int, 0); | ||
103 | MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%"); | ||
104 | |||
105 | static int lk201_compose_is_alt = 0; | ||
106 | module_param (lk201_compose_is_alt, int, 0); | ||
107 | MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key " | ||
108 | "will act as an Alt key"); | ||
109 | |||
110 | |||
111 | |||
112 | #undef LKKBD_DEBUG | ||
113 | #ifdef LKKBD_DEBUG | ||
114 | #define DBG(x...) printk (x) | ||
115 | #else | ||
116 | #define DBG(x...) do {} while (0) | ||
117 | #endif | ||
118 | |||
119 | /* LED control */ | ||
120 | #define LK_LED_WAIT 0x81 | ||
121 | #define LK_LED_COMPOSE 0x82 | ||
122 | #define LK_LED_SHIFTLOCK 0x84 | ||
123 | #define LK_LED_SCROLLLOCK 0x88 | ||
124 | #define LK_CMD_LED_ON 0x13 | ||
125 | #define LK_CMD_LED_OFF 0x11 | ||
126 | |||
127 | /* Mode control */ | ||
128 | #define LK_MODE_DOWN 0x80 | ||
129 | #define LK_MODE_AUTODOWN 0x82 | ||
130 | #define LK_MODE_UPDOWN 0x86 | ||
131 | #define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3)) | ||
132 | |||
133 | /* Misc commands */ | ||
134 | #define LK_CMD_ENABLE_KEYCLICK 0x1b | ||
135 | #define LK_CMD_DISABLE_KEYCLICK 0x99 | ||
136 | #define LK_CMD_DISABLE_BELL 0xa1 | ||
137 | #define LK_CMD_SOUND_BELL 0xa7 | ||
138 | #define LK_CMD_ENABLE_BELL 0x23 | ||
139 | #define LK_CMD_DISABLE_CTRCLICK 0xb9 | ||
140 | #define LK_CMD_ENABLE_CTRCLICK 0xbb | ||
141 | #define LK_CMD_SET_DEFAULTS 0xd3 | ||
142 | #define LK_CMD_POWERCYCLE_RESET 0xfd | ||
143 | #define LK_CMD_ENABLE_LK401 0xe9 | ||
144 | #define LK_CMD_REQUEST_ID 0xab | ||
145 | |||
146 | /* Misc responses from keyboard */ | ||
147 | #define LK_STUCK_KEY 0x3d | ||
148 | #define LK_SELFTEST_FAILED 0x3e | ||
149 | #define LK_ALL_KEYS_UP 0xb3 | ||
150 | #define LK_METRONOME 0xb4 | ||
151 | #define LK_OUTPUT_ERROR 0xb5 | ||
152 | #define LK_INPUT_ERROR 0xb6 | ||
153 | #define LK_KBD_LOCKED 0xb7 | ||
154 | #define LK_KBD_TEST_MODE_ACK 0xb8 | ||
155 | #define LK_PREFIX_KEY_DOWN 0xb9 | ||
156 | #define LK_MODE_CHANGE_ACK 0xba | ||
157 | #define LK_RESPONSE_RESERVED 0xbb | ||
158 | |||
159 | #define LK_NUM_KEYCODES 256 | ||
160 | #define LK_NUM_IGNORE_BYTES 6 | ||
161 | typedef u_int16_t lk_keycode_t; | ||
162 | |||
163 | |||
164 | |||
165 | static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = { | ||
166 | [0x56] = KEY_F1, | ||
167 | [0x57] = KEY_F2, | ||
168 | [0x58] = KEY_F3, | ||
169 | [0x59] = KEY_F4, | ||
170 | [0x5a] = KEY_F5, | ||
171 | [0x64] = KEY_F6, | ||
172 | [0x65] = KEY_F7, | ||
173 | [0x66] = KEY_F8, | ||
174 | [0x67] = KEY_F9, | ||
175 | [0x68] = KEY_F10, | ||
176 | [0x71] = KEY_F11, | ||
177 | [0x72] = KEY_F12, | ||
178 | [0x73] = KEY_F13, | ||
179 | [0x74] = KEY_F14, | ||
180 | [0x7c] = KEY_F15, | ||
181 | [0x7d] = KEY_F16, | ||
182 | [0x80] = KEY_F17, | ||
183 | [0x81] = KEY_F18, | ||
184 | [0x82] = KEY_F19, | ||
185 | [0x83] = KEY_F20, | ||
186 | [0x8a] = KEY_FIND, | ||
187 | [0x8b] = KEY_INSERT, | ||
188 | [0x8c] = KEY_DELETE, | ||
189 | [0x8d] = KEY_SELECT, | ||
190 | [0x8e] = KEY_PAGEUP, | ||
191 | [0x8f] = KEY_PAGEDOWN, | ||
192 | [0x92] = KEY_KP0, | ||
193 | [0x94] = KEY_KPDOT, | ||
194 | [0x95] = KEY_KPENTER, | ||
195 | [0x96] = KEY_KP1, | ||
196 | [0x97] = KEY_KP2, | ||
197 | [0x98] = KEY_KP3, | ||
198 | [0x99] = KEY_KP4, | ||
199 | [0x9a] = KEY_KP5, | ||
200 | [0x9b] = KEY_KP6, | ||
201 | [0x9c] = KEY_KPCOMMA, | ||
202 | [0x9d] = KEY_KP7, | ||
203 | [0x9e] = KEY_KP8, | ||
204 | [0x9f] = KEY_KP9, | ||
205 | [0xa0] = KEY_KPMINUS, | ||
206 | [0xa1] = KEY_PROG1, | ||
207 | [0xa2] = KEY_PROG2, | ||
208 | [0xa3] = KEY_PROG3, | ||
209 | [0xa4] = KEY_PROG4, | ||
210 | [0xa7] = KEY_LEFT, | ||
211 | [0xa8] = KEY_RIGHT, | ||
212 | [0xa9] = KEY_DOWN, | ||
213 | [0xaa] = KEY_UP, | ||
214 | [0xab] = KEY_RIGHTSHIFT, | ||
215 | [0xac] = KEY_LEFTALT, | ||
216 | [0xad] = KEY_COMPOSE, /* Right Compose, that is. */ | ||
217 | [0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */ | ||
218 | [0xaf] = KEY_LEFTCTRL, | ||
219 | [0xb0] = KEY_CAPSLOCK, | ||
220 | [0xb1] = KEY_COMPOSE, /* Left Compose, that is. */ | ||
221 | [0xb2] = KEY_RIGHTALT, | ||
222 | [0xbc] = KEY_BACKSPACE, | ||
223 | [0xbd] = KEY_ENTER, | ||
224 | [0xbe] = KEY_TAB, | ||
225 | [0xbf] = KEY_ESC, | ||
226 | [0xc0] = KEY_1, | ||
227 | [0xc1] = KEY_Q, | ||
228 | [0xc2] = KEY_A, | ||
229 | [0xc3] = KEY_Z, | ||
230 | [0xc5] = KEY_2, | ||
231 | [0xc6] = KEY_W, | ||
232 | [0xc7] = KEY_S, | ||
233 | [0xc8] = KEY_X, | ||
234 | [0xc9] = KEY_102ND, | ||
235 | [0xcb] = KEY_3, | ||
236 | [0xcc] = KEY_E, | ||
237 | [0xcd] = KEY_D, | ||
238 | [0xce] = KEY_C, | ||
239 | [0xd0] = KEY_4, | ||
240 | [0xd1] = KEY_R, | ||
241 | [0xd2] = KEY_F, | ||
242 | [0xd3] = KEY_V, | ||
243 | [0xd4] = KEY_SPACE, | ||
244 | [0xd6] = KEY_5, | ||
245 | [0xd7] = KEY_T, | ||
246 | [0xd8] = KEY_G, | ||
247 | [0xd9] = KEY_B, | ||
248 | [0xdb] = KEY_6, | ||
249 | [0xdc] = KEY_Y, | ||
250 | [0xdd] = KEY_H, | ||
251 | [0xde] = KEY_N, | ||
252 | [0xe0] = KEY_7, | ||
253 | [0xe1] = KEY_U, | ||
254 | [0xe2] = KEY_J, | ||
255 | [0xe3] = KEY_M, | ||
256 | [0xe5] = KEY_8, | ||
257 | [0xe6] = KEY_I, | ||
258 | [0xe7] = KEY_K, | ||
259 | [0xe8] = KEY_COMMA, | ||
260 | [0xea] = KEY_9, | ||
261 | [0xeb] = KEY_O, | ||
262 | [0xec] = KEY_L, | ||
263 | [0xed] = KEY_DOT, | ||
264 | [0xef] = KEY_0, | ||
265 | [0xf0] = KEY_P, | ||
266 | [0xf2] = KEY_SEMICOLON, | ||
267 | [0xf3] = KEY_SLASH, | ||
268 | [0xf5] = KEY_EQUAL, | ||
269 | [0xf6] = KEY_RIGHTBRACE, | ||
270 | [0xf7] = KEY_BACKSLASH, | ||
271 | [0xf9] = KEY_MINUS, | ||
272 | [0xfa] = KEY_LEFTBRACE, | ||
273 | [0xfb] = KEY_APOSTROPHE, | ||
274 | }; | ||
275 | |||
276 | #define CHECK_LED(LED, BITS) do { \ | ||
277 | if (test_bit (LED, lk->dev.led)) \ | ||
278 | leds_on |= BITS; \ | ||
279 | else \ | ||
280 | leds_off |= BITS; \ | ||
281 | } while (0) | ||
282 | |||
283 | /* | ||
284 | * Per-keyboard data | ||
285 | */ | ||
286 | struct lkkbd { | ||
287 | lk_keycode_t keycode[LK_NUM_KEYCODES]; | ||
288 | int ignore_bytes; | ||
289 | unsigned char id[LK_NUM_IGNORE_BYTES]; | ||
290 | struct input_dev dev; | ||
291 | struct serio *serio; | ||
292 | struct work_struct tq; | ||
293 | char name[64]; | ||
294 | char phys[32]; | ||
295 | char type; | ||
296 | int bell_volume; | ||
297 | int keyclick_volume; | ||
298 | int ctrlclick_volume; | ||
299 | }; | ||
300 | |||
301 | /* | ||
302 | * Calculate volume parameter byte for a given volume. | ||
303 | */ | ||
304 | static unsigned char | ||
305 | volume_to_hw (int volume_percent) | ||
306 | { | ||
307 | unsigned char ret = 0; | ||
308 | |||
309 | if (volume_percent < 0) | ||
310 | volume_percent = 0; | ||
311 | if (volume_percent > 100) | ||
312 | volume_percent = 100; | ||
313 | |||
314 | if (volume_percent >= 0) | ||
315 | ret = 7; | ||
316 | if (volume_percent >= 13) /* 12.5 */ | ||
317 | ret = 6; | ||
318 | if (volume_percent >= 25) | ||
319 | ret = 5; | ||
320 | if (volume_percent >= 38) /* 37.5 */ | ||
321 | ret = 4; | ||
322 | if (volume_percent >= 50) | ||
323 | ret = 3; | ||
324 | if (volume_percent >= 63) /* 62.5 */ | ||
325 | ret = 2; /* This is the default volume */ | ||
326 | if (volume_percent >= 75) | ||
327 | ret = 1; | ||
328 | if (volume_percent >= 88) /* 87.5 */ | ||
329 | ret = 0; | ||
330 | |||
331 | ret |= 0x80; | ||
332 | |||
333 | return ret; | ||
334 | } | ||
335 | |||
336 | static void | ||
337 | lkkbd_detection_done (struct lkkbd *lk) | ||
338 | { | ||
339 | int i; | ||
340 | |||
341 | /* | ||
342 | * Reset setting for Compose key. Let Compose be KEY_COMPOSE. | ||
343 | */ | ||
344 | lk->keycode[0xb1] = KEY_COMPOSE; | ||
345 | |||
346 | /* | ||
347 | * Print keyboard name and modify Compose=Alt on user's request. | ||
348 | */ | ||
349 | switch (lk->id[4]) { | ||
350 | case 1: | ||
351 | sprintf (lk->name, "DEC LK201 keyboard"); | ||
352 | |||
353 | if (lk201_compose_is_alt) | ||
354 | lk->keycode[0xb1] = KEY_LEFTALT; | ||
355 | break; | ||
356 | |||
357 | case 2: | ||
358 | sprintf (lk->name, "DEC LK401 keyboard"); | ||
359 | break; | ||
360 | |||
361 | default: | ||
362 | sprintf (lk->name, "Unknown DEC keyboard"); | ||
363 | printk (KERN_ERR "lkkbd: keyboard on %s is unknown, " | ||
364 | "please report to Jan-Benedict Glaw " | ||
365 | "<jbglaw@lug-owl.de>\n", lk->phys); | ||
366 | printk (KERN_ERR "lkkbd: keyboard ID'ed as:"); | ||
367 | for (i = 0; i < LK_NUM_IGNORE_BYTES; i++) | ||
368 | printk (" 0x%02x", lk->id[i]); | ||
369 | printk ("\n"); | ||
370 | break; | ||
371 | } | ||
372 | printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n", | ||
373 | lk->phys, lk->name); | ||
374 | |||
375 | /* | ||
376 | * Report errors during keyboard boot-up. | ||
377 | */ | ||
378 | switch (lk->id[2]) { | ||
379 | case 0x00: | ||
380 | /* All okay */ | ||
381 | break; | ||
382 | |||
383 | case LK_STUCK_KEY: | ||
384 | printk (KERN_ERR "lkkbd: Stuck key on keyboard at " | ||
385 | "%s\n", lk->phys); | ||
386 | break; | ||
387 | |||
388 | case LK_SELFTEST_FAILED: | ||
389 | printk (KERN_ERR "lkkbd: Selftest failed on keyboard " | ||
390 | "at %s, keyboard may not work " | ||
391 | "properly\n", lk->phys); | ||
392 | break; | ||
393 | |||
394 | default: | ||
395 | printk (KERN_ERR "lkkbd: Unknown error %02x on " | ||
396 | "keyboard at %s\n", lk->id[2], | ||
397 | lk->phys); | ||
398 | break; | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * Try to hint user if there's a stuck key. | ||
403 | */ | ||
404 | if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0) | ||
405 | printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode " | ||
406 | "is 0x%04x\n", lk->id[3], | ||
407 | lk->keycode[lk->id[3]]); | ||
408 | |||
409 | return; | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * lkkbd_interrupt() is called by the low level driver when a character | ||
414 | * is received. | ||
415 | */ | ||
416 | static irqreturn_t | ||
417 | lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags, | ||
418 | struct pt_regs *regs) | ||
419 | { | ||
420 | struct lkkbd *lk = serio_get_drvdata (serio); | ||
421 | int i; | ||
422 | |||
423 | DBG (KERN_INFO "Got byte 0x%02x\n", data); | ||
424 | |||
425 | if (lk->ignore_bytes > 0) { | ||
426 | DBG (KERN_INFO "Ignoring a byte on %s\n", | ||
427 | lk->name); | ||
428 | lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; | ||
429 | |||
430 | if (lk->ignore_bytes == 0) | ||
431 | lkkbd_detection_done (lk); | ||
432 | |||
433 | return IRQ_HANDLED; | ||
434 | } | ||
435 | |||
436 | switch (data) { | ||
437 | case LK_ALL_KEYS_UP: | ||
438 | input_regs (&lk->dev, regs); | ||
439 | for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++) | ||
440 | if (lk->keycode[i] != KEY_RESERVED) | ||
441 | input_report_key (&lk->dev, lk->keycode[i], 0); | ||
442 | input_sync (&lk->dev); | ||
443 | break; | ||
444 | case LK_METRONOME: | ||
445 | DBG (KERN_INFO "Got LK_METRONOME and don't " | ||
446 | "know how to handle...\n"); | ||
447 | break; | ||
448 | case LK_OUTPUT_ERROR: | ||
449 | DBG (KERN_INFO "Got LK_OUTPUT_ERROR and don't " | ||
450 | "know how to handle...\n"); | ||
451 | break; | ||
452 | case LK_INPUT_ERROR: | ||
453 | DBG (KERN_INFO "Got LK_INPUT_ERROR and don't " | ||
454 | "know how to handle...\n"); | ||
455 | break; | ||
456 | case LK_KBD_LOCKED: | ||
457 | DBG (KERN_INFO "Got LK_KBD_LOCKED and don't " | ||
458 | "know how to handle...\n"); | ||
459 | break; | ||
460 | case LK_KBD_TEST_MODE_ACK: | ||
461 | DBG (KERN_INFO "Got LK_KBD_TEST_MODE_ACK and don't " | ||
462 | "know how to handle...\n"); | ||
463 | break; | ||
464 | case LK_PREFIX_KEY_DOWN: | ||
465 | DBG (KERN_INFO "Got LK_PREFIX_KEY_DOWN and don't " | ||
466 | "know how to handle...\n"); | ||
467 | break; | ||
468 | case LK_MODE_CHANGE_ACK: | ||
469 | DBG (KERN_INFO "Got LK_MODE_CHANGE_ACK and ignored " | ||
470 | "it properly...\n"); | ||
471 | break; | ||
472 | case LK_RESPONSE_RESERVED: | ||
473 | DBG (KERN_INFO "Got LK_RESPONSE_RESERVED and don't " | ||
474 | "know how to handle...\n"); | ||
475 | break; | ||
476 | case 0x01: | ||
477 | DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n"); | ||
478 | lk->ignore_bytes = LK_NUM_IGNORE_BYTES; | ||
479 | lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; | ||
480 | schedule_work (&lk->tq); | ||
481 | break; | ||
482 | |||
483 | default: | ||
484 | if (lk->keycode[data] != KEY_RESERVED) { | ||
485 | input_regs (&lk->dev, regs); | ||
486 | if (!test_bit (lk->keycode[data], lk->dev.key)) | ||
487 | input_report_key (&lk->dev, lk->keycode[data], 1); | ||
488 | else | ||
489 | input_report_key (&lk->dev, lk->keycode[data], 0); | ||
490 | input_sync (&lk->dev); | ||
491 | } else | ||
492 | printk (KERN_WARNING "%s: Unknown key with " | ||
493 | "scancode 0x%02x on %s.\n", | ||
494 | __FILE__, data, lk->name); | ||
495 | } | ||
496 | |||
497 | return IRQ_HANDLED; | ||
498 | } | ||
499 | |||
500 | /* | ||
501 | * lkkbd_event() handles events from the input module. | ||
502 | */ | ||
503 | static int | ||
504 | lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code, | ||
505 | int value) | ||
506 | { | ||
507 | struct lkkbd *lk = dev->private; | ||
508 | unsigned char leds_on = 0; | ||
509 | unsigned char leds_off = 0; | ||
510 | |||
511 | switch (type) { | ||
512 | case EV_LED: | ||
513 | CHECK_LED (LED_CAPSL, LK_LED_SHIFTLOCK); | ||
514 | CHECK_LED (LED_COMPOSE, LK_LED_COMPOSE); | ||
515 | CHECK_LED (LED_SCROLLL, LK_LED_SCROLLLOCK); | ||
516 | CHECK_LED (LED_SLEEP, LK_LED_WAIT); | ||
517 | if (leds_on != 0) { | ||
518 | lk->serio->write (lk->serio, LK_CMD_LED_ON); | ||
519 | lk->serio->write (lk->serio, leds_on); | ||
520 | } | ||
521 | if (leds_off != 0) { | ||
522 | lk->serio->write (lk->serio, LK_CMD_LED_OFF); | ||
523 | lk->serio->write (lk->serio, leds_off); | ||
524 | } | ||
525 | return 0; | ||
526 | |||
527 | case EV_SND: | ||
528 | switch (code) { | ||
529 | case SND_CLICK: | ||
530 | if (value == 0) { | ||
531 | DBG ("%s: Deactivating key clicks\n", __FUNCTION__); | ||
532 | lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK); | ||
533 | lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK); | ||
534 | } else { | ||
535 | DBG ("%s: Activating key clicks\n", __FUNCTION__); | ||
536 | lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK); | ||
537 | lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume)); | ||
538 | lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK); | ||
539 | lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume)); | ||
540 | } | ||
541 | return 0; | ||
542 | |||
543 | case SND_BELL: | ||
544 | if (value != 0) | ||
545 | lk->serio->write (lk->serio, LK_CMD_SOUND_BELL); | ||
546 | |||
547 | return 0; | ||
548 | } | ||
549 | break; | ||
550 | |||
551 | default: | ||
552 | printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n", | ||
553 | __FUNCTION__, type, code, value); | ||
554 | } | ||
555 | |||
556 | return -1; | ||
557 | } | ||
558 | |||
559 | /* | ||
560 | * lkkbd_reinit() sets leds and beeps to a state the computer remembers they | ||
561 | * were in. | ||
562 | */ | ||
563 | static void | ||
564 | lkkbd_reinit (void *data) | ||
565 | { | ||
566 | struct lkkbd *lk = data; | ||
567 | int division; | ||
568 | unsigned char leds_on = 0; | ||
569 | unsigned char leds_off = 0; | ||
570 | |||
571 | /* Ask for ID */ | ||
572 | lk->serio->write (lk->serio, LK_CMD_REQUEST_ID); | ||
573 | |||
574 | /* Reset parameters */ | ||
575 | lk->serio->write (lk->serio, LK_CMD_SET_DEFAULTS); | ||
576 | |||
577 | /* Set LEDs */ | ||
578 | CHECK_LED (LED_CAPSL, LK_LED_SHIFTLOCK); | ||
579 | CHECK_LED (LED_COMPOSE, LK_LED_COMPOSE); | ||
580 | CHECK_LED (LED_SCROLLL, LK_LED_SCROLLLOCK); | ||
581 | CHECK_LED (LED_SLEEP, LK_LED_WAIT); | ||
582 | if (leds_on != 0) { | ||
583 | lk->serio->write (lk->serio, LK_CMD_LED_ON); | ||
584 | lk->serio->write (lk->serio, leds_on); | ||
585 | } | ||
586 | if (leds_off != 0) { | ||
587 | lk->serio->write (lk->serio, LK_CMD_LED_OFF); | ||
588 | lk->serio->write (lk->serio, leds_off); | ||
589 | } | ||
590 | |||
591 | /* | ||
592 | * Try to activate extended LK401 mode. This command will | ||
593 | * only work with a LK401 keyboard and grants access to | ||
594 | * LAlt, RAlt, RCompose and RShift. | ||
595 | */ | ||
596 | lk->serio->write (lk->serio, LK_CMD_ENABLE_LK401); | ||
597 | |||
598 | /* Set all keys to UPDOWN mode */ | ||
599 | for (division = 1; division <= 14; division++) | ||
600 | lk->serio->write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN, | ||
601 | division)); | ||
602 | |||
603 | /* Enable bell and set volume */ | ||
604 | lk->serio->write (lk->serio, LK_CMD_ENABLE_BELL); | ||
605 | lk->serio->write (lk->serio, volume_to_hw (lk->bell_volume)); | ||
606 | |||
607 | /* Enable/disable keyclick (and possibly set volume) */ | ||
608 | if (test_bit (SND_CLICK, lk->dev.snd)) { | ||
609 | lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK); | ||
610 | lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume)); | ||
611 | lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK); | ||
612 | lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume)); | ||
613 | } else { | ||
614 | lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK); | ||
615 | lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK); | ||
616 | } | ||
617 | |||
618 | /* Sound the bell if needed */ | ||
619 | if (test_bit (SND_BELL, lk->dev.snd)) | ||
620 | lk->serio->write (lk->serio, LK_CMD_SOUND_BELL); | ||
621 | } | ||
622 | |||
623 | /* | ||
624 | * lkkbd_connect() probes for a LK keyboard and fills the necessary structures. | ||
625 | */ | ||
626 | static int | ||
627 | lkkbd_connect (struct serio *serio, struct serio_driver *drv) | ||
628 | { | ||
629 | struct lkkbd *lk; | ||
630 | int i; | ||
631 | int err; | ||
632 | |||
633 | if (!(lk = kmalloc (sizeof (struct lkkbd), GFP_KERNEL))) | ||
634 | return -ENOMEM; | ||
635 | |||
636 | memset (lk, 0, sizeof (struct lkkbd)); | ||
637 | |||
638 | init_input_dev (&lk->dev); | ||
639 | set_bit (EV_KEY, lk->dev.evbit); | ||
640 | set_bit (EV_LED, lk->dev.evbit); | ||
641 | set_bit (EV_SND, lk->dev.evbit); | ||
642 | set_bit (EV_REP, lk->dev.evbit); | ||
643 | set_bit (LED_CAPSL, lk->dev.ledbit); | ||
644 | set_bit (LED_SLEEP, lk->dev.ledbit); | ||
645 | set_bit (LED_COMPOSE, lk->dev.ledbit); | ||
646 | set_bit (LED_SCROLLL, lk->dev.ledbit); | ||
647 | set_bit (SND_BELL, lk->dev.sndbit); | ||
648 | set_bit (SND_CLICK, lk->dev.sndbit); | ||
649 | |||
650 | lk->serio = serio; | ||
651 | |||
652 | INIT_WORK (&lk->tq, lkkbd_reinit, lk); | ||
653 | |||
654 | lk->bell_volume = bell_volume; | ||
655 | lk->keyclick_volume = keyclick_volume; | ||
656 | lk->ctrlclick_volume = ctrlclick_volume; | ||
657 | |||
658 | lk->dev.keycode = lk->keycode; | ||
659 | lk->dev.keycodesize = sizeof (lk_keycode_t); | ||
660 | lk->dev.keycodemax = LK_NUM_KEYCODES; | ||
661 | |||
662 | lk->dev.event = lkkbd_event; | ||
663 | lk->dev.private = lk; | ||
664 | |||
665 | serio_set_drvdata (serio, lk); | ||
666 | |||
667 | err = serio_open (serio, drv); | ||
668 | if (err) { | ||
669 | serio_set_drvdata (serio, NULL); | ||
670 | kfree (lk); | ||
671 | return err; | ||
672 | } | ||
673 | |||
674 | sprintf (lk->name, "DEC LK keyboard"); | ||
675 | sprintf (lk->phys, "%s/input0", serio->phys); | ||
676 | |||
677 | memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES); | ||
678 | for (i = 0; i < LK_NUM_KEYCODES; i++) | ||
679 | set_bit (lk->keycode[i], lk->dev.keybit); | ||
680 | |||
681 | lk->dev.name = lk->name; | ||
682 | lk->dev.phys = lk->phys; | ||
683 | lk->dev.id.bustype = BUS_RS232; | ||
684 | lk->dev.id.vendor = SERIO_LKKBD; | ||
685 | lk->dev.id.product = 0; | ||
686 | lk->dev.id.version = 0x0100; | ||
687 | lk->dev.dev = &serio->dev; | ||
688 | |||
689 | input_register_device (&lk->dev); | ||
690 | |||
691 | printk (KERN_INFO "input: %s on %s, initiating reset\n", lk->name, serio->phys); | ||
692 | lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET); | ||
693 | |||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | /* | ||
698 | * lkkbd_disconnect() unregisters and closes behind us. | ||
699 | */ | ||
700 | static void | ||
701 | lkkbd_disconnect (struct serio *serio) | ||
702 | { | ||
703 | struct lkkbd *lk = serio_get_drvdata (serio); | ||
704 | |||
705 | input_unregister_device (&lk->dev); | ||
706 | serio_close (serio); | ||
707 | serio_set_drvdata (serio, NULL); | ||
708 | kfree (lk); | ||
709 | } | ||
710 | |||
711 | static struct serio_device_id lkkbd_serio_ids[] = { | ||
712 | { | ||
713 | .type = SERIO_RS232, | ||
714 | .proto = SERIO_LKKBD, | ||
715 | .id = SERIO_ANY, | ||
716 | .extra = SERIO_ANY, | ||
717 | }, | ||
718 | { 0 } | ||
719 | }; | ||
720 | |||
721 | MODULE_DEVICE_TABLE(serio, lkkbd_serio_ids); | ||
722 | |||
723 | static struct serio_driver lkkbd_drv = { | ||
724 | .driver = { | ||
725 | .name = "lkkbd", | ||
726 | }, | ||
727 | .description = DRIVER_DESC, | ||
728 | .id_table = lkkbd_serio_ids, | ||
729 | .connect = lkkbd_connect, | ||
730 | .disconnect = lkkbd_disconnect, | ||
731 | .interrupt = lkkbd_interrupt, | ||
732 | }; | ||
733 | |||
734 | /* | ||
735 | * The functions for insering/removing us as a module. | ||
736 | */ | ||
737 | static int __init | ||
738 | lkkbd_init (void) | ||
739 | { | ||
740 | serio_register_driver(&lkkbd_drv); | ||
741 | return 0; | ||
742 | } | ||
743 | |||
744 | static void __exit | ||
745 | lkkbd_exit (void) | ||
746 | { | ||
747 | serio_unregister_driver(&lkkbd_drv); | ||
748 | } | ||
749 | |||
750 | module_init (lkkbd_init); | ||
751 | module_exit (lkkbd_exit); | ||
752 | |||