| 1 | |
| 2 | |
| 3 | * USB Serial Console driver
|
| 4 | *
|
| 5 | * Copyright (C) 2001 - 2002 Greg Kroah-Hartman ([email protected])
|
| 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 |
|
| 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(co->index);
|
| 113 | if (port == NULL) {
|
| 114 |
|
| 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(serial->interface);
|
| 121 | if (retval)
|
| 122 | goto error_get_interface;
|
| 123 |
|
| 124 | tty_port_tty_set(&port->port, NULL);
|
| 125 |
|
| 126 | info->port = port;
|
| 127 |
|
| 128 | ++port->port.count;
|
| 129 | if (!tty_port_initialized(&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_obj(*tty);
|
| 137 | if (!tty) {
|
| 138 | retval = -ENOMEM;
|
| 139 | goto reset_open_count;
|
| 140 | }
|
| 141 | kref_init(&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(&tty->tty_files);
|
| 147 | kref_get(&tty->driver->kref);
|
| 148 | __module_get(tty->driver->owner);
|
| 149 | tty->ops = &usb_console_fake_tty_ops;
|
| 150 | tty_init_termios(tty);
|
| 151 | tty_port_tty_set(&port->port, tty);
|
| 152 | }
|
| 153 |
|
| 154 | |
| 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(&tty->termios, baud, baud);
|
| 165 | memset(&dummy, 0, sizeof(struct ktermios));
|
| 166 | serial->type->set_termios(tty, port, &dummy);
|
| 167 |
|
| 168 | tty_port_tty_set(&port->port, NULL);
|
| 169 | tty_save_termios(tty);
|
| 170 | tty_kref_put(tty);
|
| 171 | }
|
| 172 | tty_port_set_initialized(&port->port, true);
|
| 173 | }
|
| 174 | |
| 175 | * the tty port count
|
| 176 | --port->port.count;
|
| 177 | |
| 178 | * indicate this port is now acting as a system console.
|
| 179 | port->port.console = 1;
|
| 180 |
|
| 181 | mutex_unlock(&serial->disc_mutex);
|
| 182 | return retval;
|
| 183 |
|
| 184 | fail:
|
| 185 | tty_port_tty_set(&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(serial->interface);
|
| 191 | error_get_interface:
|
| 192 | mutex_unlock(&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 |
|
| 223 | for (i = 0, lf = 0 ; i < count ; i++) {
|
| 224 | if (*(buf + i) == 10) {
|
| 225 | lf = 1;
|
| 226 | i++;
|
| 227 | break;
|
| 228 | }
|
| 229 | }
|
| 230 | |
| 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 |
|
| 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 |
|