or' content='cgit v1.2.2'/>
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/char/epca.c
blob: ffcecde9e2a5b317319e605480c5520264c77ee3 (plain) (tree)
1
2
3
4
5
6
7
8
  
                                               
 


                                                                

                                                        







                                                                            
 









                                                                         
 













                            
                           

                      
 





                       
                                          

                                                                       
                                                             






                                                               





                                         



                                                                               
                                  
 
                                                                                  

                                           




                                                                            






                                                                               

                                               




                                                                               



                                          



                                                                           







                                                              


                                                                          





                                                                    


                                                                        





                                                                   



                                                                              





                                                                   


                                                                            







                                                                    
 

                                                          
























                                                              
                                                                   
                                                 






                                                                          
 
                                                                     
                         
                          
 








                                                                               
                                                            
 
                            

 
                                                             
 
                             

 
                                           
 
                                   

 
                                       
 
                               

 
                                       
 
                               

 
                                      
 
                              
 
                                            
 
                                    

 
                                            
 
                                    

 
                                                                               
                                                                  
 
                                        

 
                                                                   
 
                               

 
                                                 



                                                  
                                             



                                                    
                                             



                                                    
                                            




                                                                         
                                                                 
 
                                          

 
                                                                  
 
                                                    
                               

 
                                                
 
                                                 

 
                                            
 
                                                    

 
                                            
 
                                                    

 
                                           





                                                                            
                                                                 
 
                                               

 
                                                                  
 
                                                

 
                                                
 
                                        

 
                                            
 
                                        

 
                                            
 
                                        

 
                                           
 
                                   

 
                                                 
 
                                                                       

 
                                                 
 
                                                                  

 






                                                                               
                                                                  


 
                                                                   


 
                                                 


 
                                             


 
                                             


 
                                            


 
                                                  


 
                                                  


 
                                                            






                                                                              


                                                                                 


                                                    
         
                    
 
 

                                                         



                                                                               

                                   
 

                                           
 
                                                                      
 
 
                                                               
 

                            




                                                                         


                                                                  

                               

                                                                  



                                                                             
                                                                  
                               
                 

                                                                        
                                      
 




                                                                               
                                                

                                 


                                                          
                                                                                         
                                                   

                                                                                




                                                       

                                                     


                                 
                                                          
 

                                            
                                                                                        
                                                              

                                                                             

                                                       

         

                                        
 

                               
                                      
 
                                                  

                       
                                             
 
                        

                         




                                                                           
               
                                      

                      
                                                                              
                                             


                                                                      

                   



                                                                               
                                                                          
                                             
                                                  
 

                                             
 
                           
 




                                                                         

                                    




                                                       
                                                     


                                 
                                                                             
                                                          
                                                      

         
 
                                           
                                                                 
 



                                


                            
                                      
 







                                                                               
 



                                                                         



                                                                              

                             
                         
 
                                             

                        

                                            
 

                                        

                           









                                                                               

                                                  

                                             

                                         




                                                                               
                                                     
                    

                                                     
 



                                                                             
                                                       
                                                            




                                          
                                   


                                       
         

                                  
                               
 
                                                
                                           
                                     

                   
                                                  

                            

                                                                
 
                             
 

                                                
 



                                
                                      


                   



                                                                         

                                                     


                                   

                                                             
 

                                                





                                                    
                                                                 
                                                   
                                             

                           
                                                          
         

                                                  
 

                                                     
 




                                       
                                      
 



                                                                         
                                              
                         
 
                                             


                         


                                          
 
                                                                                        
                          

                                                                   
                                            




                                                                               

                                                    
                                                      






                                                                            

                                                   
                                                           
                   
                                                  
                                                           

                     

                                                   
 


                            
                                      



                                                                         


                                              
                                             
                        
                           
                                
                                                                              
                                                    
                   
                                                  
                        
 

                                                  





                                                                         
                                                
                                    
                                                     



                                                                          

                                                                                 
                                                          
         
 
 
                                                  
                                                                 
 
                                        
                                  

                            
                                  


                                                      

                                              

         



                                                                           
                                             







                                                        
                                          



                                                                            
                                                      

                         

                                           
                                                                              
 

                                              
 
                                             


                                                                         
                           
                   
                                                      
                                          
                                                          



                                                              
                                                      

                              
                                                        

                                                                
                                              


                                              
                                                          





                                                                              
                           
                                                     
         
 
                                          
                                                 

                                 

                           

                                                  



                                              
                 
 

                                                              
 


                                   
                                      
                          

                          

                                       





                                                          




                                                                              
                                  
                                                          
                                                                                                            
                                                         
                                                                                                                 
                                                        
                                                                                                                      
                                                         
                                                                                                                    
                                                            
                                                                                                              
                                                      
                                                                                                              
                                                                           
                               
         
                                                                            


                                                                           
 
                                      
                                        
                               

         
                                             




                                                                             
                    



                                                                               
                              



                                                                            



                                              
                                       
 



                                                                               

                                



                                                       



                                                                         
                          

                                            
                                                  


                                                
                              



                                                                               
                                             
                      
                        
                                     
                              
                   
                                                  
                 
 
 
                                        
 
                         
 
                              
 
                                     


                                         


                                     


                                    
                                                                               
         
                                                                                               




                                  
                                               
                                  
                                             

                                                                                    
                 
                                   
                                                                      

                                                    


                                            

                              
 
                                             


























                                                                
                               
 


                                   
                          
 


                                        


                                                
                          

                                              

                          
 








                                                                               

                                     


                                                             
 





                                                                               
 



                                                                     

                                                                          














                                                                               
                             
                                  


                                               
                                       

                                      







                                                                               

                                                













                                                                    

                                              



                                               





                                                                               



                                             










                                                              

                              











                                                              
 










                                                              
 










                                                             
 
                        
                              
                 
 












                                                                               
 























                                                                                                                  
 




                                                                     
 




                                                                    
 
                                                                           


                                                

                 







                                         
 

                                           
 
              

                                       
                              
                                      

                                    
 




                                                                             
 







                                                                               
                                                                     













                                                                               

                                                                              

                                                                                        




                                                                   






                                                                             

                                                                        
                                     
 




                                                                              
                                  
 




                                                                              
                              
 



                                                                            
                                                                                       


                                                                            
                               

                        




                                                                              
                                    
                               
 

                                 
                                                   
                                         
 

                                                     






























                                                                     
 
                                         


                                                              
                        


                                            
 




                                            
                                



                                                           


                                        
                                   


















                                                                       
 














                                                                                        

                                  
                                                     
 
                                  
                                                     
 





                                                                              


                                                                     

                                                                         

                                         
 



                                                  
 






                                   
 




                                                     

                                                          
         
 

                                                                                  
                                                                                                
                         
 

                                           
 





                                         







                                                                             



                                                                      
                                 
 




                                                                              

                                                     



                                 



                                                                               

                                                 
 
                                                                  

                                     

                           
                                                          
                                 
                                                    
 

                            
 
                               

                                      
                              
                                      


                                
 



                                                                          



                                                                           
                            
                                                                                                                                          
                                    
                                                              
                                                           
                                          
                                                             
                                            





                                                                           

                                            

                                     
                                                                








                                                    
                                                             

                                         

                                                                 
                                           
                                                                      
                                           
                                                              




                                                                                                   
                 
                              

                                                
                                                                
                                                                        


                                                                

                                                                    

                                                         
                                                                                  
                                                           
                                                                  

                                                                      


                                 

                                


                                                                                
                                   
                                                  
 


                                                                 
 
                                






                                                                              
                       
                         
                                                                              
                                        
                                        
                                               




                                                                         
                                                            

                                            


                                                                                                                    

                       


                                                                       
                                                       
                                                                     
                                                  




                                                                       
         
                                                    
                                        

                           
                  
                        
                                  


                                                                                      

                                                    
                                                               



                                                                             
                                                     


                              
 





                                                                              
                                                                  
 

                         
                              

























                                                              
 
 
                                                                  

                                                                   
                                                                 


                                                
 

                                                                  
 
                         
                              
                                                    



                                                                             
                                 







                                                                          
                                                                              



















                                                                               
                              






                                                                              
                                                              
                                 
         
                   
 
 
                                
                                                                 
 
                             
                            
                                      





                                              
                          

                                                                     
                                           
                                          
                                                                               




                                                                           

                                                               




                                                                               
                                                        
                                             



                                                                            




                                                                               
                                         
                                                          
                    
                                                         
                                             
                                      
                                                
                                    
                                     
                                     




                                                                           


                                                                                          




                                                                              
                                   

                                                                               

                                             
                                                
                                  
                                  



                                                                           

                                                        

                                                   
                                 
                                  






                                                                               



                                                                         
         
                                                                        

                                           



                                                                          

                                                                       
                                                                            

                                             



                                                                                

                                                                            
 
 
                       
                                            
 
                     
                                   
                               
                                      

                                                
                              
 



                                                                              
                        

                                        


                                  
                         
                    

                                     



                                                                              
                               
                         
                                           

                                                  


                                
                                                                         
                                                     
                                        


                       
                                                                  

                       


                                                                                      
                                                          
         
                    
                                                                                 
                                                                              




                                                                             
                                                                                   
                                                            
                                                                             

                                    



                                                                         
                                                                  

                                                      
                                                   
                        
                                
                                             

                                   
 
                                                                

                                                        



                                            

                                
                                                                      


                                                                          


                                                   
                                       


                                                            


                                                                            
 
                                                                              


                                               
                 
 



                                                         
 

                                                                                        
                                               
                         





















                                                                              
 



                                                                 
                                      





                                      
                               
 
                                             
                        
                                  
                   
                                                  


                                   

                                   

                                   

                                   

                                  

                                  








                                                                 

                               
 
                                             
          


                                                                               
















                                           
                        



                                                                         

                          
                                                  




                                                               
 




                                    
                                      

                                                                 
 

                                 
            
                               
 





















                                                                                 
 











                                                                                 









                                                                     
















                                                                  
 















                                                                                         
                                                             
                                                  
                                                                  
















                                                                       
                         
 

                                                     
 




































                                                                           
 



                                                                                                     
                                                             
                                        
 
                                                  


                                                                                       
                                


                                                                                            

                         

                                                        
 






                                                                  
                 
 
 
                                                                                
 

                            



                                                                         

                                                                              


                                   
                                                          








                                                                  
                                    
 
 
                                                
 
                                                                        
                                                        
                                            

                                                 
                                              
                                                                                


                                                                                                    
                         
                 

         
 



                                                                            
                                           
 

                            




                                                                         
                                                     
                                                                                                
                                        
                                                      
                                                        

                                                     
                                                      
                                                          

         

                                            
 
                           




                                                                         
                                    
                                                     
                                                                                      
                                                                                         
                                                      


                                                      
                                                     
                                                                       
                                                         

                                                      
                                                     
                                                          











                                                                              

                            




                                                                         

                                                         

                                                        


                                                     
                                                          

         

                                                 
 

                            




                                                                         
                                                                                      

                                                     
                                        
                                                         


                                                      
                                                          

         

                                                  
 

                            
                                             
                        






                                                                               

                                              
                                                  
 
 
                               
                                                                         
 
                                                    
 

                                     



                                                                            
                               
                   
 
 
                                     
 




                                            







                                                                           





                                                                            





















































                                                                                                             
 





                                                                                                                
                                       

















                                                                                                                      


                                        
                                                                      

                                                       


                                                                             



                                    


                                                         














                                                                                        
 
































                                                                                             
 



                                            
 





                                                                                            
 












































                                                                                             
                                       






                                                                         

                           

                                               

                                                                        

                       
 
                                                     

                                                                         
                                                               

                                                                                        
                                                                               
                    
 







                       










                                                                     
                                                        












                                                                 
 









                                                                               

                                                      


























                                                                                



                                                                          
                                  
                                                     

                                                     
 























                                                                                   
 





                                                       
 

                      
/*
	Copyright (C) 1996  Digi International.

	For technical support please email digiLinux@dgii.com or
	call Digi tech support at (612) 912-3456

	** This driver is no longer supported by Digi **

	Much of this design and code came from epca.c which was
	copyright (C) 1994, 1995 Troy De Jongh, and subsquently
	modified by David Nugent, Christoph Lameter, Mike McLagan.

	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 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* See README.epca for change history --DAT*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include "digiPCI.h"


#include "digi1.h"
#include "digiFep1.h"
#include "epca.h"
#include "epcaconfig.h"

#define VERSION            "1.3.0.1-LK2.6"

/* This major needs to be submitted to Linux to join the majors list */
#define DIGIINFOMAJOR       35  /* For Digi specific ioctl */


