1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) |
4 | */ |
5 | |
6 | #include <linux/fs.h> |
7 | #include <linux/tty.h> |
8 | #include <linux/tty_driver.h> |
9 | #include <linux/major.h> |
10 | #include <linux/mm.h> |
11 | #include <linux/init.h> |
12 | #include <linux/console.h> |
13 | #include <asm/termbits.h> |
14 | #include <asm/irq.h> |
15 | #include "chan.h" |
16 | #include <init.h> |
17 | #include <irq_user.h> |
18 | #include "mconsole_kern.h" |
19 | |
20 | static const int ssl_version = 1; |
21 | |
22 | #define NR_PORTS 64 |
23 | |
24 | static void ssl_announce(char *dev_name, int dev) |
25 | { |
26 | printk(KERN_INFO "Serial line %d assigned device '%s'\n" , dev, |
27 | dev_name); |
28 | } |
29 | |
30 | /* Almost const, except that xterm_title may be changed in an initcall */ |
31 | static struct chan_opts opts = { |
32 | .announce = ssl_announce, |
33 | .xterm_title = "Serial Line #%d" , |
34 | .raw = 1, |
35 | }; |
36 | |
37 | static int ssl_config(char *str, char **error_out); |
38 | static int ssl_get_config(char *dev, char *str, int size, char **error_out); |
39 | static int ssl_remove(int n, char **error_out); |
40 | |
41 | |
42 | /* Const, except for .mc.list */ |
43 | static struct line_driver driver = { |
44 | .name = "UML serial line" , |
45 | .device_name = "ttyS" , |
46 | .major = TTY_MAJOR, |
47 | .minor_start = 64, |
48 | .type = TTY_DRIVER_TYPE_SERIAL, |
49 | .subtype = 0, |
50 | .read_irq_name = "ssl" , |
51 | .write_irq_name = "ssl-write" , |
52 | .mc = { |
53 | .list = LIST_HEAD_INIT(driver.mc.list), |
54 | .name = "ssl" , |
55 | .config = ssl_config, |
56 | .get_config = ssl_get_config, |
57 | .id = line_id, |
58 | .remove = ssl_remove, |
59 | }, |
60 | }; |
61 | |
62 | /* The array is initialized by line_init, at initcall time. The |
63 | * elements are locked individually as needed. |
64 | */ |
65 | static char *conf[NR_PORTS]; |
66 | static char *def_conf = CONFIG_SSL_CHAN; |
67 | static struct line serial_lines[NR_PORTS]; |
68 | |
69 | static int ssl_config(char *str, char **error_out) |
70 | { |
71 | return line_config(lines: serial_lines, ARRAY_SIZE(serial_lines), str, opts: &opts, |
72 | error_out); |
73 | } |
74 | |
75 | static int ssl_get_config(char *dev, char *str, int size, char **error_out) |
76 | { |
77 | return line_get_config(dev, lines: serial_lines, ARRAY_SIZE(serial_lines), str, |
78 | size, error_out); |
79 | } |
80 | |
81 | static int ssl_remove(int n, char **error_out) |
82 | { |
83 | return line_remove(lines: serial_lines, ARRAY_SIZE(serial_lines), n, |
84 | error_out); |
85 | } |
86 | |
87 | static int ssl_install(struct tty_driver *driver, struct tty_struct *tty) |
88 | { |
89 | return line_install(driver, tty, line: &serial_lines[tty->index]); |
90 | } |
91 | |
92 | static const struct tty_operations ssl_ops = { |
93 | .open = line_open, |
94 | .close = line_close, |
95 | .write = line_write, |
96 | .write_room = line_write_room, |
97 | .chars_in_buffer = line_chars_in_buffer, |
98 | .flush_buffer = line_flush_buffer, |
99 | .flush_chars = line_flush_chars, |
100 | .throttle = line_throttle, |
101 | .unthrottle = line_unthrottle, |
102 | .install = ssl_install, |
103 | .hangup = line_hangup, |
104 | }; |
105 | |
106 | /* Changed by ssl_init and referenced by ssl_exit, which are both serialized |
107 | * by being an initcall and exitcall, respectively. |
108 | */ |
109 | static int ssl_init_done; |
110 | |
111 | static void ssl_console_write(struct console *c, const char *string, |
112 | unsigned len) |
113 | { |
114 | struct line *line = &serial_lines[c->index]; |
115 | unsigned long flags; |
116 | |
117 | spin_lock_irqsave(&line->lock, flags); |
118 | console_write_chan(chan: line->chan_out, buf: string, len); |
119 | spin_unlock_irqrestore(lock: &line->lock, flags); |
120 | } |
121 | |
122 | static struct tty_driver *ssl_console_device(struct console *c, int *index) |
123 | { |
124 | *index = c->index; |
125 | return driver.driver; |
126 | } |
127 | |
128 | static int ssl_console_setup(struct console *co, char *options) |
129 | { |
130 | struct line *line = &serial_lines[co->index]; |
131 | |
132 | return console_open_chan(line, co); |
133 | } |
134 | |
135 | /* No locking for register_console call - relies on single-threaded initcalls */ |
136 | static struct console ssl_cons = { |
137 | .name = "ttyS" , |
138 | .write = ssl_console_write, |
139 | .device = ssl_console_device, |
140 | .setup = ssl_console_setup, |
141 | .flags = CON_PRINTBUFFER|CON_ANYTIME, |
142 | .index = -1, |
143 | }; |
144 | |
145 | static int ssl_init(void) |
146 | { |
147 | char *new_title; |
148 | int err; |
149 | int i; |
150 | |
151 | printk(KERN_INFO "Initializing software serial port version %d\n" , |
152 | ssl_version); |
153 | |
154 | err = register_lines(line_driver: &driver, driver: &ssl_ops, lines: serial_lines, |
155 | ARRAY_SIZE(serial_lines)); |
156 | if (err) |
157 | return err; |
158 | |
159 | new_title = add_xterm_umid(base: opts.xterm_title); |
160 | if (new_title != NULL) |
161 | opts.xterm_title = new_title; |
162 | |
163 | for (i = 0; i < NR_PORTS; i++) { |
164 | char *error; |
165 | char *s = conf[i]; |
166 | if (!s) |
167 | s = def_conf; |
168 | if (setup_one_line(lines: serial_lines, n: i, init: s, opts: &opts, error_out: &error)) |
169 | printk(KERN_ERR "setup_one_line failed for " |
170 | "device %d : %s\n" , i, error); |
171 | } |
172 | |
173 | ssl_init_done = 1; |
174 | register_console(&ssl_cons); |
175 | return 0; |
176 | } |
177 | late_initcall(ssl_init); |
178 | |
179 | static void ssl_exit(void) |
180 | { |
181 | if (!ssl_init_done) |
182 | return; |
183 | close_lines(lines: serial_lines, ARRAY_SIZE(serial_lines)); |
184 | } |
185 | __uml_exitcall(ssl_exit); |
186 | |
187 | static int ssl_chan_setup(char *str) |
188 | { |
189 | line_setup(conf, NR_PORTS, def: &def_conf, init: str, name: "serial line" ); |
190 | return 1; |
191 | } |
192 | |
193 | __setup("ssl" , ssl_chan_setup); |
194 | __channel_help(ssl_chan_setup, "ssl" ); |
195 | |
196 | static int ssl_non_raw_setup(char *str) |
197 | { |
198 | opts.raw = 0; |
199 | return 1; |
200 | } |
201 | __setup("ssl-non-raw" , ssl_non_raw_setup); |
202 | __channel_help(ssl_non_raw_setup, "set serial lines to non-raw mode" ); |
203 | |