From patchwork Thu Aug 12 09:31:50 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Baryshkov X-Patchwork-Id: 119220 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o7C9TXkW011546 for ; Thu, 12 Aug 2010 09:32:10 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753934Ab0HLJcK (ORCPT ); Thu, 12 Aug 2010 05:32:10 -0400 Received: from mail-ww0-f44.google.com ([74.125.82.44]:62580 "EHLO mail-ww0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753000Ab0HLJcJ (ORCPT ); Thu, 12 Aug 2010 05:32:09 -0400 Received: by mail-ww0-f44.google.com with SMTP id 40so1297310wwj.1 for ; Thu, 12 Aug 2010 02:32:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references; bh=f/WV6Sjl0tFOx7effZRJKrACyv44BgGfgrQFSVvNACQ=; b=FWzVXRomLiGb853HPGkUGYJBtuB855ElG52jg8xt5nCKlxU8flfLflLKcf/2udLeHy NOHSrQkqhxgcp21OciCNlbrqw4yvfyB+iDhwtKJ48pTXsvOgHY+ZL5E1W8e4nckizze0 dr4iYsWOX2LvXGuU+0N02wcMID5Nj/Nb87TV0= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=toqKh5a4sjwOhpA0u+rpGF994/C1lPcg4njmij9SeRqG9UByvpp06tz5H1eqD1kndn rBfmLvX02zwD+lOGJq67weiT/QDjhviVnQqqPC2vzL2tN07IHBzoCb3+PfT76CAkvoa+ ksetZpWGsu2aadJPQFZs1+HAiUmlbuKGAwoBg= Received: by 10.216.179.20 with SMTP id g20mr17804006wem.45.1281605527898; Thu, 12 Aug 2010 02:32:07 -0700 (PDT) Received: from doriath.ww600.siemens.net ([91.213.169.4]) by mx.google.com with ESMTPS id e8sm678968wej.22.2010.08.12.02.32.06 (version=SSLv3 cipher=RC4-MD5); Thu, 12 Aug 2010 02:32:07 -0700 (PDT) From: Dmitry Eremin-Solenikov To: linux-input@vger.kernel.org Cc: Dmitry Torokhov Subject: [PATCH 1/4] serio: support multiple child devices per single parent Date: Thu, 12 Aug 2010 13:31:50 +0400 Message-Id: <1281605513-11586-2-git-send-email-dbaryshkov@gmail.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1281605513-11586-1-git-send-email-dbaryshkov@gmail.com> References: <1281605513-11586-1-git-send-email-dbaryshkov@gmail.com> 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 (demeter.kernel.org [140.211.167.41]); Thu, 12 Aug 2010 09:32:11 +0000 (UTC) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 979c502..0eeed6c 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1582,7 +1582,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co if (!new_dev) return -ENOMEM; - while (serio->child) { + while (!list_empty(&serio->children)) { if (++retry > 3) { printk(KERN_WARNING "psmouse: failed to destroy child port, " diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 705589d..9295ad0 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -315,7 +315,9 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet static void synaptics_pt_activate(struct psmouse *psmouse) { - struct serio *ptport = psmouse->ps2dev.serio->child; + struct serio *ptport = + list_first_entry(&psmouse->ps2dev.serio->children, + struct serio, child_list); struct psmouse *child = serio_get_drvdata(ptport); struct synaptics_data *priv = psmouse->private; @@ -577,8 +579,11 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) priv->pkt_type = synaptics_detect_pkt_type(psmouse); if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { - if (psmouse->ps2dev.serio->child) - synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet); + if (!list_empty(&psmouse->ps2dev.serio->children)) + synaptics_pass_pt_packet( + list_first_entry(&psmouse->ps2dev.serio->children, + struct serio, child_list), + psmouse->packet); } else synaptics_process_packet(psmouse); diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index c3b626e..323b8cd 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -312,12 +312,9 @@ static void serio_handle_event(void) * Remove all events that have been submitted for a given * object, be it serio port or driver. */ -static void serio_remove_pending_events(void *object) +static void __serio_remove_pending_events(void *object) { struct serio_event *event, *next; - unsigned long flags; - - spin_lock_irqsave(&serio_event_lock, flags); list_for_each_entry_safe(event, next, &serio_event_list, node) { if (event->object == object) { @@ -325,38 +322,41 @@ static void serio_remove_pending_events(void *object) serio_free_event(event); } } +} + +static void serio_remove_pending_events(void *object) +{ + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + __serio_remove_pending_events(object); spin_unlock_irqrestore(&serio_event_lock, flags); } /* * Destroy child serio port (if any) that has not been fully registered yet. - * - * Note that we rely on the fact that port can have only one child and therefore - * only one child registration request can be pending. Additionally, children - * are registered by driver's connect() handler so there can't be a grandchild - * pending registration together with a child. */ -static struct serio *serio_get_pending_child(struct serio *parent) +static void serio_drop_pending_children(struct serio *parent) { - struct serio_event *event; - struct serio *serio, *child = NULL; + struct serio_event *event, *temp; + struct serio *serio; unsigned long flags; spin_lock_irqsave(&serio_event_lock, flags); - list_for_each_entry(event, &serio_event_list, node) { + list_for_each_entry_safe(event, temp, &serio_event_list, node) { if (event->type == SERIO_REGISTER_PORT) { serio = event->object; if (serio->parent == parent) { - child = serio; - break; + __serio_remove_pending_events(serio); + put_device(&serio->dev); } } } spin_unlock_irqrestore(&serio_event_lock, flags); - return child; } static int serio_thread(void *nothing) @@ -516,6 +516,8 @@ static void serio_init_port(struct serio *serio) __module_get(THIS_MODULE); INIT_LIST_HEAD(&serio->node); + INIT_LIST_HEAD(&serio->children); + INIT_LIST_HEAD(&serio->child_list); spin_lock_init(&serio->lock); mutex_init(&serio->drv_mutex); device_initialize(&serio->dev); @@ -542,7 +544,7 @@ static void serio_add_port(struct serio *serio) if (serio->parent) { serio_pause_rx(serio->parent); - serio->parent->child = serio; + list_add_tail(&serio->child_list, &serio->parent->children); serio_continue_rx(serio->parent); } @@ -564,20 +566,14 @@ static void serio_add_port(struct serio *serio) */ static void serio_destroy_port(struct serio *serio) { - struct serio *child; - - child = serio_get_pending_child(serio); - if (child) { - serio_remove_pending_events(child); - put_device(&child->dev); - } + serio_drop_pending_children(serio); if (serio->stop) serio->stop(serio); if (serio->parent) { serio_pause_rx(serio->parent); - serio->parent->child = NULL; + list_del(&serio->child_list); serio_continue_rx(serio->parent); serio->parent = NULL; } @@ -613,13 +609,16 @@ static int serio_reconnect_port(struct serio *serio) */ static void serio_reconnect_chain(struct serio *serio) { - do { - if (serio_reconnect_port(serio)) { - /* Ok, old children are now gone, we are done */ - break; - } - serio = serio->child; - } while (serio); + struct serio *child; + + if (serio_reconnect_port(serio)) { + /* Ok, old children are now gone, we are done */ + return; + } + + list_for_each_entry(child, &serio->children, child_list) { + serio_reconnect_chain(child); + } } /* @@ -628,23 +627,13 @@ static void serio_reconnect_chain(struct serio *serio) */ static void serio_disconnect_port(struct serio *serio) { - struct serio *s, *parent; - - if (serio->child) { - /* - * Children ports should be disconnected and destroyed - * first, staring with the leaf one, since we don't want - * to do recursion - */ - for (s = serio; s->child; s = s->child) - /* empty */; - - do { - parent = s->parent; - - device_release_driver(&s->dev); - serio_destroy_port(s); - } while ((s = parent) != serio); + struct serio *s, *temp; + + list_for_each_entry_safe(s, temp, &serio->children, child_list) { + + serio_disconnect_port(s); + + serio_destroy_port(s); } /* @@ -689,14 +678,15 @@ void serio_unregister_port(struct serio *serio) EXPORT_SYMBOL(serio_unregister_port); /* - * Safely unregisters child port if one is present. + * Safely unregisters child ports if any is present. */ void serio_unregister_child_port(struct serio *serio) { + struct serio *s, *temp; mutex_lock(&serio_mutex); - if (serio->child) { - serio_disconnect_port(serio->child); - serio_destroy_port(serio->child); + list_for_each_entry_safe(s, temp, &serio->children, child_list) { + serio_disconnect_port(s); + serio_destroy_port(s); } mutex_unlock(&serio_mutex); } diff --git a/include/linux/serio.h b/include/linux/serio.h index b555256..d2ae60d 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -41,7 +41,9 @@ struct serio { int (*start)(struct serio *); void (*stop)(struct serio *); - struct serio *parent, *child; + struct serio *parent; + struct list_head child_list; + struct list_head children; unsigned int depth; /* level of nesting in serio hierarchy */ struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock and serio->sem */