diff options
Diffstat (limited to 'drivers/char/vt.c')
-rw-r--r-- | drivers/char/vt.c | 580 |
1 files changed, 549 insertions, 31 deletions
diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 6c94879e0b99..714d95ff2f1e 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c | |||
@@ -98,7 +98,22 @@ | |||
98 | #include <asm/system.h> | 98 | #include <asm/system.h> |
99 | #include <asm/uaccess.h> | 99 | #include <asm/uaccess.h> |
100 | 100 | ||
101 | #define MAX_NR_CON_DRIVER 16 | ||
101 | 102 | ||
103 | #define CON_DRIVER_FLAG_MODULE 1 | ||
104 | #define CON_DRIVER_FLAG_INIT 2 | ||
105 | |||
106 | struct con_driver { | ||
107 | const struct consw *con; | ||
108 | const char *desc; | ||
109 | struct class_device *class_dev; | ||
110 | int node; | ||
111 | int first; | ||
112 | int last; | ||
113 | int flag; | ||
114 | }; | ||
115 | |||
116 | static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER]; | ||
102 | const struct consw *conswitchp; | 117 | const struct consw *conswitchp; |
103 | 118 | ||
104 | /* A bitmap for codes <32. A bit of 1 indicates that the code | 119 | /* A bitmap for codes <32. A bit of 1 indicates that the code |
@@ -2557,7 +2572,7 @@ static int __init con_init(void) | |||
2557 | { | 2572 | { |
2558 | const char *display_desc = NULL; | 2573 | const char *display_desc = NULL; |
2559 | struct vc_data *vc; | 2574 | struct vc_data *vc; |
2560 | unsigned int currcons = 0; | 2575 | unsigned int currcons = 0, i; |
2561 | 2576 | ||
2562 | acquire_console_sem(); | 2577 | acquire_console_sem(); |
2563 | 2578 | ||
@@ -2569,6 +2584,22 @@ static int __init con_init(void) | |||
2569 | return 0; | 2584 | return 0; |
2570 | } | 2585 | } |
2571 | 2586 | ||
2587 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2588 | struct con_driver *con_driver = ®istered_con_driver[i]; | ||
2589 | |||
2590 | if (con_driver->con == NULL) { | ||
2591 | con_driver->con = conswitchp; | ||
2592 | con_driver->desc = display_desc; | ||
2593 | con_driver->flag = CON_DRIVER_FLAG_INIT; | ||
2594 | con_driver->first = 0; | ||
2595 | con_driver->last = MAX_NR_CONSOLES - 1; | ||
2596 | break; | ||
2597 | } | ||
2598 | } | ||
2599 | |||
2600 | for (i = 0; i < MAX_NR_CONSOLES; i++) | ||
2601 | con_driver_map[i] = conswitchp; | ||
2602 | |||
2572 | init_timer(&console_timer); | 2603 | init_timer(&console_timer); |
2573 | console_timer.function = blank_screen_t; | 2604 | console_timer.function = blank_screen_t; |
2574 | if (blankinterval) { | 2605 | if (blankinterval) { |
@@ -2656,38 +2687,53 @@ int __init vty_init(void) | |||
2656 | } | 2687 | } |
2657 | 2688 | ||
2658 | #ifndef VT_SINGLE_DRIVER | 2689 | #ifndef VT_SINGLE_DRIVER |
2690 | #include <linux/device.h> | ||
2659 | 2691 | ||
2660 | /* | 2692 | static struct class *vtconsole_class; |
2661 | * If we support more console drivers, this function is used | ||
2662 | * when a driver wants to take over some existing consoles | ||
2663 | * and become default driver for newly opened ones. | ||
2664 | */ | ||
2665 | 2693 | ||
2666 | int take_over_console(const struct consw *csw, int first, int last, int deflt) | 2694 | static int bind_con_driver(const struct consw *csw, int first, int last, |
2695 | int deflt) | ||
2667 | { | 2696 | { |
2668 | int i, j = -1; | 2697 | struct module *owner = csw->owner; |
2669 | const char *desc; | 2698 | const char *desc = NULL; |
2670 | struct module *owner; | 2699 | struct con_driver *con_driver; |
2700 | int i, j = -1, k = -1, retval = -ENODEV; | ||
2671 | 2701 | ||
2672 | owner = csw->owner; | ||
2673 | if (!try_module_get(owner)) | 2702 | if (!try_module_get(owner)) |
2674 | return -ENODEV; | 2703 | return -ENODEV; |
2675 | 2704 | ||
2676 | acquire_console_sem(); | 2705 | acquire_console_sem(); |
2677 | 2706 | ||
2678 | desc = csw->con_startup(); | 2707 | /* check if driver is registered */ |
2679 | if (!desc) { | 2708 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { |
2680 | release_console_sem(); | 2709 | con_driver = ®istered_con_driver[i]; |
2681 | module_put(owner); | 2710 | |
2682 | return -ENODEV; | 2711 | if (con_driver->con == csw) { |
2712 | desc = con_driver->desc; | ||
2713 | retval = 0; | ||
2714 | break; | ||
2715 | } | ||
2716 | } | ||
2717 | |||
2718 | if (retval) | ||
2719 | goto err; | ||
2720 | |||
2721 | if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) { | ||
2722 | csw->con_startup(); | ||
2723 | con_driver->flag |= CON_DRIVER_FLAG_INIT; | ||
2683 | } | 2724 | } |
2725 | |||
2684 | if (deflt) { | 2726 | if (deflt) { |
2685 | if (conswitchp) | 2727 | if (conswitchp) |
2686 | module_put(conswitchp->owner); | 2728 | module_put(conswitchp->owner); |
2729 | |||
2687 | __module_get(owner); | 2730 | __module_get(owner); |
2688 | conswitchp = csw; | 2731 | conswitchp = csw; |
2689 | } | 2732 | } |
2690 | 2733 | ||
2734 | first = max(first, con_driver->first); | ||
2735 | last = min(last, con_driver->last); | ||
2736 | |||
2691 | for (i = first; i <= last; i++) { | 2737 | for (i = first; i <= last; i++) { |
2692 | int old_was_color; | 2738 | int old_was_color; |
2693 | struct vc_data *vc = vc_cons[i].d; | 2739 | struct vc_data *vc = vc_cons[i].d; |
@@ -2701,15 +2747,17 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) | |||
2701 | continue; | 2747 | continue; |
2702 | 2748 | ||
2703 | j = i; | 2749 | j = i; |
2704 | if (CON_IS_VISIBLE(vc)) | 2750 | |
2751 | if (CON_IS_VISIBLE(vc)) { | ||
2752 | k = i; | ||
2705 | save_screen(vc); | 2753 | save_screen(vc); |
2754 | } | ||
2755 | |||
2706 | old_was_color = vc->vc_can_do_color; | 2756 | old_was_color = vc->vc_can_do_color; |
2707 | vc->vc_sw->con_deinit(vc); | 2757 | vc->vc_sw->con_deinit(vc); |
2708 | vc->vc_origin = (unsigned long)vc->vc_screenbuf; | 2758 | vc->vc_origin = (unsigned long)vc->vc_screenbuf; |
2709 | vc->vc_visible_origin = vc->vc_origin; | ||
2710 | vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size; | ||
2711 | vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x; | ||
2712 | visual_init(vc, i, 0); | 2759 | visual_init(vc, i, 0); |
2760 | set_origin(vc); | ||
2713 | update_attr(vc); | 2761 | update_attr(vc); |
2714 | 2762 | ||
2715 | /* If the console changed between mono <-> color, then | 2763 | /* If the console changed between mono <-> color, then |
@@ -2718,36 +2766,506 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) | |||
2718 | */ | 2766 | */ |
2719 | if (old_was_color != vc->vc_can_do_color) | 2767 | if (old_was_color != vc->vc_can_do_color) |
2720 | clear_buffer_attributes(vc); | 2768 | clear_buffer_attributes(vc); |
2721 | |||
2722 | if (CON_IS_VISIBLE(vc)) | ||
2723 | update_screen(vc); | ||
2724 | } | 2769 | } |
2770 | |||
2725 | printk("Console: switching "); | 2771 | printk("Console: switching "); |
2726 | if (!deflt) | 2772 | if (!deflt) |
2727 | printk("consoles %d-%d ", first+1, last+1); | 2773 | printk("consoles %d-%d ", first+1, last+1); |
2728 | if (j >= 0) | 2774 | if (j >= 0) { |
2775 | struct vc_data *vc = vc_cons[j].d; | ||
2776 | |||
2729 | printk("to %s %s %dx%d\n", | 2777 | printk("to %s %s %dx%d\n", |
2730 | vc_cons[j].d->vc_can_do_color ? "colour" : "mono", | 2778 | vc->vc_can_do_color ? "colour" : "mono", |
2731 | desc, vc_cons[j].d->vc_cols, vc_cons[j].d->vc_rows); | 2779 | desc, vc->vc_cols, vc->vc_rows); |
2732 | else | 2780 | |
2781 | if (k >= 0) { | ||
2782 | vc = vc_cons[k].d; | ||
2783 | update_screen(vc); | ||
2784 | } | ||
2785 | } else | ||
2733 | printk("to %s\n", desc); | 2786 | printk("to %s\n", desc); |
2734 | 2787 | ||
2788 | retval = 0; | ||
2789 | err: | ||
2735 | release_console_sem(); | 2790 | release_console_sem(); |
2791 | module_put(owner); | ||
2792 | return retval; | ||
2793 | }; | ||
2794 | |||
2795 | #ifdef CONFIG_VT_HW_CONSOLE_BINDING | ||
2796 | static int con_is_graphics(const struct consw *csw, int first, int last) | ||
2797 | { | ||
2798 | int i, retval = 0; | ||
2799 | |||
2800 | for (i = first; i <= last; i++) { | ||
2801 | struct vc_data *vc = vc_cons[i].d; | ||
2802 | |||
2803 | if (vc && vc->vc_mode == KD_GRAPHICS) { | ||
2804 | retval = 1; | ||
2805 | break; | ||
2806 | } | ||
2807 | } | ||
2808 | |||
2809 | return retval; | ||
2810 | } | ||
2811 | |||
2812 | static int unbind_con_driver(const struct consw *csw, int first, int last, | ||
2813 | int deflt) | ||
2814 | { | ||
2815 | struct module *owner = csw->owner; | ||
2816 | const struct consw *defcsw = NULL; | ||
2817 | struct con_driver *con_driver = NULL, *con_back = NULL; | ||
2818 | int i, retval = -ENODEV; | ||
2819 | |||
2820 | if (!try_module_get(owner)) | ||
2821 | return -ENODEV; | ||
2822 | |||
2823 | acquire_console_sem(); | ||
2824 | |||
2825 | /* check if driver is registered and if it is unbindable */ | ||
2826 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2827 | con_driver = ®istered_con_driver[i]; | ||
2828 | |||
2829 | if (con_driver->con == csw && | ||
2830 | con_driver->flag & CON_DRIVER_FLAG_MODULE) { | ||
2831 | retval = 0; | ||
2832 | break; | ||
2833 | } | ||
2834 | } | ||
2835 | |||
2836 | if (retval) { | ||
2837 | release_console_sem(); | ||
2838 | goto err; | ||
2839 | } | ||
2840 | |||
2841 | retval = -ENODEV; | ||
2842 | |||
2843 | /* check if backup driver exists */ | ||
2844 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2845 | con_back = ®istered_con_driver[i]; | ||
2846 | |||
2847 | if (con_back->con && | ||
2848 | !(con_back->flag & CON_DRIVER_FLAG_MODULE)) { | ||
2849 | defcsw = con_back->con; | ||
2850 | retval = 0; | ||
2851 | break; | ||
2852 | } | ||
2853 | } | ||
2854 | |||
2855 | if (retval) { | ||
2856 | release_console_sem(); | ||
2857 | goto err; | ||
2858 | } | ||
2859 | |||
2860 | if (!con_is_bound(csw)) { | ||
2861 | release_console_sem(); | ||
2862 | goto err; | ||
2863 | } | ||
2864 | |||
2865 | first = max(first, con_driver->first); | ||
2866 | last = min(last, con_driver->last); | ||
2867 | |||
2868 | for (i = first; i <= last; i++) { | ||
2869 | if (con_driver_map[i] == csw) { | ||
2870 | module_put(csw->owner); | ||
2871 | con_driver_map[i] = NULL; | ||
2872 | } | ||
2873 | } | ||
2874 | |||
2875 | if (!con_is_bound(defcsw)) { | ||
2876 | const struct consw *defconsw = conswitchp; | ||
2877 | |||
2878 | defcsw->con_startup(); | ||
2879 | con_back->flag |= CON_DRIVER_FLAG_INIT; | ||
2880 | /* | ||
2881 | * vgacon may change the default driver to point | ||
2882 | * to dummycon, we restore it here... | ||
2883 | */ | ||
2884 | conswitchp = defconsw; | ||
2885 | } | ||
2886 | |||
2887 | if (!con_is_bound(csw)) | ||
2888 | con_driver->flag &= ~CON_DRIVER_FLAG_INIT; | ||
2736 | 2889 | ||
2890 | release_console_sem(); | ||
2891 | /* ignore return value, binding should not fail */ | ||
2892 | bind_con_driver(defcsw, first, last, deflt); | ||
2893 | err: | ||
2737 | module_put(owner); | 2894 | module_put(owner); |
2895 | return retval; | ||
2896 | |||
2897 | } | ||
2898 | |||
2899 | static int vt_bind(struct con_driver *con) | ||
2900 | { | ||
2901 | const struct consw *defcsw = NULL, *csw = NULL; | ||
2902 | int i, more = 1, first = -1, last = -1, deflt = 0; | ||
2903 | |||
2904 | if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || | ||
2905 | con_is_graphics(con->con, con->first, con->last)) | ||
2906 | goto err; | ||
2907 | |||
2908 | csw = con->con; | ||
2909 | |||
2910 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
2911 | struct con_driver *con = ®istered_con_driver[i]; | ||
2912 | |||
2913 | if (con->con && !(con->flag & CON_DRIVER_FLAG_MODULE)) { | ||
2914 | defcsw = con->con; | ||
2915 | break; | ||
2916 | } | ||
2917 | } | ||
2918 | |||
2919 | if (!defcsw) | ||
2920 | goto err; | ||
2921 | |||
2922 | while (more) { | ||
2923 | more = 0; | ||
2924 | |||
2925 | for (i = con->first; i <= con->last; i++) { | ||
2926 | if (con_driver_map[i] == defcsw) { | ||
2927 | if (first == -1) | ||
2928 | first = i; | ||
2929 | last = i; | ||
2930 | more = 1; | ||
2931 | } else if (first != -1) | ||
2932 | break; | ||
2933 | } | ||
2934 | |||
2935 | if (first == 0 && last == MAX_NR_CONSOLES -1) | ||
2936 | deflt = 1; | ||
2937 | |||
2938 | if (first != -1) | ||
2939 | bind_con_driver(csw, first, last, deflt); | ||
2940 | |||
2941 | first = -1; | ||
2942 | last = -1; | ||
2943 | deflt = 0; | ||
2944 | } | ||
2945 | |||
2946 | err: | ||
2738 | return 0; | 2947 | return 0; |
2739 | } | 2948 | } |
2740 | 2949 | ||
2741 | void give_up_console(const struct consw *csw) | 2950 | static int vt_unbind(struct con_driver *con) |
2951 | { | ||
2952 | const struct consw *csw = NULL; | ||
2953 | int i, more = 1, first = -1, last = -1, deflt = 0; | ||
2954 | |||
2955 | if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || | ||
2956 | con_is_graphics(con->con, con->first, con->last)) | ||
2957 | goto err; | ||
2958 | |||
2959 | csw = con->con; | ||
2960 | |||
2961 | while (more) { | ||
2962 | more = 0; | ||
2963 | |||
2964 | for (i = con->first; i <= con->last; i++) { | ||
2965 | if (con_driver_map[i] == csw) { | ||
2966 | if (first == -1) | ||
2967 | first = i; | ||
2968 | last = i; | ||
2969 | more = 1; | ||
2970 | } else if (first != -1) | ||
2971 | break; | ||
2972 | } | ||
2973 | |||
2974 | if (first == 0 && last == MAX_NR_CONSOLES -1) | ||
2975 | deflt = 1; | ||
2976 | |||
2977 | if (first != -1) | ||
2978 | unbind_con_driver(csw, first, last, deflt); | ||
2979 | |||
2980 | first = -1; | ||
2981 | last = -1; | ||
2982 | deflt = 0; | ||
2983 | } | ||
2984 | |||
2985 | err: | ||
2986 | return 0; | ||
2987 | } | ||
2988 | #else | ||
2989 | static inline int vt_bind(struct con_driver *con) | ||
2990 | { | ||
2991 | return 0; | ||
2992 | } | ||
2993 | static inline int vt_unbind(struct con_driver *con) | ||
2994 | { | ||
2995 | return 0; | ||
2996 | } | ||
2997 | #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ | ||
2998 | |||
2999 | static ssize_t store_bind(struct class_device *class_device, | ||
3000 | const char *buf, size_t count) | ||
3001 | { | ||
3002 | struct con_driver *con = class_get_devdata(class_device); | ||
3003 | int bind = simple_strtoul(buf, NULL, 0); | ||
3004 | |||
3005 | if (bind) | ||
3006 | vt_bind(con); | ||
3007 | else | ||
3008 | vt_unbind(con); | ||
3009 | |||
3010 | return count; | ||
3011 | } | ||
3012 | |||
3013 | static ssize_t show_bind(struct class_device *class_device, char *buf) | ||
3014 | { | ||
3015 | struct con_driver *con = class_get_devdata(class_device); | ||
3016 | int bind = con_is_bound(con->con); | ||
3017 | |||
3018 | return snprintf(buf, PAGE_SIZE, "%i\n", bind); | ||
3019 | } | ||
3020 | |||
3021 | static ssize_t show_name(struct class_device *class_device, char *buf) | ||
3022 | { | ||
3023 | struct con_driver *con = class_get_devdata(class_device); | ||
3024 | |||
3025 | return snprintf(buf, PAGE_SIZE, "%s %s\n", | ||
3026 | (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)", | ||
3027 | con->desc); | ||
3028 | |||
3029 | } | ||
3030 | |||
3031 | static struct class_device_attribute class_device_attrs[] = { | ||
3032 | __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind), | ||
3033 | __ATTR(name, S_IRUGO, show_name, NULL), | ||
3034 | }; | ||
3035 | |||
3036 | static int vtconsole_init_class_device(struct con_driver *con) | ||
3037 | { | ||
3038 | int i; | ||
3039 | |||
3040 | class_set_devdata(con->class_dev, con); | ||
3041 | for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) | ||
3042 | class_device_create_file(con->class_dev, | ||
3043 | &class_device_attrs[i]); | ||
3044 | |||
3045 | return 0; | ||
3046 | } | ||
3047 | |||
3048 | static void vtconsole_deinit_class_device(struct con_driver *con) | ||
2742 | { | 3049 | { |
2743 | int i; | 3050 | int i; |
2744 | 3051 | ||
2745 | for(i = 0; i < MAX_NR_CONSOLES; i++) | 3052 | for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) |
3053 | class_device_remove_file(con->class_dev, | ||
3054 | &class_device_attrs[i]); | ||
3055 | } | ||
3056 | |||
3057 | /** | ||
3058 | * con_is_bound - checks if driver is bound to the console | ||
3059 | * @csw: console driver | ||
3060 | * | ||
3061 | * RETURNS: zero if unbound, nonzero if bound | ||
3062 | * | ||
3063 | * Drivers can call this and if zero, they should release | ||
3064 | * all resources allocated on con_startup() | ||
3065 | */ | ||
3066 | int con_is_bound(const struct consw *csw) | ||
3067 | { | ||
3068 | int i, bound = 0; | ||
3069 | |||
3070 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | ||
2746 | if (con_driver_map[i] == csw) { | 3071 | if (con_driver_map[i] == csw) { |
2747 | module_put(csw->owner); | 3072 | bound = 1; |
2748 | con_driver_map[i] = NULL; | 3073 | break; |
3074 | } | ||
3075 | } | ||
3076 | |||
3077 | return bound; | ||
3078 | } | ||
3079 | EXPORT_SYMBOL(con_is_bound); | ||
3080 | |||
3081 | /** | ||
3082 | * register_con_driver - register console driver to console layer | ||
3083 | * @csw: console driver | ||
3084 | * @first: the first console to take over, minimum value is 0 | ||
3085 | * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 | ||
3086 | * | ||
3087 | * DESCRIPTION: This function registers a console driver which can later | ||
3088 | * bind to a range of consoles specified by @first and @last. It will | ||
3089 | * also initialize the console driver by calling con_startup(). | ||
3090 | */ | ||
3091 | int register_con_driver(const struct consw *csw, int first, int last) | ||
3092 | { | ||
3093 | struct module *owner = csw->owner; | ||
3094 | struct con_driver *con_driver; | ||
3095 | const char *desc; | ||
3096 | int i, retval = 0; | ||
3097 | |||
3098 | if (!try_module_get(owner)) | ||
3099 | return -ENODEV; | ||
3100 | |||
3101 | acquire_console_sem(); | ||
3102 | |||
3103 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3104 | con_driver = ®istered_con_driver[i]; | ||
3105 | |||
3106 | /* already registered */ | ||
3107 | if (con_driver->con == csw) | ||
3108 | retval = -EINVAL; | ||
3109 | } | ||
3110 | |||
3111 | if (retval) | ||
3112 | goto err; | ||
3113 | |||
3114 | desc = csw->con_startup(); | ||
3115 | |||
3116 | if (!desc) | ||
3117 | goto err; | ||
3118 | |||
3119 | retval = -EINVAL; | ||
3120 | |||
3121 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3122 | con_driver = ®istered_con_driver[i]; | ||
3123 | |||
3124 | if (con_driver->con == NULL) { | ||
3125 | con_driver->con = csw; | ||
3126 | con_driver->desc = desc; | ||
3127 | con_driver->node = i; | ||
3128 | con_driver->flag = CON_DRIVER_FLAG_MODULE | | ||
3129 | CON_DRIVER_FLAG_INIT; | ||
3130 | con_driver->first = first; | ||
3131 | con_driver->last = last; | ||
3132 | retval = 0; | ||
3133 | break; | ||
3134 | } | ||
3135 | } | ||
3136 | |||
3137 | if (retval) | ||
3138 | goto err; | ||
3139 | |||
3140 | con_driver->class_dev = class_device_create(vtconsole_class, NULL, | ||
3141 | MKDEV(0, con_driver->node), | ||
3142 | NULL, "vtcon%i", | ||
3143 | con_driver->node); | ||
3144 | |||
3145 | if (IS_ERR(con_driver->class_dev)) { | ||
3146 | printk(KERN_WARNING "Unable to create class_device for %s; " | ||
3147 | "errno = %ld\n", con_driver->desc, | ||
3148 | PTR_ERR(con_driver->class_dev)); | ||
3149 | con_driver->class_dev = NULL; | ||
3150 | } else { | ||
3151 | vtconsole_init_class_device(con_driver); | ||
3152 | } | ||
3153 | err: | ||
3154 | release_console_sem(); | ||
3155 | module_put(owner); | ||
3156 | return retval; | ||
3157 | } | ||
3158 | EXPORT_SYMBOL(register_con_driver); | ||
3159 | |||
3160 | /** | ||
3161 | * unregister_con_driver - unregister console driver from console layer | ||
3162 | * @csw: console driver | ||
3163 | * | ||
3164 | * DESCRIPTION: All drivers that registers to the console layer must | ||
3165 | * call this function upon exit, or if the console driver is in a state | ||
3166 | * where it won't be able to handle console services, such as the | ||
3167 | * framebuffer console without loaded framebuffer drivers. | ||
3168 | * | ||
3169 | * The driver must unbind first prior to unregistration. | ||
3170 | */ | ||
3171 | int unregister_con_driver(const struct consw *csw) | ||
3172 | { | ||
3173 | int i, retval = -ENODEV; | ||
3174 | |||
3175 | acquire_console_sem(); | ||
3176 | |||
3177 | /* cannot unregister a bound driver */ | ||
3178 | if (con_is_bound(csw)) | ||
3179 | goto err; | ||
3180 | |||
3181 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3182 | struct con_driver *con_driver = ®istered_con_driver[i]; | ||
3183 | |||
3184 | if (con_driver->con == csw && | ||
3185 | con_driver->flag & CON_DRIVER_FLAG_MODULE) { | ||
3186 | vtconsole_deinit_class_device(con_driver); | ||
3187 | class_device_destroy(vtconsole_class, | ||
3188 | MKDEV(0, con_driver->node)); | ||
3189 | con_driver->con = NULL; | ||
3190 | con_driver->desc = NULL; | ||
3191 | con_driver->class_dev = NULL; | ||
3192 | con_driver->node = 0; | ||
3193 | con_driver->flag = 0; | ||
3194 | con_driver->first = 0; | ||
3195 | con_driver->last = 0; | ||
3196 | retval = 0; | ||
3197 | break; | ||
3198 | } | ||
3199 | } | ||
3200 | err: | ||
3201 | release_console_sem(); | ||
3202 | return retval; | ||
3203 | } | ||
3204 | EXPORT_SYMBOL(unregister_con_driver); | ||
3205 | |||
3206 | /* | ||
3207 | * If we support more console drivers, this function is used | ||
3208 | * when a driver wants to take over some existing consoles | ||
3209 | * and become default driver for newly opened ones. | ||
3210 | * | ||
3211 | * take_over_console is basically a register followed by unbind | ||
3212 | */ | ||
3213 | int take_over_console(const struct consw *csw, int first, int last, int deflt) | ||
3214 | { | ||
3215 | int err; | ||
3216 | |||
3217 | err = register_con_driver(csw, first, last); | ||
3218 | |||
3219 | if (!err) | ||
3220 | bind_con_driver(csw, first, last, deflt); | ||
3221 | |||
3222 | return err; | ||
3223 | } | ||
3224 | |||
3225 | /* | ||
3226 | * give_up_console is a wrapper to unregister_con_driver. It will only | ||
3227 | * work if driver is fully unbound. | ||
3228 | */ | ||
3229 | void give_up_console(const struct consw *csw) | ||
3230 | { | ||
3231 | unregister_con_driver(csw); | ||
3232 | } | ||
3233 | |||
3234 | static int __init vtconsole_class_init(void) | ||
3235 | { | ||
3236 | int i; | ||
3237 | |||
3238 | vtconsole_class = class_create(THIS_MODULE, "vtconsole"); | ||
3239 | if (IS_ERR(vtconsole_class)) { | ||
3240 | printk(KERN_WARNING "Unable to create vt console class; " | ||
3241 | "errno = %ld\n", PTR_ERR(vtconsole_class)); | ||
3242 | vtconsole_class = NULL; | ||
3243 | } | ||
3244 | |||
3245 | /* Add system drivers to sysfs */ | ||
3246 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | ||
3247 | struct con_driver *con = ®istered_con_driver[i]; | ||
3248 | |||
3249 | if (con->con && !con->class_dev) { | ||
3250 | con->class_dev = | ||
3251 | class_device_create(vtconsole_class, NULL, | ||
3252 | MKDEV(0, con->node), NULL, | ||
3253 | "vtcon%i", con->node); | ||
3254 | |||
3255 | if (IS_ERR(con->class_dev)) { | ||
3256 | printk(KERN_WARNING "Unable to create " | ||
3257 | "class_device for %s; errno = %ld\n", | ||
3258 | con->desc, PTR_ERR(con->class_dev)); | ||
3259 | con->class_dev = NULL; | ||
3260 | } else { | ||
3261 | vtconsole_init_class_device(con); | ||
3262 | } | ||
2749 | } | 3263 | } |
3264 | } | ||
3265 | |||
3266 | return 0; | ||
2750 | } | 3267 | } |
3268 | postcore_initcall(vtconsole_class_init); | ||
2751 | 3269 | ||
2752 | #endif | 3270 | #endif |
2753 | 3271 | ||