2005-04-16 15:20:36 -07:00
/*
* 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 .
2008-07-22 11:09:57 +01:00
*
2005-04-16 15:20:36 -07:00
* 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>
2009-09-04 15:29:59 -04:00
# include <linux/serial.h>
2005-04-16 15:20:36 -07:00
# include <linux/usb.h>
2006-07-11 21:22:58 -07:00
# include <linux/usb/serial.h>
2005-04-16 15:20:36 -07:00
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 " .
*/
2006-04-12 23:41:59 +02:00
static int usb_console_setup ( struct console * co , char * options )
2005-04-16 15:20:36 -07:00
{
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 ;
2009-09-04 15:29:59 -04:00
int retval ;
2007-11-13 17:22:07 -05:00
struct tty_struct * tty = NULL ;
2010-03-16 16:05:44 -05:00
struct ktermios dummy ;
2005-04-16 15:20:36 -07:00
2008-07-22 11:09:57 +01:00
dbg ( " %s " , __func__ ) ;
2005-04-16 15:20:36 -07:00
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 ' ) ;
}
2008-07-22 11:10:08 +01:00
/* Sane default */
if ( baud = = 0 )
baud = 9600 ;
2005-04-16 15:20:36 -07:00
switch ( bits ) {
2008-07-22 11:09:57 +01:00
case 7 :
cflag | = CS7 ;
break ;
default :
case 8 :
cflag | = CS8 ;
break ;
2005-04-16 15:20:36 -07:00
}
switch ( parity ) {
2008-07-22 11:09:57 +01:00
case ' o ' : case ' O ' :
cflag | = PARODD ;
break ;
case ' e ' : case ' E ' :
cflag | = PARENB ;
break ;
2005-04-16 15:20:36 -07:00
}
co - > cflag = cflag ;
2007-11-12 15:14:49 -05:00
/*
* 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 ) ;
2005-04-16 15:20:36 -07:00
if ( serial = = NULL ) {
/* no device is connected yet, sorry :( */
2008-07-22 11:09:57 +01:00
err ( " No USB device connected to ttyUSB%i " , co - > index ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
}
2009-09-04 15:29:59 -04:00
retval = usb_autopm_get_interface ( serial - > interface ) ;
if ( retval )
goto error_get_interface ;
port = serial - > port [ co - > index - serial - > minor ] ;
2008-10-13 10:39:46 +01:00
tty_port_tty_set ( & port - > port , NULL ) ;
2005-04-16 15:20:36 -07:00
info - > port = port ;
2008-07-22 11:09:57 +01:00
2008-07-22 11:09:07 +01:00
+ + port - > port . count ;
2009-09-04 15:29:59 -04:00
if ( ! test_bit ( ASYNCB_INITIALIZED , & port - > port . flags ) ) {
2007-11-13 17:22:07 -05:00
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 ;
}
2008-12-01 11:36:16 +00:00
kref_init ( & tty - > kref ) ;
2010-03-16 16:05:44 -05:00
tty_port_tty_set ( & port - > port , tty ) ;
tty - > driver = usb_serial_tty_driver ;
tty - > index = co - > index ;
if ( tty_init_termios ( tty ) ) {
2007-11-13 17:22:07 -05:00
retval = - ENOMEM ;
err ( " no more memory " ) ;
goto free_tty ;
}
}
2008-07-22 11:09:57 +01:00
/* only call the device specific open if this
2005-04-16 15:20:36 -07:00
* is the first time the port is opened */
if ( serial - > type - > open )
2009-09-19 13:13:26 -07:00
retval = serial - > type - > open ( NULL , port ) ;
2005-04-16 15:20:36 -07:00
else
2009-09-19 13:13:26 -07:00
retval = usb_serial_generic_open ( NULL , port ) ;
2005-04-16 15:20:36 -07:00
2007-11-13 17:22:07 -05:00
if ( retval ) {
err ( " could not open USB console port " ) ;
2010-03-16 16:05:44 -05:00
goto fail ;
2005-04-16 15:20:36 -07:00
}
2007-11-13 17:22:07 -05:00
if ( serial - > type - > set_termios ) {
2010-03-16 16:05:44 -05:00
tty - > termios - > c_cflag = cflag ;
tty_termios_encode_baud_rate ( tty - > termios , baud , baud ) ;
memset ( & dummy , 0 , sizeof ( struct ktermios ) ) ;
2008-09-08 14:53:37 +01:00
serial - > type - > set_termios ( tty , port , & dummy ) ;
2007-11-13 17:22:07 -05:00
2008-10-13 10:39:46 +01:00
tty_port_tty_set ( & port - > port , NULL ) ;
2007-11-13 17:22:07 -05:00
kfree ( tty ) ;
2005-04-16 15:20:36 -07:00
}
2009-09-04 15:29:59 -04:00
set_bit ( ASYNCB_INITIALIZED , & port - > port . flags ) ;
2005-04-16 15:20:36 -07:00
}
2009-06-22 11:32:20 -05:00
/* 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 . */
2010-03-08 21:50:11 -06:00
port - > port . console = 1 ;
2005-04-16 15:20:36 -07:00
2009-09-04 15:29:59 -04:00
mutex_unlock ( & serial - > disc_mutex ) ;
2007-11-13 17:22:07 -05:00
return retval ;
2009-09-04 15:29:59 -04:00
2010-03-16 16:05:44 -05:00
fail :
2008-10-13 10:39:46 +01:00
tty_port_tty_set ( & port - > port , NULL ) ;
2009-09-04 15:29:59 -04:00
free_tty :
2007-11-13 17:22:07 -05:00
kfree ( tty ) ;
2009-09-04 15:29:59 -04:00
reset_open_count :
2008-07-22 11:09:07 +01:00
port - > port . count = 0 ;
2009-09-04 15:29:59 -04:00
usb_autopm_put_interface ( serial - > interface ) ;
error_get_interface :
usb_serial_put ( serial ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
return retval ;
2005-04-16 15:20:36 -07:00
}
2008-07-22 11:09:57 +01:00
static void usb_console_write ( struct console * co ,
const char * buf , unsigned count )
2005-04-16 15:20:36 -07:00
{
static struct usbcons_info * info = & usbcons_info ;
struct usb_serial_port * port = info - > port ;
struct usb_serial * serial ;
int retval = - ENODEV ;
2006-04-25 07:46:17 +02:00
if ( ! port | | port - > serial - > dev - > state = = USB_STATE_NOTATTACHED )
2005-04-16 15:20:36 -07:00
return ;
serial = port - > serial ;
if ( count = = 0 )
return ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - port %d, %d byte(s) " , __func__ , port - > number , count ) ;
2005-04-16 15:20:36 -07:00
2010-03-08 21:50:12 -06:00
if ( ! port - > port . console ) {
2008-07-22 11:09:57 +01:00
dbg ( " %s - port not opened " , __func__ ) ;
2006-04-13 22:26:35 +02:00
return ;
2005-04-16 15:20:36 -07:00
}
2006-04-13 22:26:35 +02:00
while ( count ) {
unsigned int i ;
unsigned int lf ;
/* search for LF so we can insert CR if necessary */
2008-07-22 11:09:57 +01:00
for ( i = 0 , lf = 0 ; i < count ; i + + ) {
2006-04-13 22:26:35 +02:00
if ( * ( buf + i ) = = 10 ) {
lf = 1 ;
i + + ;
break ;
}
}
2008-07-22 11:09:57 +01:00
/* pass on to the driver specific version of this function if
it is available */
2006-04-13 22:26:35 +02:00
if ( serial - > type - > write )
2008-07-22 11:09:07 +01:00
retval = serial - > type - > write ( NULL , port , buf , i ) ;
2006-04-13 22:26:35 +02:00
else
2008-07-22 11:09:07 +01:00
retval = usb_serial_generic_write ( NULL , port , buf , i ) ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - return value : %d " , __func__ , retval ) ;
2006-04-13 22:26:35 +02:00
if ( lf ) {
/* append CR after LF */
unsigned char cr = 13 ;
if ( serial - > type - > write )
2008-07-22 11:09:57 +01:00
retval = serial - > type - > write ( NULL ,
port , & cr , 1 ) ;
2006-04-13 22:26:35 +02:00
else
2008-07-22 11:09:57 +01:00
retval = usb_serial_generic_write ( NULL ,
port , & cr , 1 ) ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - return value : %d " , __func__ , retval ) ;
2006-04-13 22:26:35 +02:00
}
buf + = i ;
count - = i ;
}
2005-04-16 15:20:36 -07:00
}
2009-01-02 13:44:34 +00:00
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 ;
}
2005-04-16 15:20:36 -07:00
static struct console usbcons = {
. name = " ttyUSB " ,
. write = usb_console_write ,
2009-01-02 13:44:34 +00:00
. device = usb_console_device ,
2005-04-16 15:20:36 -07:00
. setup = usb_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
2009-01-02 13:44:34 +00:00
. data = & usb_serial_tty_driver ,
2005-04-16 15:20:36 -07:00
} ;
2006-04-25 07:46:17 +02:00
void usb_serial_console_disconnect ( struct usb_serial * serial )
{
2008-07-22 11:09:57 +01:00
if ( serial & & serial - > port & & serial - > port [ 0 ]
& & serial - > port [ 0 ] = = usbcons_info . port ) {
2006-04-25 07:46:17 +02:00
usb_serial_console_exit ( ) ;
usb_serial_put ( serial ) ;
}
}
2008-07-22 11:09:57 +01:00
void usb_serial_console_init ( int serial_debug , int minor )
2005-04-16 15:20:36 -07:00
{
debug = serial_debug ;
if ( minor = = 0 ) {
2008-07-22 11:09:57 +01:00
/*
2005-04-16 15:20:36 -07:00
* 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 :
2008-07-22 11:09:57 +01:00
* console_setup ( ) is called ( back ) immediately ( from
* register_console ) . console_write ( ) is called immediately
* from register_console iff CON_PRINTBUFFER is set in flags .
2005-04-16 15:20:36 -07:00
*/
2008-07-22 11:09:57 +01:00
dbg ( " registering the USB serial console. " ) ;
2005-04-16 15:20:36 -07:00
register_console ( & usbcons ) ;
}
}
2008-07-22 11:09:57 +01:00
void usb_serial_console_exit ( void )
2005-04-16 15:20:36 -07:00
{
2006-04-25 07:46:17 +02:00
if ( usbcons_info . port ) {
unregister_console ( & usbcons ) ;
2010-03-08 21:50:12 -06:00
usbcons_info . port - > port . console = 0 ;
2006-04-25 07:46:17 +02:00
usbcons_info . port = NULL ;
}
2005-04-16 15:20:36 -07:00
}