#define MAXCARDS 7
#define epcaassert(x, msg)  if (!(x)) epca_error(__LINE__, msg)

#define PFX "epca: "

static int nbdevs, num_cards, liloconfig;
static int digi_poller_inhibited = 1 ;

static int setup_error_code;
static int invalid_lilo_config;

/*
 * The ISA boards do window flipping into the same spaces so its only sane with
 * a single lock. It's still pretty efficient.
 */
static DEFINE_SPINLOCK(epca_lock);

/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted to 7 below. */
static struct board_info boards[MAXBOARDS];

static struct tty_driver *pc_driver;
static struct tty_driver *pc_info;

/* ------------------ Begin Digi specific structures -------------------- */

/*
 * digi_channels represents an array of structures that keep track of each
 * channel of the Digi product. Information such as transmit and receive
 * pointers, termio data, and signal definitions (DTR, CTS, etc ...) are stored
 * here. This structure is NOT used to overlay the cards physical channel
 * structure.
 */
static struct channel digi_channels[MAX_ALLOC];

/*
 * card_ptr is an array used to hold the address of the first channel structure
 * of each card. This array will hold the addresses of various channels located
 * in digi_channels.
 */
static struct channel *card_ptr[MAXCARDS];

static struct timer_list epca_timer;

/*
 * Begin generic memory functions. These functions will be alias (point at)
 * more specific functions dependent on the board being configured.
 */
