diff options
author | Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | 2010-10-05 00:46:10 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-10-13 10:49:28 -0400 |
commit | 0982258264d2f615612ab957634efdeb874f47c8 (patch) | |
tree | 4667defc535f028e3ee4a11ded29b050cf5926e7 | |
parent | a8b3c0f57beaba9035e5339175628b63e551b243 (diff) |
Input: serio - support multiple child devices per single parent
Some (rare) serio devices need to have multiple serio children. One of
the examples is PS/2 multiplexer present on several TQC STKxxx boards,
which connect PS/2 keyboard and mouse to single tty port.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 4 | ||||
-rw-r--r-- | drivers/input/serio/serio.c | 124 | ||||
-rw-r--r-- | include/linux/serio.h | 4 |
3 files changed, 86 insertions, 46 deletions
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 73a7af2542a8..cd9d0c97e429 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c | |||
@@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co | |||
1584 | if (!new_dev) | 1584 | if (!new_dev) |
1585 | return -ENOMEM; | 1585 | return -ENOMEM; |
1586 | 1586 | ||
1587 | while (serio->child) { | 1587 | while (!list_empty(&serio->children)) { |
1588 | if (++retry > 3) { | 1588 | if (++retry > 3) { |
1589 | printk(KERN_WARNING | 1589 | printk(KERN_WARNING |
1590 | "psmouse: failed to destroy child port, " | 1590 | "psmouse: failed to destroy children ports, " |
1591 | "protocol change aborted.\n"); | 1591 | "protocol change aborted.\n"); |
1592 | input_free_device(new_dev); | 1592 | input_free_device(new_dev); |
1593 | return -EIO; | 1593 | return -EIO; |
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 8a426375fcb3..405bf214527c 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c | |||
@@ -55,7 +55,7 @@ static struct bus_type serio_bus; | |||
55 | static void serio_add_port(struct serio *serio); | 55 | static void serio_add_port(struct serio *serio); |
56 | static int serio_reconnect_port(struct serio *serio); | 56 | static int serio_reconnect_port(struct serio *serio); |
57 | static void serio_disconnect_port(struct serio *serio); | 57 | static void serio_disconnect_port(struct serio *serio); |
58 | static void serio_reconnect_chain(struct serio *serio); | 58 | static void serio_reconnect_subtree(struct serio *serio); |
59 | static void serio_attach_driver(struct serio_driver *drv); | 59 | static void serio_attach_driver(struct serio_driver *drv); |
60 | 60 | ||
61 | static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) | 61 | static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) |
@@ -151,7 +151,7 @@ static void serio_find_driver(struct serio *serio) | |||
151 | enum serio_event_type { | 151 | enum serio_event_type { |
152 | SERIO_RESCAN_PORT, | 152 | SERIO_RESCAN_PORT, |
153 | SERIO_RECONNECT_PORT, | 153 | SERIO_RECONNECT_PORT, |
154 | SERIO_RECONNECT_CHAIN, | 154 | SERIO_RECONNECT_SUBTREE, |
155 | SERIO_REGISTER_PORT, | 155 | SERIO_REGISTER_PORT, |
156 | SERIO_ATTACH_DRIVER, | 156 | SERIO_ATTACH_DRIVER, |
157 | }; | 157 | }; |
@@ -291,8 +291,8 @@ static void serio_handle_event(void) | |||
291 | serio_find_driver(event->object); | 291 | serio_find_driver(event->object); |
292 | break; | 292 | break; |
293 | 293 | ||
294 | case SERIO_RECONNECT_CHAIN: | 294 | case SERIO_RECONNECT_SUBTREE: |
295 | serio_reconnect_chain(event->object); | 295 | serio_reconnect_subtree(event->object); |
296 | break; | 296 | break; |
297 | 297 | ||
298 | case SERIO_ATTACH_DRIVER: | 298 | case SERIO_ATTACH_DRIVER: |
@@ -329,12 +329,10 @@ static void serio_remove_pending_events(void *object) | |||
329 | } | 329 | } |
330 | 330 | ||
331 | /* | 331 | /* |
332 | * Destroy child serio port (if any) that has not been fully registered yet. | 332 | * Locate child serio port (if any) that has not been fully registered yet. |
333 | * | 333 | * |
334 | * Note that we rely on the fact that port can have only one child and therefore | 334 | * Children are registered by driver's connect() handler so there can't be a |
335 | * only one child registration request can be pending. Additionally, children | 335 | * grandchild pending registration together with a child. |
336 | * are registered by driver's connect() handler so there can't be a grandchild | ||
337 | * pending registration together with a child. | ||
338 | */ | 336 | */ |
339 | static struct serio *serio_get_pending_child(struct serio *parent) | 337 | static struct serio *serio_get_pending_child(struct serio *parent) |
340 | { | 338 | { |
@@ -448,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * | |||
448 | if (!strncmp(buf, "none", count)) { | 446 | if (!strncmp(buf, "none", count)) { |
449 | serio_disconnect_port(serio); | 447 | serio_disconnect_port(serio); |
450 | } else if (!strncmp(buf, "reconnect", count)) { | 448 | } else if (!strncmp(buf, "reconnect", count)) { |
451 | serio_reconnect_chain(serio); | 449 | serio_reconnect_subtree(serio); |
452 | } else if (!strncmp(buf, "rescan", count)) { | 450 | } else if (!strncmp(buf, "rescan", count)) { |
453 | serio_disconnect_port(serio); | 451 | serio_disconnect_port(serio); |
454 | serio_find_driver(serio); | 452 | serio_find_driver(serio); |
@@ -515,6 +513,8 @@ static void serio_init_port(struct serio *serio) | |||
515 | __module_get(THIS_MODULE); | 513 | __module_get(THIS_MODULE); |
516 | 514 | ||
517 | INIT_LIST_HEAD(&serio->node); | 515 | INIT_LIST_HEAD(&serio->node); |
516 | INIT_LIST_HEAD(&serio->child_node); | ||
517 | INIT_LIST_HEAD(&serio->children); | ||
518 | spin_lock_init(&serio->lock); | 518 | spin_lock_init(&serio->lock); |
519 | mutex_init(&serio->drv_mutex); | 519 | mutex_init(&serio->drv_mutex); |
520 | device_initialize(&serio->dev); | 520 | device_initialize(&serio->dev); |
@@ -537,12 +537,13 @@ static void serio_init_port(struct serio *serio) | |||
537 | */ | 537 | */ |
538 | static void serio_add_port(struct serio *serio) | 538 | static void serio_add_port(struct serio *serio) |
539 | { | 539 | { |
540 | struct serio *parent = serio->parent; | ||
540 | int error; | 541 | int error; |
541 | 542 | ||
542 | if (serio->parent) { | 543 | if (parent) { |
543 | serio_pause_rx(serio->parent); | 544 | serio_pause_rx(parent); |
544 | serio->parent->child = serio; | 545 | list_add_tail(&serio->child_node, &parent->children); |
545 | serio_continue_rx(serio->parent); | 546 | serio_continue_rx(parent); |
546 | } | 547 | } |
547 | 548 | ||
548 | list_add_tail(&serio->node, &serio_list); | 549 | list_add_tail(&serio->node, &serio_list); |
@@ -558,15 +559,14 @@ static void serio_add_port(struct serio *serio) | |||
558 | } | 559 | } |
559 | 560 | ||
560 | /* | 561 | /* |
561 | * serio_destroy_port() completes deregistration process and removes | 562 | * serio_destroy_port() completes unregistration process and removes |
562 | * port from the system | 563 | * port from the system |
563 | */ | 564 | */ |
564 | static void serio_destroy_port(struct serio *serio) | 565 | static void serio_destroy_port(struct serio *serio) |
565 | { | 566 | { |
566 | struct serio *child; | 567 | struct serio *child; |
567 | 568 | ||
568 | child = serio_get_pending_child(serio); | 569 | while ((child = serio_get_pending_child(serio)) != NULL) { |
569 | if (child) { | ||
570 | serio_remove_pending_events(child); | 570 | serio_remove_pending_events(child); |
571 | put_device(&child->dev); | 571 | put_device(&child->dev); |
572 | } | 572 | } |
@@ -576,7 +576,7 @@ static void serio_destroy_port(struct serio *serio) | |||
576 | 576 | ||
577 | if (serio->parent) { | 577 | if (serio->parent) { |
578 | serio_pause_rx(serio->parent); | 578 | serio_pause_rx(serio->parent); |
579 | serio->parent->child = NULL; | 579 | list_del_init(&serio->child_node); |
580 | serio_continue_rx(serio->parent); | 580 | serio_continue_rx(serio->parent); |
581 | serio->parent = NULL; | 581 | serio->parent = NULL; |
582 | } | 582 | } |
@@ -608,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio) | |||
608 | } | 608 | } |
609 | 609 | ||
610 | /* | 610 | /* |
611 | * Reconnect serio port and all its children (re-initialize attached devices) | 611 | * Reconnect serio port and all its children (re-initialize attached |
612 | * devices). | ||
612 | */ | 613 | */ |
613 | static void serio_reconnect_chain(struct serio *serio) | 614 | static void serio_reconnect_subtree(struct serio *root) |
614 | { | 615 | { |
616 | struct serio *s = root; | ||
617 | int error; | ||
618 | |||
615 | do { | 619 | do { |
616 | if (serio_reconnect_port(serio)) { | 620 | error = serio_reconnect_port(s); |
617 | /* Ok, old children are now gone, we are done */ | 621 | if (!error) { |
618 | break; | 622 | /* |
623 | * Reconnect was successful, move on to do the | ||
624 | * first child. | ||
625 | */ | ||
626 | if (!list_empty(&s->children)) { | ||
627 | s = list_first_entry(&s->children, | ||
628 | struct serio, child_node); | ||
629 | continue; | ||
630 | } | ||
619 | } | 631 | } |
620 | serio = serio->child; | 632 | |
621 | } while (serio); | 633 | /* |
634 | * Either it was a leaf node or reconnect failed and it | ||
635 | * became a leaf node. Continue reconnecting starting with | ||
636 | * the next sibling of the parent node. | ||
637 | */ | ||
638 | while (s != root) { | ||
639 | struct serio *parent = s->parent; | ||
640 | |||
641 | if (!list_is_last(&s->child_node, &parent->children)) { | ||
642 | s = list_entry(s->child_node.next, | ||
643 | struct serio, child_node); | ||
644 | break; | ||
645 | } | ||
646 | |||
647 | s = parent; | ||
648 | } | ||
649 | } while (s != root); | ||
622 | } | 650 | } |
623 | 651 | ||
624 | /* | 652 | /* |
625 | * serio_disconnect_port() unbinds a port from its driver. As a side effect | 653 | * serio_disconnect_port() unbinds a port from its driver. As a side effect |
626 | * all child ports are unbound and destroyed. | 654 | * all children ports are unbound and destroyed. |
627 | */ | 655 | */ |
628 | static void serio_disconnect_port(struct serio *serio) | 656 | static void serio_disconnect_port(struct serio *serio) |
629 | { | 657 | { |
630 | struct serio *s, *parent; | 658 | struct serio *s = serio; |
659 | |||
660 | /* | ||
661 | * Children ports should be disconnected and destroyed | ||
662 | * first; we travel the tree in depth-first order. | ||
663 | */ | ||
664 | while (!list_empty(&serio->children)) { | ||
665 | |||
666 | /* Locate a leaf */ | ||
667 | while (!list_empty(&s->children)) | ||
668 | s = list_first_entry(&s->children, | ||
669 | struct serio, child_node); | ||
631 | 670 | ||
632 | if (serio->child) { | ||
633 | /* | 671 | /* |
634 | * Children ports should be disconnected and destroyed | 672 | * Prune this leaf node unless it is the one we |
635 | * first, staring with the leaf one, since we don't want | 673 | * started with. |
636 | * to do recursion | ||
637 | */ | 674 | */ |
638 | for (s = serio; s->child; s = s->child) | 675 | if (s != serio) { |
639 | /* empty */; | 676 | struct serio *parent = s->parent; |
640 | |||
641 | do { | ||
642 | parent = s->parent; | ||
643 | 677 | ||
644 | device_release_driver(&s->dev); | 678 | device_release_driver(&s->dev); |
645 | serio_destroy_port(s); | 679 | serio_destroy_port(s); |
646 | } while ((s = parent) != serio); | 680 | |
681 | s = parent; | ||
682 | } | ||
647 | } | 683 | } |
648 | 684 | ||
649 | /* | 685 | /* |
650 | * Ok, no children left, now disconnect this port | 686 | * OK, no children left, now disconnect this port. |
651 | */ | 687 | */ |
652 | device_release_driver(&serio->dev); | 688 | device_release_driver(&serio->dev); |
653 | } | 689 | } |
@@ -660,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan); | |||
660 | 696 | ||
661 | void serio_reconnect(struct serio *serio) | 697 | void serio_reconnect(struct serio *serio) |
662 | { | 698 | { |
663 | serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN); | 699 | serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE); |
664 | } | 700 | } |
665 | EXPORT_SYMBOL(serio_reconnect); | 701 | EXPORT_SYMBOL(serio_reconnect); |
666 | 702 | ||
@@ -688,14 +724,16 @@ void serio_unregister_port(struct serio *serio) | |||
688 | EXPORT_SYMBOL(serio_unregister_port); | 724 | EXPORT_SYMBOL(serio_unregister_port); |
689 | 725 | ||
690 | /* | 726 | /* |
691 | * Safely unregisters child port if one is present. | 727 | * Safely unregisters children ports if they are present. |
692 | */ | 728 | */ |
693 | void serio_unregister_child_port(struct serio *serio) | 729 | void serio_unregister_child_port(struct serio *serio) |
694 | { | 730 | { |
731 | struct serio *s, *next; | ||
732 | |||
695 | mutex_lock(&serio_mutex); | 733 | mutex_lock(&serio_mutex); |
696 | if (serio->child) { | 734 | list_for_each_entry_safe(s, next, &serio->children, child_node) { |
697 | serio_disconnect_port(serio->child); | 735 | serio_disconnect_port(s); |
698 | serio_destroy_port(serio->child); | 736 | serio_destroy_port(s); |
699 | } | 737 | } |
700 | mutex_unlock(&serio_mutex); | 738 | mutex_unlock(&serio_mutex); |
701 | } | 739 | } |
diff --git a/include/linux/serio.h b/include/linux/serio.h index 111ad501b054..109b237603b6 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h | |||
@@ -41,7 +41,9 @@ struct serio { | |||
41 | int (*start)(struct serio *); | 41 | int (*start)(struct serio *); |
42 | void (*stop)(struct serio *); | 42 | void (*stop)(struct serio *); |
43 | 43 | ||
44 | struct serio *parent, *child; | 44 | struct serio *parent; |
45 | struct list_head child_node; /* Entry in parent->children list */ | ||
46 | struct list_head children; | ||
45 | unsigned int depth; /* level of nesting in serio hierarchy */ | 47 | unsigned int depth; /* level of nesting in serio hierarchy */ |
46 | 48 | ||
47 | struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock and serio->sem */ | 49 | struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock and serio->sem */ |