2005-04-17 02:20:36 +04:00
/*
* generic_serial . c
*
* Copyright ( C ) 1998 / 1999 R . E . Wolff @ BitWizard . nl
*
* written for the SX serial driver .
* Contains the code that should be shared over all the serial drivers .
*
* Credit for the idea to do it this way might go to Alan Cox .
*
*
* Version 0.1 - - December , 1998. Initial version .
* Version 0.2 - - March , 1999. Some more routines . Bugfixes . Etc .
* Version 0.5 - - August , 1999. Some more fixes . Reformat for Linus .
*
* BitWizard is actively maintaining this file . We sometimes find
* that someone submitted changes to this file . We really appreciate
* your help , but please submit changes through us . We ' re doing our
* best to be responsive . - - REW
* */
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/tty.h>
# include <linux/serial.h>
# include <linux/mm.h>
# include <linux/generic_serial.h>
# include <linux/interrupt.h>
# include <linux/tty_flip.h>
# include <linux/delay.h>
# include <asm/uaccess.h>
# define DEBUG
static int gs_debug ;
# ifdef DEBUG
# define gs_dprintk(f, str...) if (gs_debug & f) printk (str)
# else
# define gs_dprintk(f, str...) /* nothing */
# endif
2008-04-30 11:55:10 +04:00
# define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter %s\n", __func__)
# define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit %s\n", __func__)
2005-04-17 02:20:36 +04:00
# define RS_EVENT_WRITE_WAKEUP 1
module_param ( gs_debug , int , 0644 ) ;
2008-04-30 11:54:03 +04:00
int gs_put_char ( struct tty_struct * tty , unsigned char ch )
2005-04-17 02:20:36 +04:00
{
struct gs_port * port ;
func_enter ( ) ;
port = tty - > driver_data ;
2008-04-30 11:54:03 +04:00
if ( ! port ) return 0 ;
2005-04-17 02:20:36 +04:00
2008-07-17 00:55:20 +04:00
if ( ! ( port - > port . flags & ASYNC_INITIALIZED ) ) return 0 ;
2005-04-17 02:20:36 +04:00
/* Take a lock on the serial tranmit buffer! */
2007-07-16 10:41:47 +04:00
mutex_lock ( & port - > port_write_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( port - > xmit_cnt > = SERIAL_XMIT_SIZE - 1 ) {
/* Sorry, buffer is full, drop character. Update statistics???? -- REW */
2007-07-16 10:41:47 +04:00
mutex_unlock ( & port - > port_write_mutex ) ;
2008-04-30 11:54:03 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
port - > xmit_buf [ port - > xmit_head + + ] = ch ;
port - > xmit_head & = SERIAL_XMIT_SIZE - 1 ;
port - > xmit_cnt + + ; /* Characters in buffer */
2007-07-16 10:41:47 +04:00
mutex_unlock ( & port - > port_write_mutex ) ;
2005-04-17 02:20:36 +04:00
func_exit ( ) ;
2008-04-30 11:54:03 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
/*
> Problems to take into account are :
> - 1 - Interrupts that empty part of the buffer .
> - 2 - page faults on the access to userspace .
> - 3 - Other processes that are also trying to do a " write " .
*/
int gs_write ( struct tty_struct * tty ,
const unsigned char * buf , int count )
{
struct gs_port * port ;
int c , total = 0 ;
int t ;
func_enter ( ) ;
port = tty - > driver_data ;
if ( ! port ) return 0 ;
2008-07-17 00:55:20 +04:00
if ( ! ( port - > port . flags & ASYNC_INITIALIZED ) )
2005-04-17 02:20:36 +04:00
return 0 ;
/* get exclusive "write" access to this port (problem 3) */
/* This is not a spinlock because we can have a disk access (page
fault ) in copy_from_user */
2006-03-23 14:00:44 +03:00
mutex_lock ( & port - > port_write_mutex ) ;
2005-04-17 02:20:36 +04:00
while ( 1 ) {
c = count ;
/* This is safe because we "OWN" the "head". Noone else can
2006-03-23 14:00:44 +03:00
change the " head " : we own the port_write_mutex . */
2005-04-17 02:20:36 +04:00
/* Don't overrun the end of the buffer */
t = SERIAL_XMIT_SIZE - port - > xmit_head ;
if ( t < c ) c = t ;
/* This is safe because the xmit_cnt can only decrease. This
would increase " t " , so we might copy too little chars . */
/* Don't copy past the "head" of the buffer */
t = SERIAL_XMIT_SIZE - 1 - port - > xmit_cnt ;
if ( t < c ) c = t ;
/* Can't copy more? break out! */
if ( c < = 0 ) break ;
memcpy ( port - > xmit_buf + port - > xmit_head , buf , c ) ;
port - > xmit_cnt + = c ;
port - > xmit_head = ( port - > xmit_head + c ) & ( SERIAL_XMIT_SIZE - 1 ) ;
buf + = c ;
count - = c ;
total + = c ;
}
2006-03-23 14:00:44 +03:00
mutex_unlock ( & port - > port_write_mutex ) ;
2005-04-17 02:20:36 +04:00
gs_dprintk ( GS_DEBUG_WRITE , " write: interrupts are %s \n " ,
2008-07-17 00:55:20 +04:00
( port - > port . flags & GS_TX_INTEN ) ? " enabled " : " disabled " ) ;
2005-04-17 02:20:36 +04:00
if ( port - > xmit_cnt & &
! tty - > stopped & &
! tty - > hw_stopped & &
2008-07-17 00:55:20 +04:00
! ( port - > port . flags & GS_TX_INTEN ) ) {
port - > port . flags | = GS_TX_INTEN ;
2005-04-17 02:20:36 +04:00
port - > rd - > enable_tx_interrupts ( port ) ;
}
func_exit ( ) ;
return total ;
}
int gs_write_room ( struct tty_struct * tty )
{
struct gs_port * port = tty - > driver_data ;
int ret ;
func_enter ( ) ;
ret = SERIAL_XMIT_SIZE - port - > xmit_cnt - 1 ;
if ( ret < 0 )
ret = 0 ;
func_exit ( ) ;
return ret ;
}
int gs_chars_in_buffer ( struct tty_struct * tty )
{
struct gs_port * port = tty - > driver_data ;
func_enter ( ) ;
func_exit ( ) ;
return port - > xmit_cnt ;
}
static int gs_real_chars_in_buffer ( struct tty_struct * tty )
{
struct gs_port * port ;
func_enter ( ) ;
port = tty - > driver_data ;
if ( ! port - > rd ) return 0 ;
if ( ! port - > rd - > chars_in_buffer ) return 0 ;
func_exit ( ) ;
return port - > xmit_cnt + port - > rd - > chars_in_buffer ( port ) ;
}
static int gs_wait_tx_flushed ( void * ptr , unsigned long timeout )
{
struct gs_port * port = ptr ;
unsigned long end_jiffies ;
int jiffies_to_transmit , charsleft = 0 , rv = 0 ;
int rcib ;
func_enter ( ) ;
gs_dprintk ( GS_DEBUG_FLUSH , " port=%p. \n " , port ) ;
if ( port ) {
gs_dprintk ( GS_DEBUG_FLUSH , " xmit_cnt=%x, xmit_buf=%p, tty=%p. \n " ,
2008-07-17 00:55:20 +04:00
port - > xmit_cnt , port - > xmit_buf , port - > port . tty ) ;
2005-04-17 02:20:36 +04:00
}
if ( ! port | | port - > xmit_cnt < 0 | | ! port - > xmit_buf ) {
gs_dprintk ( GS_DEBUG_FLUSH , " ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0. \n " ) ;
func_exit ( ) ;
return - EINVAL ; /* This is an error which we don't know how to handle. */
}
2008-07-17 00:55:20 +04:00
rcib = gs_real_chars_in_buffer ( port - > port . tty ) ;
2005-04-17 02:20:36 +04:00
if ( rcib < = 0 ) {
gs_dprintk ( GS_DEBUG_FLUSH , " nothing to wait for. \n " ) ;
func_exit ( ) ;
return rv ;
}
/* stop trying: now + twice the time it would normally take + seconds */
if ( timeout = = 0 ) timeout = MAX_SCHEDULE_TIMEOUT ;
end_jiffies = jiffies ;
if ( timeout ! = MAX_SCHEDULE_TIMEOUT )
end_jiffies + = port - > baud ? ( 2 * rcib * 10 * HZ / port - > baud ) : 0 ;
end_jiffies + = timeout ;
gs_dprintk ( GS_DEBUG_FLUSH , " now=%lx, end=%lx (%ld). \n " ,
jiffies , end_jiffies , end_jiffies - jiffies ) ;
/* the expression is actually jiffies < end_jiffies, but that won't
work around the wraparound . Tricky eh ? */
2008-07-17 00:55:20 +04:00
while ( ( charsleft = gs_real_chars_in_buffer ( port - > port . tty ) ) & &
2005-04-17 02:20:36 +04:00
time_after ( end_jiffies , jiffies ) ) {
/* Units check:
chars * ( bits / char ) * ( jiffies / sec ) / ( bits / sec ) = jiffies !
check ! */
charsleft + = 16 ; /* Allow 16 chars more to be transmitted ... */
jiffies_to_transmit = port - > baud ? ( 1 + charsleft * 10 * HZ / port - > baud ) : 0 ;
/* ^^^ Round up.... */
if ( jiffies_to_transmit < = 0 ) jiffies_to_transmit = 1 ;
gs_dprintk ( GS_DEBUG_FLUSH , " Expect to finish in %d jiffies "
" (%d chars). \n " , jiffies_to_transmit , charsleft ) ;
msleep_interruptible ( jiffies_to_msecs ( jiffies_to_transmit ) ) ;
if ( signal_pending ( current ) ) {
gs_dprintk ( GS_DEBUG_FLUSH , " Signal pending. Bombing out: " ) ;
rv = - EINTR ;
break ;
}
}
gs_dprintk ( GS_DEBUG_FLUSH , " charsleft = %d. \n " , charsleft ) ;
set_current_state ( TASK_RUNNING ) ;
func_exit ( ) ;
return rv ;
}
void gs_flush_buffer ( struct tty_struct * tty )
{
struct gs_port * port ;
unsigned long flags ;
func_enter ( ) ;
port = tty - > driver_data ;
if ( ! port ) return ;
/* XXX Would the write semaphore do? */
spin_lock_irqsave ( & port - > driver_lock , flags ) ;
port - > xmit_cnt = port - > xmit_head = port - > xmit_tail = 0 ;
spin_unlock_irqrestore ( & port - > driver_lock , flags ) ;
tty_wakeup ( tty ) ;
func_exit ( ) ;
}
void gs_flush_chars ( struct tty_struct * tty )
{
struct gs_port * port ;
func_enter ( ) ;
port = tty - > driver_data ;
if ( ! port ) return ;
if ( port - > xmit_cnt < = 0 | | tty - > stopped | | tty - > hw_stopped | |
! port - > xmit_buf ) {
func_exit ( ) ;
return ;
}
/* Beats me -- REW */
2008-07-17 00:55:20 +04:00
port - > port . flags | = GS_TX_INTEN ;
2005-04-17 02:20:36 +04:00
port - > rd - > enable_tx_interrupts ( port ) ;
func_exit ( ) ;
}
void gs_stop ( struct tty_struct * tty )
{
struct gs_port * port ;
func_enter ( ) ;
port = tty - > driver_data ;
if ( ! port ) return ;
if ( port - > xmit_cnt & &
port - > xmit_buf & &
2008-07-17 00:55:20 +04:00
( port - > port . flags & GS_TX_INTEN ) ) {
port - > port . flags & = ~ GS_TX_INTEN ;
2005-04-17 02:20:36 +04:00
port - > rd - > disable_tx_interrupts ( port ) ;
}
func_exit ( ) ;
}
void gs_start ( struct tty_struct * tty )
{
struct gs_port * port ;
port = tty - > driver_data ;
if ( ! port ) return ;
if ( port - > xmit_cnt & &
port - > xmit_buf & &
2008-07-17 00:55:20 +04:00
! ( port - > port . flags & GS_TX_INTEN ) ) {
port - > port . flags | = GS_TX_INTEN ;
2005-04-17 02:20:36 +04:00
port - > rd - > enable_tx_interrupts ( port ) ;
}
func_exit ( ) ;
}
static void gs_shutdown_port ( struct gs_port * port )
{
unsigned long flags ;
func_enter ( ) ;
if ( ! port ) return ;
2008-07-17 00:55:20 +04:00
if ( ! ( port - > port . flags & ASYNC_INITIALIZED ) )
2005-04-17 02:20:36 +04:00
return ;
spin_lock_irqsave ( & port - > driver_lock , flags ) ;
if ( port - > xmit_buf ) {
free_page ( ( unsigned long ) port - > xmit_buf ) ;
port - > xmit_buf = NULL ;
}
2008-07-17 00:55:20 +04:00
if ( port - > port . tty )
set_bit ( TTY_IO_ERROR , & port - > port . tty - > flags ) ;
2005-04-17 02:20:36 +04:00
port - > rd - > shutdown_port ( port ) ;
2008-07-17 00:55:20 +04:00
port - > port . flags & = ~ ASYNC_INITIALIZED ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & port - > driver_lock , flags ) ;
func_exit ( ) ;
}
void gs_hangup ( struct tty_struct * tty )
{
2009-01-02 16:45:36 +03:00
struct gs_port * port ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
func_enter ( ) ;
port = tty - > driver_data ;
2008-07-17 00:55:20 +04:00
tty = port - > port . tty ;
2005-04-17 02:20:36 +04:00
if ( ! tty )
return ;
gs_shutdown_port ( port ) ;
2009-01-02 16:45:36 +03:00
spin_lock_irqsave ( & port - > port . lock , flags ) ;
2008-07-17 00:55:20 +04:00
port - > port . flags & = ~ ( ASYNC_NORMAL_ACTIVE | GS_ACTIVE ) ;
port - > port . tty = NULL ;
port - > port . count = 0 ;
2009-01-02 16:45:36 +03:00
spin_unlock_irqrestore ( & port - > port . lock , flags ) ;
2005-04-17 02:20:36 +04:00
2008-07-17 00:55:20 +04:00
wake_up_interruptible ( & port - > port . open_wait ) ;
2005-04-17 02:20:36 +04:00
func_exit ( ) ;
}
int gs_block_til_ready ( void * port_ , struct file * filp )
{
2009-01-02 16:45:05 +03:00
struct gs_port * gp = port_ ;
struct tty_port * port = & gp - > port ;
2005-04-17 02:20:36 +04:00
DECLARE_WAITQUEUE ( wait , current ) ;
int retval ;
int do_clocal = 0 ;
int CD ;
struct tty_struct * tty ;
unsigned long flags ;
func_enter ( ) ;
if ( ! port ) return 0 ;
2009-01-02 16:45:05 +03:00
tty = port - > tty ;
2005-04-17 02:20:36 +04:00
gs_dprintk ( GS_DEBUG_BTR , " Entering gs_block_till_ready. \n " ) ;
/*
* If the device is in the middle of being closed , then block
* until it ' s done , and then try again .
*/
2009-01-02 16:45:05 +03:00
if ( tty_hung_up_p ( filp ) | | port - > flags & ASYNC_CLOSING ) {
interruptible_sleep_on ( & port - > close_wait ) ;
if ( port - > flags & ASYNC_HUP_NOTIFY )
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
else
return - ERESTARTSYS ;
}
gs_dprintk ( GS_DEBUG_BTR , " after hung up \n " ) ;
/*
* If non - blocking mode is set , or the port is not enabled ,
* then make the check up front and then exit .
*/
if ( ( filp - > f_flags & O_NONBLOCK ) | |
( tty - > flags & ( 1 < < TTY_IO_ERROR ) ) ) {
2009-01-02 16:45:05 +03:00
port - > flags | = ASYNC_NORMAL_ACTIVE ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
gs_dprintk ( GS_DEBUG_BTR , " after nonblock \n " ) ;
if ( C_CLOCAL ( tty ) )
do_clocal = 1 ;
/*
* Block waiting for the carrier detect and the line to become
* free ( i . e . , not in use by the callout ) . While we are in
2009-01-02 16:45:05 +03:00
* this loop , port - > count is dropped by one , so that
2005-04-17 02:20:36 +04:00
* rs_close ( ) knows when to free things . We restore it upon
* exit , either normal or abnormal .
*/
retval = 0 ;
2009-01-02 16:45:05 +03:00
add_wait_queue ( & port - > open_wait , & wait ) ;
2005-04-17 02:20:36 +04:00
gs_dprintk ( GS_DEBUG_BTR , " after add waitq. \n " ) ;
2009-01-02 16:45:36 +03:00
spin_lock_irqsave ( & port - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( ! tty_hung_up_p ( filp ) ) {
2009-01-02 16:45:05 +03:00
port - > count - - ;
2005-04-17 02:20:36 +04:00
}
2009-01-02 16:45:05 +03:00
port - > blocked_open + + ;
2009-01-02 16:45:36 +03:00
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
while ( 1 ) {
2009-01-02 16:45:05 +03:00
CD = tty_port_carrier_raised ( port ) ;
2005-04-17 02:20:36 +04:00
gs_dprintk ( GS_DEBUG_BTR , " CD is now %d. \n " , CD ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( tty_hung_up_p ( filp ) | |
2009-01-02 16:45:05 +03:00
! ( port - > flags & ASYNC_INITIALIZED ) ) {
if ( port - > flags & ASYNC_HUP_NOTIFY )
2005-04-17 02:20:36 +04:00
retval = - EAGAIN ;
else
retval = - ERESTARTSYS ;
break ;
}
2009-01-02 16:45:05 +03:00
if ( ! ( port - > flags & ASYNC_CLOSING ) & &
2005-04-17 02:20:36 +04:00
( do_clocal | | CD ) )
break ;
gs_dprintk ( GS_DEBUG_BTR , " signal_pending is now: %d (%lx) \n " ,
( int ) signal_pending ( current ) , * ( long * ) ( & current - > blocked ) ) ;
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
break ;
}
schedule ( ) ;
}
gs_dprintk ( GS_DEBUG_BTR , " Got out of the loop. (%d) \n " ,
2009-01-02 16:45:05 +03:00
port - > blocked_open ) ;
2005-04-17 02:20:36 +04:00
set_current_state ( TASK_RUNNING ) ;
2009-01-02 16:45:05 +03:00
remove_wait_queue ( & port - > open_wait , & wait ) ;
2009-01-02 16:45:36 +03:00
spin_lock_irqsave ( & port - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( ! tty_hung_up_p ( filp ) ) {
2009-01-02 16:45:05 +03:00
port - > count + + ;
2005-04-17 02:20:36 +04:00
}
2009-01-02 16:45:05 +03:00
port - > blocked_open - - ;
2009-01-02 16:45:36 +03:00
if ( retval = = 0 )
port - > flags | = ASYNC_NORMAL_ACTIVE ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
func_exit ( ) ;
2009-01-02 16:45:36 +03:00
return retval ;
2005-04-17 02:20:36 +04:00
}
void gs_close ( struct tty_struct * tty , struct file * filp )
{
unsigned long flags ;
struct gs_port * port ;
func_enter ( ) ;
2009-01-02 16:47:26 +03:00
port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
if ( ! port ) return ;
2008-07-17 00:55:20 +04:00
if ( ! port - > port . tty ) {
2005-04-17 02:20:36 +04:00
/* This seems to happen when this is called from vhangup. */
2008-07-17 00:55:20 +04:00
gs_dprintk ( GS_DEBUG_CLOSE , " gs: Odd: port->port.tty is NULL \n " ) ;
port - > port . tty = tty ;
2005-04-17 02:20:36 +04:00
}
2009-01-02 16:45:36 +03:00
spin_lock_irqsave ( & port - > port . lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( tty_hung_up_p ( filp ) ) {
2009-01-02 16:45:36 +03:00
spin_unlock_irqrestore ( & port - > port . lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( port - > rd - > hungup )
port - > rd - > hungup ( port ) ;
func_exit ( ) ;
return ;
}
2008-07-17 00:55:20 +04:00
if ( ( tty - > count = = 1 ) & & ( port - > port . count ! = 1 ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " gs: gs_close port %p: bad port count; "
2008-07-17 00:55:20 +04:00
" tty->count is 1, port count is %d \n " , port , port - > port . count ) ;
port - > port . count = 1 ;
2005-04-17 02:20:36 +04:00
}
2008-07-17 00:55:20 +04:00
if ( - - port - > port . count < 0 ) {
printk ( KERN_ERR " gs: gs_close port %p: bad port count: %d \n " , port , port - > port . count ) ;
port - > port . count = 0 ;
2005-04-17 02:20:36 +04:00
}
2008-07-17 00:55:20 +04:00
if ( port - > port . count ) {
gs_dprintk ( GS_DEBUG_CLOSE , " gs_close port %p: count: %d \n " , port , port - > port . count ) ;
2009-01-02 16:45:36 +03:00
spin_unlock_irqrestore ( & port - > port . lock , flags ) ;
2005-04-17 02:20:36 +04:00
func_exit ( ) ;
return ;
}
2008-07-17 00:55:20 +04:00
port - > port . flags | = ASYNC_CLOSING ;
2005-04-17 02:20:36 +04:00
/*
* Now we wait for the transmit buffer to clear ; and we notify
* the line discipline to only process XON / XOFF characters .
*/
tty - > closing = 1 ;
/* if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent ( tty , port - > closing_wait ) ; */
/*
* At this point we stop accepting input . To do this , we
* disable the receive line status interrupts , and tell the
* interrupt driver to stop checking the data ready bit in the
* line status register .
*/
2009-01-02 16:45:36 +03:00
spin_lock_irqsave ( & port - > driver_lock , flags ) ;
2005-04-17 02:20:36 +04:00
port - > rd - > disable_rx_interrupts ( port ) ;
spin_unlock_irqrestore ( & port - > driver_lock , flags ) ;
2009-01-02 16:45:36 +03:00
spin_unlock_irqrestore ( & port - > port . lock , flags ) ;
2005-04-17 02:20:36 +04:00
/* close has no way of returning "EINTR", so discard return value */
if ( port - > closing_wait ! = ASYNC_CLOSING_WAIT_NONE )
gs_wait_tx_flushed ( port , port - > closing_wait ) ;
2008-07-17 00:55:20 +04:00
port - > port . flags & = ~ GS_ACTIVE ;
2005-04-17 02:20:36 +04:00
2008-04-30 11:53:59 +04:00
gs_flush_buffer ( tty ) ;
2005-04-17 02:20:36 +04:00
tty_ldisc_flush ( tty ) ;
tty - > closing = 0 ;
2009-01-02 16:45:36 +03:00
spin_lock_irqsave ( & port - > driver_lock , flags ) ;
2005-04-17 02:20:36 +04:00
port - > event = 0 ;
port - > rd - > close ( port ) ;
port - > rd - > shutdown_port ( port ) ;
2009-01-02 16:45:36 +03:00
spin_unlock_irqrestore ( & port - > driver_lock , flags ) ;
spin_lock_irqsave ( & port - > port . lock , flags ) ;
2008-07-17 00:55:20 +04:00
port - > port . tty = NULL ;
2005-04-17 02:20:36 +04:00
2008-07-17 00:55:20 +04:00
if ( port - > port . blocked_open ) {
2005-04-17 02:20:36 +04:00
if ( port - > close_delay ) {
2009-01-02 16:45:36 +03:00
spin_unlock_irqrestore ( & port - > port . lock , flags ) ;
2005-04-17 02:20:36 +04:00
msleep_interruptible ( jiffies_to_msecs ( port - > close_delay ) ) ;
2009-01-02 16:45:36 +03:00
spin_lock_irqsave ( & port - > port . lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-17 00:55:20 +04:00
wake_up_interruptible ( & port - > port . open_wait ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-17 00:55:20 +04:00
port - > port . flags & = ~ ( ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING | ASYNC_INITIALIZED ) ;
2009-01-02 16:45:36 +03:00
spin_unlock_irqrestore ( & port - > port . lock , flags ) ;
2008-07-17 00:55:20 +04:00
wake_up_interruptible ( & port - > port . close_wait ) ;
2005-04-17 02:20:36 +04:00
func_exit ( ) ;
}
void gs_set_termios ( struct tty_struct * tty ,
2006-12-08 13:38:45 +03:00
struct ktermios * old_termios )
2005-04-17 02:20:36 +04:00
{
struct gs_port * port ;
int baudrate , tmp , rv ;
2006-12-08 13:38:45 +03:00
struct ktermios * tiosp ;
2005-04-17 02:20:36 +04:00
func_enter ( ) ;
port = tty - > driver_data ;
if ( ! port ) return ;
2008-07-17 00:55:20 +04:00
if ( ! port - > port . tty ) {
2005-04-17 02:20:36 +04:00
/* This seems to happen when this is called after gs_close. */
2008-07-17 00:55:20 +04:00
gs_dprintk ( GS_DEBUG_TERMIOS , " gs: Odd: port->port.tty is NULL \n " ) ;
port - > port . tty = tty ;
2005-04-17 02:20:36 +04:00
}
tiosp = tty - > termios ;
if ( gs_debug & GS_DEBUG_TERMIOS ) {
gs_dprintk ( GS_DEBUG_TERMIOS , " termios structure (%p): \n " , tiosp ) ;
}
if ( old_termios & & ( gs_debug & GS_DEBUG_TERMIOS ) ) {
if ( tiosp - > c_iflag ! = old_termios - > c_iflag ) printk ( " c_iflag changed \n " ) ;
if ( tiosp - > c_oflag ! = old_termios - > c_oflag ) printk ( " c_oflag changed \n " ) ;
if ( tiosp - > c_cflag ! = old_termios - > c_cflag ) printk ( " c_cflag changed \n " ) ;
if ( tiosp - > c_lflag ! = old_termios - > c_lflag ) printk ( " c_lflag changed \n " ) ;
if ( tiosp - > c_line ! = old_termios - > c_line ) printk ( " c_line changed \n " ) ;
if ( ! memcmp ( tiosp - > c_cc , old_termios - > c_cc , NCC ) ) printk ( " c_cc changed \n " ) ;
}
2006-09-29 13:01:38 +04:00
baudrate = tty_get_baud_rate ( tty ) ;
2005-04-17 02:20:36 +04:00
if ( ( tiosp - > c_cflag & CBAUD ) = = B38400 ) {
2008-07-17 00:55:20 +04:00
if ( ( port - > port . flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_HI )
2005-04-17 02:20:36 +04:00
baudrate = 57600 ;
2008-07-17 00:55:20 +04:00
else if ( ( port - > port . flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_VHI )
2005-04-17 02:20:36 +04:00
baudrate = 115200 ;
2008-07-17 00:55:20 +04:00
else if ( ( port - > port . flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_SHI )
2005-04-17 02:20:36 +04:00
baudrate = 230400 ;
2008-07-17 00:55:20 +04:00
else if ( ( port - > port . flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_WARP )
2005-04-17 02:20:36 +04:00
baudrate = 460800 ;
2008-07-17 00:55:20 +04:00
else if ( ( port - > port . flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_CUST )
2005-04-17 02:20:36 +04:00
baudrate = ( port - > baud_base / port - > custom_divisor ) ;
}
/* I recommend using THIS instead of the mess in termios (and
duplicating the above code ) . Next we should create a clean
interface towards this variable . If your card supports arbitrary
baud rates , ( e . g . CD1400 or 16550 based cards ) then everything
will be very easy . . . . . */
port - > baud = baudrate ;
/* Two timer ticks seems enough to wakeup something like SLIP driver */
/* Baudrate/10 is cps. Divide by HZ to get chars per tick. */
tmp = ( baudrate / 10 / HZ ) * 2 ;
if ( tmp < 0 ) tmp = 0 ;
if ( tmp > = SERIAL_XMIT_SIZE ) tmp = SERIAL_XMIT_SIZE - 1 ;
port - > wakeup_chars = tmp ;
/* We should really wait for the characters to be all sent before
changing the settings . - - CAL */
rv = gs_wait_tx_flushed ( port , MAX_SCHEDULE_TIMEOUT ) ;
if ( rv < 0 ) return /* rv */ ;
rv = port - > rd - > set_real_termios ( port ) ;
if ( rv < 0 ) return /* rv */ ;
if ( ( ! old_termios | |
( old_termios - > c_cflag & CRTSCTS ) ) & &
! ( tiosp - > c_cflag & CRTSCTS ) ) {
tty - > stopped = 0 ;
gs_start ( tty ) ;
}
# ifdef tytso_patch_94Nov25_1726
/* This "makes sense", Why is it commented out? */
if ( ! ( old_termios - > c_cflag & CLOCAL ) & &
( tty - > termios - > c_cflag & CLOCAL ) )
wake_up_interruptible ( & port - > gs . open_wait ) ;
# endif
func_exit ( ) ;
return /* 0 */ ;
}
/* Must be called with interrupts enabled */
int gs_init_port ( struct gs_port * port )
{
unsigned long flags ;
func_enter ( ) ;
2008-07-17 00:55:20 +04:00
if ( port - > port . flags & ASYNC_INITIALIZED ) {
2005-04-17 02:20:36 +04:00
func_exit ( ) ;
return 0 ;
}
if ( ! port - > xmit_buf ) {
/* We may sleep in get_zeroed_page() */
unsigned long tmp ;
tmp = get_zeroed_page ( GFP_KERNEL ) ;
spin_lock_irqsave ( & port - > driver_lock , flags ) ;
if ( port - > xmit_buf )
free_page ( tmp ) ;
else
port - > xmit_buf = ( unsigned char * ) tmp ;
spin_unlock_irqrestore ( & port - > driver_lock , flags ) ;
if ( ! port - > xmit_buf ) {
func_exit ( ) ;
return - ENOMEM ;
}
}
spin_lock_irqsave ( & port - > driver_lock , flags ) ;
2008-07-17 00:55:20 +04:00
if ( port - > port . tty )
clear_bit ( TTY_IO_ERROR , & port - > port . tty - > flags ) ;
2006-03-23 14:00:44 +03:00
mutex_init ( & port - > port_write_mutex ) ;
2005-04-17 02:20:36 +04:00
port - > xmit_cnt = port - > xmit_head = port - > xmit_tail = 0 ;
spin_unlock_irqrestore ( & port - > driver_lock , flags ) ;
2008-07-17 00:55:20 +04:00
gs_set_termios ( port - > port . tty , NULL ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & port - > driver_lock , flags ) ;
2008-07-17 00:55:20 +04:00
port - > port . flags | = ASYNC_INITIALIZED ;
port - > port . flags & = ~ GS_TX_INTEN ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & port - > driver_lock , flags ) ;
func_exit ( ) ;
return 0 ;
}
int gs_setserial ( struct gs_port * port , struct serial_struct __user * sp )
{
struct serial_struct sio ;
if ( copy_from_user ( & sio , sp , sizeof ( struct serial_struct ) ) )
return ( - EFAULT ) ;
if ( ! capable ( CAP_SYS_ADMIN ) ) {
if ( ( sio . baud_base ! = port - > baud_base ) | |
( sio . close_delay ! = port - > close_delay ) | |
( ( sio . flags & ~ ASYNC_USR_MASK ) ! =
2008-07-17 00:55:20 +04:00
( port - > port . flags & ~ ASYNC_USR_MASK ) ) )
2005-04-17 02:20:36 +04:00
return ( - EPERM ) ;
}
2008-07-17 00:55:20 +04:00
port - > port . flags = ( port - > port . flags & ~ ASYNC_USR_MASK ) |
2005-04-17 02:20:36 +04:00
( sio . flags & ASYNC_USR_MASK ) ;
port - > baud_base = sio . baud_base ;
port - > close_delay = sio . close_delay ;
port - > closing_wait = sio . closing_wait ;
port - > custom_divisor = sio . custom_divisor ;
2008-07-17 00:55:20 +04:00
gs_set_termios ( port - > port . tty , NULL ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*****************************************************************************/
/*
* Generate the serial struct info .
*/
int gs_getserial ( struct gs_port * port , struct serial_struct __user * sp )
{
struct serial_struct sio ;
memset ( & sio , 0 , sizeof ( struct serial_struct ) ) ;
2008-07-17 00:55:20 +04:00
sio . flags = port - > port . flags ;
2005-04-17 02:20:36 +04:00
sio . baud_base = port - > baud_base ;
sio . close_delay = port - > close_delay ;
sio . closing_wait = port - > closing_wait ;
sio . custom_divisor = port - > custom_divisor ;
sio . hub6 = 0 ;
/* If you want you can override these. */
sio . type = PORT_UNKNOWN ;
sio . xmit_fifo_size = - 1 ;
sio . line = - 1 ;
sio . port = - 1 ;
sio . irq = - 1 ;
if ( port - > rd - > getserial )
port - > rd - > getserial ( port , & sio ) ;
if ( copy_to_user ( sp , & sio , sizeof ( struct serial_struct ) ) )
return - EFAULT ;
return 0 ;
}
void gs_got_break ( struct gs_port * port )
{
func_enter ( ) ;
2008-07-17 00:55:20 +04:00
tty_insert_flip_char ( port - > port . tty , 0 , TTY_BREAK ) ;
tty_schedule_flip ( port - > port . tty ) ;
if ( port - > port . flags & ASYNC_SAK ) {
do_SAK ( port - > port . tty ) ;
2005-04-17 02:20:36 +04:00
}
func_exit ( ) ;
}
EXPORT_SYMBOL ( gs_put_char ) ;
EXPORT_SYMBOL ( gs_write ) ;
EXPORT_SYMBOL ( gs_write_room ) ;
EXPORT_SYMBOL ( gs_chars_in_buffer ) ;
EXPORT_SYMBOL ( gs_flush_buffer ) ;
EXPORT_SYMBOL ( gs_flush_chars ) ;
EXPORT_SYMBOL ( gs_stop ) ;
EXPORT_SYMBOL ( gs_start ) ;
EXPORT_SYMBOL ( gs_hangup ) ;
EXPORT_SYMBOL ( gs_block_til_ready ) ;
EXPORT_SYMBOL ( gs_close ) ;
EXPORT_SYMBOL ( gs_set_termios ) ;
EXPORT_SYMBOL ( gs_init_port ) ;
EXPORT_SYMBOL ( gs_setserial ) ;
EXPORT_SYMBOL ( gs_getserial ) ;
EXPORT_SYMBOL ( gs_got_break ) ;
MODULE_LICENSE ( " GPL " ) ;