162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * USB Serial Console driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Thanks to Randy Dunlap for the original version of this code. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/tty.h> 1762306a36Sopenharmony_ci#include <linux/console.h> 1862306a36Sopenharmony_ci#include <linux/serial.h> 1962306a36Sopenharmony_ci#include <linux/usb.h> 2062306a36Sopenharmony_ci#include <linux/usb/serial.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct usbcons_info { 2362306a36Sopenharmony_ci int magic; 2462306a36Sopenharmony_ci int break_flag; 2562306a36Sopenharmony_ci struct usb_serial_port *port; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct usbcons_info usbcons_info; 2962306a36Sopenharmony_cistatic struct console usbcons; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * ------------------------------------------------------------ 3362306a36Sopenharmony_ci * USB Serial console driver 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Much of the code here is copied from drivers/char/serial.c 3662306a36Sopenharmony_ci * and implements a phony serial console in the same way that 3762306a36Sopenharmony_ci * serial.c does so that in case some software queries it, 3862306a36Sopenharmony_ci * it will get the same results. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Things that are different from the way the serial port code 4162306a36Sopenharmony_ci * does things, is that we call the lower level usb-serial 4262306a36Sopenharmony_ci * driver code to initialize the device, and we set the initial 4362306a36Sopenharmony_ci * console speeds based on the command line arguments. 4462306a36Sopenharmony_ci * ------------------------------------------------------------ 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const struct tty_operations usb_console_fake_tty_ops = { 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * The parsing of the command line works exactly like the 5262306a36Sopenharmony_ci * serial.c code, except that the specifier is "ttyUSB" instead 5362306a36Sopenharmony_ci * of "ttyS". 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistatic int usb_console_setup(struct console *co, char *options) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct usbcons_info *info = &usbcons_info; 5862306a36Sopenharmony_ci int baud = 9600; 5962306a36Sopenharmony_ci int bits = 8; 6062306a36Sopenharmony_ci int parity = 'n'; 6162306a36Sopenharmony_ci int doflow = 0; 6262306a36Sopenharmony_ci int cflag = CREAD | HUPCL | CLOCAL; 6362306a36Sopenharmony_ci char *s; 6462306a36Sopenharmony_ci struct usb_serial *serial; 6562306a36Sopenharmony_ci struct usb_serial_port *port; 6662306a36Sopenharmony_ci int retval; 6762306a36Sopenharmony_ci struct tty_struct *tty = NULL; 6862306a36Sopenharmony_ci struct ktermios dummy; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (options) { 7162306a36Sopenharmony_ci baud = simple_strtoul(options, NULL, 10); 7262306a36Sopenharmony_ci s = options; 7362306a36Sopenharmony_ci while (*s >= '0' && *s <= '9') 7462306a36Sopenharmony_ci s++; 7562306a36Sopenharmony_ci if (*s) 7662306a36Sopenharmony_ci parity = *s++; 7762306a36Sopenharmony_ci if (*s) 7862306a36Sopenharmony_ci bits = *s++ - '0'; 7962306a36Sopenharmony_ci if (*s) 8062306a36Sopenharmony_ci doflow = (*s++ == 'r'); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Sane default */ 8462306a36Sopenharmony_ci if (baud == 0) 8562306a36Sopenharmony_ci baud = 9600; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci switch (bits) { 8862306a36Sopenharmony_ci case 7: 8962306a36Sopenharmony_ci cflag |= CS7; 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci default: 9262306a36Sopenharmony_ci case 8: 9362306a36Sopenharmony_ci cflag |= CS8; 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci switch (parity) { 9762306a36Sopenharmony_ci case 'o': case 'O': 9862306a36Sopenharmony_ci cflag |= PARODD; 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci case 'e': case 'E': 10162306a36Sopenharmony_ci cflag |= PARENB; 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (doflow) 10662306a36Sopenharmony_ci cflag |= CRTSCTS; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * no need to check the index here: if the index is wrong, console 11062306a36Sopenharmony_ci * code won't call us 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci port = usb_serial_port_get_by_minor(co->index); 11362306a36Sopenharmony_ci if (port == NULL) { 11462306a36Sopenharmony_ci /* no device is connected yet, sorry :( */ 11562306a36Sopenharmony_ci pr_err("No USB device connected to ttyUSB%i\n", co->index); 11662306a36Sopenharmony_ci return -ENODEV; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci serial = port->serial; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci retval = usb_autopm_get_interface(serial->interface); 12162306a36Sopenharmony_ci if (retval) 12262306a36Sopenharmony_ci goto error_get_interface; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci tty_port_tty_set(&port->port, NULL); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci info->port = port; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ++port->port.count; 12962306a36Sopenharmony_ci if (!tty_port_initialized(&port->port)) { 13062306a36Sopenharmony_ci if (serial->type->set_termios) { 13162306a36Sopenharmony_ci /* 13262306a36Sopenharmony_ci * allocate a fake tty so the driver can initialize 13362306a36Sopenharmony_ci * the termios structure, then later call set_termios to 13462306a36Sopenharmony_ci * configure according to command line arguments 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci tty = kzalloc(sizeof(*tty), GFP_KERNEL); 13762306a36Sopenharmony_ci if (!tty) { 13862306a36Sopenharmony_ci retval = -ENOMEM; 13962306a36Sopenharmony_ci goto reset_open_count; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci kref_init(&tty->kref); 14262306a36Sopenharmony_ci tty->driver = usb_serial_tty_driver; 14362306a36Sopenharmony_ci tty->index = co->index; 14462306a36Sopenharmony_ci init_ldsem(&tty->ldisc_sem); 14562306a36Sopenharmony_ci spin_lock_init(&tty->files_lock); 14662306a36Sopenharmony_ci INIT_LIST_HEAD(&tty->tty_files); 14762306a36Sopenharmony_ci kref_get(&tty->driver->kref); 14862306a36Sopenharmony_ci __module_get(tty->driver->owner); 14962306a36Sopenharmony_ci tty->ops = &usb_console_fake_tty_ops; 15062306a36Sopenharmony_ci tty_init_termios(tty); 15162306a36Sopenharmony_ci tty_port_tty_set(&port->port, tty); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* only call the device specific open if this 15562306a36Sopenharmony_ci * is the first time the port is opened */ 15662306a36Sopenharmony_ci retval = serial->type->open(NULL, port); 15762306a36Sopenharmony_ci if (retval) { 15862306a36Sopenharmony_ci dev_err(&port->dev, "could not open USB console port\n"); 15962306a36Sopenharmony_ci goto fail; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (serial->type->set_termios) { 16362306a36Sopenharmony_ci tty->termios.c_cflag = cflag; 16462306a36Sopenharmony_ci tty_termios_encode_baud_rate(&tty->termios, baud, baud); 16562306a36Sopenharmony_ci memset(&dummy, 0, sizeof(struct ktermios)); 16662306a36Sopenharmony_ci serial->type->set_termios(tty, port, &dummy); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci tty_port_tty_set(&port->port, NULL); 16962306a36Sopenharmony_ci tty_save_termios(tty); 17062306a36Sopenharmony_ci tty_kref_put(tty); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci tty_port_set_initialized(&port->port, true); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci /* Now that any required fake tty operations are completed restore 17562306a36Sopenharmony_ci * the tty port count */ 17662306a36Sopenharmony_ci --port->port.count; 17762306a36Sopenharmony_ci /* The console is special in terms of closing the device so 17862306a36Sopenharmony_ci * indicate this port is now acting as a system console. */ 17962306a36Sopenharmony_ci port->port.console = 1; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci mutex_unlock(&serial->disc_mutex); 18262306a36Sopenharmony_ci return retval; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci fail: 18562306a36Sopenharmony_ci tty_port_tty_set(&port->port, NULL); 18662306a36Sopenharmony_ci tty_kref_put(tty); 18762306a36Sopenharmony_ci reset_open_count: 18862306a36Sopenharmony_ci port->port.count = 0; 18962306a36Sopenharmony_ci info->port = NULL; 19062306a36Sopenharmony_ci usb_autopm_put_interface(serial->interface); 19162306a36Sopenharmony_ci error_get_interface: 19262306a36Sopenharmony_ci mutex_unlock(&serial->disc_mutex); 19362306a36Sopenharmony_ci usb_serial_put(serial); 19462306a36Sopenharmony_ci return retval; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void usb_console_write(struct console *co, 19862306a36Sopenharmony_ci const char *buf, unsigned count) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci static struct usbcons_info *info = &usbcons_info; 20162306a36Sopenharmony_ci struct usb_serial_port *port = info->port; 20262306a36Sopenharmony_ci struct usb_serial *serial; 20362306a36Sopenharmony_ci int retval = -ENODEV; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED) 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci serial = port->serial; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (count == 0) 21062306a36Sopenharmony_ci return; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - %d byte(s)\n", __func__, count); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!port->port.console) { 21562306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - port not opened\n", __func__); 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci while (count) { 22062306a36Sopenharmony_ci unsigned int i; 22162306a36Sopenharmony_ci unsigned int lf; 22262306a36Sopenharmony_ci /* search for LF so we can insert CR if necessary */ 22362306a36Sopenharmony_ci for (i = 0, lf = 0 ; i < count ; i++) { 22462306a36Sopenharmony_ci if (*(buf + i) == 10) { 22562306a36Sopenharmony_ci lf = 1; 22662306a36Sopenharmony_ci i++; 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci /* pass on to the driver specific version of this function if 23162306a36Sopenharmony_ci it is available */ 23262306a36Sopenharmony_ci retval = serial->type->write(NULL, port, buf, i); 23362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - write: %d\n", __func__, retval); 23462306a36Sopenharmony_ci if (lf) { 23562306a36Sopenharmony_ci /* append CR after LF */ 23662306a36Sopenharmony_ci unsigned char cr = 13; 23762306a36Sopenharmony_ci retval = serial->type->write(NULL, port, &cr, 1); 23862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - write cr: %d\n", 23962306a36Sopenharmony_ci __func__, retval); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci buf += i; 24262306a36Sopenharmony_ci count -= i; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct tty_driver *usb_console_device(struct console *co, int *index) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct tty_driver **p = (struct tty_driver **)co->data; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (!*p) 25162306a36Sopenharmony_ci return NULL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci *index = co->index; 25462306a36Sopenharmony_ci return *p; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic struct console usbcons = { 25862306a36Sopenharmony_ci .name = "ttyUSB", 25962306a36Sopenharmony_ci .write = usb_console_write, 26062306a36Sopenharmony_ci .device = usb_console_device, 26162306a36Sopenharmony_ci .setup = usb_console_setup, 26262306a36Sopenharmony_ci .flags = CON_PRINTBUFFER, 26362306a36Sopenharmony_ci .index = -1, 26462306a36Sopenharmony_ci .data = &usb_serial_tty_driver, 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_civoid usb_serial_console_disconnect(struct usb_serial *serial) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci if (serial->port[0] && serial->port[0] == usbcons_info.port) { 27062306a36Sopenharmony_ci usb_serial_console_exit(); 27162306a36Sopenharmony_ci usb_serial_put(serial); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_civoid usb_serial_console_init(int minor) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci if (minor == 0) { 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * Call register_console() if this is the first device plugged 28062306a36Sopenharmony_ci * in. If we call it earlier, then the callback to 28162306a36Sopenharmony_ci * console_setup() will fail, as there is not a device seen by 28262306a36Sopenharmony_ci * the USB subsystem yet. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * Register console. 28662306a36Sopenharmony_ci * NOTES: 28762306a36Sopenharmony_ci * console_setup() is called (back) immediately (from 28862306a36Sopenharmony_ci * register_console). console_write() is called immediately 28962306a36Sopenharmony_ci * from register_console iff CON_PRINTBUFFER is set in flags. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci pr_debug("registering the USB serial console.\n"); 29262306a36Sopenharmony_ci register_console(&usbcons); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_civoid usb_serial_console_exit(void) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci if (usbcons_info.port) { 29962306a36Sopenharmony_ci unregister_console(&usbcons); 30062306a36Sopenharmony_ci usbcons_info.port->port.console = 0; 30162306a36Sopenharmony_ci usbcons_info.port = NULL; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 305