/* * USB Serial Console driver * * Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * Thanks to Randy Dunlap for the original version of this code. * */ #include #include #include #include #include #include #include static int debug; struct usbcons_info { int magic; int break_flag; struct usb_serial_port *port; }; static struct usbcons_info usbcons_info; static struct console usbcons; /* * ------------------------------------------------------------ * USB Serial console driver * * Much of the code here is copied from drivers/char/serial.c * and implements a phony serial console in the same way that * serial.c does so that in case some software queries it, * it will get the same results. * * Things that are different from the way the serial port code * does things, is that we call the lower level usb-serial * driver code to initialize the device, and we set the initial * console speeds based on the command line arguments. * ------------------------------------------------------------ */ /* * The parsing of the command line works exactly like the * serial.c code, except that the specifier is "ttyUSB" instead * of "ttyS". */ static int usb_console_setup(struct console *co, char *options) { struct usbcons_info *info = &usbcons_info; int baud = 9600; int bits = 8; int parity = 'n'; int doflow = 0; int cflag = CREAD | HUPCL | CLOCAL; char *s; struct usb_serial *serial; struct usb_serial_port *port; int retval = 0; struct tty_struct *tty = NULL; struct ktermios *termios = NULL, dummy; dbg("%s", __func__); if (options) { baud = simple_strtoul(options, NULL, 10); s = options; while (*s >= '0' && *s <= '9') s++; if (*s) parity = *s++; if (*s) bits = *s++ - '0'; if (*s) doflow = (*s++ == 'r'); } /* build a cflag setting */ switch (baud) { case 1200: cflag |= B1200; break; case 2400: cflag |= B2400; break; case 4800: cflag |= B4800; break; case 19200: cflag |= B19200; break; case 38400: cflag |= B38400; break; case 57600: cflag |= B57600; break; case 115200: cflag |= B115200; break; case 9600: default: cflag |= B9600; /* * Set this to a sane value to prevent a divide error */ baud = 9600; break; } switch (bits) { case 7: cflag |= CS7; break; default: case 8: cflag |= CS8; break; } switch (parity) { case 'o': case 'O': cflag |= PARODD; break; case 'e': case 'E': cflag |= PARENB; break; } co->cflag = cflag; /* * no need to check the index here: if the index is wrong, console * code won't call us */ serial = usb_serial_get_by_index(co->index); if (serial == NULL) { /* no device is connected yet, sorry :( */ err("No USB device connected to ttyUSB%i", co->index); return -ENODEV; } port = serial->port[0]; port->port.tty = NULL; info->port = port; ++port->port.count; if (port->port.count == 1) { if (serial->type->set_termios) { /* * allocate a fake tty so the driver can initialize * the termios structure, then later call set_termios to * configure according to command line arguments */ tty = kzalloc(sizeof(*tty), GFP_KERNEL); if (!tty) { retval = -ENOMEM; err("no more memory"); goto reset_open_count; } termios = kzalloc(sizeof(*termios), GFP_KERNEL); if (!termios) { retval = -ENOMEM; err("no more memory"); goto free_tty; } memset(&dummy, 0, sizeof(struct ktermios)); tty->termios = termios; port->port.tty = tty; } /* only call the device specific open if this * is the first time the port is opened */ if (serial->type->open) retval = serial->type->open(NULL, port, NULL); else retval = usb_serial_generic_open(NULL, port, NULL); if (retval) { err("could not open USB console port"); goto free_termios; } if (serial->type->set_termios) { termios->c_cflag = cflag; serial->type->set_termios(NULL, port, &dummy); port->port.tty = NULL; kfree(termios); kfree(tty); } } port->console = 1; retval = 0; out: return retval; free_termios: kfree(termios); port->port.tty = NULL; free_tty: kfree(tty); reset_open_count: port->port.count = 0; goto out; } static void usb_console_write(struct console *co, const char *buf, unsigned count) { static struct usbcons_info *info = &usbcons_info; struct usb_serial_port *port = info->port; struct usb_serial *serial; int retval = -ENODEV; if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED) return; serial = port->serial; if (count == 0) return; dbg("%s - port %d, %d byte(s)", __func__, port->number, count); if (!port->port.count) { dbg("%s - port not opened", __func__); return; } while (count) { unsigned int i; unsigned int lf; /* search for LF so we can insert CR if necessary */ for (i = 0, lf = 0 ; i < count ; i++) { if (*(buf + i) == 10) { lf = 1; i++; break; } } /* pass on to the driver specific version of this function if it is available */ if (serial->type->write) retval = serial->type->write(NULL, port, buf, i); else retval = usb_serial_generic_write(NULL, port, buf, i); dbg("%s - return value : %d", __func__, retval); if (lf) { /* append CR after LF */ unsigned char cr = 13; if (serial->type->write) retval = serial->type->write(NULL, port, &cr, 1); else retval = usb_serial_generic_write(NULL, port, &cr, 1); dbg("%s - return value : %d", __func__, retval); } buf += i; count -= i; } } static struct console usbcons = { .name = "ttyUSB", .write = usb_console_write, .setup = usb_console_setup, .flags = CON_PRINTBUFFER, .index = -1, }; void usb_serial_console_disconnect(struct usb_serial *serial) { if (serial && serial->port && serial->port[0] && serial->port[0] == usbcons_info.port) { usb_serial_console_exit(); usb_serial_put(serial); } } void usb_serial_console_init(int serial_debug, int minor) { debug = serial_debug; if (minor == 0) { /* * Call register_console() if this is the first device plugged * in. If we call it earlier, then the callback to * console_setup() will fail, as there is not a device seen by * the USB subsystem yet. */ /* * Register console. * NOTES: * console_setup() is called (back) immediately (from * register_console). console_write() is called immediately * from register_console iff CON_PRINTBUFFER is set in flags. */ dbg("registering the USB serial console."); register_console(&usbcons); } } void usb_serial_console_exit(void) { if (usbcons_info.port) { unregister_console(&usbcons); if (usbcons_info.port->port.count) usbcons_info.port->port.count--; usbcons_info.port = NULL; } }