static void memwinon(struct board_info *b, unsigned int win);
static void memwinoff(struct board_info *b, unsigned int win);
static void globalwinon(struct channel *ch);
static void rxwinon(struct channel *ch);
static void txwinon(struct channel *ch);
static void memoff(struct channel *ch);
static void assertgwinon(struct channel *ch);
static void assertmemoff(struct channel *ch);

/* ---- Begin more 'specific' memory functions for cx_like products --- */

static void pcxem_memwinon(struct board_info *b, unsigned int win);
static void pcxem_memwinoff(struct board_info *b, unsigned int win);
static void pcxem_globalwinon(struct channel *ch);
static void pcxem_rxwinon(struct channel *ch);
static void pcxem_txwinon(struct channel *ch);
static void pcxem_memoff(struct channel *ch);

/* ------ Begin more 'specific' memory functions for the pcxe ------- */

static void pcxe_memwinon(struct board_info *b, unsigned int win);
static void pcxe_memwinoff(struct board_info *b, unsigned int win);
static void pcxe_globalwinon(struct channel *ch);
static void pcxe_rxwinon(struct channel *ch);
static void pcxe_txwinon(struct channel *ch);
static void pcxe_memoff(struct channel *ch);

/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */
/* Note : pc64xe and pcxi share the same windowing routines */

