2019-06-01 11:08:17 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-08-25 22:44:07 +04:00
/*
* linux / drivers / char / ttyprintk . c
*
* Copyright ( C ) 2010 Samo Pogacnik
*/
/*
* This pseudo device allows user to make printk messages . It is possible
* to store " console " messages inline with kernel messages for better analyses
* of the boot process , for example .
*/
2022-02-15 17:17:49 +03:00
# include <linux/console.h>
2010-08-25 22:44:07 +04:00
# include <linux/device.h>
# include <linux/serial.h>
# include <linux/tty.h>
2014-04-02 16:45:22 +04:00
# include <linux/module.h>
2020-01-13 06:48:42 +03:00
# include <linux/spinlock.h>
2010-08-25 22:44:07 +04:00
struct ttyprintk_port {
struct tty_port port ;
2020-01-13 06:48:42 +03:00
spinlock_t spinlock ;
2010-08-25 22:44:07 +04:00
} ;
static struct ttyprintk_port tpk_port ;
/*
* Our simple preformatting supports transparent output of ( time - stamped )
* printk messages ( also suitable for logging service ) :
* - any cr is replaced by nl
* - adds a ttyprintk source tag in front of each line
2016-09-06 19:49:04 +03:00
* - too long message is fragmented , with ' \ ' nl between fragments
* - TPK_STR_SIZE isn ' t really the write_room limiting factor , because
2010-08-25 22:44:07 +04:00
* it is emptied on the fly during preformatting .
*/
# define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
# define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */
2018-11-07 01:11:37 +03:00
# define TPK_PREFIX KERN_SOH __stringify(CONFIG_TTY_PRINTK_LEVEL)
2010-08-25 22:44:07 +04:00
static int tpk_curr ;
2016-09-06 19:49:04 +03:00
static char tpk_buffer [ TPK_STR_SIZE + 4 ] ;
static void tpk_flush ( void )
{
if ( tpk_curr > 0 ) {
tpk_buffer [ tpk_curr ] = ' \0 ' ;
2018-11-07 01:11:37 +03:00
printk ( TPK_PREFIX " [U] %s \n " , tpk_buffer ) ;
2016-09-06 19:49:04 +03:00
tpk_curr = 0 ;
}
}
2010-08-25 22:44:07 +04:00
static int tpk_printk ( const unsigned char * buf , int count )
{
2021-05-18 21:21:26 +03:00
int i ;
2010-08-25 22:44:07 +04:00
for ( i = 0 ; i < count ; i + + ) {
2016-09-06 19:49:04 +03:00
if ( tpk_curr > = TPK_STR_SIZE ) {
2010-08-25 22:44:07 +04:00
/* end of tmp buffer reached: cut the message in two */
2016-09-06 19:49:04 +03:00
tpk_buffer [ tpk_curr + + ] = ' \\ ' ;
tpk_flush ( ) ;
}
switch ( buf [ i ] ) {
case ' \r ' :
tpk_flush ( ) ;
if ( ( i + 1 ) < count & & buf [ i + 1 ] = = ' \n ' )
i + + ;
break ;
case ' \n ' :
tpk_flush ( ) ;
break ;
default :
tpk_buffer [ tpk_curr + + ] = buf [ i ] ;
break ;
2010-08-25 22:44:07 +04:00
}
}
return count ;
}
/*
* TTY operations open function .
*/
static int tpk_open ( struct tty_struct * tty , struct file * filp )
{
tty - > driver_data = & tpk_port ;
return tty_port_open ( & tpk_port . port , tty , filp ) ;
}
/*
* TTY operations close function .
*/
static void tpk_close ( struct tty_struct * tty , struct file * filp )
{
struct ttyprintk_port * tpkp = tty - > driver_data ;
tty_port_close ( & tpkp - > port , tty , filp ) ;
}
/*
* TTY operations write function .
*/
static int tpk_write ( struct tty_struct * tty ,
const unsigned char * buf , int count )
{
struct ttyprintk_port * tpkp = tty - > driver_data ;
2020-01-13 06:48:42 +03:00
unsigned long flags ;
2010-08-25 22:44:07 +04:00
int ret ;
/* exclusive use of tpk_printk within this tty */
2020-01-13 06:48:42 +03:00
spin_lock_irqsave ( & tpkp - > spinlock , flags ) ;
2010-08-25 22:44:07 +04:00
ret = tpk_printk ( buf , count ) ;
2020-01-13 06:48:42 +03:00
spin_unlock_irqrestore ( & tpkp - > spinlock , flags ) ;
2010-08-25 22:44:07 +04:00
return ret ;
}
/*
* TTY operations write_room function .
*/
2021-05-05 12:19:15 +03:00
static unsigned int tpk_write_room ( struct tty_struct * tty )
2010-08-25 22:44:07 +04:00
{
return TPK_MAX_ROOM ;
}
2021-04-15 03:22:22 +03:00
/*
* TTY operations hangup function .
*/
static void tpk_hangup ( struct tty_struct * tty )
{
struct ttyprintk_port * tpkp = tty - > driver_data ;
tty_port_hangup ( & tpkp - > port ) ;
}
2021-04-27 14:40:51 +03:00
/*
* TTY port operations shutdown function .
*/
static void tpk_port_shutdown ( struct tty_port * tport )
{
struct ttyprintk_port * tpkp =
container_of ( tport , struct ttyprintk_port , port ) ;
unsigned long flags ;
spin_lock_irqsave ( & tpkp - > spinlock , flags ) ;
tpk_flush ( ) ;
spin_unlock_irqrestore ( & tpkp - > spinlock , flags ) ;
}
2010-08-25 22:44:07 +04:00
static const struct tty_operations ttyprintk_ops = {
. open = tpk_open ,
. close = tpk_close ,
. write = tpk_write ,
. write_room = tpk_write_room ,
2021-04-15 03:22:22 +03:00
. hangup = tpk_hangup ,
2010-08-25 22:44:07 +04:00
} ;
2021-04-27 14:40:51 +03:00
static const struct tty_port_operations tpk_port_ops = {
. shutdown = tpk_port_shutdown ,
} ;
2010-08-25 22:44:07 +04:00
static struct tty_driver * ttyprintk_driver ;
2022-02-15 17:17:49 +03:00
static struct tty_driver * ttyprintk_console_device ( struct console * c ,
int * index )
{
* index = 0 ;
return ttyprintk_driver ;
}
static struct console ttyprintk_console = {
. name = " ttyprintk " ,
. device = ttyprintk_console_device ,
} ;
2010-08-25 22:44:07 +04:00
static int __init ttyprintk_init ( void )
{
2020-06-11 18:31:08 +03:00
int ret ;
2010-08-25 22:44:07 +04:00
2020-01-13 06:48:42 +03:00
spin_lock_init ( & tpk_port . spinlock ) ;
2012-08-07 23:47:40 +04:00
2012-08-09 00:26:43 +04:00
ttyprintk_driver = tty_alloc_driver ( 1 ,
TTY_DRIVER_RESET_TERMIOS |
TTY_DRIVER_REAL_RAW |
TTY_DRIVER_UNNUMBERED_NODE ) ;
2012-08-16 17:16:56 +04:00
if ( IS_ERR ( ttyprintk_driver ) )
return PTR_ERR ( ttyprintk_driver ) ;
2010-08-25 22:44:07 +04:00
2012-11-15 12:49:56 +04:00
tty_port_init ( & tpk_port . port ) ;
2021-04-27 14:40:51 +03:00
tpk_port . port . ops = & tpk_port_ops ;
2012-11-15 12:49:56 +04:00
2010-08-25 22:44:07 +04:00
ttyprintk_driver - > driver_name = " ttyprintk " ;
ttyprintk_driver - > name = " ttyprintk " ;
ttyprintk_driver - > major = TTYAUX_MAJOR ;
ttyprintk_driver - > minor_start = 3 ;
ttyprintk_driver - > type = TTY_DRIVER_TYPE_CONSOLE ;
ttyprintk_driver - > init_termios = tty_std_termios ;
ttyprintk_driver - > init_termios . c_oflag = OPOST | OCRNL | ONOCR | ONLRET ;
tty_set_operations ( ttyprintk_driver , & ttyprintk_ops ) ;
2012-08-07 23:47:51 +04:00
tty_port_link_device ( & tpk_port . port , ttyprintk_driver , 0 ) ;
2010-08-25 22:44:07 +04:00
ret = tty_register_driver ( ttyprintk_driver ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " Couldn't register ttyprintk driver \n " ) ;
goto error ;
}
2022-02-15 17:17:49 +03:00
register_console ( & ttyprintk_console ) ;
2010-08-25 22:44:07 +04:00
return 0 ;
error :
2021-07-23 10:43:16 +03:00
tty_driver_kref_put ( ttyprintk_driver ) ;
2012-11-15 12:49:56 +04:00
tty_port_destroy ( & tpk_port . port ) ;
2010-08-25 22:44:07 +04:00
return ret ;
}
2014-04-02 16:45:22 +04:00
static void __exit ttyprintk_exit ( void )
{
2022-02-15 17:17:49 +03:00
unregister_console ( & ttyprintk_console ) ;
2014-04-02 16:45:22 +04:00
tty_unregister_driver ( ttyprintk_driver ) ;
2021-07-23 10:43:16 +03:00
tty_driver_kref_put ( ttyprintk_driver ) ;
2014-04-02 16:45:22 +04:00
tty_port_destroy ( & tpk_port . port ) ;
}
2014-01-12 21:37:56 +04:00
device_initcall ( ttyprintk_init ) ;
2014-04-02 16:45:22 +04:00
module_exit ( ttyprintk_exit ) ;
MODULE_LICENSE ( " GPL " ) ;