cregit-Linux how code gets into the kernel

Release 4.11 drivers/input/serio/ps2mult.c

/*
 * TQC PS/2 Multiplexer driver
 *
 * Copyright (C) 2010 Dmitry Eremin-Solenikov
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */


#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/serio.h>

MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
MODULE_LICENSE("GPL");


#define PS2MULT_KB_SELECTOR		0xA0

#define PS2MULT_MS_SELECTOR		0xA1

#define PS2MULT_ESCAPE			0x7D

#define PS2MULT_BSYNC			0x7E

#define PS2MULT_SESSION_START		0x55

#define PS2MULT_SESSION_END		0x56


struct ps2mult_port {
	
struct serio *serio;
	
unsigned char sel;
	
bool registered;
};


#define PS2MULT_NUM_PORTS	2

#define PS2MULT_KBD_PORT	0

#define PS2MULT_MOUSE_PORT	1


struct ps2mult {
	
struct serio *mx_serio;
	
struct ps2mult_port ports[PS2MULT_NUM_PORTS];

	
spinlock_t lock;
	
struct ps2mult_port *in_port;
	
struct ps2mult_port *out_port;
	
bool escape;
};

/* First MUST come PS2MULT_NUM_PORTS selectors */

static const unsigned char ps2mult_controls[] = {
	PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
	PS2MULT_ESCAPE, PS2MULT_BSYNC,
	PS2MULT_SESSION_START, PS2MULT_SESSION_END,
};


static const struct serio_device_id ps2mult_serio_ids[] = {
	{
		.type	= SERIO_RS232,
		.proto	= SERIO_PS2MULT,
		.id	= SERIO_ANY,
		.extra	= SERIO_ANY,
        },
	{ 0 }
};

MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);


