1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2000 Justin Cormack |
4 | */ |
5 | |
6 | /* |
7 | * Newton keyboard driver for Linux |
8 | */ |
9 | |
10 | #include <linux/slab.h> |
11 | #include <linux/module.h> |
12 | #include <linux/input.h> |
13 | #include <linux/serio.h> |
14 | |
15 | #define DRIVER_DESC "Newton keyboard driver" |
16 | |
17 | MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>" ); |
18 | MODULE_DESCRIPTION(DRIVER_DESC); |
19 | MODULE_LICENSE("GPL" ); |
20 | |
21 | #define NKBD_KEY 0x7f |
22 | #define NKBD_PRESS 0x80 |
23 | |
24 | static unsigned char nkbd_keycode[128] = { |
25 | KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X, |
26 | KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R, |
27 | KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5, |
28 | KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O, |
29 | KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE, |
30 | KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT, |
31 | KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA, |
32 | KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0, |
33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
35 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
36 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
37 | KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0 |
38 | }; |
39 | |
40 | struct nkbd { |
41 | unsigned char keycode[128]; |
42 | struct input_dev *dev; |
43 | struct serio *serio; |
44 | char phys[32]; |
45 | }; |
46 | |
47 | static irqreturn_t nkbd_interrupt(struct serio *serio, |
48 | unsigned char data, unsigned int flags) |
49 | { |
50 | struct nkbd *nkbd = serio_get_drvdata(serio); |
51 | |
52 | /* invalid scan codes are probably the init sequence, so we ignore them */ |
53 | if (nkbd->keycode[data & NKBD_KEY]) { |
54 | input_report_key(dev: nkbd->dev, code: nkbd->keycode[data & NKBD_KEY], value: data & NKBD_PRESS); |
55 | input_sync(dev: nkbd->dev); |
56 | } |
57 | |
58 | else if (data == 0xe7) /* end of init sequence */ |
59 | printk(KERN_INFO "input: %s on %s\n" , nkbd->dev->name, serio->phys); |
60 | return IRQ_HANDLED; |
61 | |
62 | } |
63 | |
64 | static int nkbd_connect(struct serio *serio, struct serio_driver *drv) |
65 | { |
66 | struct nkbd *nkbd; |
67 | struct input_dev *input_dev; |
68 | int err = -ENOMEM; |
69 | int i; |
70 | |
71 | nkbd = kzalloc(size: sizeof(struct nkbd), GFP_KERNEL); |
72 | input_dev = input_allocate_device(); |
73 | if (!nkbd || !input_dev) |
74 | goto fail1; |
75 | |
76 | nkbd->serio = serio; |
77 | nkbd->dev = input_dev; |
78 | snprintf(buf: nkbd->phys, size: sizeof(nkbd->phys), fmt: "%s/input0" , serio->phys); |
79 | memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode)); |
80 | |
81 | input_dev->name = "Newton Keyboard" ; |
82 | input_dev->phys = nkbd->phys; |
83 | input_dev->id.bustype = BUS_RS232; |
84 | input_dev->id.vendor = SERIO_NEWTON; |
85 | input_dev->id.product = 0x0001; |
86 | input_dev->id.version = 0x0100; |
87 | input_dev->dev.parent = &serio->dev; |
88 | |
89 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); |
90 | input_dev->keycode = nkbd->keycode; |
91 | input_dev->keycodesize = sizeof(unsigned char); |
92 | input_dev->keycodemax = ARRAY_SIZE(nkbd_keycode); |
93 | for (i = 0; i < 128; i++) |
94 | set_bit(nr: nkbd->keycode[i], addr: input_dev->keybit); |
95 | clear_bit(nr: 0, addr: input_dev->keybit); |
96 | |
97 | serio_set_drvdata(serio, data: nkbd); |
98 | |
99 | err = serio_open(serio, drv); |
100 | if (err) |
101 | goto fail2; |
102 | |
103 | err = input_register_device(nkbd->dev); |
104 | if (err) |
105 | goto fail3; |
106 | |
107 | return 0; |
108 | |
109 | fail3: serio_close(serio); |
110 | fail2: serio_set_drvdata(serio, NULL); |
111 | fail1: input_free_device(dev: input_dev); |
112 | kfree(objp: nkbd); |
113 | return err; |
114 | } |
115 | |
116 | static void nkbd_disconnect(struct serio *serio) |
117 | { |
118 | struct nkbd *nkbd = serio_get_drvdata(serio); |
119 | |
120 | serio_close(serio); |
121 | serio_set_drvdata(serio, NULL); |
122 | input_unregister_device(nkbd->dev); |
123 | kfree(objp: nkbd); |
124 | } |
125 | |
126 | static const struct serio_device_id nkbd_serio_ids[] = { |
127 | { |
128 | .type = SERIO_RS232, |
129 | .proto = SERIO_NEWTON, |
130 | .id = SERIO_ANY, |
131 | .extra = SERIO_ANY, |
132 | }, |
133 | { 0 } |
134 | }; |
135 | |
136 | MODULE_DEVICE_TABLE(serio, nkbd_serio_ids); |
137 | |
138 | static struct serio_driver nkbd_drv = { |
139 | .driver = { |
140 | .name = "newtonkbd" , |
141 | }, |
142 | .description = DRIVER_DESC, |
143 | .id_table = nkbd_serio_ids, |
144 | .interrupt = nkbd_interrupt, |
145 | .connect = nkbd_connect, |
146 | .disconnect = nkbd_disconnect, |
147 | }; |
148 | |
149 | module_serio_driver(nkbd_drv); |
150 | |