epca.c

来自「linux 内核源代码」· C语言 代码 · 共 2,328 行 · 第 1/5 页

C
2,328
字号
/*	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);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?