2008-10-13 13:37:07 +04:00
/*
* Tty port functions
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
2009-01-02 16:45:26 +03:00
# include <linux/serial.h>
2008-10-13 13:37:07 +04:00
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/sched.h>
# include <linux/init.h>
# include <linux/wait.h>
# include <linux/bitops.h>
# include <linux/delay.h>
# include <linux/module.h>
void tty_port_init ( struct tty_port * port )
{
memset ( port , 0 , sizeof ( * port ) ) ;
init_waitqueue_head ( & port - > open_wait ) ;
init_waitqueue_head ( & port - > close_wait ) ;
2009-09-20 00:13:31 +04:00
init_waitqueue_head ( & port - > delta_msr_wait ) ;
2008-10-13 13:37:07 +04:00
mutex_init ( & port - > mutex ) ;
2009-11-30 16:16:41 +03:00
mutex_init ( & port - > buf_mutex ) ;
2008-10-13 13:39:46 +04:00
spin_lock_init ( & port - > lock ) ;
2008-10-13 13:37:07 +04:00
port - > close_delay = ( 50 * HZ ) / 100 ;
port - > closing_wait = ( 3000 * HZ ) / 100 ;
2009-11-30 16:17:14 +03:00
kref_init ( & port - > kref ) ;
2008-10-13 13:37:07 +04:00
}
EXPORT_SYMBOL ( tty_port_init ) ;
2012-08-07 23:47:50 +04:00
/**
* tty_port_link_device - link tty and tty_port
* @ port : tty_port of the device
* @ driver : tty_driver for this device
* @ index : index of the tty
*
* Provide the tty layer wit ha link from a tty ( specified by @ index ) to a
* tty_port ( @ port ) . Use this only if neither tty_port_register_device nor
* tty_port_install is used in the driver . If used , this has to be called before
* tty_register_driver .
*/
void tty_port_link_device ( struct tty_port * port ,
struct tty_driver * driver , unsigned index )
{
if ( WARN_ON ( index > = driver - > num ) )
return ;
driver - > ports [ index ] = port ;
}
EXPORT_SYMBOL_GPL ( tty_port_link_device ) ;
2012-08-07 23:47:49 +04:00
/**
* tty_port_register_device - register tty device
* @ port : tty_port of the device
* @ driver : tty_driver for this device
* @ index : index of the tty
* @ device : parent if exists , otherwise NULL
*
* It is the same as tty_register_device except the provided @ port is linked to
* a concrete tty specified by @ index . Use this or tty_port_install ( or both ) .
* Call tty_port_link_device as a last resort .
*/
2012-06-04 15:35:37 +04:00
struct device * tty_port_register_device ( struct tty_port * port ,
struct tty_driver * driver , unsigned index ,
struct device * device )
{
2012-08-07 23:47:50 +04:00
tty_port_link_device ( port , driver , index ) ;
2012-06-04 15:35:37 +04:00
return tty_register_device ( driver , index , device ) ;
}
EXPORT_SYMBOL_GPL ( tty_port_register_device ) ;
2008-10-13 13:37:07 +04:00
int tty_port_alloc_xmit_buf ( struct tty_port * port )
{
/* We may sleep in get_zeroed_page() */
2009-11-30 16:16:41 +03:00
mutex_lock ( & port - > buf_mutex ) ;
2008-10-13 13:37:07 +04:00
if ( port - > xmit_buf = = NULL )
port - > xmit_buf = ( unsigned char * ) get_zeroed_page ( GFP_KERNEL ) ;
2009-11-30 16:16:41 +03:00
mutex_unlock ( & port - > buf_mutex ) ;
2008-10-13 13:37:07 +04:00
if ( port - > xmit_buf = = NULL )
return - ENOMEM ;
return 0 ;
}
EXPORT_SYMBOL ( tty_port_alloc_xmit_buf ) ;
void tty_port_free_xmit_buf ( struct tty_port * port )
{
2009-11-30 16:16:41 +03:00
mutex_lock ( & port - > buf_mutex ) ;
2008-10-13 13:37:07 +04:00
if ( port - > xmit_buf ! = NULL ) {
free_page ( ( unsigned long ) port - > xmit_buf ) ;
port - > xmit_buf = NULL ;
}
2009-11-30 16:16:41 +03:00
mutex_unlock ( & port - > buf_mutex ) ;
2008-10-13 13:37:07 +04:00
}
EXPORT_SYMBOL ( tty_port_free_xmit_buf ) ;
2009-11-30 16:17:14 +03:00
static void tty_port_destructor ( struct kref * kref )
{
struct tty_port * port = container_of ( kref , struct tty_port , kref ) ;
if ( port - > xmit_buf )
free_page ( ( unsigned long ) port - > xmit_buf ) ;
if ( port - > ops - > destruct )
port - > ops - > destruct ( port ) ;
else
kfree ( port ) ;
}
void tty_port_put ( struct tty_port * port )
{
if ( port )
kref_put ( & port - > kref , tty_port_destructor ) ;
}
EXPORT_SYMBOL ( tty_port_put ) ;
2008-10-13 13:37:07 +04:00
2008-10-13 13:39:46 +04:00
/**
* tty_port_tty_get - get a tty reference
* @ port : tty port
*
* Return a refcount protected tty instance or NULL if the port is not
* associated with a tty ( eg due to close or hangup )
*/
struct tty_struct * tty_port_tty_get ( struct tty_port * port )
{
unsigned long flags ;
struct tty_struct * tty ;
spin_lock_irqsave ( & port - > lock , flags ) ;
tty = tty_kref_get ( port - > tty ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return tty ;
}
EXPORT_SYMBOL ( tty_port_tty_get ) ;
/**
* tty_port_tty_set - set the tty of a port
* @ port : tty port
* @ tty : the tty
*
* Associate the port and tty pair . Manages any internal refcounts .
* Pass NULL to deassociate a port
*/
void tty_port_tty_set ( struct tty_port * port , struct tty_struct * tty )
{
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
if ( port - > tty )
tty_kref_put ( port - > tty ) ;
2008-10-21 16:47:44 +04:00
port - > tty = tty_kref_get ( tty ) ;
2008-10-13 13:39:46 +04:00
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
EXPORT_SYMBOL ( tty_port_tty_set ) ;
2009-01-02 16:45:05 +03:00
2009-09-20 00:13:20 +04:00
static void tty_port_shutdown ( struct tty_port * port )
{
2009-10-06 19:06:11 +04:00
mutex_lock ( & port - > mutex ) ;
2010-03-09 06:50:11 +03:00
if ( port - > ops - > shutdown & & ! port - > console & &
2009-08-20 23:23:47 +04:00
test_and_clear_bit ( ASYNCB_INITIALIZED , & port - > flags ) )
2009-09-20 00:13:20 +04:00
port - > ops - > shutdown ( port ) ;
2009-10-06 19:06:11 +04:00
mutex_unlock ( & port - > mutex ) ;
2009-09-20 00:13:20 +04:00
}
2009-01-02 16:45:26 +03:00
/**
* tty_port_hangup - hangup helper
* @ port : tty port
*
* Perform port level tty hangup flag and count changes . Drop the tty
* reference .
*/
void tty_port_hangup ( struct tty_port * port )
{
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
port - > count = 0 ;
port - > flags & = ~ ASYNC_NORMAL_ACTIVE ;
2009-11-30 16:16:52 +03:00
if ( port - > tty ) {
set_bit ( TTY_IO_ERROR , & port - > tty - > flags ) ;
2009-01-02 16:45:26 +03:00
tty_kref_put ( port - > tty ) ;
2009-11-30 16:16:52 +03:00
}
2009-01-02 16:45:26 +03:00
port - > tty = NULL ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
wake_up_interruptible ( & port - > open_wait ) ;
2009-09-20 00:13:31 +04:00
wake_up_interruptible ( & port - > delta_msr_wait ) ;
2009-09-20 00:13:20 +04:00
tty_port_shutdown ( port ) ;
2009-01-02 16:45:26 +03:00
}
EXPORT_SYMBOL ( tty_port_hangup ) ;
2009-01-02 16:45:05 +03:00
/**
* tty_port_carrier_raised - carrier raised check
* @ port : tty port
*
* Wrapper for the carrier detect logic . For the moment this is used
* to hide some internal details . This will eventually become entirely
* internal to the tty port .
*/
int tty_port_carrier_raised ( struct tty_port * port )
{
if ( port - > ops - > carrier_raised = = NULL )
return 1 ;
return port - > ops - > carrier_raised ( port ) ;
}
EXPORT_SYMBOL ( tty_port_carrier_raised ) ;
2009-01-02 16:45:19 +03:00
/**
2009-06-11 15:24:17 +04:00
* tty_port_raise_dtr_rts - Raise DTR / RTS
2009-01-02 16:45:19 +03:00
* @ port : tty port
*
* Wrapper for the DTR / RTS raise logic . For the moment this is used
* to hide some internal details . This will eventually become entirely
* internal to the tty port .
*/
void tty_port_raise_dtr_rts ( struct tty_port * port )
{
2009-06-11 15:24:17 +04:00
if ( port - > ops - > dtr_rts )
port - > ops - > dtr_rts ( port , 1 ) ;
2009-01-02 16:45:19 +03:00
}
EXPORT_SYMBOL ( tty_port_raise_dtr_rts ) ;
2009-01-02 16:46:10 +03:00
2009-06-11 15:24:17 +04:00
/**
* tty_port_lower_dtr_rts - Lower DTR / RTS
* @ port : tty port
*
* Wrapper for the DTR / RTS raise logic . For the moment this is used
* to hide some internal details . This will eventually become entirely
* internal to the tty port .
*/
void tty_port_lower_dtr_rts ( struct tty_port * port )
{
if ( port - > ops - > dtr_rts )
port - > ops - > dtr_rts ( port , 0 ) ;
}
EXPORT_SYMBOL ( tty_port_lower_dtr_rts ) ;
2009-01-02 16:46:10 +03:00
/**
* tty_port_block_til_ready - Waiting logic for tty open
* @ port : the tty port being opened
* @ tty : the tty device being bound
* @ filp : the file pointer of the opener
*
* Implement the core POSIX / SuS tty behaviour when opening a tty device .
* Handles :
* - hangup ( both before and during )
* - non blocking open
* - rts / dtr / dcd
* - signals
* - port flags and counts
*
* The passed tty_port must implement the carrier_raised method if it can
2009-06-11 15:24:17 +04:00
* do carrier detect and the dtr_rts method if it supports software
2009-01-02 16:46:10 +03:00
* management of these lines . Note that the dtr / rts raise is done each
* iteration as a hangup may have previously dropped them while we wait .
*/
2009-10-06 19:06:21 +04:00
2009-01-02 16:46:10 +03:00
int tty_port_block_til_ready ( struct tty_port * port ,
struct tty_struct * tty , struct file * filp )
{
int do_clocal = 0 , retval ;
unsigned long flags ;
2009-06-24 21:35:05 +04:00
DEFINE_WAIT ( wait ) ;
2009-01-02 16:46:10 +03:00
/* block if port is in the process of being closed */
if ( tty_hung_up_p ( filp ) | | port - > flags & ASYNC_CLOSING ) {
2012-08-08 19:30:13 +04:00
wait_event_interruptible_tty ( tty , port - > close_wait ,
2009-06-11 17:32:42 +04:00
! ( port - > flags & ASYNC_CLOSING ) ) ;
2009-01-02 16:46:10 +03:00
if ( port - > flags & ASYNC_HUP_NOTIFY )
return - EAGAIN ;
else
return - ERESTARTSYS ;
}
/* if non-blocking mode is set we can pass directly to open unless
the port has just hung up or is in another error state */
2009-11-18 17:12:58 +03:00
if ( tty - > flags & ( 1 < < TTY_IO_ERROR ) ) {
port - > flags | = ASYNC_NORMAL_ACTIVE ;
return 0 ;
}
if ( filp - > f_flags & O_NONBLOCK ) {
2009-10-28 23:12:32 +03:00
/* Indicate we are open */
2012-07-14 18:31:47 +04:00
if ( tty - > termios . c_cflag & CBAUD )
2009-10-28 23:12:32 +03:00
tty_port_raise_dtr_rts ( port ) ;
2009-01-02 16:46:10 +03:00
port - > flags | = ASYNC_NORMAL_ACTIVE ;
return 0 ;
}
if ( C_CLOCAL ( tty ) )
do_clocal = 1 ;
/* Block waiting until we can proceed. We may need to wait for the
carrier , but we must also wait for any close that is in progress
before the next open may complete */
retval = 0 ;
/* The port lock protects the port counts */
spin_lock_irqsave ( & port - > lock , flags ) ;
if ( ! tty_hung_up_p ( filp ) )
port - > count - - ;
port - > blocked_open + + ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
while ( 1 ) {
/* Indicate we are open */
2012-07-14 18:31:47 +04:00
if ( tty - > termios . c_cflag & CBAUD )
2009-01-02 16:46:43 +03:00
tty_port_raise_dtr_rts ( port ) ;
2009-01-02 16:46:10 +03:00
2009-06-11 17:33:37 +04:00
prepare_to_wait ( & port - > open_wait , & wait , TASK_INTERRUPTIBLE ) ;
2009-10-06 19:06:21 +04:00
/* Check for a hangup or uninitialised port.
Return accordingly */
2009-01-02 16:46:10 +03:00
if ( tty_hung_up_p ( filp ) | | ! ( port - > flags & ASYNC_INITIALIZED ) ) {
if ( port - > flags & ASYNC_HUP_NOTIFY )
retval = - EAGAIN ;
else
retval = - ERESTARTSYS ;
break ;
}
2012-01-13 01:55:15 +04:00
/*
* Probe the carrier . For devices with no carrier detect
* tty_port_carrier_raised will always return true .
* Never ask drivers if CLOCAL is set , this causes troubles
* on some hardware .
*/
2009-01-02 16:46:10 +03:00
if ( ! ( port - > flags & ASYNC_CLOSING ) & &
2012-01-13 01:55:15 +04:00
( do_clocal | | tty_port_carrier_raised ( port ) ) )
2009-01-02 16:46:10 +03:00
break ;
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
break ;
}
2012-08-08 19:30:13 +04:00
tty_unlock ( tty ) ;
2009-01-02 16:46:10 +03:00
schedule ( ) ;
2012-08-08 19:30:13 +04:00
tty_lock ( tty ) ;
2009-01-02 16:46:10 +03:00
}
2009-06-11 17:33:37 +04:00
finish_wait ( & port - > open_wait , & wait ) ;
2009-01-02 16:46:10 +03:00
/* Update counts. A parallel hangup will have set count to zero and
we must not mess that up further */
spin_lock_irqsave ( & port - > lock , flags ) ;
if ( ! tty_hung_up_p ( filp ) )
port - > count + + ;
port - > blocked_open - - ;
if ( retval = = 0 )
port - > flags | = ASYNC_NORMAL_ACTIVE ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2009-07-17 19:17:26 +04:00
return retval ;
2009-01-02 16:46:10 +03:00
}
EXPORT_SYMBOL ( tty_port_block_til_ready ) ;
2009-10-06 19:06:21 +04:00
int tty_port_close_start ( struct tty_port * port ,
struct tty_struct * tty , struct file * filp )
2009-01-02 16:46:50 +03:00
{
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
if ( tty_hung_up_p ( filp ) ) {
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return 0 ;
}
2009-10-06 19:06:21 +04:00
if ( tty - > count = = 1 & & port - > count ! = 1 ) {
2009-01-02 16:46:50 +03:00
printk ( KERN_WARNING
" tty_port_close_start: tty->count = 1 port count = %d. \n " ,
port - > count ) ;
port - > count = 1 ;
}
if ( - - port - > count < 0 ) {
printk ( KERN_WARNING " tty_port_close_start: count = %d \n " ,
port - > count ) ;
port - > count = 0 ;
}
if ( port - > count ) {
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2009-09-20 00:13:20 +04:00
if ( port - > ops - > drop )
port - > ops - > drop ( port ) ;
2009-01-02 16:46:50 +03:00
return 0 ;
}
2009-08-20 23:23:47 +04:00
set_bit ( ASYNCB_CLOSING , & port - > flags ) ;
2009-01-02 16:46:50 +03:00
tty - > closing = 1 ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2009-01-02 16:48:39 +03:00
/* Don't block on a stalled port, just pull the chain */
if ( tty - > flow_stopped )
tty_driver_flush_buffer ( tty ) ;
2009-09-20 00:13:20 +04:00
if ( test_bit ( ASYNCB_INITIALIZED , & port - > flags ) & &
2009-01-02 16:48:11 +03:00
port - > closing_wait ! = ASYNC_CLOSING_WAIT_NONE )
2011-08-25 17:12:07 +04:00
tty_wait_until_sent_from_close ( tty , port - > closing_wait ) ;
2009-06-11 15:25:25 +04:00
if ( port - > drain_delay ) {
unsigned int bps = tty_get_baud_rate ( tty ) ;
long timeout ;
if ( bps > 1200 )
2009-10-06 19:06:21 +04:00
timeout = max_t ( long ,
( HZ * 10 * port - > drain_delay ) / bps , HZ / 10 ) ;
2009-06-11 15:25:25 +04:00
else
timeout = 2 * HZ ;
schedule_timeout_interruptible ( timeout ) ;
}
2009-11-05 16:27:57 +03:00
/* Flush the ldisc buffering */
tty_ldisc_flush ( tty ) ;
/* Drop DTR/RTS if HUPCL is set. This causes any attached modem to
hang up the line */
2012-07-14 18:31:47 +04:00
if ( tty - > termios . c_cflag & HUPCL )
2009-11-05 16:27:57 +03:00
tty_port_lower_dtr_rts ( port ) ;
2009-09-20 00:13:20 +04:00
/* Don't call port->drop for the last reference. Callers will want
to drop the last active reference in - > shutdown ( ) or the tty
shutdown path */
2009-01-02 16:46:50 +03:00
return 1 ;
}
EXPORT_SYMBOL ( tty_port_close_start ) ;
void tty_port_close_end ( struct tty_port * port , struct tty_struct * tty )
{
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
tty - > closing = 0 ;
if ( port - > blocked_open ) {
spin_unlock_irqrestore ( & port - > lock , flags ) ;
if ( port - > close_delay ) {
msleep_interruptible (
jiffies_to_msecs ( port - > close_delay ) ) ;
}
spin_lock_irqsave ( & port - > lock , flags ) ;
wake_up_interruptible ( & port - > open_wait ) ;
}
port - > flags & = ~ ( ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING ) ;
wake_up_interruptible ( & port - > close_wait ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
EXPORT_SYMBOL ( tty_port_close_end ) ;
2009-09-20 00:13:20 +04:00
void tty_port_close ( struct tty_port * port , struct tty_struct * tty ,
struct file * filp )
{
if ( tty_port_close_start ( port , tty , filp ) = = 0 )
return ;
tty_port_shutdown ( port ) ;
2009-11-30 16:16:52 +03:00
set_bit ( TTY_IO_ERROR , & tty - > flags ) ;
2009-09-20 00:13:20 +04:00
tty_port_close_end ( port , tty ) ;
tty_port_tty_set ( port , NULL ) ;
}
EXPORT_SYMBOL ( tty_port_close ) ;
2009-10-06 19:06:11 +04:00
2012-08-07 23:47:49 +04:00
/**
* tty_port_install - generic tty - > ops - > install handler
* @ port : tty_port of the device
* @ driver : tty_driver for this device
* @ tty : tty to be installed
*
* It is the same as tty_standard_install except the provided @ port is linked
* to a concrete tty specified by @ tty . Use this or tty_port_register_device
* ( or both ) . Call tty_port_link_device as a last resort .
*/
2012-06-04 15:35:32 +04:00
int tty_port_install ( struct tty_port * port , struct tty_driver * driver ,
struct tty_struct * tty )
{
tty - > port = port ;
return tty_standard_install ( driver , tty ) ;
}
EXPORT_SYMBOL_GPL ( tty_port_install ) ;
2009-10-06 19:06:11 +04:00
int tty_port_open ( struct tty_port * port , struct tty_struct * tty ,
2009-10-06 19:06:21 +04:00
struct file * filp )
2009-10-06 19:06:11 +04:00
{
spin_lock_irq ( & port - > lock ) ;
if ( ! tty_hung_up_p ( filp ) )
+ + port - > count ;
spin_unlock_irq ( & port - > lock ) ;
tty_port_tty_set ( port , tty ) ;
/*
* Do the device - specific open only if the hardware isn ' t
* already initialized . Serialize open and shutdown using the
* port mutex .
*/
mutex_lock ( & port - > mutex ) ;
if ( ! test_bit ( ASYNCB_INITIALIZED , & port - > flags ) ) {
2009-11-30 16:16:57 +03:00
clear_bit ( TTY_IO_ERROR , & tty - > flags ) ;
2009-10-06 19:06:11 +04:00
if ( port - > ops - > activate ) {
int retval = port - > ops - > activate ( port , tty ) ;
if ( retval ) {
2009-10-06 19:06:21 +04:00
mutex_unlock ( & port - > mutex ) ;
return retval ;
}
}
2009-10-06 19:06:11 +04:00
set_bit ( ASYNCB_INITIALIZED , & port - > flags ) ;
}
mutex_unlock ( & port - > mutex ) ;
return tty_port_block_til_ready ( port , tty , filp ) ;
}
EXPORT_SYMBOL ( tty_port_open ) ;