2008-04-17 22:05:37 +04:00
/*
* Based on the same principle as kgdboe using the NETPOLL api , this
* driver uses a console polling api to implement a gdb serial inteface
* which is multiplexed on a console port .
*
* Maintainer : Jason Wessel < jason . wessel @ windriver . com >
*
* 2007 - 2008 ( c ) Jason Wessel - Wind River Systems , Inc .
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/kernel.h>
# include <linux/ctype.h>
# include <linux/kgdb.h>
2010-05-21 06:04:24 +04:00
# include <linux/kdb.h>
2008-04-17 22:05:37 +04:00
# include <linux/tty.h>
2010-05-21 06:04:26 +04:00
# include <linux/console.h>
2008-04-17 22:05:37 +04:00
# define MAX_CONFIG_LEN 40
static struct kgdb_io kgdboc_io_ops ;
/* -1 = init not run yet, 0 = unconfigured, 1 = configured. */
static int configured = - 1 ;
static char config [ MAX_CONFIG_LEN ] ;
static struct kparam_string kps = {
. string = config ,
. maxlen = MAX_CONFIG_LEN ,
} ;
static struct tty_driver * kgdb_tty_driver ;
static int kgdb_tty_line ;
2010-05-21 06:04:24 +04:00
# ifdef CONFIG_KDB_KEYBOARD
static int kgdboc_register_kbd ( char * * cptr )
{
if ( strncmp ( * cptr , " kbd " , 3 ) = = 0 ) {
if ( kdb_poll_idx < KDB_POLL_FUNC_MAX ) {
kdb_poll_funcs [ kdb_poll_idx ] = kdb_get_kbd_char ;
kdb_poll_idx + + ;
if ( cptr [ 0 ] [ 3 ] = = ' , ' )
* cptr + = 4 ;
else
return 1 ;
}
}
return 0 ;
}
static void kgdboc_unregister_kbd ( void )
{
int i ;
for ( i = 0 ; i < kdb_poll_idx ; i + + ) {
if ( kdb_poll_funcs [ i ] = = kdb_get_kbd_char ) {
kdb_poll_idx - - ;
kdb_poll_funcs [ i ] = kdb_poll_funcs [ kdb_poll_idx ] ;
kdb_poll_funcs [ kdb_poll_idx ] = NULL ;
i - - ;
}
}
}
# else /* ! CONFIG_KDB_KEYBOARD */
# define kgdboc_register_kbd(x) 0
# define kgdboc_unregister_kbd()
# endif /* ! CONFIG_KDB_KEYBOARD */
2008-04-17 22:05:37 +04:00
static int kgdboc_option_setup ( char * opt )
{
if ( strlen ( opt ) > MAX_CONFIG_LEN ) {
printk ( KERN_ERR " kgdboc: config string too long \n " ) ;
return - ENOSPC ;
}
strcpy ( config , opt ) ;
return 0 ;
}
__setup ( " kgdboc= " , kgdboc_option_setup ) ;
2010-05-21 06:04:24 +04:00
static void cleanup_kgdboc ( void )
{
kgdboc_unregister_kbd ( ) ;
if ( configured = = 1 )
kgdb_unregister_io_module ( & kgdboc_io_ops ) ;
}
2008-04-17 22:05:37 +04:00
static int configure_kgdboc ( void )
{
struct tty_driver * p ;
int tty_line = 0 ;
int err ;
2010-05-21 06:04:24 +04:00
char * cptr = config ;
2010-05-21 06:04:26 +04:00
struct console * cons ;
2008-04-17 22:05:37 +04:00
err = kgdboc_option_setup ( config ) ;
if ( err | | ! strlen ( config ) | | isspace ( config [ 0 ] ) )
goto noconfig ;
err = - ENODEV ;
2010-05-21 06:04:26 +04:00
kgdboc_io_ops . is_console = 0 ;
2010-05-21 06:04:24 +04:00
kgdb_tty_driver = NULL ;
if ( kgdboc_register_kbd ( & cptr ) )
goto do_register ;
2008-04-17 22:05:37 +04:00
2010-05-21 06:04:24 +04:00
p = tty_find_polling_driver ( cptr , & tty_line ) ;
2008-04-17 22:05:37 +04:00
if ( ! p )
goto noconfig ;
2010-05-21 06:04:26 +04:00
cons = console_drivers ;
while ( cons ) {
int idx ;
if ( cons - > device & & cons - > device ( cons , & idx ) = = p & &
idx = = tty_line ) {
kgdboc_io_ops . is_console = 1 ;
break ;
}
cons = cons - > next ;
}
2008-04-17 22:05:37 +04:00
kgdb_tty_driver = p ;
kgdb_tty_line = tty_line ;
2010-05-21 06:04:24 +04:00
do_register :
2008-04-17 22:05:37 +04:00
err = kgdb_register_io_module ( & kgdboc_io_ops ) ;
if ( err )
goto noconfig ;
configured = 1 ;
return 0 ;
noconfig :
config [ 0 ] = 0 ;
configured = 0 ;
2010-05-21 06:04:24 +04:00
cleanup_kgdboc ( ) ;
2008-04-17 22:05:37 +04:00
return err ;
}
static int __init init_kgdboc ( void )
{
/* Already configured? */
if ( configured = = 1 )
return 0 ;
return configure_kgdboc ( ) ;
}
static int kgdboc_get_char ( void )
{
2010-05-21 06:04:24 +04:00
if ( ! kgdb_tty_driver )
return - 1 ;
2008-04-30 11:54:13 +04:00
return kgdb_tty_driver - > ops - > poll_get_char ( kgdb_tty_driver ,
kgdb_tty_line ) ;
2008-04-17 22:05:37 +04:00
}
static void kgdboc_put_char ( u8 chr )
{
2010-05-21 06:04:24 +04:00
if ( ! kgdb_tty_driver )
return ;
2008-04-30 11:54:13 +04:00
kgdb_tty_driver - > ops - > poll_put_char ( kgdb_tty_driver ,
kgdb_tty_line , chr ) ;
2008-04-17 22:05:37 +04:00
}
static int param_set_kgdboc_var ( const char * kmessage , struct kernel_param * kp )
{
2008-02-15 23:55:52 +03:00
int len = strlen ( kmessage ) ;
if ( len > = MAX_CONFIG_LEN ) {
2008-04-17 22:05:37 +04:00
printk ( KERN_ERR " kgdboc: config string too long \n " ) ;
return - ENOSPC ;
}
/* Only copy in the string if the init function has not run yet */
if ( configured < 0 ) {
strcpy ( config , kmessage ) ;
return 0 ;
}
if ( kgdb_connected ) {
printk ( KERN_ERR
" kgdboc: Cannot reconfigure while KGDB is connected. \n " ) ;
return - EBUSY ;
}
strcpy ( config , kmessage ) ;
2008-02-15 23:55:52 +03:00
/* Chop out \n char as a result of echo */
if ( config [ len - 1 ] = = ' \n ' )
config [ len - 1 ] = ' \0 ' ;
2008-04-17 22:05:37 +04:00
if ( configured = = 1 )
cleanup_kgdboc ( ) ;
/* Go and configure with the new params. */
return configure_kgdboc ( ) ;
}
static void kgdboc_pre_exp_handler ( void )
{
/* Increment the module count when the debugger is active */
if ( ! kgdb_connected )
try_module_get ( THIS_MODULE ) ;
}
static void kgdboc_post_exp_handler ( void )
{
/* decrement the module count when the debugger detaches */
if ( ! kgdb_connected )
module_put ( THIS_MODULE ) ;
}
static struct kgdb_io kgdboc_io_ops = {
. name = " kgdboc " ,
. read_char = kgdboc_get_char ,
. write_char = kgdboc_put_char ,
. pre_exception = kgdboc_pre_exp_handler ,
. post_exception = kgdboc_post_exp_handler ,
} ;
2010-05-21 06:04:30 +04:00
# ifdef CONFIG_KGDB_SERIAL_CONSOLE
/* This is only available if kgdboc is a built in for early debugging */
int __init kgdboc_early_init ( char * opt )
{
/* save the first character of the config string because the
* init routine can destroy it .
*/
char save_ch ;
kgdboc_option_setup ( opt ) ;
save_ch = config [ 0 ] ;
init_kgdboc ( ) ;
config [ 0 ] = save_ch ;
return 0 ;
}
early_param ( " ekgdboc " , kgdboc_early_init ) ;
# endif /* CONFIG_KGDB_SERIAL_CONSOLE */
2008-04-17 22:05:37 +04:00
module_init ( init_kgdboc ) ;
module_exit ( cleanup_kgdboc ) ;
module_param_call ( kgdboc , param_set_kgdboc_var , param_get_string , & kps , 0644 ) ;
MODULE_PARM_DESC ( kgdboc , " <serial_device>[,baud] " ) ;
MODULE_DESCRIPTION ( " KGDB Console TTY Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;