Linux Audio

Check our new training course

Loading...
v6.8
  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
 14MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
 15MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
 16MODULE_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
 25struct 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
 35struct 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 */
 46static 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
 52static 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
 62MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
 63
 64static 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
 73static 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
101static 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
114static 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
125static 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	strscpy(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
149static 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
163static 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
204err_out:
205	while (--i >= 0)
206		kfree(psm->ports[i].serio);
207	kfree(psm);
208	return error;
209}
210
211static 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
223static 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
232static 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
292static 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
304module_serio_driver(ps2mult_drv);
v6.2
  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
 14MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
 15MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
 16MODULE_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
 25struct 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
 35struct 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 */
 46static 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
 52static 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
 62MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
 63
 64static 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
 73static 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
101static 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
114static 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
125static 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	strscpy(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
149static 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
163static 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
204err_out:
205	while (--i >= 0)
206		kfree(psm->ports[i].serio);
207	kfree(psm);
208	return error;
209}
210
211static 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
223static 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
232static 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
292static 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
304module_serio_driver(ps2mult_drv);