diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/Kconfig | 4 | ||||
-rw-r--r-- | drivers/char/hvc_console.c | 56 |
2 files changed, 42 insertions, 18 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 1232ace097e1..57b801716fbe 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
@@ -585,8 +585,8 @@ config TIPAR | |||
585 | config HVC_DRIVER | 585 | config HVC_DRIVER |
586 | bool | 586 | bool |
587 | help | 587 | help |
588 | Users of pSeries machines that want to utilize the hvc console front-end | 588 | Generic "hypervisor virtual console" infrastructure for various |
589 | module for their backend console driver should select this option. | 589 | hypervisors (pSeries, Xen, lguest). |
590 | It will automatically be selected if one of the back-end console drivers | 590 | It will automatically be selected if one of the back-end console drivers |
591 | is selected. | 591 | is selected. |
592 | 592 | ||
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 83c1151ec7a2..8252f8668538 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c | |||
@@ -69,6 +69,8 @@ static struct task_struct *hvc_task; | |||
69 | /* Picks up late kicks after list walk but before schedule() */ | 69 | /* Picks up late kicks after list walk but before schedule() */ |
70 | static int hvc_kicked; | 70 | static int hvc_kicked; |
71 | 71 | ||
72 | static int hvc_init(void); | ||
73 | |||
72 | #ifdef CONFIG_MAGIC_SYSRQ | 74 | #ifdef CONFIG_MAGIC_SYSRQ |
73 | static int sysrq_pressed; | 75 | static int sysrq_pressed; |
74 | #endif | 76 | #endif |
@@ -754,6 +756,13 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq, | |||
754 | struct hvc_struct *hp; | 756 | struct hvc_struct *hp; |
755 | int i; | 757 | int i; |
756 | 758 | ||
759 | /* We wait until a driver actually comes along */ | ||
760 | if (!hvc_driver) { | ||
761 | int err = hvc_init(); | ||
762 | if (err) | ||
763 | return ERR_PTR(err); | ||
764 | } | ||
765 | |||
757 | hp = kmalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size, | 766 | hp = kmalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size, |
758 | GFP_KERNEL); | 767 | GFP_KERNEL); |
759 | if (!hp) | 768 | if (!hp) |
@@ -829,16 +838,18 @@ int __devexit hvc_remove(struct hvc_struct *hp) | |||
829 | return 0; | 838 | return 0; |
830 | } | 839 | } |
831 | 840 | ||
832 | /* Driver initialization. Follow console initialization. This is where the TTY | 841 | /* Driver initialization: called as soon as someone uses hvc_alloc(). */ |
833 | * interfaces start to become available. */ | 842 | static int hvc_init(void) |
834 | static int __init hvc_init(void) | ||
835 | { | 843 | { |
836 | struct tty_driver *drv; | 844 | struct tty_driver *drv; |
845 | int err; | ||
837 | 846 | ||
838 | /* We need more than hvc_count adapters due to hotplug additions. */ | 847 | /* We need more than hvc_count adapters due to hotplug additions. */ |
839 | drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS); | 848 | drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS); |
840 | if (!drv) | 849 | if (!drv) { |
841 | return -ENOMEM; | 850 | err = -ENOMEM; |
851 | goto out; | ||
852 | } | ||
842 | 853 | ||
843 | drv->owner = THIS_MODULE; | 854 | drv->owner = THIS_MODULE; |
844 | drv->driver_name = "hvc"; | 855 | drv->driver_name = "hvc"; |
@@ -854,30 +865,43 @@ static int __init hvc_init(void) | |||
854 | * added later. */ | 865 | * added later. */ |
855 | hvc_task = kthread_run(khvcd, NULL, "khvcd"); | 866 | hvc_task = kthread_run(khvcd, NULL, "khvcd"); |
856 | if (IS_ERR(hvc_task)) { | 867 | if (IS_ERR(hvc_task)) { |
857 | panic("Couldn't create kthread for console.\n"); | 868 | printk(KERN_ERR "Couldn't create kthread for console.\n"); |
858 | put_tty_driver(drv); | 869 | err = PTR_ERR(hvc_task); |
859 | return -EIO; | 870 | goto put_tty; |
860 | } | 871 | } |
861 | 872 | ||
862 | if (tty_register_driver(drv)) | 873 | err = tty_register_driver(drv); |
863 | panic("Couldn't register hvc console driver\n"); | 874 | if (err) { |
875 | printk(KERN_ERR "Couldn't register hvc console driver\n"); | ||
876 | goto stop_thread; | ||
877 | } | ||
864 | 878 | ||
879 | /* FIXME: This mb() seems completely random. Remove it. */ | ||
865 | mb(); | 880 | mb(); |
866 | hvc_driver = drv; | 881 | hvc_driver = drv; |
867 | return 0; | 882 | return 0; |
883 | |||
884 | put_tty: | ||
885 | put_tty_driver(hvc_driver); | ||
886 | stop_thread: | ||
887 | kthread_stop(hvc_task); | ||
888 | hvc_task = NULL; | ||
889 | out: | ||
890 | return err; | ||
868 | } | 891 | } |
869 | module_init(hvc_init); | ||
870 | 892 | ||
871 | /* This isn't particularly necessary due to this being a console driver | 893 | /* This isn't particularly necessary due to this being a console driver |
872 | * but it is nice to be thorough. | 894 | * but it is nice to be thorough. |
873 | */ | 895 | */ |
874 | static void __exit hvc_exit(void) | 896 | static void __exit hvc_exit(void) |
875 | { | 897 | { |
876 | kthread_stop(hvc_task); | 898 | if (hvc_driver) { |
899 | kthread_stop(hvc_task); | ||
877 | 900 | ||
878 | tty_unregister_driver(hvc_driver); | 901 | tty_unregister_driver(hvc_driver); |
879 | /* return tty_struct instances allocated in hvc_init(). */ | 902 | /* return tty_struct instances allocated in hvc_init(). */ |
880 | put_tty_driver(hvc_driver); | 903 | put_tty_driver(hvc_driver); |
881 | unregister_console(&hvc_con_driver); | 904 | unregister_console(&hvc_con_driver); |
905 | } | ||
882 | } | 906 | } |
883 | module_exit(hvc_exit); | 907 | module_exit(hvc_exit); |