diff options
Diffstat (limited to 'drivers/input/serio/serio.c')
-rw-r--r-- | drivers/input/serio/serio.c | 125 |
1 files changed, 81 insertions, 44 deletions
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index c3b626e9eae7..405bf214527c 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c | |||
@@ -37,7 +37,6 @@ | |||
37 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
38 | #include <linux/kthread.h> | 38 | #include <linux/kthread.h> |
39 | #include <linux/mutex.h> | 39 | #include <linux/mutex.h> |
40 | #include <linux/freezer.h> | ||
41 | 40 | ||
42 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | 41 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
43 | MODULE_DESCRIPTION("Serio abstraction core"); | 42 | MODULE_DESCRIPTION("Serio abstraction core"); |
@@ -56,7 +55,7 @@ static struct bus_type serio_bus; | |||
56 | static void serio_add_port(struct serio *serio); | 55 | static void serio_add_port(struct serio *serio); |
57 | static int serio_reconnect_port(struct serio *serio); | 56 | static int serio_reconnect_port(struct serio *serio); |
58 | static void serio_disconnect_port(struct serio *serio); | 57 | static void serio_disconnect_port(struct serio *serio); |
59 | static void serio_reconnect_chain(struct serio *serio); | 58 | static void serio_reconnect_subtree(struct serio *serio); |
60 | static void serio_attach_driver(struct serio_driver *drv); | 59 | static void serio_attach_driver(struct serio_driver *drv); |
61 | 60 | ||
62 | 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) |
@@ -152,7 +151,7 @@ static void serio_find_driver(struct serio *serio) | |||
152 | enum serio_event_type { | 151 | enum serio_event_type { |
153 | SERIO_RESCAN_PORT, | 152 | SERIO_RESCAN_PORT, |
154 | SERIO_RECONNECT_PORT, | 153 | SERIO_RECONNECT_PORT, |
155 | SERIO_RECONNECT_CHAIN, | 154 | SERIO_RECONNECT_SUBTREE, |
156 | SERIO_REGISTER_PORT, | 155 | SERIO_REGISTER_PORT, |
157 | SERIO_ATTACH_DRIVER, | 156 | SERIO_ATTACH_DRIVER, |
158 | }; | 157 | }; |
@@ -292,8 +291,8 @@ static void serio_handle_event(void) | |||
292 | serio_find_driver(event->object); | 291 | serio_find_driver(event->object); |
293 | break; | 292 | break; |
294 | 293 | ||
295 | case SERIO_RECONNECT_CHAIN: | 294 | case SERIO_RECONNECT_SUBTREE: |
296 | serio_reconnect_chain(event->object); | 295 | serio_reconnect_subtree(event->object); |
297 | break; | 296 | break; |
298 | 297 | ||
299 | case SERIO_ATTACH_DRIVER: | 298 | case SERIO_ATTACH_DRIVER: |
@@ -330,12 +329,10 @@ static void serio_remove_pending_events(void *object) | |||
330 | } | 329 | } |
331 | 330 | ||
332 | /* | 331 | /* |
333 | * 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. |
334 | * | 333 | * |
335 | * 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 |
336 | * only one child registration request can be pending. Additionally, children | 335 | * grandchild pending registration together with a child. |
337 | * are registered by driver's connect() handler so there can't be a grandchild | ||
338 | * pending registration together with a child. | ||
339 | */ | 336 | */ |
340 | static struct serio *serio_get_pending_child(struct serio *parent) | 337 | static struct serio *serio_get_pending_child(struct serio *parent) |
341 | { | 338 | { |
@@ -449,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * | |||
449 | if (!strncmp(buf, "none", count)) { | 446 | if (!strncmp(buf, "none", count)) { |
450 | serio_disconnect_port(serio); | 447 | serio_disconnect_port(serio); |
451 | } else if (!strncmp(buf, "reconnect", count)) { | 448 | } else if (!strncmp(buf, "reconnect", count)) { |
452 | serio_reconnect_chain(serio); | 449 | serio_reconnect_subtree(serio); |
453 | } else if (!strncmp(buf, "rescan", count)) { | 450 | } else if (!strncmp(buf, "rescan", count)) { |
454 | serio_disconnect_port(serio); | 451 | serio_disconnect_port(serio); |
455 | serio_find_driver(serio); | 452 | serio_find_driver(serio); |
@@ -516,6 +513,8 @@ static void serio_init_port(struct serio *serio) | |||
516 | __module_get(THIS_MODULE); | 513 | __module_get(THIS_MODULE); |
517 | 514 | ||
518 | INIT_LIST_HEAD(&serio->node); | 515 | INIT_LIST_HEAD(&serio->node); |
516 | INIT_LIST_HEAD(&serio->child_node); | ||
517 | INIT_LIST_HEAD(&serio->children); | ||
519 | spin_lock_init(&serio->lock); | 518 | spin_lock_init(&serio->lock); |
520 | mutex_init(&serio->drv_mutex); | 519 | mutex_init(&serio->drv_mutex); |
521 | device_initialize(&serio->dev); | 520 | device_initialize(&serio->dev); |
@@ -538,12 +537,13 @@ static void serio_init_port(struct serio *serio) | |||
538 | */ | 537 | */ |
539 | static void serio_add_port(struct serio *serio) | 538 | static void serio_add_port(struct serio *serio) |
540 | { | 539 | { |
540 | struct serio *parent = serio->parent; | ||
541 | int error; | 541 | int error; |
542 | 542 | ||
543 | if (serio->parent) { | 543 | if (parent) { |
544 | serio_pause_rx(serio->parent); | 544 | serio_pause_rx(parent); |
545 | serio->parent->child = serio; | 545 | list_add_tail(&serio->child_node, &parent->children); |
546 | serio_continue_rx(serio->parent); | 546 | serio_continue_rx(parent); |
547 | } | 547 | } |
548 | 548 | ||
549 | list_add_tail(&serio->node, &serio_list); | 549 | list_add_tail(&serio->node, &serio_list); |
@@ -559,15 +559,14 @@ static void serio_add_port(struct serio *serio) | |||
559 | } | 559 | } |
560 | 560 | ||
561 | /* | 561 | /* |
562 | * serio_destroy_port() completes deregistration process and removes | 562 | * serio_destroy_port() completes unregistration process and removes |
563 | * port from the system | 563 | * port from the system |
564 | */ | 564 | */ |
565 | static void serio_destroy_port(struct serio *serio) | 565 | static void serio_destroy_port(struct serio *serio) |
566 | { | 566 | { |
567 | struct serio *child; | 567 | struct serio *child; |
568 | 568 | ||
569 | child = serio_get_pending_child(serio); | 569 | while ((child = serio_get_pending_child(serio)) != NULL) { |
570 | if (child) { | ||
571 | serio_remove_pending_events(child); | 570 | serio_remove_pending_events(child); |
572 | put_device(&child->dev); | 571 | put_device(&child->dev); |
573 | } | 572 | } |
@@ -577,7 +576,7 @@ static void serio_destroy_port(struct serio *serio) | |||
577 | 576 | ||
578 | if (serio->parent) { | 577 | if (serio->parent) { |
579 | serio_pause_rx(serio->parent); | 578 | serio_pause_rx(serio->parent); |
580 | serio->parent->child = NULL; | 579 | list_del_init(&serio->child_node); |
581 | serio_continue_rx(serio->parent); | 580 | serio_continue_rx(serio->parent); |
582 | serio->parent = NULL; | 581 | serio->parent = NULL; |
583 | } | 582 | } |
@@ -609,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio) | |||
609 | } | 608 | } |
610 | 609 | ||
611 | /* | 610 | /* |
612 | * Reconnect serio port and all its children (re-initialize attached devices) | 611 | * Reconnect serio port and all its children (re-initialize attached |
612 | * devices). | ||
613 | */ | 613 | */ |
614 | static void serio_reconnect_chain(struct serio *serio) | 614 | static void serio_reconnect_subtree(struct serio *root) |
615 | { | 615 | { |
616 | struct serio *s = root; | ||
617 | int error; | ||
618 | |||
616 | do { | 619 | do { |
617 | if (serio_reconnect_port(serio)) { | 620 | error = serio_reconnect_port(s); |
618 | /* Ok, old children are now gone, we are done */ | 621 | if (!error) { |
619 | 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 | } | ||
620 | } | 631 | } |
621 | serio = serio->child; | 632 | |
622 | } 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); | ||
623 | } | 650 | } |
624 | 651 | ||
625 | /* | 652 | /* |
626 | * 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 |
627 | * all child ports are unbound and destroyed. | 654 | * all children ports are unbound and destroyed. |
628 | */ | 655 | */ |
629 | static void serio_disconnect_port(struct serio *serio) | 656 | static void serio_disconnect_port(struct serio *serio) |
630 | { | 657 | { |
631 | 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); | ||
632 | 670 | ||
633 | if (serio->child) { | ||
634 | /* | 671 | /* |
635 | * Children ports should be disconnected and destroyed | 672 | * Prune this leaf node unless it is the one we |
636 | * first, staring with the leaf one, since we don't want | 673 | * started with. |
637 | * to do recursion | ||
638 | */ | 674 | */ |
639 | for (s = serio; s->child; s = s->child) | 675 | if (s != serio) { |
640 | /* empty */; | 676 | struct serio *parent = s->parent; |
641 | |||
642 | do { | ||
643 | parent = s->parent; | ||
644 | 677 | ||
645 | device_release_driver(&s->dev); | 678 | device_release_driver(&s->dev); |
646 | serio_destroy_port(s); | 679 | serio_destroy_port(s); |
647 | } while ((s = parent) != serio); | 680 | |
681 | s = parent; | ||
682 | } | ||
648 | } | 683 | } |
649 | 684 | ||
650 | /* | 685 | /* |
651 | * Ok, no children left, now disconnect this port | 686 | * OK, no children left, now disconnect this port. |
652 | */ | 687 | */ |
653 | device_release_driver(&serio->dev); | 688 | device_release_driver(&serio->dev); |
654 | } | 689 | } |
@@ -661,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan); | |||
661 | 696 | ||
662 | void serio_reconnect(struct serio *serio) | 697 | void serio_reconnect(struct serio *serio) |
663 | { | 698 | { |
664 | serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN); | 699 | serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE); |
665 | } | 700 | } |
666 | EXPORT_SYMBOL(serio_reconnect); | 701 | EXPORT_SYMBOL(serio_reconnect); |
667 | 702 | ||
@@ -689,14 +724,16 @@ void serio_unregister_port(struct serio *serio) | |||
689 | EXPORT_SYMBOL(serio_unregister_port); | 724 | EXPORT_SYMBOL(serio_unregister_port); |
690 | 725 | ||
691 | /* | 726 | /* |
692 | * Safely unregisters child port if one is present. | 727 | * Safely unregisters children ports if they are present. |
693 | */ | 728 | */ |
694 | void serio_unregister_child_port(struct serio *serio) | 729 | void serio_unregister_child_port(struct serio *serio) |
695 | { | 730 | { |
731 | struct serio *s, *next; | ||
732 | |||
696 | mutex_lock(&serio_mutex); | 733 | mutex_lock(&serio_mutex); |
697 | if (serio->child) { | 734 | list_for_each_entry_safe(s, next, &serio->children, child_node) { |
698 | serio_disconnect_port(serio->child); | 735 | serio_disconnect_port(s); |
699 | serio_destroy_port(serio->child); | 736 | serio_destroy_port(s); |
700 | } | 737 | } |
701 | mutex_unlock(&serio_mutex); | 738 | mutex_unlock(&serio_mutex); |
702 | } | 739 | } |