static void pcxi_memwinon(struct board_info *b, unsigned int win);
static void pcxi_memwinoff(struct board_info *b, unsigned int win);
static void pcxi_globalwinon(struct channel *ch);
static void pcxi_rxwinon(struct channel *ch);
static void pcxi_txwinon(struct channel *ch);
static void pcxi_memoff(struct channel *ch);

/* - Begin 'specific' do nothing memory functions needed for some cards - */

static void dummy_memwinon(struct board_info *b, unsigned int win);
static void dummy_memwinoff(struct board_info *b, unsigned int win);
static void dummy_globalwinon(struct channel *ch);
static void dummy_rxwinon(struct channel *ch);
static void dummy_txwinon(struct channel *ch);
static void dummy_memoff(struct channel *ch);
static void dummy_assertgwinon(struct channel *ch);
static void dummy_assertmemoff(struct channel *ch);

static struct channel *verifyChannel(struct tty_struct *);
static void pc_sched_event(struct channel *, int);
static void epca_error(int, char *);
static void pc_close(struct tty_struct *, struct file *);
static void shutdown(struct channel *);
static void pc_hangup(struct tty_struct *);
static void pc_put_char(struct tty_struct *, unsigned char);
static int pc_write_room(struct tty_struct *);
static int pc_chars_in_buffer(struct tty_struct *);
static void pc_flush_buffer(struct tty_struct *);
static void pc_flush_chars(struct tty_struct *);
static int block_til_ready(struct tty_struct *, struct file *,
                           struct channel *);
static int pc_open(struct tty_struct *, struct file *);
static void post_fep_init(unsigned int crd);
static void epcapoll(unsigned long);
static void doevent(int);
static void fepcmd(struct channel *, int, int, int, int, int);
static unsigned termios2digi_h(struct channel *ch, unsigned);
static unsigned termios2digi_i(struct channel *ch, unsigned);
static unsigned termios2digi_c(struct channel *ch, unsigned);
static void epcaparam(struct tty_struct *, struct channel *);
static void receive_data(struct channel *);
static int pc_ioctl(struct tty_struct *, struct file *,
                    unsigned int, unsigned long);
static int info_ioctl(struct tty_struct *, struct file *,
                    unsigned int, unsigned long);
static void pc_set_termios(struct tty_struct *, struct ktermios *);
static void do_softint(struct work_struct *work);
static void pc_stop(struct tty_struct *);
static void pc_start(struct tty_struct *);
static void pc_throttle(struct tty_struct * tty);
static void pc_unthrottle(struct tty_struct *tty);
static void digi_send_break(struct channel *ch, int msec);
static void setup_empty_event(struct tty_struct *tty, struct channel *ch);
void epca_setup(char *, int *);

static int pc_write(struct tty_struct *, const unsigned char *, int);
static int pc_init(void);
static int init_PCI(void);

/*
 * Table of functions for each board to handle memory. Mantaining parallelism
 * is a *very* good idea here. The idea is for the runtime code to blindly call
 * these functions, not knowing/caring about the underlying hardware. This
 * stuff should contain no conditionals; if more functionality is needed a
 * different entry should be established. These calls are the interface calls
 * and are the only functions that should be accessed. Anyone caught making
 * direct calls deserves what they get.
 */
static void memwinon(struct board_info *b, unsigned int win)
{
	b->memwinon(b, win);
}

static void memwinoff(struct board_info *b, unsigned int win)
{
	b->memwinoff(b, win);
}

static void globalwinon(struct channel *ch)
{
	ch->board->globalwinon(ch);
}

static void rxwinon(struct channel *ch)
{
	ch->board->rxwinon(ch);
}

static void txwinon(struct channel *ch)
{
	ch->board->txwinon(ch);
}

static void memoff(struct channel *ch)
{
	ch->board->memoff(ch);
}
static void assertgwinon(struct channel *ch)
{
	ch->board->assertgwinon(ch);
}

static void assertmemoff(struct channel *ch)
{
	ch->board->assertmemoff(ch);
}

/* PCXEM windowing is the same as that used in the PCXR and CX series cards. */
static void pcxem_memwinon(struct board_info *b, unsigned int win)
{
        outb_p(FEPWIN|win, b->port + 1);
}

static void pcxem_memwinoff(struct board_info *b, unsigned int win)
{
	outb_p(0, b->port + 1);
}

static void pcxem_globalwinon(struct channel *ch)
{
	outb_p( FEPWIN, (int)ch->board->port + 1);
}

static void pcxem_rxwinon(struct channel *ch)
{
	outb_p(ch->rxwin, (int)ch->board->port + 1);
}

static void pcxem_txwinon(struct channel *ch)
{
	outb_p(ch->txwin, (int)ch->board->port + 1);
}

static void pcxem_memoff(struct channel *ch)
{
	outb_p(0, (int)ch->board->port + 1);
}

