]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/input/keyboard/sunkbd.c
Input: samsung-keypad - properly state IOMEM dependency
[mirror_ubuntu-jammy-kernel.git] / drivers / input / keyboard / sunkbd.c
CommitLineData
1a59d1b8 1// SPDX-License-Identifier: GPL-2.0-or-later
1da177e4 2/*
1da177e4
LT
3 * Copyright (c) 1999-2001 Vojtech Pavlik
4 */
5
6/*
7 * Sun keyboard driver for Linux
8 */
9
10/*
1da177e4
LT
11 */
12
13#include <linux/delay.h>
d43c36dc 14#include <linux/sched.h>
1da177e4
LT
15#include <linux/slab.h>
16#include <linux/module.h>
17#include <linux/interrupt.h>
1da177e4
LT
18#include <linux/input.h>
19#include <linux/serio.h>
20#include <linux/workqueue.h>
21
22#define DRIVER_DESC "Sun keyboard driver"
23
24MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
25MODULE_DESCRIPTION(DRIVER_DESC);
26MODULE_LICENSE("GPL");
27
28static unsigned char sunkbd_keycode[128] = {
8d9a9ae3 29 0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
1da177e4
LT
30 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
31 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
32 116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
33 26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
34 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
35 104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
36 79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
37};
38
39#define SUNKBD_CMD_RESET 0x1
40#define SUNKBD_CMD_BELLON 0x2
41#define SUNKBD_CMD_BELLOFF 0x3
42#define SUNKBD_CMD_CLICK 0xa
43#define SUNKBD_CMD_NOCLICK 0xb
44#define SUNKBD_CMD_SETLED 0xe
45#define SUNKBD_CMD_LAYOUT 0xf
46
47#define SUNKBD_RET_RESET 0xff
48#define SUNKBD_RET_ALLUP 0x7f
49#define SUNKBD_RET_LAYOUT 0xfe
50
51#define SUNKBD_LAYOUT_5_MASK 0x20
52#define SUNKBD_RELEASE 0x80
53#define SUNKBD_KEY 0x7f
54
55/*
56 * Per-keyboard data.
57 */
58
59struct sunkbd {
7cac9cd9 60 unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
3c42f0c3 61 struct input_dev *dev;
1da177e4
LT
62 struct serio *serio;
63 struct work_struct tq;
64 wait_queue_head_t wait;
65 char name[64];
66 char phys[32];
67 char type;
7cac9cd9 68 bool enabled;
1da177e4
LT
69 volatile s8 reset;
70 volatile s8 layout;
71};
72
73/*
74 * sunkbd_interrupt() is called by the low level driver when a character
75 * is received.
76 */
77
78static irqreturn_t sunkbd_interrupt(struct serio *serio,
7d12e780 79 unsigned char data, unsigned int flags)
1da177e4 80{
7cac9cd9 81 struct sunkbd *sunkbd = serio_get_drvdata(serio);
1da177e4 82
7cac9cd9
DT
83 if (sunkbd->reset <= -1) {
84 /*
85 * If cp[i] is 0xff, sunkbd->reset will stay -1.
86 * The keyboard sends 0xff 0xff 0xID on powerup.
87 */
88 sunkbd->reset = data;
1da177e4
LT
89 wake_up_interruptible(&sunkbd->wait);
90 goto out;
91 }
92
93 if (sunkbd->layout == -1) {
94 sunkbd->layout = data;
95 wake_up_interruptible(&sunkbd->wait);
96 goto out;
97 }
98
99 switch (data) {
100
7cac9cd9 101 case SUNKBD_RET_RESET:
77e70d35
DT
102 if (sunkbd->enabled)
103 schedule_work(&sunkbd->tq);
7cac9cd9
DT
104 sunkbd->reset = -1;
105 break;
1da177e4 106
7cac9cd9
DT
107 case SUNKBD_RET_LAYOUT:
108 sunkbd->layout = -1;
109 break;
1da177e4 110
7cac9cd9
DT
111 case SUNKBD_RET_ALLUP: /* All keys released */
112 break;
113
114 default:
115 if (!sunkbd->enabled)
1da177e4
LT
116 break;
117
7cac9cd9
DT
118 if (sunkbd->keycode[data & SUNKBD_KEY]) {
119 input_report_key(sunkbd->dev,
120 sunkbd->keycode[data & SUNKBD_KEY],
121 !(data & SUNKBD_RELEASE));
122 input_sync(sunkbd->dev);
123 } else {
124 printk(KERN_WARNING
125 "sunkbd.c: Unknown key (scancode %#x) %s.\n",
126 data & SUNKBD_KEY,
127 data & SUNKBD_RELEASE ? "released" : "pressed");
128 }
1da177e4
LT
129 }
130out:
131 return IRQ_HANDLED;
132}
133
134/*
135 * sunkbd_event() handles events from the input module.
136 */
137
7cac9cd9
DT
138static int sunkbd_event(struct input_dev *dev,
139 unsigned int type, unsigned int code, int value)
1da177e4 140{
b356872f 141 struct sunkbd *sunkbd = input_get_drvdata(dev);
1da177e4
LT
142
143 switch (type) {
144
7cac9cd9 145 case EV_LED:
1da177e4 146
7cac9cd9
DT
147 serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
148 serio_write(sunkbd->serio,
149 (!!test_bit(LED_CAPSL, dev->led) << 3) |
150 (!!test_bit(LED_SCROLLL, dev->led) << 2) |
151 (!!test_bit(LED_COMPOSE, dev->led) << 1) |
152 !!test_bit(LED_NUML, dev->led));
153 return 0;
1da177e4 154
7cac9cd9 155 case EV_SND:
1da177e4 156
7cac9cd9 157 switch (code) {
1da177e4 158
7cac9cd9
DT
159 case SND_CLICK:
160 serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
161 return 0;
1da177e4 162
7cac9cd9
DT
163 case SND_BELL:
164 serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
165 return 0;
166 }
1da177e4 167
7cac9cd9 168 break;
1da177e4
LT
169 }
170
171 return -1;
172}
173
174/*
175 * sunkbd_initialize() checks for a Sun keyboard attached, and determines
176 * its type.
177 */
178
179static int sunkbd_initialize(struct sunkbd *sunkbd)
180{
181 sunkbd->reset = -2;
dd0d5443 182 serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
1da177e4 183 wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
3c42f0c3 184 if (sunkbd->reset < 0)
1da177e4
LT
185 return -1;
186
187 sunkbd->type = sunkbd->reset;
188
189 if (sunkbd->type == 4) { /* Type 4 keyboard */
190 sunkbd->layout = -2;
dd0d5443 191 serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
7cac9cd9
DT
192 wait_event_interruptible_timeout(sunkbd->wait,
193 sunkbd->layout >= 0, HZ / 4);
194 if (sunkbd->layout < 0)
195 return -1;
196 if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
197 sunkbd->type = 5;
1da177e4
LT
198 }
199
200 return 0;
201}
202
203/*
77e70d35
DT
204 * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers
205 * they were in.
1da177e4
LT
206 */
207
77e70d35 208static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd)
1da177e4 209{
dd0d5443
DT
210 serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
211 serio_write(sunkbd->serio,
212 (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) |
213 (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
214 (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
215 !!test_bit(LED_NUML, sunkbd->dev->led));
7cac9cd9
DT
216 serio_write(sunkbd->serio,
217 SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
218 serio_write(sunkbd->serio,
219 SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
3c42f0c3
DT
220}
221
77e70d35
DT
222
223/*
224 * sunkbd_reinit() wait for the keyboard reset to complete and restores state
225 * of leds and beeps.
226 */
227
228static void sunkbd_reinit(struct work_struct *work)
229{
230 struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
231
232 /*
233 * It is OK that we check sunkbd->enabled without pausing serio,
234 * as we only want to catch true->false transition that will
235 * happen once and we will be woken up for it.
236 */
237 wait_event_interruptible_timeout(sunkbd->wait,
238 sunkbd->reset >= 0 || !sunkbd->enabled,
239 HZ);
240
241 if (sunkbd->reset >= 0 && sunkbd->enabled)
242 sunkbd_set_leds_beeps(sunkbd);
243}
244
7cac9cd9 245static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
3c42f0c3
DT
246{
247 serio_pause_rx(sunkbd->serio);
9bc83dcf 248 sunkbd->enabled = enable;
3c42f0c3 249 serio_continue_rx(sunkbd->serio);
77e70d35
DT
250
251 if (!enable) {
252 wake_up_interruptible(&sunkbd->wait);
253 cancel_work_sync(&sunkbd->tq);
254 }
1da177e4
LT
255}
256
257/*
7cac9cd9
DT
258 * sunkbd_connect() probes for a Sun keyboard and fills the necessary
259 * structures.
1da177e4
LT
260 */
261
262static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
263{
264 struct sunkbd *sunkbd;
3c42f0c3
DT
265 struct input_dev *input_dev;
266 int err = -ENOMEM;
1da177e4 267 int i;
1da177e4 268
3c42f0c3
DT
269 sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
270 input_dev = input_allocate_device();
271 if (!sunkbd || !input_dev)
2b03b60e 272 goto fail1;
1da177e4
LT
273
274 sunkbd->serio = serio;
3c42f0c3
DT
275 sunkbd->dev = input_dev;
276 init_waitqueue_head(&sunkbd->wait);
c4028958 277 INIT_WORK(&sunkbd->tq, sunkbd_reinit);
3c42f0c3 278 snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
1da177e4
LT
279
280 serio_set_drvdata(serio, sunkbd);
281
282 err = serio_open(serio, drv);
3c42f0c3 283 if (err)
2b03b60e 284 goto fail2;
1da177e4
LT
285
286 if (sunkbd_initialize(sunkbd) < 0) {
2b03b60e
DT
287 err = -ENODEV;
288 goto fail3;
1da177e4
LT
289 }
290
7cac9cd9
DT
291 snprintf(sunkbd->name, sizeof(sunkbd->name),
292 "Sun Type %d keyboard", sunkbd->type);
1da177e4 293 memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
1da177e4 294
3c42f0c3
DT
295 input_dev->name = sunkbd->name;
296 input_dev->phys = sunkbd->phys;
297 input_dev->id.bustype = BUS_RS232;
298 input_dev->id.vendor = SERIO_SUNKBD;
299 input_dev->id.product = sunkbd->type;
300 input_dev->id.version = 0x0100;
469ba4df 301 input_dev->dev.parent = &serio->dev;
b356872f
DT
302
303 input_set_drvdata(input_dev, sunkbd);
304
3c42f0c3
DT
305 input_dev->event = sunkbd_event;
306
7b19ada2
JS
307 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
308 BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
309 input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
310 BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
311 input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
3c42f0c3
DT
312
313 input_dev->keycode = sunkbd->keycode;
314 input_dev->keycodesize = sizeof(unsigned char);
315 input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
7cac9cd9
DT
316 for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
317 __set_bit(sunkbd->keycode[i], input_dev->keybit);
318 __clear_bit(KEY_RESERVED, input_dev->keybit);
1da177e4 319
7cac9cd9 320 sunkbd_enable(sunkbd, true);
2b03b60e
DT
321
322 err = input_register_device(sunkbd->dev);
323 if (err)
324 goto fail4;
325
1da177e4 326 return 0;
3c42f0c3 327
7cac9cd9 328 fail4: sunkbd_enable(sunkbd, false);
2b03b60e
DT
329 fail3: serio_close(serio);
330 fail2: serio_set_drvdata(serio, NULL);
331 fail1: input_free_device(input_dev);
3c42f0c3
DT
332 kfree(sunkbd);
333 return err;
1da177e4
LT
334}
335
336/*
337 * sunkbd_disconnect() unregisters and closes behind us.
338 */
339
340static void sunkbd_disconnect(struct serio *serio)
341{
342 struct sunkbd *sunkbd = serio_get_drvdata(serio);
3c42f0c3 343
7cac9cd9 344 sunkbd_enable(sunkbd, false);
3c42f0c3 345 input_unregister_device(sunkbd->dev);
1da177e4
LT
346 serio_close(serio);
347 serio_set_drvdata(serio, NULL);
348 kfree(sunkbd);
349}
350
1966e005 351static const struct serio_device_id sunkbd_serio_ids[] = {
1da177e4
LT
352 {
353 .type = SERIO_RS232,
354 .proto = SERIO_SUNKBD,
355 .id = SERIO_ANY,
356 .extra = SERIO_ANY,
357 },
358 {
359 .type = SERIO_RS232,
360 .proto = SERIO_UNKNOWN, /* sunkbd does probe */
361 .id = SERIO_ANY,
362 .extra = SERIO_ANY,
363 },
364 { 0 }
365};
366
367MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
368
369static struct serio_driver sunkbd_drv = {
370 .driver = {
371 .name = "sunkbd",
372 },
373 .description = DRIVER_DESC,
374 .id_table = sunkbd_serio_ids,
375 .interrupt = sunkbd_interrupt,
376 .connect = sunkbd_connect,
377 .disconnect = sunkbd_disconnect,
378};
379
65ac9f7a 380module_serio_driver(sunkbd_drv);