1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/posix_types.h>
7*4882a593Smuzhiyun #include <linux/tty.h>
8*4882a593Smuzhiyun #include <linux/tty_flip.h>
9*4882a593Smuzhiyun #include <linux/types.h>
10*4882a593Smuzhiyun #include <linux/major.h>
11*4882a593Smuzhiyun #include <linux/kdev_t.h>
12*4882a593Smuzhiyun #include <linux/console.h>
13*4882a593Smuzhiyun #include <linux/string.h>
14*4882a593Smuzhiyun #include <linux/sched.h>
15*4882a593Smuzhiyun #include <linux/list.h>
16*4882a593Smuzhiyun #include <linux/init.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun #include <linux/hardirq.h>
20*4882a593Smuzhiyun #include <asm/current.h>
21*4882a593Smuzhiyun #include <asm/irq.h>
22*4882a593Smuzhiyun #include "stdio_console.h"
23*4882a593Smuzhiyun #include "chan.h"
24*4882a593Smuzhiyun #include <irq_user.h>
25*4882a593Smuzhiyun #include "mconsole_kern.h"
26*4882a593Smuzhiyun #include <init.h>
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define MAX_TTYS (16)
29*4882a593Smuzhiyun
stdio_announce(char * dev_name,int dev)30*4882a593Smuzhiyun static void stdio_announce(char *dev_name, int dev)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun printk(KERN_INFO "Virtual console %d assigned device '%s'\n", dev,
33*4882a593Smuzhiyun dev_name);
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /* Almost const, except that xterm_title may be changed in an initcall */
37*4882a593Smuzhiyun static struct chan_opts opts = {
38*4882a593Smuzhiyun .announce = stdio_announce,
39*4882a593Smuzhiyun .xterm_title = "Virtual Console #%d",
40*4882a593Smuzhiyun .raw = 1,
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun static int con_config(char *str, char **error_out);
44*4882a593Smuzhiyun static int con_get_config(char *dev, char *str, int size, char **error_out);
45*4882a593Smuzhiyun static int con_remove(int n, char **con_remove);
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun /* Const, except for .mc.list */
49*4882a593Smuzhiyun static struct line_driver driver = {
50*4882a593Smuzhiyun .name = "UML console",
51*4882a593Smuzhiyun .device_name = "tty",
52*4882a593Smuzhiyun .major = TTY_MAJOR,
53*4882a593Smuzhiyun .minor_start = 0,
54*4882a593Smuzhiyun .type = TTY_DRIVER_TYPE_CONSOLE,
55*4882a593Smuzhiyun .subtype = SYSTEM_TYPE_CONSOLE,
56*4882a593Smuzhiyun .read_irq = CONSOLE_IRQ,
57*4882a593Smuzhiyun .read_irq_name = "console",
58*4882a593Smuzhiyun .write_irq = CONSOLE_WRITE_IRQ,
59*4882a593Smuzhiyun .write_irq_name = "console-write",
60*4882a593Smuzhiyun .mc = {
61*4882a593Smuzhiyun .list = LIST_HEAD_INIT(driver.mc.list),
62*4882a593Smuzhiyun .name = "con",
63*4882a593Smuzhiyun .config = con_config,
64*4882a593Smuzhiyun .get_config = con_get_config,
65*4882a593Smuzhiyun .id = line_id,
66*4882a593Smuzhiyun .remove = con_remove,
67*4882a593Smuzhiyun },
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun /* The array is initialized by line_init, at initcall time. The
71*4882a593Smuzhiyun * elements are locked individually as needed.
72*4882a593Smuzhiyun */
73*4882a593Smuzhiyun static char *vt_conf[MAX_TTYS];
74*4882a593Smuzhiyun static char *def_conf;
75*4882a593Smuzhiyun static struct line vts[MAX_TTYS];
76*4882a593Smuzhiyun
con_config(char * str,char ** error_out)77*4882a593Smuzhiyun static int con_config(char *str, char **error_out)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun return line_config(vts, ARRAY_SIZE(vts), str, &opts, error_out);
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun
con_get_config(char * dev,char * str,int size,char ** error_out)82*4882a593Smuzhiyun static int con_get_config(char *dev, char *str, int size, char **error_out)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun return line_get_config(dev, vts, ARRAY_SIZE(vts), str, size, error_out);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
con_remove(int n,char ** error_out)87*4882a593Smuzhiyun static int con_remove(int n, char **error_out)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun return line_remove(vts, ARRAY_SIZE(vts), n, error_out);
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun /* Set in an initcall, checked in an exitcall */
93*4882a593Smuzhiyun static int con_init_done = 0;
94*4882a593Smuzhiyun
con_install(struct tty_driver * driver,struct tty_struct * tty)95*4882a593Smuzhiyun static int con_install(struct tty_driver *driver, struct tty_struct *tty)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun return line_install(driver, tty, &vts[tty->index]);
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun static const struct tty_operations console_ops = {
101*4882a593Smuzhiyun .open = line_open,
102*4882a593Smuzhiyun .install = con_install,
103*4882a593Smuzhiyun .close = line_close,
104*4882a593Smuzhiyun .write = line_write,
105*4882a593Smuzhiyun .write_room = line_write_room,
106*4882a593Smuzhiyun .chars_in_buffer = line_chars_in_buffer,
107*4882a593Smuzhiyun .flush_buffer = line_flush_buffer,
108*4882a593Smuzhiyun .flush_chars = line_flush_chars,
109*4882a593Smuzhiyun .set_termios = line_set_termios,
110*4882a593Smuzhiyun .throttle = line_throttle,
111*4882a593Smuzhiyun .unthrottle = line_unthrottle,
112*4882a593Smuzhiyun .hangup = line_hangup,
113*4882a593Smuzhiyun };
114*4882a593Smuzhiyun
uml_console_write(struct console * console,const char * string,unsigned len)115*4882a593Smuzhiyun static void uml_console_write(struct console *console, const char *string,
116*4882a593Smuzhiyun unsigned len)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun struct line *line = &vts[console->index];
119*4882a593Smuzhiyun unsigned long flags;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun spin_lock_irqsave(&line->lock, flags);
122*4882a593Smuzhiyun console_write_chan(line->chan_out, string, len);
123*4882a593Smuzhiyun spin_unlock_irqrestore(&line->lock, flags);
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun
uml_console_device(struct console * c,int * index)126*4882a593Smuzhiyun static struct tty_driver *uml_console_device(struct console *c, int *index)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun *index = c->index;
129*4882a593Smuzhiyun return driver.driver;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
uml_console_setup(struct console * co,char * options)132*4882a593Smuzhiyun static int uml_console_setup(struct console *co, char *options)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun struct line *line = &vts[co->index];
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun return console_open_chan(line, co);
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun /* No locking for register_console call - relies on single-threaded initcalls */
140*4882a593Smuzhiyun static struct console stdiocons = {
141*4882a593Smuzhiyun .name = "tty",
142*4882a593Smuzhiyun .write = uml_console_write,
143*4882a593Smuzhiyun .device = uml_console_device,
144*4882a593Smuzhiyun .setup = uml_console_setup,
145*4882a593Smuzhiyun .flags = CON_PRINTBUFFER|CON_ANYTIME,
146*4882a593Smuzhiyun .index = -1,
147*4882a593Smuzhiyun };
148*4882a593Smuzhiyun
stdio_init(void)149*4882a593Smuzhiyun static int stdio_init(void)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun char *new_title;
152*4882a593Smuzhiyun int err;
153*4882a593Smuzhiyun int i;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun err = register_lines(&driver, &console_ops, vts,
156*4882a593Smuzhiyun ARRAY_SIZE(vts));
157*4882a593Smuzhiyun if (err)
158*4882a593Smuzhiyun return err;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun printk(KERN_INFO "Initialized stdio console driver\n");
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun new_title = add_xterm_umid(opts.xterm_title);
163*4882a593Smuzhiyun if(new_title != NULL)
164*4882a593Smuzhiyun opts.xterm_title = new_title;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun for (i = 0; i < MAX_TTYS; i++) {
167*4882a593Smuzhiyun char *error;
168*4882a593Smuzhiyun char *s = vt_conf[i];
169*4882a593Smuzhiyun if (!s)
170*4882a593Smuzhiyun s = def_conf;
171*4882a593Smuzhiyun if (!s)
172*4882a593Smuzhiyun s = i ? CONFIG_CON_CHAN : CONFIG_CON_ZERO_CHAN;
173*4882a593Smuzhiyun if (setup_one_line(vts, i, s, &opts, &error))
174*4882a593Smuzhiyun printk(KERN_ERR "setup_one_line failed for "
175*4882a593Smuzhiyun "device %d : %s\n", i, error);
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun con_init_done = 1;
179*4882a593Smuzhiyun register_console(&stdiocons);
180*4882a593Smuzhiyun return 0;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun late_initcall(stdio_init);
183*4882a593Smuzhiyun
console_exit(void)184*4882a593Smuzhiyun static void console_exit(void)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun if (!con_init_done)
187*4882a593Smuzhiyun return;
188*4882a593Smuzhiyun close_lines(vts, ARRAY_SIZE(vts));
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun __uml_exitcall(console_exit);
191*4882a593Smuzhiyun
console_chan_setup(char * str)192*4882a593Smuzhiyun static int console_chan_setup(char *str)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun if (!strncmp(str, "sole=", 5)) /* console= option specifies tty */
195*4882a593Smuzhiyun return 0;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun line_setup(vt_conf, MAX_TTYS, &def_conf, str, "console");
198*4882a593Smuzhiyun return 1;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun __setup("con", console_chan_setup);
201*4882a593Smuzhiyun __channel_help(console_chan_setup, "con");
202