Linux-libre 5.4.48-gnu
[librecmc/linux-libre.git] / drivers / input / serio / ps2mult.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * TQC PS/2 Multiplexer driver
4  *
5  * Copyright (C) 2010 Dmitry Eremin-Solenikov
6  */
7
8
9 #include <linux/kernel.h>
10 #include <linux/slab.h>
11 #include <linux/module.h>
12 #include <linux/serio.h>
13
14 MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
15 MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
16 MODULE_LICENSE("GPL");
17
18 #define PS2MULT_KB_SELECTOR             0xA0
19 #define PS2MULT_MS_SELECTOR             0xA1
20 #define PS2MULT_ESCAPE                  0x7D
21 #define PS2MULT_BSYNC                   0x7E
22 #define PS2MULT_SESSION_START           0x55
23 #define PS2MULT_SESSION_END             0x56
24
25 struct ps2mult_port {
26         struct serio *serio;
27         unsigned char sel;
28         bool registered;
29 };
30
31 #define PS2MULT_NUM_PORTS       2
32 #define PS2MULT_KBD_PORT        0
33 #define PS2MULT_MOUSE_PORT      1
34
35 struct ps2mult {
36         struct serio *mx_serio;
37         struct ps2mult_port ports[PS2MULT_NUM_PORTS];
38
39         spinlock_t lock;
40         struct ps2mult_port *in_port;
41         struct ps2mult_port *out_port;
42         bool escape;
43 };
44
45 /* First MUST come PS2MULT_NUM_PORTS selectors */
46 static const unsigned char ps2mult_controls[] = {
47         PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
48         PS2MULT_ESCAPE, PS2MULT_BSYNC,
49         PS2MULT_SESSION_START, PS2MULT_SESSION_END,
50 };
51
52 static const struct serio_device_id ps2mult_serio_ids[] = {
53         {
54                 .type   = SERIO_RS232,
55                 .proto  = SERIO_PS2MULT,
56                 .id     = SERIO_ANY,
57                 .extra  = SERIO_ANY,
58         },
59         { 0 }
60 };
61
62 MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
63
64 static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
65 {
66         struct serio *mx_serio = psm->mx_serio;
67
68         serio_write(mx_serio, port->sel);
69         psm->out_port = port;
70         dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
71 }
72
73 static int ps2mult_serio_write(struct serio *serio, unsigned char data)
74 {
75         struct serio *mx_port = serio->parent;
76         struct ps2mult *psm = serio_get_drvdata(mx_port);
77         struct ps2mult_port *port = serio->port_data;
78         bool need_escape;
79         unsigned long flags;
80
81         spin_lock_irqsave(&psm->lock, flags);
82
83         if (psm->out_port != port)
84                 ps2mult_select_port(psm, port);
85
86         need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
87
88         dev_dbg(&serio->dev,
89                 "write: %s%02x\n", need_escape ? "ESC " : "", data);
90
91         if (need_escape)
92                 serio_write(mx_port, PS2MULT_ESCAPE);
93
94         serio_write(mx_port, data);
95
96         spin_unlock_irqrestore(&psm->lock, flags);
97
98         return 0;
99 }
100
101 static int ps2mult_serio_start(struct serio *serio)
102 {
103         struct ps2mult *psm = serio_get_drvdata(serio->parent);
104         struct ps2mult_port *port = serio->port_data;
105         unsigned long flags;
106
107         spin_lock_irqsave(&psm->lock, flags);
108         port->registered = true;
109         spin_unlock_irqrestore(&psm->lock, flags);
110
111         return 0;
112 }
113
114 static void ps2mult_serio_stop(struct serio *serio)
115 {
116         struct ps2mult *psm = serio_get_drvdata(serio->parent);
117         struct ps2mult_port *port = serio->port_data;
118         unsigned long flags;
119
120         spin_lock_irqsave(&psm->lock, flags);
121         port->registered = false;
122         spin_unlock_irqrestore(&psm->lock, flags);
123 }
124
125 static int ps2mult_create_port(struct ps2mult *psm, int i)
126 {
127         struct serio *mx_serio = psm->mx_serio;
128         struct serio *serio;
129
130         serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
131         if (!serio)
132                 return -ENOMEM;
133
134         strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
135         snprintf(serio->phys, sizeof(serio->phys),
136                  "%s/port%d", mx_serio->phys, i);
137         serio->id.type = SERIO_8042;
138         serio->write = ps2mult_serio_write;
139         serio->start = ps2mult_serio_start;
140         serio->stop = ps2mult_serio_stop;
141         serio->parent = psm->mx_serio;
142         serio->port_data = &psm->ports[i];
143
144         psm->ports[i].serio = serio;
145
146         return 0;
147 }
148
149 static void ps2mult_reset(struct ps2mult *psm)
150 {
151         unsigned long flags;
152
153         spin_lock_irqsave(&psm->lock, flags);
154
155         serio_write(psm->mx_serio, PS2MULT_SESSION_END);
156         serio_write(psm->mx_serio, PS2MULT_SESSION_START);
157
158         ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
159
160         spin_unlock_irqrestore(&psm->lock, flags);
161 }
162
163 static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
164 {
165         struct ps2mult *psm;
166         int i;
167         int error;
168
169         if (!serio->write)
170                 return -EINVAL;
171
172         psm = kzalloc(sizeof(*psm), GFP_KERNEL);
173         if (!psm)
174                 return -ENOMEM;
175
176         spin_lock_init(&psm->lock);
177         psm->mx_serio = serio;
178
179         for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
180                 psm->ports[i].sel = ps2mult_controls[i];
181                 error = ps2mult_create_port(psm, i);
182                 if (error)
183                         goto err_out;
184         }
185
186         psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
187
188         serio_set_drvdata(serio, psm);
189         error = serio_open(serio, drv);
190         if (error)
191                 goto err_out;
192
193         ps2mult_reset(psm);
194
195         for (i = 0; i <  PS2MULT_NUM_PORTS; i++) {
196                 struct serio *s = psm->ports[i].serio;
197
198                 dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
199                 serio_register_port(s);
200         }
201
202         return 0;
203
204 err_out:
205         while (--i >= 0)
206                 kfree(psm->ports[i].serio);
207         kfree(psm);
208         return error;
209 }
210
211 static void ps2mult_disconnect(struct serio *serio)
212 {
213         struct ps2mult *psm = serio_get_drvdata(serio);
214
215         /* Note that serio core already take care of children ports */
216         serio_write(serio, PS2MULT_SESSION_END);
217         serio_close(serio);
218         kfree(psm);
219
220         serio_set_drvdata(serio, NULL);
221 }
222
223 static int ps2mult_reconnect(struct serio *serio)
224 {
225         struct ps2mult *psm = serio_get_drvdata(serio);
226
227         ps2mult_reset(psm);
228
229         return 0;
230 }
231
232 static irqreturn_t ps2mult_interrupt(struct serio *serio,
233                                      unsigned char data, unsigned int dfl)
234 {
235         struct ps2mult *psm = serio_get_drvdata(serio);
236         struct ps2mult_port *in_port;
237         unsigned long flags;
238
239         dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
240
241         spin_lock_irqsave(&psm->lock, flags);
242
243         if (psm->escape) {
244                 psm->escape = false;
245                 in_port = psm->in_port;
246                 if (in_port->registered)
247                         serio_interrupt(in_port->serio, data, dfl);
248                 goto out;
249         }
250
251         switch (data) {
252         case PS2MULT_ESCAPE:
253                 dev_dbg(&serio->dev, "ESCAPE\n");
254                 psm->escape = true;
255                 break;
256
257         case PS2MULT_BSYNC:
258                 dev_dbg(&serio->dev, "BSYNC\n");
259                 psm->in_port = psm->out_port;
260                 break;
261
262         case PS2MULT_SESSION_START:
263                 dev_dbg(&serio->dev, "SS\n");
264                 break;
265
266         case PS2MULT_SESSION_END:
267                 dev_dbg(&serio->dev, "SE\n");
268                 break;
269
270         case PS2MULT_KB_SELECTOR:
271                 dev_dbg(&serio->dev, "KB\n");
272                 psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
273                 break;
274
275         case PS2MULT_MS_SELECTOR:
276                 dev_dbg(&serio->dev, "MS\n");
277                 psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
278                 break;
279
280         default:
281                 in_port = psm->in_port;
282                 if (in_port->registered)
283                         serio_interrupt(in_port->serio, data, dfl);
284                 break;
285         }
286
287  out:
288         spin_unlock_irqrestore(&psm->lock, flags);
289         return IRQ_HANDLED;
290 }
291
292 static struct serio_driver ps2mult_drv = {
293         .driver         = {
294                 .name   = "ps2mult",
295         },
296         .description    = "TQC PS/2 Multiplexer driver",
297         .id_table       = ps2mult_serio_ids,
298         .interrupt      = ps2mult_interrupt,
299         .connect        = ps2mult_connect,
300         .disconnect     = ps2mult_disconnect,
301         .reconnect      = ps2mult_reconnect,
302 };
303
304 module_serio_driver(ps2mult_drv);