/* ----------------- Begin pcxe memory window stuff ------------------ */
static void pcxe_memwinon(struct board_info *b, unsigned int win)
{
	outb_p(FEPWIN | win, b->port + 1);
}

static void pcxe_memwinoff(struct board_info *b, unsigned int win)
{
	outb_p(inb(b->port) & ~FEPMEM, b->port + 1);
	outb_p(0, b->port + 1);
}

static void pcxe_globalwinon(struct channel *ch)
{
	outb_p(FEPWIN, (int)ch->board->port + 1);
}

static void pcxe_rxwinon(struct channel *ch)
{
	outb_p(ch->rxwin, (int)ch->board->port + 1);
}

static void pcxe_txwinon(struct channel *ch)
{
	outb_p(ch->txwin, (int)ch->board->port + 1);
}

static void pcxe_memoff(struct channel *ch)
{
	outb_p(0, (int)ch->board->port);
	outb_p(0, (int)ch->board->port + 1);
}

/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */
static void pcxi_memwinon(struct board_info *b, unsigned int win)
{
	outb_p(inb(b->port) | FEPMEM, b->port);
}

static void pcxi_memwinoff(struct board_info *b, unsigned int win)
{
	outb_p(inb(b->port) & ~FEPMEM, b->port);
}

static void pcxi_globalwinon(struct channel *ch)
{
	outb_p(FEPMEM, ch->board->port);
}

static void pcxi_rxwinon(struct channel *ch)
{
	outb_p(FEPMEM, ch->board->port);
}

static void pcxi_txwinon(struct channel *ch)
{
	outb_p(FEPMEM, ch->board->port);
}

static void pcxi_memoff(struct channel *ch)
{
	outb_p(0, ch->board->port);
}

static void pcxi_assertgwinon(struct channel *ch)
{
	epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off");
}

static void pcxi_assertmemoff(struct channel *ch)
{
	epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on");
}

/*
 * Not all of the cards need specific memory windowing routines. Some cards
 * (Such as PCI) needs no windowing routines at all. We provide these do
 * nothing routines so that the same code base can be used. The driver will
 * ALWAYS call a windowing routine if it thinks it needs to; regardless of the
 * card. However, dependent on the card the routine may or may not do anything.
 */
static void dummy_memwinon(struct board_info *b, unsigned int win)
{
}

static void dummy_memwinoff(struct board_info *b, unsigned int win)
{
}

static void dummy_globalwinon(struct channel *ch)
{
}

static void dummy_rxwinon(struct channel *ch)
{
}

static void dummy_txwinon(struct channel *ch)
{
}

static void dummy_memoff(struct channel *ch)
{
}

static void dummy_assertgwinon(struct channel *ch)
{
}

static void dummy_assertmemoff(struct channel *ch)
{
}

static struct channel *verifyChannel(struct tty_struct *tty)
{
	/*
	 * This routine basically provides a sanity check. It insures that the
	 * channel returned is within the proper range of addresses as well as
	 * properly initialized. If some bogus info gets passed in
	 * through tty->driver_data this should catch it.
	 */
	if (tty) {
		struct channel *ch = (struct channel *)tty->driver_data;
		if ((ch >= &digi_channels[0]) && (ch < &digi_channels[nbdevs])) {
			if (ch->magic == EPCA_MAGIC)
				return ch;
		}
	}
	return NULL;
}

static void pc_sched_event(struct channel *ch, int event)
{
	/*
	 * We call this to schedule interrupt processing on some event. The
	 * kernel sees our request and calls the related routine in OUR driver.
	 */
	ch->event |= 1 << event;
	schedule_work(&ch->tqueue);
}

static void epca_error(int line, char *msg)
{
	printk(KERN_ERR "epca_error (Digi): line = %d %s\n",line,msg);
}

static void pc_close(struct tty_struct *tty, struct file *filp)
{
	struct channel *ch;
	unsigned long flags;
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
	if ((ch = verifyChannel(tty)) != NULL) {
		spin_lock_irqsave(&epca_lock, flags);
		if (tty_hung_up_p(filp)) {
			spin_unlock_irqrestore(&epca_lock, flags);
			return;
		}
		if (ch->count-- > 1)  {
			/* Begin channel is open more than once */
			/*
			 * Return without doing anything. Someone might still
			 * be using the channel.
			 */
			spin_unlock_irqrestore(&epca_lock, flags);
			return;
		}

		/* Port open only once go ahead with shutdown & reset */
		BUG_ON(ch->count < 0);

		/*
		 * Let the rest of the driver know the channel is being closed.
		 * This becomes important if an open is attempted before close
		 * is finished.
		 */
		ch->asyncflags |= ASYNC_CLOSING;
		tty->closing = 1;

		spin_unlock_irqrestore(&epca_lock, flags);

		if (ch->asyncflags & ASYNC_INITIALIZED)  {
			/* Setup an event to indicate when the transmit buffer empties */
			setup_empty_event(tty, ch);
			tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
		}
		if (tty->driver->flush_buffer)
			tty->driver->flush_buffer(tty);

		tty_ldisc_flush(tty);
		shutdown(ch);

		spin_lock_irqsave(&epca_lock, flags);
		tty->closing = 0;
		ch->event = 0;
		ch->tty = NULL;
		spin_unlock_irqrestore(&epca_lock, flags);

		if (ch->blocked_open) {
			if (ch->close_delay)
				msleep_interruptible(jiffies_to_msecs(ch->close_delay));
			wake_up_interruptible(&ch->open_wait);
		}
		ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED |
		                      ASYNC_CLOSING);
		wake_up_interruptible(&ch->close_wait);
	}
}

