From patchwork Thu Sep 30 06:25:48 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Torokhov X-Patchwork-Id: 218962 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o8U6QNBM030835 for ; Thu, 30 Sep 2010 06:26:24 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754481Ab0I3GZ4 (ORCPT ); Thu, 30 Sep 2010 02:25:56 -0400 Received: from mail-iw0-f174.google.com ([209.85.214.174]:45968 "EHLO mail-iw0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754234Ab0I3GZz (ORCPT ); Thu, 30 Sep 2010 02:25:55 -0400 Received: by iwn5 with SMTP id 5so2028274iwn.19 for ; Wed, 29 Sep 2010 23:25:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:date:from:to:cc:subject :message-id:references:mime-version:content-type:content-disposition :in-reply-to:user-agent; bh=DNlrH+Hm5CxAeYKEPN7/Zx6Uk5rDqtXqyrBW7uylXQ0=; b=wc1C6Zxf9BbnLLfgBLXAlk8Xwh0GEOQwKh4wjx2RVtv92w4/jeXIldRPw4czxTUDXQ GAss6O63H/kA7d8/m82PZvqee6ps8MxBwi1jrwemvq6L1Kxh1bwlZ4M/s7qlKJPgEMhP bWbMO42Smij3jnYItlwM8rcHmwMN+jA2XCEYM= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=date:from:to:cc:subject:message-id:references:mime-version :content-type:content-disposition:in-reply-to:user-agent; b=guOm1Eqxafqnx8O72kxqpUFjG0/86/T4dqwisblbfit7uujzOnZuN2jRFd3tNh1+3J mcxWK4eKarhrQKujspUkZFyP/Kpxj8hxFqQgaOaoLvT0SKOEE2dGa1LzBuu6EiDbn+KK sCx5u7FXEkPtlmYl5CxwNcJwjb0LF2q3uzkQw= Received: by 10.231.193.135 with SMTP id du7mr2007578ibb.176.1285827954944; Wed, 29 Sep 2010 23:25:54 -0700 (PDT) Received: from mailhub.coreip.homeip.net (c-24-6-153-206.hsd1.ca.comcast.net [24.6.153.206]) by mx.google.com with ESMTPS id g31sm9852654ibh.22.2010.09.29.23.25.51 (version=TLSv1/SSLv3 cipher=RC4-MD5); Wed, 29 Sep 2010 23:25:52 -0700 (PDT) Date: Wed, 29 Sep 2010 23:25:48 -0700 From: Dmitry Torokhov To: Dmitry Eremin-Solenikov Cc: linux-input@vger.kernel.org Subject: Re: [PATCH v3] serio: add support for PS2Mult multiplexer protocol Message-ID: <20100930062547.GF5260@core.coreip.homeip.net> References: <1285260285-660-1-git-send-email-dbaryshkov@gmail.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.21 (2010-09-15) Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Thu, 30 Sep 2010 06:26:24 +0000 (UTC) diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c index bd45e76..3664398 100644 --- a/drivers/input/serio/ps2mult.c +++ b/drivers/input/serio/ps2mult.c @@ -31,25 +31,27 @@ struct ps2mult_port { }; #define PS2MULT_NUM_PORTS 2 +#define PS2MULT_KBD_PORT 0 +#define PS2MULT_MOUSE_PORT 1 struct ps2mult { - struct serio *serio; + struct serio *mx_serio; struct ps2mult_port ports[PS2MULT_NUM_PORTS]; spinlock_t lock; - struct serio *in_serio; - struct serio *out_serio; + struct ps2mult_port *in_port; + struct ps2mult_port *out_port; bool escape; }; -/* First MUST com PS2MULT_NUM_PORTS selectors */ -static unsigned char ps2mult_controls[] = { +/* 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 struct serio_device_id ps2mult_serio_ids[] = { +static const struct serio_device_id ps2mult_serio_ids[] = { { .type = SERIO_RS232, .proto = SERIO_PS2MULT, @@ -61,113 +63,112 @@ static struct serio_device_id ps2mult_serio_ids[] = { MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids); -static int ps2mult_serio_write(struct serio *serio, unsigned char data) +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); +} +static int ps2mult_serio_write(struct serio *serio, unsigned char data) { - struct ps2mult *psm = serio_get_drvdata(serio->parent); - struct ps2mult_port *psmp = serio->port_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_serio != serio) { - psm->serio->write(psm->serio, psmp->sel); - psm->out_serio = serio; - dev_dbg(&serio->dev, "switched to sel %02x\n", psmp->sel); - } + + 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); + dev_dbg(&serio->dev, + "write: %s%02x\n", need_escape ? "ESC " : "", data); if (need_escape) - psm->serio->write(psm->serio, PS2MULT_ESCAPE); - psm->serio->write(psm->serio, data); + serio_write(mx_port, PS2MULT_ESCAPE); + + serio_write(mx_port, data); spin_unlock_irqrestore(&psm->lock, flags); return 0; } -static void ps2mult_serio_stop(struct serio *serio) +static int ps2mult_serio_start(struct serio *serio) { struct ps2mult *psm = serio_get_drvdata(serio->parent); - struct ps2mult_port *psmp = serio->port_data; - + struct ps2mult_port *port = serio->port_data; unsigned long flags; spin_lock_irqsave(&psm->lock, flags); + port->serio = serio; + spin_unlock_irqrestore(&psm->lock, flags); - psmp->serio = NULL; - if (psm->in_serio == serio) - psm->in_serio = NULL; - if (psm->out_serio == serio) - psm->out_serio = NULL; + return 0; +} - spin_unlock_irqrestore(&psm->lock, flags); +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->serio = NULL; + spin_unlock_irqrestore(&psm->lock, flags); } static int ps2mult_create_port(struct ps2mult *psm, int i) { - struct serio *serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + 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", psm->serio->phys, i); + "%s/port%d", mx_serio->phys, i); serio->id.type = SERIO_8042; serio->id.proto = SERIO_PS2MULT; serio->write = ps2mult_serio_write; + serio->start = ps2mult_serio_start; serio->stop = ps2mult_serio_stop; - serio->parent = psm->serio; - + serio->parent = psm->mx_serio; serio->port_data = &psm->ports[i]; - psm->ports[i].serio = serio; - psm->ports[i].sel = ps2mult_controls[i]; - serio_register_port(serio); - dev_info(&serio->dev, "%s port at %s\n", serio->name, psm->serio->phys); + dev_info(&serio->dev, "%s port at %s\n", serio->name, mx_serio->phys); return 0; } -static int ps2mult_reconnect(struct serio *serio) +static void ps2mult_reset(struct ps2mult *psm) { - struct ps2mult *psm = serio_get_drvdata(serio); unsigned long flags; - serio->write(serio, PS2MULT_SESSION_END); - serio->write(serio, PS2MULT_SESSION_START); - spin_lock_irqsave(&psm->lock, flags); - psm->out_serio = psm->ports[0].serio; - serio->write(serio, psm->ports[0].sel); - spin_unlock_irqrestore(&psm->lock, flags); - - return 0; -} - -static void ps2mult_disconnect(struct serio *serio) -{ - struct ps2mult *psm = serio_get_drvdata(serio); - serio->write(serio, PS2MULT_SESSION_END); + serio_write(psm->mx_serio, PS2MULT_SESSION_END); + serio_write(psm->mx_serio, PS2MULT_SESSION_START); - serio_close(serio); - serio_set_drvdata(serio, NULL); + ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]); - kfree(psm); + spin_unlock_irqrestore(&psm->lock, flags); } static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) { struct ps2mult *psm; int i; - int rc; + int error; if (!serio->write) return -EINVAL; @@ -177,83 +178,113 @@ static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) return -ENOMEM; spin_lock_init(&psm->lock); - psm->serio = serio; + psm->mx_serio = serio; - serio_set_drvdata(serio, psm); - - for (i = 0; i < PS2MULT_NUM_PORTS; i++) { - rc = ps2mult_create_port(psm, i); - if (rc) + 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; } - serio_open(serio, drv); + psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT]; - rc = ps2mult_reconnect(serio); - if (rc) + 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++) + serio_register_port(psm->ports[i].serio); + return 0; err_out: - ps2mult_disconnect(serio); + while (--i >= 0) + kfree(psm->ports[i].serio); + kfree(serio); + return error; +} + +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); - return rc; + serio_set_drvdata(serio, NULL); +} + +static int ps2mult_reconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + ps2mult_reset(psm); + + return 0; } -static irqreturn_t ps2mult_interrupt(struct serio *serio, unsigned char data, - unsigned int flags) +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); - dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags); if (psm->escape) { - spin_lock(&psm->lock); - if (psm->in_serio) - serio_interrupt(psm->in_serio, data, flags); - spin_unlock(&psm->lock); - - psm->escape = 0; - } else - switch (data) { - case PS2MULT_ESCAPE: - dev_dbg(&serio->dev, "ESCAPE\n"); - psm->escape = 1; - break; - case PS2MULT_BSYNC: - dev_dbg(&serio->dev, "BSYNC\n"); - spin_lock(&psm->lock); - psm->in_serio = psm->out_serio; - spin_unlock(&psm->lock); - 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"); - - spin_lock(&psm->lock); - psm->in_serio = psm->ports[0].serio; - spin_unlock(&psm->lock); - - break; - case PS2MULT_MS_SELECTOR: - dev_dbg(&serio->dev, "MS\n"); - - spin_lock(&psm->lock); - psm->in_serio = psm->ports[1].serio; - spin_unlock(&psm->lock); - - break; - default: - spin_lock(&psm->lock); - if (psm->in_serio) - serio_interrupt(psm->in_serio, data, flags); - spin_unlock(&psm->lock); - } + psm->escape = false; + in_port = psm->in_port; + if (in_port->serio) + 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->serio) + serio_interrupt(in_port->serio, data, dfl); + } + + out: + spin_unlock_irqrestore(&psm->lock, flags); return IRQ_HANDLED; }