2005-04-17 02:20:36 +04:00
/************************************************************************
* Copyright 2003 Digi International ( www . digi . com )
*
* Copyright ( C ) 2004 IBM Corporation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY , EXPRESS OR IMPLIED ; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE . See the GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 * Temple Place - Suite 330 , Boston ,
* MA 02111 - 1307 , USA .
*
* Contact Information :
* Scott H Kilau < Scott_Kilau @ digi . com >
2006-02-03 14:04:30 +03:00
* Ananda Venkatarman < mansarov @ us . ibm . com >
* Modifications :
* 01 / 19 / 06 : changed jsm_input routine to use the dynamically allocated
* tty_buffer changes . Contributors : Scott Kilau and Ananda V .
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/serial_reg.h>
# include <linux/delay.h> /* For udelay */
# include <linux/pci.h>
# include "jsm.h"
2005-05-01 19:59:29 +04:00
static void jsm_carrier ( struct jsm_channel * ch ) ;
2005-04-17 02:20:36 +04:00
static inline int jsm_get_mstat ( struct jsm_channel * ch )
{
unsigned char mstat ;
unsigned result ;
jsm_printk ( IOCTL , INFO , & ch - > ch_bd - > pci_dev , " start \n " ) ;
mstat = ( ch - > ch_mostat | ch - > ch_mistat ) ;
result = 0 ;
if ( mstat & UART_MCR_DTR )
result | = TIOCM_DTR ;
if ( mstat & UART_MCR_RTS )
result | = TIOCM_RTS ;
if ( mstat & UART_MSR_CTS )
result | = TIOCM_CTS ;
if ( mstat & UART_MSR_DSR )
result | = TIOCM_DSR ;
if ( mstat & UART_MSR_RI )
result | = TIOCM_RI ;
if ( mstat & UART_MSR_DCD )
result | = TIOCM_CD ;
jsm_printk ( IOCTL , INFO , & ch - > ch_bd - > pci_dev , " finish \n " ) ;
return result ;
}
static unsigned int jsm_tty_tx_empty ( struct uart_port * port )
{
return TIOCSER_TEMT ;
}
/*
* Return modem signals to ld .
*/
static unsigned int jsm_tty_get_mctrl ( struct uart_port * port )
{
int result ;
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
jsm_printk ( IOCTL , INFO , & channel - > ch_bd - > pci_dev , " start \n " ) ;
result = jsm_get_mstat ( channel ) ;
if ( result < 0 )
return - ENXIO ;
jsm_printk ( IOCTL , INFO , & channel - > ch_bd - > pci_dev , " finish \n " ) ;
return result ;
}
/*
* jsm_set_modem_info ( )
*
* Set modem signals , called by ld .
*/
static void jsm_tty_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
jsm_printk ( IOCTL , INFO , & channel - > ch_bd - > pci_dev , " start \n " ) ;
if ( mctrl & TIOCM_RTS )
channel - > ch_mostat | = UART_MCR_RTS ;
else
channel - > ch_mostat & = ~ UART_MCR_RTS ;
if ( mctrl & TIOCM_DTR )
channel - > ch_mostat | = UART_MCR_DTR ;
else
channel - > ch_mostat & = ~ UART_MCR_DTR ;
channel - > ch_bd - > bd_ops - > assert_modem_signals ( channel ) ;
jsm_printk ( IOCTL , INFO , & channel - > ch_bd - > pci_dev , " finish \n " ) ;
udelay ( 10 ) ;
}
2005-08-31 13:12:14 +04:00
static void jsm_tty_start_tx ( struct uart_port * port )
2005-04-17 02:20:36 +04:00
{
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
jsm_printk ( IOCTL , INFO , & channel - > ch_bd - > pci_dev , " start \n " ) ;
channel - > ch_flags & = ~ ( CH_STOP ) ;
jsm_tty_write ( port ) ;
jsm_printk ( IOCTL , INFO , & channel - > ch_bd - > pci_dev , " finish \n " ) ;
}
2005-08-31 13:12:14 +04:00
static void jsm_tty_stop_tx ( struct uart_port * port )
2005-04-17 02:20:36 +04:00
{
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
jsm_printk ( IOCTL , INFO , & channel - > ch_bd - > pci_dev , " start \n " ) ;
channel - > ch_flags | = ( CH_STOP ) ;
jsm_printk ( IOCTL , INFO , & channel - > ch_bd - > pci_dev , " finish \n " ) ;
}
static void jsm_tty_send_xchar ( struct uart_port * port , char ch )
{
unsigned long lock_flags ;
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
2006-12-08 13:38:45 +03:00
struct ktermios * termios ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & port - > lock , lock_flags ) ;
2006-03-31 14:32:12 +04:00
termios = port - > info - > tty - > termios ;
if ( ch = = termios - > c_cc [ VSTART ] )
2005-04-17 02:20:36 +04:00
channel - > ch_bd - > bd_ops - > send_start_character ( channel ) ;
2006-03-31 14:32:12 +04:00
if ( ch = = termios - > c_cc [ VSTOP ] )
2005-04-17 02:20:36 +04:00
channel - > ch_bd - > bd_ops - > send_stop_character ( channel ) ;
spin_unlock_irqrestore ( & port - > lock , lock_flags ) ;
}
static void jsm_tty_stop_rx ( struct uart_port * port )
{
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
channel - > ch_bd - > bd_ops - > disable_receiver ( channel ) ;
}
static void jsm_tty_break ( struct uart_port * port , int break_state )
{
unsigned long lock_flags ;
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
spin_lock_irqsave ( & port - > lock , lock_flags ) ;
if ( break_state = = - 1 )
channel - > ch_bd - > bd_ops - > send_break ( channel ) ;
else
channel - > ch_bd - > bd_ops - > clear_break ( channel , 0 ) ;
spin_unlock_irqrestore ( & port - > lock , lock_flags ) ;
}
static int jsm_tty_open ( struct uart_port * port )
{
struct jsm_board * brd ;
int rc = 0 ;
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
2006-12-08 13:38:45 +03:00
struct ktermios * termios ;
2005-04-17 02:20:36 +04:00
/* Get board pointer from our array of majors we have allocated */
brd = channel - > ch_bd ;
/*
* Allocate channel buffers for read / write / error .
* Set flag , so we don ' t get trounced on .
*/
channel - > ch_flags | = ( CH_OPENING ) ;
/* Drop locks, as malloc with GFP_KERNEL can sleep */
if ( ! channel - > ch_rqueue ) {
2007-02-14 11:33:07 +03:00
channel - > ch_rqueue = kzalloc ( RQUEUESIZE , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! channel - > ch_rqueue ) {
jsm_printk ( INIT , ERR , & channel - > ch_bd - > pci_dev ,
" unable to allocate read queue buf " ) ;
return - ENOMEM ;
}
}
if ( ! channel - > ch_equeue ) {
2007-02-14 11:33:07 +03:00
channel - > ch_equeue = kzalloc ( EQUEUESIZE , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! channel - > ch_equeue ) {
jsm_printk ( INIT , ERR , & channel - > ch_bd - > pci_dev ,
" unable to allocate error queue buf " ) ;
return - ENOMEM ;
}
}
if ( ! channel - > ch_wqueue ) {
2007-02-14 11:33:07 +03:00
channel - > ch_wqueue = kzalloc ( WQUEUESIZE , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! channel - > ch_wqueue ) {
jsm_printk ( INIT , ERR , & channel - > ch_bd - > pci_dev ,
" unable to allocate write queue buf " ) ;
return - ENOMEM ;
}
}
channel - > ch_flags & = ~ ( CH_OPENING ) ;
/*
* Initialize if neither terminal is open .
*/
jsm_printk ( OPEN , INFO , & channel - > ch_bd - > pci_dev ,
" jsm_open: initializing channel in open... \n " ) ;
/*
* Flush input queues .
*/
channel - > ch_r_head = channel - > ch_r_tail = 0 ;
channel - > ch_e_head = channel - > ch_e_tail = 0 ;
channel - > ch_w_head = channel - > ch_w_tail = 0 ;
brd - > bd_ops - > flush_uart_write ( channel ) ;
brd - > bd_ops - > flush_uart_read ( channel ) ;
channel - > ch_flags = 0 ;
channel - > ch_cached_lsr = 0 ;
channel - > ch_stops_sent = 0 ;
2006-03-31 14:32:12 +04:00
termios = port - > info - > tty - > termios ;
channel - > ch_c_cflag = termios - > c_cflag ;
channel - > ch_c_iflag = termios - > c_iflag ;
channel - > ch_c_oflag = termios - > c_oflag ;
channel - > ch_c_lflag = termios - > c_lflag ;
channel - > ch_startc = termios - > c_cc [ VSTART ] ;
channel - > ch_stopc = termios - > c_cc [ VSTOP ] ;
2005-04-17 02:20:36 +04:00
/* Tell UART to init itself */
brd - > bd_ops - > uart_init ( channel ) ;
/*
* Run param in case we changed anything
*/
brd - > bd_ops - > param ( channel ) ;
jsm_carrier ( channel ) ;
channel - > ch_open_count + + ;
jsm_printk ( OPEN , INFO , & channel - > ch_bd - > pci_dev , " finish \n " ) ;
return rc ;
}
static void jsm_tty_close ( struct uart_port * port )
{
struct jsm_board * bd ;
2006-12-08 13:38:45 +03:00
struct ktermios * ts ;
2005-04-17 02:20:36 +04:00
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
jsm_printk ( CLOSE , INFO , & channel - > ch_bd - > pci_dev , " start \n " ) ;
bd = channel - > ch_bd ;
ts = channel - > uart_port . info - > tty - > termios ;
channel - > ch_flags & = ~ ( CH_STOPI ) ;
channel - > ch_open_count - - ;
/*
* If we have HUPCL set , lower DTR and RTS
*/
if ( channel - > ch_c_cflag & HUPCL ) {
jsm_printk ( CLOSE , INFO , & channel - > ch_bd - > pci_dev ,
" Close. HUPCL set, dropping DTR/RTS \n " ) ;
/* Drop RTS/DTR */
channel - > ch_mostat & = ~ ( UART_MCR_DTR | UART_MCR_RTS ) ;
bd - > bd_ops - > assert_modem_signals ( channel ) ;
}
channel - > ch_old_baud = 0 ;
/* Turn off UART interrupts for this port */
channel - > ch_bd - > bd_ops - > uart_off ( channel ) ;
jsm_printk ( CLOSE , INFO , & channel - > ch_bd - > pci_dev , " finish \n " ) ;
}
static void jsm_tty_set_termios ( struct uart_port * port ,
2006-12-08 13:38:45 +03:00
struct ktermios * termios ,
struct ktermios * old_termios )
2005-04-17 02:20:36 +04:00
{
unsigned long lock_flags ;
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
spin_lock_irqsave ( & port - > lock , lock_flags ) ;
channel - > ch_c_cflag = termios - > c_cflag ;
channel - > ch_c_iflag = termios - > c_iflag ;
channel - > ch_c_oflag = termios - > c_oflag ;
channel - > ch_c_lflag = termios - > c_lflag ;
channel - > ch_startc = termios - > c_cc [ VSTART ] ;
channel - > ch_stopc = termios - > c_cc [ VSTOP ] ;
channel - > ch_bd - > bd_ops - > param ( channel ) ;
jsm_carrier ( channel ) ;
spin_unlock_irqrestore ( & port - > lock , lock_flags ) ;
}
static const char * jsm_tty_type ( struct uart_port * port )
{
return " jsm " ;
}
static void jsm_tty_release_port ( struct uart_port * port )
{
}
static int jsm_tty_request_port ( struct uart_port * port )
{
return 0 ;
}
static void jsm_config_port ( struct uart_port * port , int flags )
{
port - > type = PORT_JSM ;
}
static struct uart_ops jsm_ops = {
. tx_empty = jsm_tty_tx_empty ,
. set_mctrl = jsm_tty_set_mctrl ,
. get_mctrl = jsm_tty_get_mctrl ,
. stop_tx = jsm_tty_stop_tx ,
. start_tx = jsm_tty_start_tx ,
. send_xchar = jsm_tty_send_xchar ,
. stop_rx = jsm_tty_stop_rx ,
. break_ctl = jsm_tty_break ,
. startup = jsm_tty_open ,
. shutdown = jsm_tty_close ,
. set_termios = jsm_tty_set_termios ,
. type = jsm_tty_type ,
. release_port = jsm_tty_release_port ,
. request_port = jsm_tty_request_port ,
. config_port = jsm_config_port ,
} ;
/*
* jsm_tty_init ( )
*
* Init the tty subsystem . Called once per board after board has been
* downloaded and init ' ed .
*/
int jsm_tty_init ( struct jsm_board * brd )
{
int i ;
void __iomem * vaddr ;
struct jsm_channel * ch ;
if ( ! brd )
return - ENXIO ;
jsm_printk ( INIT , INFO , & brd - > pci_dev , " start \n " ) ;
/*
* Initialize board structure elements .
*/
brd - > nasync = brd - > maxports ;
/*
* Allocate channel memory that might not have been allocated
* when the driver was first loaded .
*/
for ( i = 0 ; i < brd - > nasync ; i + + ) {
if ( ! brd - > channels [ i ] ) {
/*
* Okay to malloc with GFP_KERNEL , we are not at
* interrupt context , and there are no locks held .
*/
2007-02-14 11:33:07 +03:00
brd - > channels [ i ] = kzalloc ( sizeof ( struct jsm_channel ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! brd - > channels [ i ] ) {
jsm_printk ( CORE , ERR , & brd - > pci_dev ,
" %s:%d Unable to allocate memory for channel struct \n " ,
__FILE__ , __LINE__ ) ;
}
}
}
ch = brd - > channels [ 0 ] ;
vaddr = brd - > re_map_membase ;
/* Set up channel variables */
for ( i = 0 ; i < brd - > nasync ; i + + , ch = brd - > channels [ i ] ) {
if ( ! brd - > channels [ i ] )
continue ;
spin_lock_init ( & ch - > ch_lock ) ;
if ( brd - > bd_uart_offset = = 0x200 )
ch - > ch_neo_uart = vaddr + ( brd - > bd_uart_offset * i ) ;
ch - > ch_bd = brd ;
ch - > ch_portnum = i ;
/* .25 second delay */
ch - > ch_close_delay = 250 ;
init_waitqueue_head ( & ch - > ch_flags_wait ) ;
}
jsm_printk ( INIT , INFO , & brd - > pci_dev , " finish \n " ) ;
return 0 ;
}
int jsm_uart_port_init ( struct jsm_board * brd )
{
int i ;
struct jsm_channel * ch ;
if ( ! brd )
return - ENXIO ;
jsm_printk ( INIT , INFO , & brd - > pci_dev , " start \n " ) ;
/*
* Initialize board structure elements .
*/
brd - > nasync = brd - > maxports ;
/* Set up channel variables */
for ( i = 0 ; i < brd - > nasync ; i + + , ch = brd - > channels [ i ] ) {
if ( ! brd - > channels [ i ] )
continue ;
brd - > channels [ i ] - > uart_port . irq = brd - > irq ;
2007-05-08 11:26:30 +04:00
brd - > channels [ i ] - > uart_port . uartclk = 14745600 ;
2005-04-17 02:20:36 +04:00
brd - > channels [ i ] - > uart_port . type = PORT_JSM ;
brd - > channels [ i ] - > uart_port . iotype = UPIO_MEM ;
brd - > channels [ i ] - > uart_port . membase = brd - > re_map_membase ;
brd - > channels [ i ] - > uart_port . fifosize = 16 ;
brd - > channels [ i ] - > uart_port . ops = & jsm_ops ;
brd - > channels [ i ] - > uart_port . line = brd - > channels [ i ] - > ch_portnum + brd - > boardnum * 2 ;
if ( uart_add_one_port ( & jsm_uart_driver , & brd - > channels [ i ] - > uart_port ) )
printk ( KERN_INFO " Added device failed \n " ) ;
else
printk ( KERN_INFO " Added device \n " ) ;
}
jsm_printk ( INIT , INFO , & brd - > pci_dev , " finish \n " ) ;
return 0 ;
}
int jsm_remove_uart_port ( struct jsm_board * brd )
{
int i ;
struct jsm_channel * ch ;
if ( ! brd )
return - ENXIO ;
jsm_printk ( INIT , INFO , & brd - > pci_dev , " start \n " ) ;
/*
* Initialize board structure elements .
*/
brd - > nasync = brd - > maxports ;
/* Set up channel variables */
for ( i = 0 ; i < brd - > nasync ; i + + ) {
if ( ! brd - > channels [ i ] )
continue ;
ch = brd - > channels [ i ] ;
uart_remove_one_port ( & jsm_uart_driver , & brd - > channels [ i ] - > uart_port ) ;
}
jsm_printk ( INIT , INFO , & brd - > pci_dev , " finish \n " ) ;
return 0 ;
}
void jsm_input ( struct jsm_channel * ch )
{
struct jsm_board * bd ;
struct tty_struct * tp ;
2006-02-03 14:04:30 +03:00
struct tty_ldisc * ld ;
2005-04-17 02:20:36 +04:00
u32 rmask ;
u16 head ;
u16 tail ;
int data_len ;
unsigned long lock_flags ;
2006-02-03 14:04:30 +03:00
int flip_len = 0 ;
2005-04-17 02:20:36 +04:00
int len = 0 ;
int n = 0 ;
int s = 0 ;
int i = 0 ;
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev , " start \n " ) ;
if ( ! ch )
return ;
tp = ch - > uart_port . info - > tty ;
bd = ch - > ch_bd ;
if ( ! bd )
return ;
spin_lock_irqsave ( & ch - > ch_lock , lock_flags ) ;
/*
* Figure the number of characters in the buffer .
* Exit immediately if none .
*/
rmask = RQUEUEMASK ;
head = ch - > ch_r_head & rmask ;
tail = ch - > ch_r_tail & rmask ;
data_len = ( head - tail ) & rmask ;
if ( data_len = = 0 ) {
spin_unlock_irqrestore ( & ch - > ch_lock , lock_flags ) ;
return ;
}
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev , " start \n " ) ;
/*
* If the device is not open , or CREAD is off , flush
* input data and return immediately .
*/
if ( ! tp | |
! ( tp - > termios - > c_cflag & CREAD ) ) {
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev ,
" input. dropping %d bytes on port %d... \n " , data_len , ch - > ch_portnum ) ;
ch - > ch_r_head = tail ;
/* Force queue flow control to be released, if needed */
jsm_check_queue_flow_control ( ch ) ;
spin_unlock_irqrestore ( & ch - > ch_lock , lock_flags ) ;
return ;
}
/*
* If we are throttled , simply don ' t read any data .
*/
if ( ch - > ch_flags & CH_STOPI ) {
spin_unlock_irqrestore ( & ch - > ch_lock , lock_flags ) ;
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev ,
" Port %d throttled, not reading any data. head: %x tail: %x \n " ,
ch - > ch_portnum , head , tail ) ;
return ;
}
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev , " start 2 \n " ) ;
/*
* If the rxbuf is empty and we are not throttled , put as much
2006-02-03 14:04:30 +03:00
* as we can directly into the linux TTY buffer .
2005-04-17 02:20:36 +04:00
*
*/
2006-02-03 14:04:30 +03:00
flip_len = TTY_FLIPBUF_SIZE ;
2005-04-17 02:20:36 +04:00
len = min ( data_len , flip_len ) ;
len = min ( len , ( N_TTY_BUF_SIZE - 1 ) - tp - > read_cnt ) ;
2006-02-03 14:04:30 +03:00
ld = tty_ldisc_ref ( tp ) ;
2005-04-17 02:20:36 +04:00
/*
2006-02-03 14:04:30 +03:00
* If we were unable to get a reference to the ld ,
* don ' t flush our buffer , and act like the ld doesn ' t
* have any space to put the data right now .
2005-04-17 02:20:36 +04:00
*/
2006-02-03 14:04:30 +03:00
if ( ! ld ) {
len = 0 ;
2005-04-17 02:20:36 +04:00
} else {
2006-02-03 14:04:30 +03:00
/*
* If ld doesn ' t have a pointer to a receive_buf function ,
* flush the data , then act like the ld doesn ' t have any
* space to put the data right now .
*/
if ( ! ld - > receive_buf ) {
ch - > ch_r_head = ch - > ch_r_tail ;
len = 0 ;
}
}
if ( len < = 0 ) {
spin_unlock_irqrestore ( & ch - > ch_lock , lock_flags ) ;
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev , " jsm_input 1 \n " ) ;
if ( ld )
tty_ldisc_deref ( ld ) ;
return ;
2005-04-17 02:20:36 +04:00
}
2006-02-03 14:04:30 +03:00
len = tty_buffer_request_room ( tp , len ) ;
2005-04-17 02:20:36 +04:00
n = len ;
/*
* n now contains the most amount of data we can copy ,
* bounded either by the flip buffer size or the amount
* of data the card actually has pending . . .
*/
while ( n ) {
s = ( ( head > = tail ) ? head : RQUEUESIZE ) - tail ;
s = min ( s , n ) ;
if ( s < = 0 )
break ;
2006-02-03 14:04:30 +03:00
/*
* If conditions are such that ld needs to see all
* UART errors , we will have to walk each character
* and error byte and send them to the buffer one at
* a time .
*/
2005-04-17 02:20:36 +04:00
if ( I_PARMRK ( tp ) | | I_BRKINT ( tp ) | | I_INPCK ( tp ) ) {
2006-02-03 14:04:30 +03:00
for ( i = 0 ; i < s ; i + + ) {
2005-04-17 02:20:36 +04:00
/*
* Give the Linux ld the flags in the
* format it likes .
*/
2006-02-03 14:04:30 +03:00
if ( * ( ch - > ch_equeue + tail + i ) & UART_LSR_BI )
tty_insert_flip_char ( tp , * ( ch - > ch_rqueue + tail + i ) , TTY_BREAK ) ;
else if ( * ( ch - > ch_equeue + tail + i ) & UART_LSR_PE )
tty_insert_flip_char ( tp , * ( ch - > ch_rqueue + tail + i ) , TTY_PARITY ) ;
else if ( * ( ch - > ch_equeue + tail + i ) & UART_LSR_FE )
tty_insert_flip_char ( tp , * ( ch - > ch_rqueue + tail + i ) , TTY_FRAME ) ;
2005-04-17 02:20:36 +04:00
else
2006-02-03 14:04:30 +03:00
tty_insert_flip_char ( tp , * ( ch - > ch_rqueue + tail + i ) , TTY_NORMAL ) ;
2005-04-17 02:20:36 +04:00
}
} else {
2006-02-03 14:04:30 +03:00
tty_insert_flip_string ( tp , ch - > ch_rqueue + tail , s ) ;
2005-04-17 02:20:36 +04:00
}
2006-02-03 14:04:30 +03:00
tail + = s ;
n - = s ;
/* Flip queue if needed */
tail & = rmask ;
2005-04-17 02:20:36 +04:00
}
2006-02-03 14:04:30 +03:00
ch - > ch_r_tail = tail & rmask ;
ch - > ch_e_tail = tail & rmask ;
jsm_check_queue_flow_control ( ch ) ;
spin_unlock_irqrestore ( & ch - > ch_lock , lock_flags ) ;
2005-04-17 02:20:36 +04:00
2006-02-03 14:04:30 +03:00
/* Tell the tty layer its okay to "eat" the data now */
tty_flip_buffer_push ( tp ) ;
2005-04-17 02:20:36 +04:00
2006-02-03 14:04:30 +03:00
if ( ld )
tty_ldisc_deref ( ld ) ;
2005-04-17 02:20:36 +04:00
jsm_printk ( IOCTL , INFO , & ch - > ch_bd - > pci_dev , " finish \n " ) ;
}
2005-05-01 19:59:29 +04:00
static void jsm_carrier ( struct jsm_channel * ch )
2005-04-17 02:20:36 +04:00
{
struct jsm_board * bd ;
int virt_carrier = 0 ;
int phys_carrier = 0 ;
jsm_printk ( CARR , INFO , & ch - > ch_bd - > pci_dev , " start \n " ) ;
if ( ! ch )
return ;
bd = ch - > ch_bd ;
if ( ! bd )
return ;
if ( ch - > ch_mistat & UART_MSR_DCD ) {
jsm_printk ( CARR , INFO , & ch - > ch_bd - > pci_dev ,
" mistat: %x D_CD: %x \n " , ch - > ch_mistat , ch - > ch_mistat & UART_MSR_DCD ) ;
phys_carrier = 1 ;
}
if ( ch - > ch_c_cflag & CLOCAL )
virt_carrier = 1 ;
jsm_printk ( CARR , INFO , & ch - > ch_bd - > pci_dev ,
" DCD: physical: %d virt: %d \n " , phys_carrier , virt_carrier ) ;
/*
* Test for a VIRTUAL carrier transition to HIGH .
*/
if ( ( ( ch - > ch_flags & CH_FCAR ) = = 0 ) & & ( virt_carrier = = 1 ) ) {
/*
* When carrier rises , wake any threads waiting
* for carrier in the open routine .
*/
jsm_printk ( CARR , INFO , & ch - > ch_bd - > pci_dev ,
" carrier: virt DCD rose \n " ) ;
if ( waitqueue_active ( & ( ch - > ch_flags_wait ) ) )
wake_up_interruptible ( & ch - > ch_flags_wait ) ;
}
/*
* Test for a PHYSICAL carrier transition to HIGH .
*/
if ( ( ( ch - > ch_flags & CH_CD ) = = 0 ) & & ( phys_carrier = = 1 ) ) {
/*
* When carrier rises , wake any threads waiting
* for carrier in the open routine .
*/
jsm_printk ( CARR , INFO , & ch - > ch_bd - > pci_dev ,
" carrier: physical DCD rose \n " ) ;
if ( waitqueue_active ( & ( ch - > ch_flags_wait ) ) )
wake_up_interruptible ( & ch - > ch_flags_wait ) ;
}
/*
* Test for a PHYSICAL transition to low , so long as we aren ' t
* currently ignoring physical transitions ( which is what " virtual
* carrier " indicates).
*
* The transition of the virtual carrier to low really doesn ' t
* matter . . . it really only means " ignore carrier state " , not
* " make pretend that carrier is there " .
*/
if ( ( virt_carrier = = 0 ) & & ( ( ch - > ch_flags & CH_CD ) ! = 0 )
& & ( phys_carrier = = 0 ) ) {
/*
* When carrier drops :
*
* Drop carrier on all open units .
*
* Flush queues , waking up any task waiting in the
* line discipline .
*
* Send a hangup to the control terminal .
*
* Enable all select calls .
*/
if ( waitqueue_active ( & ( ch - > ch_flags_wait ) ) )
wake_up_interruptible ( & ch - > ch_flags_wait ) ;
}
/*
* Make sure that our cached values reflect the current reality .
*/
if ( virt_carrier = = 1 )
ch - > ch_flags | = CH_FCAR ;
else
ch - > ch_flags & = ~ CH_FCAR ;
if ( phys_carrier = = 1 )
ch - > ch_flags | = CH_CD ;
else
ch - > ch_flags & = ~ CH_CD ;
}
void jsm_check_queue_flow_control ( struct jsm_channel * ch )
{
2006-03-31 14:32:12 +04:00
struct board_ops * bd_ops = ch - > ch_bd - > bd_ops ;
2005-04-17 02:20:36 +04:00
int qleft = 0 ;
/* Store how much space we have left in the queue */
if ( ( qleft = ch - > ch_r_tail - ch - > ch_r_head - 1 ) < 0 )
qleft + = RQUEUEMASK + 1 ;
/*
* Check to see if we should enforce flow control on our queue because
* the ld ( or user ) isn ' t reading data out of our queue fast enuf .
*
* NOTE : This is done based on what the current flow control of the
* port is set for .
*
* 1 ) HWFLOW ( RTS ) - Turn off the UART ' s Receive interrupt .
* This will cause the UART ' s FIFO to back up , and force
* the RTS signal to be dropped .
* 2 ) SWFLOW ( IXOFF ) - Keep trying to send a stop character to
* the other side , in hopes it will stop sending data to us .
* 3 ) NONE - Nothing we can do . We will simply drop any extra data
* that gets sent into us when the queue fills up .
*/
if ( qleft < 256 ) {
/* HWFLOW */
if ( ch - > ch_c_cflag & CRTSCTS ) {
if ( ! ( ch - > ch_flags & CH_RECEIVER_OFF ) ) {
2006-03-31 14:32:12 +04:00
bd_ops - > disable_receiver ( ch ) ;
2005-04-17 02:20:36 +04:00
ch - > ch_flags | = ( CH_RECEIVER_OFF ) ;
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev ,
" Internal queue hit hilevel mark (%d)! Turning off interrupts. \n " ,
qleft ) ;
}
}
/* SWFLOW */
else if ( ch - > ch_c_iflag & IXOFF ) {
if ( ch - > ch_stops_sent < = MAX_STOPS_SENT ) {
2006-03-31 14:32:12 +04:00
bd_ops - > send_stop_character ( ch ) ;
2005-04-17 02:20:36 +04:00
ch - > ch_stops_sent + + ;
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev ,
" Sending stop char! Times sent: %x \n " , ch - > ch_stops_sent ) ;
}
}
}
/*
* Check to see if we should unenforce flow control because
* ld ( or user ) finally read enuf data out of our queue .
*
* NOTE : This is done based on what the current flow control of the
* port is set for .
*
* 1 ) HWFLOW ( RTS ) - Turn back on the UART ' s Receive interrupt .
* This will cause the UART ' s FIFO to raise RTS back up ,
* which will allow the other side to start sending data again .
* 2 ) SWFLOW ( IXOFF ) - Send a start character to
* the other side , so it will start sending data to us again .
* 3 ) NONE - Do nothing . Since we didn ' t do anything to turn off the
* other side , we don ' t need to do anything now .
*/
if ( qleft > ( RQUEUESIZE / 2 ) ) {
/* HWFLOW */
if ( ch - > ch_c_cflag & CRTSCTS ) {
if ( ch - > ch_flags & CH_RECEIVER_OFF ) {
2006-03-31 14:32:12 +04:00
bd_ops - > enable_receiver ( ch ) ;
2005-04-17 02:20:36 +04:00
ch - > ch_flags & = ~ ( CH_RECEIVER_OFF ) ;
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev ,
" Internal queue hit lowlevel mark (%d)! Turning on interrupts. \n " ,
qleft ) ;
}
}
/* SWFLOW */
else if ( ch - > ch_c_iflag & IXOFF & & ch - > ch_stops_sent ) {
ch - > ch_stops_sent = 0 ;
2006-03-31 14:32:12 +04:00
bd_ops - > send_start_character ( ch ) ;
2005-04-17 02:20:36 +04:00
jsm_printk ( READ , INFO , & ch - > ch_bd - > pci_dev , " Sending start char! \n " ) ;
}
}
}
/*
* jsm_tty_write ( )
*
* Take data from the user or kernel and send it out to the FEP .
* In here exists all the Transparent Print magic as well .
*/
int jsm_tty_write ( struct uart_port * port )
{
int bufcount = 0 , n = 0 ;
int data_count = 0 , data_count1 = 0 ;
u16 head ;
u16 tail ;
u16 tmask ;
u32 remain ;
int temp_tail = port - > info - > xmit . tail ;
struct jsm_channel * channel = ( struct jsm_channel * ) port ;
tmask = WQUEUEMASK ;
head = ( channel - > ch_w_head ) & tmask ;
tail = ( channel - > ch_w_tail ) & tmask ;
if ( ( bufcount = tail - head - 1 ) < 0 )
bufcount + = WQUEUESIZE ;
n = bufcount ;
n = min ( n , 56 ) ;
remain = WQUEUESIZE - head ;
data_count = 0 ;
if ( n > = remain ) {
n - = remain ;
while ( ( port - > info - > xmit . head ! = temp_tail ) & &
( data_count < remain ) ) {
channel - > ch_wqueue [ head + + ] =
port - > info - > xmit . buf [ temp_tail ] ;
temp_tail + + ;
temp_tail & = ( UART_XMIT_SIZE - 1 ) ;
data_count + + ;
}
if ( data_count = = remain ) head = 0 ;
}
data_count1 = 0 ;
if ( n > 0 ) {
remain = n ;
while ( ( port - > info - > xmit . head ! = temp_tail ) & &
( data_count1 < remain ) ) {
channel - > ch_wqueue [ head + + ] =
port - > info - > xmit . buf [ temp_tail ] ;
temp_tail + + ;
temp_tail & = ( UART_XMIT_SIZE - 1 ) ;
data_count1 + + ;
}
}
port - > info - > xmit . tail = temp_tail ;
data_count + = data_count1 ;
if ( data_count ) {
head & = tmask ;
channel - > ch_w_head = head ;
}
if ( data_count ) {
channel - > ch_bd - > bd_ops - > copy_data_from_queue_to_uart ( channel ) ;
}
return data_count ;
}