static void shutdown(struct channel *ch)
{
	unsigned long flags;
	struct tty_struct *tty;
	struct board_chan __iomem *bc;

	if (!(ch->asyncflags & ASYNC_INITIALIZED))
		return;

	spin_lock_irqsave(&epca_lock, flags);

	globalwinon(ch);
	bc = ch->brdchan;

	/*
	 * In order for an event to be generated on the receipt of data the
	 * idata flag must be set. Since we are shutting down, this is not
	 * necessary clear this flag.
	 */
	if (bc)
		writeb(0, &bc->idata);
	tty = ch->tty;

	/* If we're a modem control device and HUPCL is on, drop RTS & DTR. */
	if (tty->termios->c_cflag & HUPCL)  {
		ch->omodem &= ~(ch->m_rts | ch->m_dtr);
		fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1);
	}
	memoff(ch);

	/*
	 * The channel has officialy been closed. The next time it is opened it
	 * will have to reinitialized. Set a flag to indicate this.
	 */
	/* Prevent future Digi programmed interrupts from coming active */
	ch->asyncflags &= ~ASYNC_INITIALIZED;
	spin_unlock_irqrestore(&epca_lock, flags);
}

static void pc_hangup(struct tty_struct *tty)
{
	struct channel *ch;

	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
	if ((ch = verifyChannel(tty)) != NULL) {
		unsigned long flags;

		if (tty->driver->flush_buffer)
			tty->driver->flush_buffer(tty);
		tty_ldisc_flush(tty);
		shutdown(ch);

		spin_lock_irqsave(&epca_lock, flags);
		ch->tty   = NULL;
		ch->event = 0;
		ch->count = 0;
		ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED);
		spin_unlock_irqrestore(&epca_lock, flags);
		wake_up_interruptible(&ch->open_wait);
	}
}

static int pc_write(struct tty_struct *tty,
                    const unsigned char *buf, int bytesAvailable)
{
	unsigned int head, tail;
	int dataLen;
	int size;
	int amountCopied;
	struct channel *ch;
	unsigned long flags;
	int remain;
	struct board_chan __iomem *bc;

	/*
	 * pc_write is primarily called directly by the kernel routine
	 * tty_write (Though it can also be called by put_char) found in
	 * tty_io.c. pc_write is passed a line discipline buffer where the data
	 * to be written out is stored. The line discipline implementation
	 * itself is done at the kernel level and is not brought into the
	 * driver.
	 */

	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
	if ((ch = verifyChannel(tty)) == NULL)
		return 0;

	/* Make a pointer to the channel data structure found on the board. */
	bc   = ch->brdchan;
	size = ch->txbufsize;
	amountCopied = 0;

	spin_lock_irqsave(&epca_lock, flags);
	globalwinon(ch);

	head = readw(&bc->tin) & (size - 1);
	tail = readw(&bc->tout);

	if (tail != readw(&bc->tout))
		tail = readw(&bc->tout);
	tail &= (size - 1);

	if (head >= tail) {
		/* head has not wrapped */
		/*
		 * remain (much like dataLen above) represents the total amount
		 * of space available on the card for data. Here dataLen
		 * represents the space existing between the head pointer and
		 * the end of buffer. This is important because a memcpy cannot
		 * be told to automatically wrap around when it hits the buffer
		 * end.
		 */
		dataLen = size - head;
		remain = size - (head - tail) - 1;
	} else {
		/* head has wrapped around */
		remain = tail - head - 1;
		dataLen = remain;
	}
	/*
	 * Check the space on the card. If we have more data than space; reduce
	 * the amount of data to fit the space.
	 */
	bytesAvailable = min(remain, bytesAvailable);
	txwinon(ch);
	while (bytesAvailable > 0) {
		/* there is data to copy onto card */

		/*
		 * If head is not wrapped, the below will make sure the first
		 * data copy fills to the end of card buffer.
		 */
		dataLen = min(bytesAvailable, dataLen);
		memcpy_toio(ch->txptr + head, buf, dataLen);
		buf += dataLen;
		head += dataLen;
		amountCopied += dataLen;
		bytesAvailable -= dataLen;

		if (head >= size) {
			head = 0;
			dataLen = tail;
		}
	}
	ch->statusflags |= TXBUSY;
	globalwinon(ch);
	writew(head, &bc->tin);

	if ((ch->statusflags & LOWWAIT) == 0)  {
		ch->statusflags |= LOWWAIT;
		writeb(1, &bc->ilow);
	}
	memoff(ch);
	spin_unlock_irqrestore(&epca_lock, flags);
	return amountCopied;
}

