console.c 7.4 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 14 15 16 17 18
 * Thanks to Randy Dunlap for the original version of this code.
 *
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/console.h>
19
#include <linux/serial.h>
L
Linus Torvalds 已提交
20
#include <linux/usb.h>
21
#include <linux/usb/serial.h>
L
Linus Torvalds 已提交
22 23 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

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".
 */
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 69
	struct tty_struct *tty = NULL;
	struct ktermios *termios = NULL, dummy;
L
Linus Torvalds 已提交
70

71
	dbg("%s", __func__);
L
Linus Torvalds 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84

	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 已提交
85 86 87 88
	
	/* Sane default */
	if (baud == 0)
		baud = 9600;
L
Linus Torvalds 已提交
89 90

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

109 110 111 112 113
	/*
	 * 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);
L
Linus Torvalds 已提交
114 115
	if (serial == NULL) {
		/* no device is connected yet, sorry :( */
116
		err("No USB device connected to ttyUSB%i", co->index);
L
Linus Torvalds 已提交
117 118 119
		return -ENODEV;
	}

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

	port = serial->port[co->index - serial->minor];
A
Alan Cox 已提交
125
	tty_port_tty_set(&port->port, NULL);
L
Linus Torvalds 已提交
126 127

	info->port = port;
128

A
Alan Cox 已提交
129
	++port->port.count;
130
	if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
131 132 133 134 135 136 137 138 139 140 141 142
		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;
			}
143
			kref_init(&tty->kref);
144 145 146 147 148 149 150 151
			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;
A
Alan Cox 已提交
152
			tty_port_tty_set(&port->port, tty);
153 154
		}

155
		/* only call the device specific open if this
L
Linus Torvalds 已提交
156 157
		 * is the first time the port is opened */
		if (serial->type->open)
158
			retval = serial->type->open(NULL, port);
L
Linus Torvalds 已提交
159
		else
160
			retval = usb_serial_generic_open(NULL, port);
L
Linus Torvalds 已提交
161

162 163 164
		if (retval) {
			err("could not open USB console port");
			goto free_termios;
L
Linus Torvalds 已提交
165
		}
166 167 168

		if (serial->type->set_termios) {
			termios->c_cflag = cflag;
A
Alan Cox 已提交
169
			tty_termios_encode_baud_rate(termios, baud, baud);
170
			serial->type->set_termios(tty, port, &dummy);
171

A
Alan Cox 已提交
172
			tty_port_tty_set(&port->port, NULL);
173 174
			kfree(termios);
			kfree(tty);
L
Linus Torvalds 已提交
175
		}
176
		set_bit(ASYNCB_INITIALIZED, &port->port.flags);
L
Linus Torvalds 已提交
177
	}
178 179 180 181 182
	/* 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. */
183
	port->console = 1;
L
Linus Torvalds 已提交
184

185
	mutex_unlock(&serial->disc_mutex);
186
	return retval;
187 188

 free_termios:
189
	kfree(termios);
A
Alan Cox 已提交
190
	tty_port_tty_set(&port->port, NULL);
191
 free_tty:
192
	kfree(tty);
193
 reset_open_count:
A
Alan Cox 已提交
194
	port->port.count = 0;
195 196 197 198 199
	usb_autopm_put_interface(serial->interface);
 error_get_interface:
	usb_serial_put(serial);
	mutex_unlock(&serial->disc_mutex);
	return retval;
L
Linus Torvalds 已提交
200 201
}

202 203
static void usb_console_write(struct console *co,
					const char *buf, unsigned count)
L
Linus Torvalds 已提交
204 205 206 207 208 209
{
	static struct usbcons_info *info = &usbcons_info;
	struct usb_serial_port *port = info->port;
	struct usb_serial *serial;
	int retval = -ENODEV;

210
	if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED)
L
Linus Torvalds 已提交
211 212 213 214 215 216
		return;
	serial = port->serial;

	if (count == 0)
		return;

217
	dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
L
Linus Torvalds 已提交
218

219
	if (!port->console) {
220
		dbg("%s - port not opened", __func__);
221
		return;
L
Linus Torvalds 已提交
222 223
	}

224 225 226 227
	while (count) {
		unsigned int i;
		unsigned int lf;
		/* search for LF so we can insert CR if necessary */
228
		for (i = 0, lf = 0 ; i < count ; i++) {
229 230 231 232 233 234
			if (*(buf + i) == 10) {
				lf = 1;
				i++;
				break;
			}
		}
235 236
		/* pass on to the driver specific version of this function if
		   it is available */
237
		if (serial->type->write)
A
Alan Cox 已提交
238
			retval = serial->type->write(NULL, port, buf, i);
239
		else
A
Alan Cox 已提交
240
			retval = usb_serial_generic_write(NULL, port, buf, i);
241
		dbg("%s - return value : %d", __func__, retval);
242 243 244 245
		if (lf) {
			/* append CR after LF */
			unsigned char cr = 13;
			if (serial->type->write)
246 247
				retval = serial->type->write(NULL,
								port, &cr, 1);
248
			else
249 250
				retval = usb_serial_generic_write(NULL,
								port, &cr, 1);
251
			dbg("%s - return value : %d", __func__, retval);
252 253 254 255
		}
		buf += i;
		count -= i;
	}
L
Linus Torvalds 已提交
256 257
}

258 259 260 261 262 263 264 265 266 267 268
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 已提交
269 270 271
static struct console usbcons = {
	.name =		"ttyUSB",
	.write =	usb_console_write,
272
	.device =	usb_console_device,
L
Linus Torvalds 已提交
273 274 275
	.setup =	usb_console_setup,
	.flags =	CON_PRINTBUFFER,
	.index =	-1,
276
	.data = 	&usb_serial_tty_driver,
L
Linus Torvalds 已提交
277 278
};

279 280
void usb_serial_console_disconnect(struct usb_serial *serial)
{
281 282
	if (serial && serial->port && serial->port[0]
				&& serial->port[0] == usbcons_info.port) {
283 284 285 286 287
		usb_serial_console_exit();
		usb_serial_put(serial);
	}
}

288
void usb_serial_console_init(int serial_debug, int minor)
L
Linus Torvalds 已提交
289 290 291 292
{
	debug = serial_debug;

	if (minor == 0) {
293
		/*
L
Linus Torvalds 已提交
294 295 296 297 298 299 300 301
		 * 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:
302 303 304
		 * 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 已提交
305
		 */
306
		dbg("registering the USB serial console.");
L
Linus Torvalds 已提交
307 308 309 310
		register_console(&usbcons);
	}
}

311
void usb_serial_console_exit(void)
L
Linus Torvalds 已提交
312
{
313 314
	if (usbcons_info.port) {
		unregister_console(&usbcons);
315
		usbcons_info.port->console = 0;
316 317
		usbcons_info.port = NULL;
	}
L
Linus Torvalds 已提交
318 319
}