Add linux-next specific files for 20110831
[linux-2.6/next.git] / drivers / input / serio / ps2mult.c
blob15aa81c9f1fb0215d1552bd36d22c5adf10d62c4
1 /*
2 * TQC PS/2 Multiplexer driver
4 * Copyright (C) 2010 Dmitry Eremin-Solenikov
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 */
12 #include <linux/kernel.h>
13 #include <linux/slab.h>
14 #include <linux/module.h>
15 #include <linux/serio.h>
17 MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
18 MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
19 MODULE_LICENSE("GPL");
21 #define PS2MULT_KB_SELECTOR 0xA0
22 #define PS2MULT_MS_SELECTOR 0xA1
23 #define PS2MULT_ESCAPE 0x7D
24 #define PS2MULT_BSYNC 0x7E
25 #define PS2MULT_SESSION_START 0x55
26 #define PS2MULT_SESSION_END 0x56
28 struct ps2mult_port {
29 struct serio *serio;
30 unsigned char sel;
31 bool registered;
34 #define PS2MULT_NUM_PORTS 2
35 #define PS2MULT_KBD_PORT 0
36 #define PS2MULT_MOUSE_PORT 1
38 struct ps2mult {
39 struct serio *mx_serio;
40 struct ps2mult_port ports[PS2MULT_NUM_PORTS];
42 spinlock_t lock;
43 struct ps2mult_port *in_port;
44 struct ps2mult_port *out_port;
45 bool escape;
48 /* First MUST come PS2MULT_NUM_PORTS selectors */
49 static const unsigned char ps2mult_controls[] = {
50 PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
51 PS2MULT_ESCAPE, PS2MULT_BSYNC,
52 PS2MULT_SESSION_START, PS2MULT_SESSION_END,
55 static const struct serio_device_id ps2mult_serio_ids[] = {
57 .type = SERIO_RS232,
58 .proto = SERIO_PS2MULT,
59 .id = SERIO_ANY,
60 .extra = SERIO_ANY,
62 { 0 }
65 MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
67 static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
69 struct serio *mx_serio = psm->mx_serio;
71 serio_write(mx_serio, port->sel);
72 psm->out_port = port;
73 dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
76 static int ps2mult_serio_write(struct serio *serio, unsigned char data)
78 struct serio *mx_port = serio->parent;
79 struct ps2mult *psm = serio_get_drvdata(mx_port);
80 struct ps2mult_port *port = serio->port_data;
81 bool need_escape;
82 unsigned long flags;
84 spin_lock_irqsave(&psm->lock, flags);
86 if (psm->out_port != port)
87 ps2mult_select_port(psm, port);
89 need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
91 dev_dbg(&serio->dev,
92 "write: %s%02x\n", need_escape ? "ESC " : "", data);
94 if (need_escape)
95 serio_write(mx_port, PS2MULT_ESCAPE);
97 serio_write(mx_port, data);
99 spin_unlock_irqrestore(&psm->lock, flags);
101 return 0;
104 static int ps2mult_serio_start(struct serio *serio)
106 struct ps2mult *psm = serio_get_drvdata(serio->parent);
107 struct ps2mult_port *port = serio->port_data;
108 unsigned long flags;
110 spin_lock_irqsave(&psm->lock, flags);
111 port->registered = true;
112 spin_unlock_irqrestore(&psm->lock, flags);
114 return 0;
117 static void ps2mult_serio_stop(struct serio *serio)
119 struct ps2mult *psm = serio_get_drvdata(serio->parent);
120 struct ps2mult_port *port = serio->port_data;
121 unsigned long flags;
123 spin_lock_irqsave(&psm->lock, flags);
124 port->registered = false;
125 spin_unlock_irqrestore(&psm->lock, flags);
128 static int ps2mult_create_port(struct ps2mult *psm, int i)
130 struct serio *mx_serio = psm->mx_serio;
131 struct serio *serio;
133 serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
134 if (!serio)
135 return -ENOMEM;
137 strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
138 snprintf(serio->phys, sizeof(serio->phys),
139 "%s/port%d", mx_serio->phys, i);
140 serio->id.type = SERIO_8042;
141 serio->write = ps2mult_serio_write;
142 serio->start = ps2mult_serio_start;
143 serio->stop = ps2mult_serio_stop;
144 serio->parent = psm->mx_serio;
145 serio->port_data = &psm->ports[i];
147 psm->ports[i].serio = serio;
149 return 0;
152 static void ps2mult_reset(struct ps2mult *psm)
154 unsigned long flags;
156 spin_lock_irqsave(&psm->lock, flags);
158 serio_write(psm->mx_serio, PS2MULT_SESSION_END);
159 serio_write(psm->mx_serio, PS2MULT_SESSION_START);
161 ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
163 spin_unlock_irqrestore(&psm->lock, flags);
166 static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
168 struct ps2mult *psm;
169 int i;
170 int error;
172 if (!serio->write)
173 return -EINVAL;
175 psm = kzalloc(sizeof(*psm), GFP_KERNEL);
176 if (!psm)
177 return -ENOMEM;
179 spin_lock_init(&psm->lock);
180 psm->mx_serio = serio;
182 for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
183 psm->ports[i].sel = ps2mult_controls[i];
184 error = ps2mult_create_port(psm, i);
185 if (error)
186 goto err_out;
189 psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
191 serio_set_drvdata(serio, psm);
192 error = serio_open(serio, drv);
193 if (error)
194 goto err_out;
196 ps2mult_reset(psm);
198 for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
199 struct serio *s = psm->ports[i].serio;
201 dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
202 serio_register_port(s);
205 return 0;
207 err_out:
208 while (--i >= 0)
209 kfree(psm->ports[i].serio);
210 kfree(psm);
211 return error;
214 static void ps2mult_disconnect(struct serio *serio)
216 struct ps2mult *psm = serio_get_drvdata(serio);
218 /* Note that serio core already take care of children ports */
219 serio_write(serio, PS2MULT_SESSION_END);
220 serio_close(serio);
221 kfree(psm);
223 serio_set_drvdata(serio, NULL);
226 static int ps2mult_reconnect(struct serio *serio)
228 struct ps2mult *psm = serio_get_drvdata(serio);
230 ps2mult_reset(psm);
232 return 0;
235 static irqreturn_t ps2mult_interrupt(struct serio *serio,
236 unsigned char data, unsigned int dfl)
238 struct ps2mult *psm = serio_get_drvdata(serio);
239 struct ps2mult_port *in_port;
240 unsigned long flags;
242 dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
244 spin_lock_irqsave(&psm->lock, flags);
246 if (psm->escape) {
247 psm->escape = false;
248 in_port = psm->in_port;
249 if (in_port->registered)
250 serio_interrupt(in_port->serio, data, dfl);
251 goto out;
254 switch (data) {
255 case PS2MULT_ESCAPE:
256 dev_dbg(&serio->dev, "ESCAPE\n");
257 psm->escape = true;
258 break;
260 case PS2MULT_BSYNC:
261 dev_dbg(&serio->dev, "BSYNC\n");
262 psm->in_port = psm->out_port;
263 break;
265 case PS2MULT_SESSION_START:
266 dev_dbg(&serio->dev, "SS\n");
267 break;
269 case PS2MULT_SESSION_END:
270 dev_dbg(&serio->dev, "SE\n");
271 break;
273 case PS2MULT_KB_SELECTOR:
274 dev_dbg(&serio->dev, "KB\n");
275 psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
276 break;
278 case PS2MULT_MS_SELECTOR:
279 dev_dbg(&serio->dev, "MS\n");
280 psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
281 break;
283 default:
284 in_port = psm->in_port;
285 if (in_port->registered)
286 serio_interrupt(in_port->serio, data, dfl);
287 break;
290 out:
291 spin_unlock_irqrestore(&psm->lock, flags);
292 return IRQ_HANDLED;
295 static struct serio_driver ps2mult_drv = {
296 .driver = {
297 .name = "ps2mult",
299 .description = "TQC PS/2 Multiplexer driver",
300 .id_table = ps2mult_serio_ids,
301 .interrupt = ps2mult_interrupt,
302 .connect = ps2mult_connect,
303 .disconnect = ps2mult_disconnect,
304 .reconnect = ps2mult_reconnect,
307 static int __init ps2mult_init(void)
309 return serio_register_driver(&ps2mult_drv);
312 static void __exit ps2mult_exit(void)
314 serio_unregister_driver(&ps2mult_drv);
317 module_init(ps2mult_init);
318 module_exit(ps2mult_exit);