static void pc_put_char(struct tty_struct *tty, unsigned char c)
{
	pc_write(tty, &c, 1);
}

static int pc_write_room(struct tty_struct *tty)
{
	int remain;
	struct channel *ch;
	unsigned long flags;
	unsigned int head, tail;
	struct board_chan __iomem *bc;

	remain = 0;

	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
	if ((ch = verifyChannel(tty)) != NULL)  {
		spin_lock_irqsave(&epca_lock, flags);
		globalwinon(ch);

		bc   = ch->brdchan;
		head = readw(&bc->tin) & (ch->txbufsize - 1);
		tail = readw(&bc->tout);

		if (tail != readw(&bc->tout))
			tail = readw(&bc->tout);
		/* Wrap tail if necessary */
		tail &= (ch->txbufsize - 1);

		if ((remain = tail - head - 1) < 0 )
			remain += ch->txbufsize;

		if (remain && (ch->statusflags & LOWWAIT) == 0) {
			ch->statusflags |= LOWWAIT;
			writeb(1, &bc->ilow);
		}
		memoff(ch);
		spin_unlock_irqrestore(&epca_lock, flags);
	}
	/* Return how much room is left on card */
	return remain;
}

static int pc_chars_in_buffer(struct tty_struct *tty)
{
	int chars;
	unsigned int ctail, head, tail;
	int remain;
	unsigned long flags;
	struct channel *ch;
	struct board_chan __iomem *bc;

	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
	if ((ch = verifyChannel(tty)) == NULL)
		return 0;

	spin_lock_irqsave(&epca_lock, flags);
	globalwinon(ch);

	bc = ch->brdchan;
	tail = readw(&bc->tout);
	head = readw(&bc->tin);
	ctail = readw(&ch->mailbox->cout);

	if (tail == head && readw(&ch->mailbox->cin) == ctail && readb(&bc->tbusy) == 0)
		chars = 0;
	else  { /* Begin if some space on the card has been used */
		head = readw(&bc->tin) & (ch->txbufsize - 1);
		tail &= (ch->txbufsize - 1);
		/*
		 * The logic here is basically opposite of the above
		 * pc_write_room here we are finding the amount of bytes in the
		 * buffer filled. Not the amount of bytes empty.
		 */
		if ((remain = tail - head - 1) < 0 )
			remain += ch->txbufsize;
		chars = (int)(ch->txbufsize - remain);
		/*
		 * Make it possible to wakeup anything waiting for output in
		 * tty_ioctl.c, etc.
		 *
		 * If not already set. Setup an event to indicate when the
		 * transmit buffer empties.
		 */
		if (!(ch->statusflags & EMPTYWAIT))
			setup_empty_event(tty,ch);
	} /* End if some space on the card has been used */
	memoff(ch);
	spin_unlock_irqrestore(&epca_lock, flags);
	/* Return number of characters residing on card. */
	return chars;
}

static void pc_flush_buffer(struct tty_struct *tty)
{
	unsigned int tail;
	unsigned long flags;
	struct channel *ch;
	struct board_chan __iomem *bc;
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
	if ((ch = verifyChannel(tty)) == NULL)
		return;

	spin_lock_irqsave(&epca_lock, flags);
	globalwinon(ch);
	bc   = ch->brdchan;
	tail = readw(&bc->tout);
	/* Have FEP move tout pointer; effectively flushing transmit buffer */
	fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0);
	memoff(ch);
	spin_unlock_irqrestore(&epca_lock, flags);
	tty_wakeup(tty);
}

static void pc_flush_chars(struct tty_struct *tty)
{
	struct channel *ch;
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
	if ((ch = verifyChannel(tty)) != NULL) {
		unsigned long flags;
		spin_lock_irqsave(&epca_lock, flags);
		/*
		 * If not already set and the transmitter is busy setup an
		 * event to indicate when the transmit empties.
		 */
		if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT))
			setup_empty_event(tty,ch);
		spin_unlock_irqrestore(&epca_lock, flags);
	}
}

static int block_til_ready(struct tty_struct *tty,
                           struct file *filp, struct channel *ch)
{
	DECLARE_WAITQUEUE(wait,current);
	int retval, do_clocal = 0;
	unsigned long flags;

	if (tty_hung_up_p(filp)) {
		if (ch->asyncflags & ASYNC_HUP_NOTIFY)
			retval = -EAGAIN;
		else
			retval = -ERESTARTSYS;
		return retval;
	}

	/*
	 * If the device is in the middle of being closed, then block until
	 * it's done, and then try again.
	 */
	if (ch->asyncflags & ASYNC_CLOSING) {
		interruptible_sleep_on(&ch->close_wait);

		if (ch->asyncflags & ASYNC_HUP_NOTIFY)
			return -EAGAIN;
		else
			return -ERESTARTSYS;
	}

	if (filp->f_flags & O_NONBLOCK)  {
		/*
		 * If non-blocking mode is set, then make the check up front
		 * and then exit.
		 */
		ch->asyncflags |= ASYNC_NORMAL_ACTIVE;
		return 0;
	}
	if (tty->termios->c_cflag & CLOCAL)
		do_clocal = 1;
	/* Block waiting for the carrier detect and the line to become free */

