console.c 7.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/*
 * 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.
9
 *
L
Linus Torvalds 已提交
10 11 12 13
 * Thanks to Randy Dunlap for the original version of this code.
 *
 */

14 15
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

L
Linus Torvalds 已提交
16 17 18 19 20
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/console.h>
21
#include <linux/serial.h>
L
Linus Torvalds 已提交
22
#include <linux/usb.h>
23
#include <linux/usb/serial.h>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

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".
 */
56
static int usb_console_setup(struct console *co, char *options)
L
Linus Torvalds 已提交
57 58 59 60 61 62 63 64 65 66
{
	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;
67
	int retval;
68
	struct tty_struct *tty = NULL;
69
	struct ktermios dummy;
L
Linus Torvalds 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82

	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');
	}
A
Alan Cox 已提交
83 84 85 86
	
	/* Sane default */
	if (baud == 0)
		baud = 9600;
L
Linus Torvalds 已提交
87 88

	switch (bits) {
89 90 91 92 93 94 95
	case 7:
		cflag |= CS7;
		break;
	default:
	case 8:
		cflag |= CS8;
		break;
L
Linus Torvalds 已提交
96 97
	}
	switch (parity) {
98 99 100 101 102 103
	case 'o': case 'O':
		cflag |= PARODD;
		break;
	case 'e': case 'E':
		cflag |= PARENB;
		break;
L
Linus Torvalds 已提交
104 105 106
	}
	co->cflag = cflag;

107 108 109 110
	/*
	 * no need to check the index here: if the index is wrong, console
	 * code won't call us
	 */
111 112
	port = usb_serial_port_get_by_minor(co->index);
	if (port == NULL) {
L
Linus Torvalds 已提交
113
		/* no device is connected yet, sorry :( */
114
		pr_err("No USB device connected to ttyUSB%i\n", co->index);
L
Linus Torvalds 已提交
115 116
		return -ENODEV;
	}
117
	serial = port->serial;
L
Linus Torvalds 已提交
118

119 120 121 122
	retval = usb_autopm_get_interface(serial->interface);
	if (retval)
		goto error_get_interface;

A
Alan Cox 已提交
123
	tty_port_tty_set(&port->port, NULL);
L
Linus Torvalds 已提交
124 125

	info->port = port;
126

A
Alan Cox 已提交
127
	++port->port.count;
128
	if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
129 130 131 132 133 134 135 136 137
		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;
138
				dev_err(&port->dev, "no more memory\n");
139 140
				goto reset_open_count;
			}
141
			kref_init(&tty->kref);
142 143 144 145
			tty_port_tty_set(&port->port, tty);
			tty->driver = usb_serial_tty_driver;
			tty->index = co->index;
			if (tty_init_termios(tty)) {
146
				retval = -ENOMEM;
147
				dev_err(&port->dev, "no more memory\n");
148 149 150 151
				goto free_tty;
			}
		}

152
		/* only call the device specific open if this
L
Linus Torvalds 已提交
153
		 * is the first time the port is opened */
154
		retval = serial->type->open(NULL, port);
155
		if (retval) {
156
			dev_err(&port->dev, "could not open USB console port\n");
157
			goto fail;
L
Linus Torvalds 已提交
158
		}
159 160

		if (serial->type->set_termios) {
161 162
			tty->termios.c_cflag = cflag;
			tty_termios_encode_baud_rate(&tty->termios, baud, baud);
163
			memset(&dummy, 0, sizeof(struct ktermios));
164
			serial->type->set_termios(tty, port, &dummy);
165

A
Alan Cox 已提交
166
			tty_port_tty_set(&port->port, NULL);
167
			kfree(tty);
L
Linus Torvalds 已提交
168
		}
169
		set_bit(ASYNCB_INITIALIZED, &port->port.flags);
L
Linus Torvalds 已提交
170
	}
171 172 173 174 175
	/* Now that any required fake tty operations are completed restore
	 * the tty port count */
	--port->port.count;
	/* The console is special in terms of closing the device so
	 * indicate this port is now acting as a system console. */
176
	port->port.console = 1;
L
Linus Torvalds 已提交
177

178
	mutex_unlock(&serial->disc_mutex);
179
	return retval;
180

181
 fail:
