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