	retval = 0;
	add_wait_queue(&ch->open_wait, &wait);

	spin_lock_irqsave(&epca_lock, flags);
	/* We dec count so that pc_close will know when to free things */
	if (!tty_hung_up_p(filp))
		ch->count--;
	ch->blocked_open++;
	while (1) {
		set_current_state(TASK_INTERRUPTIBLE);
		if (tty_hung_up_p(filp) ||
		    !(ch->asyncflags & ASYNC_INITIALIZED))
		{
			if (ch->asyncflags & ASYNC_HUP_NOTIFY)
				retval = -EAGAIN;
			else
				retval = -ERESTARTSYS;
			break;
		}
		if (!(ch->asyncflags & ASYNC_CLOSING) &&
			  (do_clocal || (ch->imodem & ch->dcd)))
			break;
		if (signal_pending(current)) {
			retval = -ERESTARTSYS;
			break;
		}
		spin_unlock_irqrestore(&epca_lock, flags);
		/*
		 * Allow someone else to be scheduled. We will occasionally go
		 * through this loop until one of the above conditions change.
		 * The below schedule call will allow other processes to enter
		 * and prevent this loop from hogging the cpu.
		 */
		schedule();
		spin_lock_irqsave(&epca_lock, flags);
	}

	__set_current_state(TASK_RUNNING);
	remove_wait_queue(&ch->open_wait, &wait);
	if (!tty_hung_up_p(filp))
		ch->count++;
	ch->blocked_open--;

	spin_unlock_irqrestore(&epca_lock, flags);

	if (retval)
		return retval;

	ch->asyncflags |= ASYNC_NORMAL_ACTIVE;
	return 0;
}

static int pc_open(struct tty_struct *tty, struct file * filp)
{
	struct channel *ch;
	unsigned long flags;
	int line, retval, boardnum;
	struct board_chan __iomem *bc;
	unsigned int head;

	line = tty->index;
	if (line < 0 || line >= nbdevs)
		return -ENODEV;

	ch = &digi_channels[line];
	boardnum = ch->boardnum;

	/* Check status of board configured in system.  */

	/*
	 * I check to see if the epca_setup routine detected an user error. It
	 * might be better to put this in pc_init, but for the moment it goes
	 * here.
	 */
	if (invalid_lilo_config) {
		if (setup_error_code & INVALID_BOARD_TYPE)
			printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n");
		if (setup_error_code & INVALID_NUM_PORTS)
			printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n");
		if (setup_error_code & INVALID_MEM_BASE)
			printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n");
		if (setup_error_code & INVALID_PORT_BASE)
			printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n");
		if (setup_error_code & INVALID_BOARD_STATUS)
			printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n");
		if (setup_error_code & INVALID_ALTPIN)
			printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n");
		tty->driver_data = NULL;   /* Mark this device as 'down' */
		return -ENODEV;
	}
	if (boardnum >= num_cards || boards[boardnum].status == DISABLED)  {
		tty->driver_data = NULL;   /* Mark this device as 'down' */
		return(-ENODEV);
	}

	if ((bc = ch->brdchan) == 0) {
		tty->driver_data = NULL;
		return -ENODEV;
	}

	spin_lock_irqsave(&epca_lock, flags);
	/*
	 * Every time a channel is opened, increment a counter. This is
	 * necessary because we do not wish to flush and shutdown the channel
	 * until the last app holding the channel open, closes it.
	 */
	ch->count++;
	/*
	 * Set a kernel structures pointer to our local channel structure. This
	 * way we can get to it when passed only a tty struct.
	 */
	tty->driver_data = ch;
	/*
	 * If this is the first time the channel has been opened, initialize
	 * the tty->termios struct otherwise let pc_close handle it.
	 */
	globalwinon(ch);
	ch->statusflags = 0;

	/* Save boards current modem status */
	ch->imodem = readb(&bc->mstat);

	/*
	 * Set receive head and tail ptrs to each other. This indicates no data
	 * available to read.
	 */
	head = readw(&bc->rin);
	writew(head, &bc->rout);

	/* Set the channels associated tty structure */
	ch->tty = tty;

	/*
	 * The below routine generally sets up parity, baud, flow control
	 * issues, etc.... It effect both control flags and input flags.
	 */
	epcaparam(tty,ch);
	ch->asyncflags |= ASYNC_INITIALIZED;
	memoff(ch);
	spin_unlock_irqrestore(&epca_lock, flags);

	retval = block_til_ready(tty, filp, ch);
	if (retval)
		return retval;
	/*
	 * Set this again in case a hangup set it to zero while this open() was
	 * waiting for the line...
	 */
	spin_lock_irqsave(&epca_lock, flags);
	ch->tty = tty;
	globalwinon(ch);
	/* Enable Digi Data events */
	writeb(1, &bc->idata);
	memoff(ch);
	spin_unlock_irqrestore(&epca_lock, flags);
	return 0;
}

static int __init epca_module_init(void)
{
	return pc_init();
}
module_init(epca_module_init);

static struct pci_driver epca_driver;