A
Alan Cox 已提交
182
	tty_port_tty_set(&port->port, NULL);
183
 free_tty:
184
	kfree(tty);
185
 reset_open_count:
A
Alan Cox 已提交
186
	port->port.count = 0;
187 188 189 190 191
	usb_autopm_put_interface(serial->interface);
 error_get_interface:
	usb_serial_put(serial);
	mutex_unlock(&serial->disc_mutex);
	return retval;
L
Linus Torvalds 已提交
192 193
}

194 195
static void usb_console_write(struct console *co,
					const char *buf, unsigned count)
L
Linus Torvalds 已提交
196 197 198 199 200 201
{
	static struct usbcons_info *info = &usbcons_info;
	struct usb_serial_port *port = info->port;
	struct usb_serial *serial;
	int retval = -ENODEV;

202
	if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED)
L
Linus Torvalds 已提交
203 204 205 206 207 208
		return;
	serial = port->serial;

	if (count == 0)
		return;

J
Johan Hovold 已提交
209
	dev_dbg(&port->dev, "%s - %d byte(s)\n", __func__, count);
L
Linus Torvalds 已提交
210

211
	if (!port->port.console) {
J
Johan Hovold 已提交
212
		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
213
		return;
L
Linus Torvalds 已提交
214 215
	}

216 217 218 219
	while (count) {
		unsigned int i;
		unsigned int lf;
		/* search for LF so we can insert CR if necessary */
220
		for (i = 0, lf = 0 ; i < count ; i++) {
221 222 223 224 225 226
			if (*(buf + i) == 10) {
				lf = 1;
				i++;
				break;
			}
		}
227 228
		/* pass on to the driver specific version of this function if
		   it is available */
229
		retval = serial->type->write(NULL, port, buf, i);
J
Johan Hovold 已提交
230
		dev_dbg(&port->dev, "%s - write: %d\n", __func__, retval);
231 232 233
		if (lf) {
			/* append CR after LF */
			unsigned char cr = 13;
234
			retval = serial->type->write(NULL, port, &cr, 1);
J
Johan Hovold 已提交
235 236
			dev_dbg(&port->dev, "%s - write cr: %d\n",
							__func__, retval);
237 238 239 240
		}
		buf += i;
		count -= i;
	}
L
Linus Torvalds 已提交
241 242
}

243 244 245 246 247 248 249 250 251 252 253
static struct tty_driver *usb_console_device(struct console *co, int *index)
{
	struct tty_driver **p = (struct tty_driver **)co->data;

	if (!*p)
		return NULL;

	*index = co->index;
	return *p;
}

L
Linus Torvalds 已提交
254 255 256
static struct console usbcons = {
	.name =		"ttyUSB",
	.write =	usb_console_write,
257
	.device =	usb_console_device,
L
Linus Torvalds 已提交
258 259 260
	.setup =	usb_console_setup,
	.flags =	CON_PRINTBUFFER,
	.index =	-1,
261
	.data = 	&usb_serial_tty_driver,
L
Linus Torvalds 已提交
262 263
};

264 265
void usb_serial_console_disconnect(struct usb_serial *serial)
{
266 267
	if (serial && serial->port && serial->port[0]
				&& serial->port[0] == usbcons_info.port) {
268 269 270 271 272
		usb_serial_console_exit();
		usb_serial_put(serial);
	}
}

273
void usb_serial_console_init(int minor)
L
Linus Torvalds 已提交
274 275
{
	if (minor == 0) {
276
		/*
L
Linus Torvalds 已提交
277 278 279 280 281 282 283 284
		 * 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:
285 286 287
		 * console_setup() is called (back) immediately (from
		 * register_console). console_write() is called immediately
		 * from register_console iff CON_PRINTBUFFER is set in flags.
L
Linus Torvalds 已提交
288
		 */
289
		pr_debug("registering the USB serial console.\n");
L
Linus Torvalds 已提交
290 291 292 293
		register_console(&usbcons);
	}
}

294
void usb_serial_console_exit(void)
L
Linus Torvalds 已提交
295
{
296 297
	if (usbcons_info.port) {
		unregister_console(&usbcons);
298
		usbcons_info.port->port.console = 0;
299 300
		usbcons_info.port = NULL;
	}
L
Linus Torvalds 已提交
301 302
}