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 ) ;
mutex_init ( & port - > 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 ;
}
EXPORT_SYMBOL ( tty_port_init ) ;
int tty_port_alloc_xmit_buf ( struct tty_port * port )
{
/* We may sleep in get_zeroed_page() */
mutex_lock ( & port - > mutex ) ;
if ( port - > xmit_buf = = NULL )
port - > xmit_buf = ( unsigned char * ) get_zeroed_page ( GFP_KERNEL ) ;
mutex_unlock ( & port - > mutex ) ;
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 )
{
mutex_lock ( & port - > mutex ) ;
if ( port - > xmit_buf ! = NULL ) {
free_page ( ( unsigned long ) port - > xmit_buf ) ;
port - > xmit_buf = NULL ;
}
mutex_unlock ( & port - > mutex ) ;
}
EXPORT_SYMBOL ( tty_port_free_xmit_buf ) ;
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-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 ;
if ( port - > tty )
tty_kref_put ( port - > tty ) ;
port - > tty = NULL ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
wake_up_interruptible ( & port - > open_wait ) ;
}
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 .
*/
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 ;
DECLARE_WAITQUEUE ( wait , current ) ;
int cd ;
/* block if port is in the process of being closed */
if ( tty_hung_up_p ( filp ) | | port - > flags & ASYNC_CLOSING ) {
2009-06-11 17:32:42 +04:00
wait_event_interruptible ( port - > close_wait ,
! ( 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 */
if ( ( filp - > f_flags & O_NONBLOCK ) | |
( tty - > flags & ( 1 < < TTY_IO_ERROR ) ) ) {
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 ;
add_wait_queue ( & port - > open_wait , & wait ) ;
/* 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 */
2009-01-02 16:46:43 +03:00
if ( tty - > termios - > c_cflag & CBAUD )
tty_port_raise_dtr_rts ( port ) ;
2009-01-02 16:46:10 +03:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
/* Check for a hangup or uninitialised port. Return accordingly */
if ( tty_hung_up_p ( filp ) | | ! ( port - > flags & ASYNC_INITIALIZED ) ) {
if ( port - > flags & ASYNC_HUP_NOTIFY )
retval = - EAGAIN ;
else
retval = - ERESTARTSYS ;
break ;
}
/* Probe the carrier. For devices with no carrier detect this
will always return true */
cd = tty_port_carrier_raised ( port ) ;
if ( ! ( port - > flags & ASYNC_CLOSING ) & &
( do_clocal | | cd ) )
break ;
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
break ;
}
schedule ( ) ;
}
set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & port - > open_wait , & wait ) ;
/* 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 ) ;
return 0 ;
}
EXPORT_SYMBOL ( tty_port_block_til_ready ) ;
2009-01-02 16:46:50 +03:00
int tty_port_close_start ( struct tty_port * port , struct tty_struct * tty , struct file * filp )
{
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
if ( tty_hung_up_p ( filp ) ) {
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return 0 ;
}
if ( tty - > count = = 1 & & port - > count ! = 1 ) {
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 ) ;
return 0 ;
}
port - > flags | = ASYNC_CLOSING ;
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-01-02 16:48:11 +03:00
if ( port - > flags & ASYNC_INITIALIZED & &
port - > closing_wait ! = ASYNC_CLOSING_WAIT_NONE )
2009-01-02 16:46:50 +03:00
tty_wait_until_sent ( 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 )
timeout = max_t ( long , ( HZ * 10 * port - > drain_delay ) / bps ,
HZ / 10 ) ;
else
timeout = 2 * HZ ;
schedule_timeout_interruptible ( timeout ) ;
}
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 ;
tty_ldisc_flush ( tty ) ;
2009-06-11 15:24:17 +04:00
if ( tty - > termios - > c_cflag & HUPCL )
tty_port_lower_dtr_rts ( port ) ;
2009-01-02 16:46:50 +03:00
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 ) ;