1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * USB Serial Console driver |
4 | * |
5 | * Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com) |
6 | * |
7 | * Thanks to Randy Dunlap for the original version of this code. |
8 | * |
9 | */ |
10 | |
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/tty.h> |
17 | #include <linux/console.h> |
18 | #include <linux/serial.h> |
19 | #include <linux/usb.h> |
20 | #include <linux/usb/serial.h> |
21 | |
22 | struct usbcons_info { |
23 | int magic; |
24 | int break_flag; |
25 | struct usb_serial_port *port; |
26 | }; |
27 | |
28 | static struct usbcons_info usbcons_info; |
29 | static struct console usbcons; |
30 | |
31 | /* |
32 | * ------------------------------------------------------------ |
33 | * USB Serial console driver |
34 | * |
35 | * Much of the code here is copied from drivers/char/serial.c |
36 | * and implements a phony serial console in the same way that |
37 | * serial.c does so that in case some software queries it, |
38 | * it will get the same results. |
39 | * |
40 | * Things that are different from the way the serial port code |
41 | * does things, is that we call the lower level usb-serial |
42 | * driver code to initialize the device, and we set the initial |
43 | * console speeds based on the command line arguments. |
44 | * ------------------------------------------------------------ |
45 | */ |
46 | |
47 | static const struct tty_operations usb_console_fake_tty_ops = { |
48 | }; |
49 | |
50 | /* |
51 | * The parsing of the command line works exactly like the |
52 | * serial.c code, except that the specifier is "ttyUSB" instead |
53 | * of "ttyS". |
54 | */ |
55 | static int usb_console_setup(struct console *co, char *options) |
56 | { |
57 | struct usbcons_info *info = &usbcons_info; |
58 | int baud = 9600; |
59 | int bits = 8; |
60 | int parity = 'n'; |
61 | int doflow = 0; |
62 | int cflag = CREAD | HUPCL | CLOCAL; |
63 | char *s; |
64 | struct usb_serial *serial; |
65 | struct usb_serial_port *port; |
66 | int retval; |
67 | struct tty_struct *tty = NULL; |
68 | struct ktermios dummy; |
69 | |
70 | if (options) { |
71 | baud = simple_strtoul(options, NULL, 10); |
72 | s = options; |
73 | while (*s >= '0' && *s <= '9') |
74 | s++; |
75 | if (*s) |
76 | parity = *s++; |
77 | if (*s) |
78 | bits = *s++ - '0'; |
79 | if (*s) |
80 | doflow = (*s++ == 'r'); |
81 | } |
82 | |
83 | /* Sane default */ |
84 | if (baud == 0) |
85 | baud = 9600; |
86 | |
87 | switch (bits) { |
88 | case 7: |
89 | cflag |= CS7; |
90 | break; |
91 | default: |
92 | case 8: |
93 | cflag |= CS8; |
94 | break; |
95 | } |
96 | switch (parity) { |
97 | case 'o': case 'O': |
98 | cflag |= PARODD; |
99 | break; |
100 | case 'e': case 'E': |
101 | cflag |= PARENB; |
102 | break; |
103 | } |
104 | |
105 | if (doflow) |
106 | cflag |= CRTSCTS; |
107 | |
108 | /* |
109 | * no need to check the index here: if the index is wrong, console |
110 | * code won't call us |
111 | */ |
112 | port = usb_serial_port_get_by_minor(minor: co->index); |
113 | if (port == NULL) { |
114 | /* no device is connected yet, sorry :( */ |
115 | pr_err("No USB device connected to ttyUSB%i\n" , co->index); |
116 | return -ENODEV; |
117 | } |
118 | serial = port->serial; |
119 | |
120 | retval = usb_autopm_get_interface(intf: serial->interface); |
121 | if (retval) |
122 | goto error_get_interface; |
123 | |
124 | tty_port_tty_set(port: &port->port, NULL); |
125 | |
126 | info->port = port; |
127 | |
128 | ++port->port.count; |
129 | if (!tty_port_initialized(port: &port->port)) { |
130 | if (serial->type->set_termios) { |
131 | /* |
132 | * allocate a fake tty so the driver can initialize |
133 | * the termios structure, then later call set_termios to |
134 | * configure according to command line arguments |
135 | */ |
136 | tty = kzalloc(size: sizeof(*tty), GFP_KERNEL); |
137 | if (!tty) { |
138 | retval = -ENOMEM; |
139 | goto reset_open_count; |
140 | } |
141 | kref_init(kref: &tty->kref); |
142 | tty->driver = usb_serial_tty_driver; |
143 | tty->index = co->index; |
144 | init_ldsem(&tty->ldisc_sem); |
145 | spin_lock_init(&tty->files_lock); |
146 | INIT_LIST_HEAD(list: &tty->tty_files); |
147 | kref_get(kref: &tty->driver->kref); |
148 | __module_get(module: tty->driver->owner); |
149 | tty->ops = &usb_console_fake_tty_ops; |
150 | tty_init_termios(tty); |
151 | tty_port_tty_set(port: &port->port, tty); |
152 | } |
153 | |
154 | /* only call the device specific open if this |
155 | * is the first time the port is opened */ |
156 | retval = serial->type->open(NULL, port); |
157 | if (retval) { |
158 | dev_err(&port->dev, "could not open USB console port\n" ); |
159 | goto fail; |
160 | } |
161 | |
162 | if (serial->type->set_termios) { |
163 | tty->termios.c_cflag = cflag; |
164 | tty_termios_encode_baud_rate(termios: &tty->termios, ibaud: baud, obaud: baud); |
165 | memset(&dummy, 0, sizeof(struct ktermios)); |
166 | serial->type->set_termios(tty, port, &dummy); |
167 | |
168 | tty_port_tty_set(port: &port->port, NULL); |
169 | tty_save_termios(tty); |
170 | tty_kref_put(tty); |
171 | } |
172 | tty_port_set_initialized(port: &port->port, val: true); |
173 | } |
174 | /* Now that any required fake tty operations are completed restore |
175 | * the tty port count */ |
176 | --port->port.count; |
177 | /* The console is special in terms of closing the device so |
178 | * indicate this port is now acting as a system console. */ |
179 | port->port.console = 1; |
180 | |
181 | mutex_unlock(lock: &serial->disc_mutex); |
182 | return retval; |
183 | |
184 | fail: |
185 | tty_port_tty_set(port: &port->port, NULL); |
186 | tty_kref_put(tty); |
187 | reset_open_count: |
188 | port->port.count = 0; |
189 | info->port = NULL; |
190 | usb_autopm_put_interface(intf: serial->interface); |
191 | error_get_interface: |
192 | mutex_unlock(lock: &serial->disc_mutex); |
193 | usb_serial_put(serial); |
194 | return retval; |
195 | } |
196 | |
197 | static void usb_console_write(struct console *co, |
198 | const char *buf, unsigned count) |
199 | { |
200 | static struct usbcons_info *info = &usbcons_info; |
201 | struct usb_serial_port *port = info->port; |
202 | struct usb_serial *serial; |
203 | int retval = -ENODEV; |
204 | |
205 | if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED) |
206 | return; |
207 | serial = port->serial; |
208 | |
209 | if (count == 0) |
210 | return; |
211 | |
212 | dev_dbg(&port->dev, "%s - %d byte(s)\n" , __func__, count); |
213 | |
214 | if (!port->port.console) { |
215 | dev_dbg(&port->dev, "%s - port not opened\n" , __func__); |
216 | return; |
217 | } |
218 | |
219 | while (count) { |
220 | unsigned int i; |
221 | unsigned int lf; |
222 | /* search for LF so we can insert CR if necessary */ |
223 | for (i = 0, lf = 0 ; i < count ; i++) { |
224 | if (*(buf + i) == 10) { |
225 | lf = 1; |
226 | i++; |
227 | break; |
228 | } |
229 | } |
230 | /* pass on to the driver specific version of this function if |
231 | it is available */ |
232 | retval = serial->type->write(NULL, port, buf, i); |
233 | dev_dbg(&port->dev, "%s - write: %d\n" , __func__, retval); |
234 | if (lf) { |
235 | /* append CR after LF */ |
236 | unsigned char cr = 13; |
237 | retval = serial->type->write(NULL, port, &cr, 1); |
238 | dev_dbg(&port->dev, "%s - write cr: %d\n" , |
239 | __func__, retval); |
240 | } |
241 | buf += i; |
242 | count -= i; |
243 | } |
244 | } |
245 | |
246 | static struct tty_driver *usb_console_device(struct console *co, int *index) |
247 | { |
248 | struct tty_driver **p = (struct tty_driver **)co->data; |
249 | |
250 | if (!*p) |
251 | return NULL; |
252 | |
253 | *index = co->index; |
254 | return *p; |
255 | } |
256 | |
257 | static struct console usbcons = { |
258 | .name = "ttyUSB" , |
259 | .write = usb_console_write, |
260 | .device = usb_console_device, |
261 | .setup = usb_console_setup, |
262 | .flags = CON_PRINTBUFFER, |
263 | .index = -1, |
264 | .data = &usb_serial_tty_driver, |
265 | }; |
266 | |
267 | void usb_serial_console_disconnect(struct usb_serial *serial) |
268 | { |
269 | if (serial->port[0] && serial->port[0] == usbcons_info.port) { |
270 | usb_serial_console_exit(); |
271 | usb_serial_put(serial); |
272 | } |
273 | } |
274 | |
275 | void usb_serial_console_init(int minor) |
276 | { |
277 | if (minor == 0) { |
278 | /* |
279 | * Call register_console() if this is the first device plugged |
280 | * in. If we call it earlier, then the callback to |
281 | * console_setup() will fail, as there is not a device seen by |
282 | * the USB subsystem yet. |
283 | */ |
284 | /* |
285 | * Register console. |
286 | * NOTES: |
287 | * console_setup() is called (back) immediately (from |
288 | * register_console). console_write() is called immediately |
289 | * from register_console iff CON_PRINTBUFFER is set in flags. |
290 | */ |
291 | pr_debug("registering the USB serial console.\n" ); |
292 | register_console(&usbcons); |
293 | } |
294 | } |
295 | |
296 | void usb_serial_console_exit(void) |
297 | { |
298 | if (usbcons_info.port) { |
299 | unregister_console(&usbcons); |
300 | usbcons_info.port->port.console = 0; |
301 | usbcons_info.port = NULL; |
302 | } |
303 | } |
304 | |
305 | |