static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port) { struct serio *mx_serio = psm->mx_serio; serio_write(mx_serio, port->sel); psm->out_port = port; dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov54100.00%1100.00%
Total54100.00%1100.00%


static int ps2mult_serio_write(struct serio *serio, unsigned char data) { struct serio *mx_port = serio->parent; struct ps2mult *psm = serio_get_drvdata(mx_port); struct ps2mult_port *port = serio->port_data; bool need_escape; unsigned long flags; spin_lock_irqsave(&psm->lock, flags); if (psm->out_port != port) ps2mult_select_port(psm, port); need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls)); dev_dbg(&serio->dev, "write: %s%02x\n", need_escape ? "ESC " : "", data); if (need_escape) serio_write(mx_port, PS2MULT_ESCAPE); serio_write(mx_port, data); spin_unlock_irqrestore(&psm->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov138100.00%1100.00%
Total138100.00%1100.00%


static int ps2mult_serio_start(struct serio *serio) { struct ps2mult *psm = serio_get_drvdata(serio->parent); struct ps2mult_port *port = serio->port_data; unsigned long flags; spin_lock_irqsave(&psm->lock, flags); port->registered = true; spin_unlock_irqrestore(&psm->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov65100.00%1100.00%
Total65100.00%1100.00%


static void ps2mult_serio_stop(struct serio *serio) { struct ps2mult *psm = serio_get_drvdata(serio->parent); struct ps2mult_port *port = serio->port_data; unsigned long flags; spin_lock_irqsave(&psm->lock, flags); port->registered = false; spin_unlock_irqrestore(&psm->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov62100.00%1100.00%
Total62100.00%1100.00%


static int ps2mult_create_port(struct ps2mult *psm, int i) { struct serio *mx_serio = psm->mx_serio; struct serio *serio; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!serio) return -ENOMEM; strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name)); snprintf(serio->phys, sizeof(serio->phys), "%s/port%d", mx_serio->phys, i); serio->id.type = SERIO_8042; serio->write = ps2mult_serio_write; serio->start = ps2mult_serio_start; serio->stop = ps2mult_serio_stop; serio->parent = psm->mx_serio; serio->port_data = &psm->ports[i]; psm->ports[i].serio = serio; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov148100.00%1100.00%
Total148100.00%1100.00%


static void ps2mult_reset(struct ps2mult *psm) { unsigned long flags; spin_lock_irqsave(&psm->lock, flags); serio_write(psm->mx_serio, PS2MULT_SESSION_END); serio_write(psm->mx_serio, PS2MULT_SESSION_START); ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]); spin_unlock_irqrestore(&psm->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov66100.00%1100.00%
Total66100.00%1100.00%


static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) { struct ps2mult *psm; int i; int error; if (!serio->write) return -EINVAL; psm = kzalloc(sizeof(*psm), GFP_KERNEL); if (!psm) return -ENOMEM; spin_lock_init(&psm->lock); psm->mx_serio = serio; for (i = 0; i < PS2MULT_NUM_PORTS; i++) { psm->ports[i].sel = ps2mult_controls[i]; error = ps2mult_create_port(psm, i); if (error) goto err_out; } psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT]; serio_set_drvdata(serio, psm); error = serio_open(serio, drv); if (error) goto err_out; ps2mult_reset(psm); for (i = 0; i < PS2MULT_NUM_PORTS; i++) { struct serio *s = psm->ports[i].serio; dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys); serio_register_port(s); } return 0; err_out: while (--i >= 0) kfree(psm->ports[i].serio); kfree(psm); return error; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov24699.60%150.00%
Axel Lin10.40%150.00%
Total247100.00%2100.00%


static void ps2mult_disconnect(struct serio *serio) { struct ps2mult *psm = serio_get_drvdata(serio); /* Note that serio core already take care of children ports */ serio_write(serio, PS2MULT_SESSION_END); serio_close(serio); kfree(psm); serio_set_drvdata(serio, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov46100.00%1100.00%
Total46100.00%1100.00%


static int ps2mult_reconnect(struct serio *serio) { struct ps2mult *psm = serio_get_drvdata(serio); ps2mult_reset(psm); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov29100.00%1100.00%
Total29100.00%1100.00%


static irqreturn_t ps2mult_interrupt(struct serio *serio, unsigned char data, unsigned int dfl) { struct ps2mult *psm = serio_get_drvdata(serio); struct ps2mult_port *in_port; unsigned long flags; dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl); spin_lock_irqsave(&psm->lock, flags); if (psm->escape) { psm->escape = false; in_port = psm->in_port; if (in_port->registered) serio_interrupt(in_port->serio, data, dfl); goto out; } switch (data) { case PS2MULT_ESCAPE: dev_dbg(&serio->dev, "ESCAPE\n"); psm->escape = true; break; case PS2MULT_BSYNC: dev_dbg(&serio->dev, "BSYNC\n"); psm->in_port = psm->out_port; break; case PS2MULT_SESSION_START: dev_dbg(&serio->dev, "SS\n"); break; case PS2MULT_SESSION_END: dev_dbg(&serio->dev, "SE\n"); break; case PS2MULT_KB_SELECTOR: dev_dbg(&serio->dev, "KB\n"); psm->in_port = &psm->ports[PS2MULT_KBD_PORT]; break; case PS2MULT_MS_SELECTOR: dev_dbg(&serio->dev, "MS\n"); psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT]; break; default: in_port = psm->in_port; if (in_port->registered) serio_interrupt(in_port->serio, data, dfl); break; } out: spin_unlock_irqrestore(&psm->lock, flags); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov270100.00%1100.00%
Total270100.00%1100.00%

static struct serio_driver ps2mult_drv = { .driver = { .name = "ps2mult", }, .description = "TQC PS/2 Multiplexer driver", .id_table = ps2mult_serio_ids, .interrupt = ps2mult_interrupt, .connect = ps2mult_connect, .disconnect = ps2mult_disconnect, .reconnect = ps2mult_reconnect, }; module_serio_driver(ps2mult_drv);

Overall Contributors

PersonTokensPropCommitsCommitProp
Dmitry Baryshkov135299.78%133.33%
Axel Lin30.22%266.67%
Total1355100.00%3100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.