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