pcxx.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,385 行 · 第 1/4 页

C
2,385
字号
/* *  linux/drivers/char/pcxx.c *  *  Written by Troy De Jongh, November, 1994 * *  Copyright (C) 1994,1995 Troy De Jongh *  This software may be used and distributed according to the terms  *  of the GNU General Public License. * *  This driver is for the DigiBoard PC/Xe and PC/Xi line of products. * *  This driver does NOT support DigiBoard's fastcook FEP option and *  does not support the transparent print (i.e. digiprint) option. * * This Driver is currently maintained by Christoph Lameter (christoph@lameter.com) * * Please contact digi for support issues at digilnux@dgii.com. * Some more information can be found at * http://lameter.com/digi. * *  1.5.2 Fall 1995 Bug fixes by David Nugent *  1.5.3 March 9, 1996 Christoph Lameter: Fixed 115.2K Support. Memory *		allocation harmonized with 1.3.X Series. *  1.5.4 March 30, 1996 Christoph Lameter: Fixup for 1.3.81. Use init_bh *		instead of direct assignment to kernel arrays. *  1.5.5 April 5, 1996 Major device numbers corrected. *              Mike McLagan<mike.mclagan@linux.org>: Add setup *              variable handling, instead of using the old pcxxconfig.h *  1.5.6 April 16, 1996 Christoph Lameter: Pointer cleanup, macro cleanup. *		Call out devices changed to /dev/cudxx. *  1.5.7 July 22, 1996 Martin Mares: CLOCAL fix, pcxe_table clearing. *		David Nugent: Bug in pcxe_open. *		Brian J. Murrell: Modem Control fixes, Majors correctly assigned *  1.6.1 April 6, 1997 Bernhard Kaindl: fixed virtual memory access for 2.1 *              i386-kernels and use on other archtitectures, Allowing use *              as module, added module parameters, added switch to enable *              verbose messages to assist user during card configuration. *              Currently only tested on a PC/Xi card, but should work on Xe *              and Xeve also. *  1.6.2 August, 7, 2000: Arnaldo Carvalho de Melo <acme@conectiva.com.br> *  		get rid of panics, release previously allocated resources *  1.6.3 August, 23, 2000: Arnaldo Carvalho de Melo <acme@conectiva.com.br> *  		cleaned up wrt verify_area. *              Christoph Lameter: Update documentation, email addresses *              and URLs. Remove some obsolete code. * */#include <linux/module.h>#include <linux/mm.h>#include <linux/ioport.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/delay.h>#include <linux/serial.h>#include <linux/tty_driver.h>#include <linux/slab.h>#include <linux/init.h>#ifndef MODULE#include <linux/ctype.h> /* We only need it for parsing the "digi="-line */#endif#include <asm/system.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/semaphore.h>#define VERSION 	"1.6.3"#include "digi.h"#include "fep.h"#include "pcxx.h"#include "digi_fep.h"#include "digi_bios.h"/* * Define one default setting if no digi= config line is used. * Default is altpin = disabled, 16 ports, I/O 200h, Memory 0D0000h */static struct board_info boards[MAX_DIGI_BOARDS] = { {/* Board is enabled       */	ENABLED,/* Type is auto-detected  */	0,/* altping is disabled    */    DISABLED,/* number of ports = 16   */	16,/* io address is 0x200    */	0x200,/* card memory at 0xd0000 */	0xd0000,/* first minor device no. */	0} }; static int verbose = 0;static int debug   = 0;#ifdef MODULE/* Variables for insmod */static int io[]           = {0, 0, 0, 0};static int membase[]      = {0, 0, 0, 0};static int memsize[]      = {0, 0, 0, 0};static int altpin[]       = {0, 0, 0, 0};static int numports[]     = {0, 0, 0, 0};MODULE_AUTHOR("Bernhard Kaindl");MODULE_DESCRIPTION("Digiboard PC/X{i,e,eve} driver");MODULE_LICENSE("GPL");MODULE_PARM(verbose,     "i");MODULE_PARM(debug,       "i");MODULE_PARM(io,          "1-4i");MODULE_PARM(membase,     "1-4i");MODULE_PARM(memsize,     "1-4i");MODULE_PARM(altpin,      "1-4i");MODULE_PARM(numports,    "1-4i");#endif /* MODULE */static int numcards = 1;static int nbdevs = 0; static struct channel    *digi_channels; int pcxx_ncook=sizeof(pcxx_cook);int pcxx_nbios=sizeof(pcxx_bios);#define pcxxassert(x, msg)  if(!(x)) pcxx_error(__LINE__, msg)#define FEPTIMEOUT 200000  #define SERIAL_TYPE_NORMAL	1#define PCXE_EVENT_HANGUP   1static struct tty_driver *pcxe_driver;static struct timer_list pcxx_timer;static void pcxxpoll(unsigned long dummy);static void fepcmd(struct channel *, int, int, int, int, int);static void pcxe_put_char(struct tty_struct *, unsigned char);static void pcxe_flush_chars(struct tty_struct *);static void pcxx_error(int, char *);static void pcxe_close(struct tty_struct *, struct file *);static int pcxe_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long);static void pcxe_set_termios(struct tty_struct *, struct termios *);static int pcxe_write(struct tty_struct *, int, const unsigned char *, int);static int pcxe_write_room(struct tty_struct *);static int pcxe_chars_in_buffer(struct tty_struct *);static void pcxe_flush_buffer(struct tty_struct *);static void doevent(int);static void receive_data(struct channel *);static void pcxxparam(struct tty_struct *, struct channel *ch);static void do_softint(void *);static inline void pcxe_sched_event(struct channel *, int);static void pcxe_start(struct tty_struct *);static void pcxe_stop(struct tty_struct *);static void pcxe_throttle(struct tty_struct *);static void pcxe_unthrottle(struct tty_struct *);static void digi_send_break(struct channel *ch, int msec);static void shutdown(struct channel *);static void setup_empty_event(struct tty_struct *tty, struct channel *ch);static inline void memwinon(struct board_info *b, unsigned int win);static inline void memwinoff(struct board_info *b, unsigned int win);static inline void globalwinon(struct channel *ch);static inline void rxwinon(struct channel *ch);static inline void txwinon(struct channel *ch);static inline void memoff(struct channel *ch);static inline void assertgwinon(struct channel *ch);static inline void assertmemoff(struct channel *ch);static int pcxe_tiocmget(struct tty_struct *tty, struct file *file);static int pcxe_tiocmset(struct tty_struct *tty, struct file *file,			 unsigned int set, unsigned int clear);#define TZ_BUFSZ 4096/* function definitions *//*****************************************************************************/static void cleanup_board_resources(void){	int crd, i;	struct board_info *bd;	struct channel *ch;        for(crd = 0; crd < numcards; crd++) {                bd = &boards[crd];		ch = digi_channels + bd->first_minor;		if (bd->region)			release_region(bd->port, 4);		for(i = 0; i < bd->numports; i++, ch++)			if (ch->tmp_buf)				kfree(ch->tmp_buf);	}}static void __exit pcxe_cleanup(void){	unsigned long	flags;	int e1;	printk(KERN_NOTICE "Unloading PC/Xx version %s\n", VERSION);	save_flags(flags);	cli();	del_timer_sync(&pcxx_timer);	if ((e1 = tty_unregister_driver(pcxe_driver)))		printk("SERIAL: failed to unregister serial driver (%d)\n", e1);	put_tty_driver(pcxe_driver);	cleanup_board_resources();	kfree(digi_channels);	restore_flags(flags);}static inline struct channel *chan(register struct tty_struct *tty){	if (tty) {		register struct channel *ch=(struct channel *)tty->driver_data;		if (ch >= digi_channels && ch < digi_channels+nbdevs) {			if (ch->magic==PCXX_MAGIC)				return ch;		}	}	return NULL;}/* These inline routines are to turn board memory on and off */static inline void memwinon(struct board_info *b, unsigned int win){	if(b->type == PCXEVE)		outb_p(FEPWIN|win, b->port+1);	else		outb_p(inb(b->port)|FEPMEM, b->port);}static inline void memwinoff(struct board_info *b, unsigned int win){	outb_p(inb(b->port)&~FEPMEM, b->port);	if(b->type == PCXEVE)		outb_p(0, b->port + 1);}static inline void globalwinon(struct channel *ch){	if(ch->board->type == PCXEVE)		outb_p(FEPWIN, ch->board->port+1);	else		outb_p(FEPMEM, ch->board->port);}static inline void rxwinon(struct channel *ch){	if(ch->rxwin == 0)		outb_p(FEPMEM, ch->board->port);	else 		outb_p(ch->rxwin, ch->board->port+1);}static inline void txwinon(struct channel *ch){	if(ch->txwin == 0)		outb_p(FEPMEM, ch->board->port);	else		outb_p(ch->txwin, ch->board->port+1);}static inline void memoff(struct channel *ch){	outb_p(0, ch->board->port);	if(ch->board->type == PCXEVE)		outb_p(0, ch->board->port+1);}static inline void assertgwinon(struct channel *ch){	if(ch->board->type != PCXEVE)		pcxxassert(inb(ch->board->port) & FEPMEM, "Global memory off");}static inline void assertmemoff(struct channel *ch){	if(ch->board->type != PCXEVE)		pcxxassert(!(inb(ch->board->port) & FEPMEM), "Memory on");}static inline void pcxe_sched_event(struct channel *info, int event){	info->event |= 1 << event;	schedule_work(&info->tqueue);}static void pcxx_error(int line, char *msg){	printk("pcxx_error (DigiBoard): line=%d %s\n", line, msg);}static int pcxx_waitcarrier(struct tty_struct *tty,struct file *filp,struct channel *info){	DECLARE_WAITQUEUE(wait, current);	int	retval = 0;	int	do_clocal = 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(&info->open_wait, &wait);	info->count--;	info->blocked_open++;	for (;;) {		cli();		globalwinon(info);		info->omodem |= DTR|RTS;		fepcmd(info, SETMODEM, DTR|RTS, 0, 10, 1);		memoff(info);		sti();		set_current_state(TASK_INTERRUPTIBLE);		if(tty_hung_up_p(filp) || (info->asyncflags & ASYNC_INITIALIZED) == 0) {			if(info->asyncflags & ASYNC_HUP_NOTIFY)				retval = -EAGAIN;			else				retval = -ERESTARTSYS;				break;		}		if ((info->asyncflags & ASYNC_CLOSING) == 0 &&			(do_clocal || (info->imodem & info->dcd)))			break;		if(signal_pending(current)) {			retval = -ERESTARTSYS;			break;		}		schedule();	}	current->state = TASK_RUNNING;	remove_wait_queue(&info->open_wait, &wait);	if(!tty_hung_up_p(filp))		info->count++;	info->blocked_open--;	return retval;}	int pcxe_open(struct tty_struct *tty, struct file * filp){	volatile struct board_chan *bc;	struct channel *ch;	unsigned long flags;	int line;	int boardnum;	int retval;	line = tty->index;	if(line < 0 || line >= nbdevs) {		printk("line out of range in pcxe_open\n");		tty->driver_data = NULL;		return(-ENODEV);	}	for(boardnum=0;boardnum<numcards;boardnum++)		if ((line >= boards[boardnum].first_minor) && 			(line < boards[boardnum].first_minor + boards[boardnum].numports))		break;	if(boardnum >= numcards || boards[boardnum].status == DISABLED ||		(line - boards[boardnum].first_minor) >= boards[boardnum].numports) {		tty->driver_data = NULL;   /* Mark this device as 'down' */		return(-ENODEV);	}	ch = digi_channels+line;	if(ch->brdchan == 0) {		tty->driver_data = NULL;		return(-ENODEV);	}	/*	 * 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;	}	save_flags(flags);	cli();	ch->count++;	tty->driver_data = ch;	ch->tty = tty;	if ((ch->asyncflags & ASYNC_INITIALIZED) == 0) {		unsigned int head;		globalwinon(ch);		ch->statusflags = 0;		bc=ch->brdchan;		ch->imodem = bc->mstat;		head = bc->rin;		bc->rout = head;		ch->tty = tty;		pcxxparam(tty,ch);		ch->imodem = bc->mstat;		bc->idata = 1;		ch->omodem = DTR|RTS;		fepcmd(ch, SETMODEM, DTR|RTS, 0, 10, 1);		memoff(ch);		ch->asyncflags |= ASYNC_INITIALIZED;	}	restore_flags(flags);	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)) {		/* this has to be set in order for the "block until		 * CD" code to work correctly.  i'm not sure under		 * what circumstances asyncflags should be set to		 * ASYNC_NORMAL_ACTIVE though		 * brian@ilinx.com		 */		ch->asyncflags |= ASYNC_NORMAL_ACTIVE;		if ((retval = pcxx_waitcarrier(tty, filp, ch)) != 0)			return retval;	}	ch->asyncflags |= ASYNC_NORMAL_ACTIVE; 		return 0;} static void shutdown(struct channel *info){	unsigned long flags;	volatile struct board_chan *bc;	struct tty_struct *tty;	if (!(info->asyncflags & ASYNC_INITIALIZED)) 		return;	save_flags(flags);	cli();	globalwinon(info);	bc = info->brdchan;	if(bc)		bc->idata = 0;	tty = info->tty;	/*	 * If we're a modem control device and HUPCL is on, drop RTS & DTR.	 */	if(tty->termios->c_cflag & HUPCL) {		info->omodem &= ~(RTS|DTR);		fepcmd(info, SETMODEM, 0, DTR|RTS, 10, 1);	}	memoff(info);	info->asyncflags &= ~ASYNC_INITIALIZED;	restore_flags(flags);}static void pcxe_close(struct tty_struct * tty, struct file * filp){	struct channel *info;	if ((info=chan(tty))!=NULL) {		unsigned long flags;		save_flags(flags);		cli();		if(tty_hung_up_p(filp)) {			/* flag that somebody is done with this module */			restore_flags(flags);			return;		}		/* this check is in serial.c, it won't hurt to do it here too */		if ((tty->count == 1) && (info->count != 1)) {			/*			 * Uh, oh.  tty->count is 1, which means that the tty			 * structure will be freed.  Info->count should always			 * be one in these conditions.  If it's greater than			 * one, we've got real problems, since it means the			 * serial port won't be shutdown.			 */			printk("pcxe_close: bad serial port count; tty->count is 1, info->count is %d\n", info->count);			info->count = 1;		}		if (info->count-- > 1) {			restore_flags(flags);			return;		}		if (info->count < 0) {			info->count = 0;		}		info->asyncflags |= ASYNC_CLOSING;			tty->closing = 1;		if(info->asyncflags & ASYNC_INITIALIZED) {			setup_empty_event(tty,info);					tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */		}			if(tty->driver->flush_buffer)			tty->driver->flush_buffer(tty);		tty_ldisc_flush(tty);		shutdown(info);		tty->closing = 0;		info->event = 0;		info->tty = NULL;		if(info->blocked_open) {			if(info->close_delay) {				current->state = TASK_INTERRUPTIBLE;				schedule_timeout(info->close_delay);			}			wake_up_interruptible(&info->open_wait);		}		info->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);		wake_up_interruptible(&info->close_wait);		restore_flags(flags);	}}void pcxe_hangup(struct tty_struct *tty){	struct channel *ch;	if ((ch=chan(tty))!=NULL) {		unsigned long flags;		save_flags(flags);		cli();		shutdown(ch);		ch->event = 0;		ch->count = 0;		ch->tty = NULL;		ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE;		wake_up_interruptible(&ch->open_wait);		restore_flags(flags);	}}static int pcxe_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){	struct channel *ch;	volatile struct board_chan *bc;	int total, remain, size, stlen;	unsigned int head, tail;	unsigned long flags;	/* printk("Entering pcxe_write()\n"); */	if ((ch=chan(tty))==NULL)		return 0;	bc = ch->brdchan;	size = ch->txbufsize;	if (from_user) {		down(&ch->tmp_buf_sem);		save_flags(flags);		cli();		globalwinon(ch);		head = bc->tin & (size - 1);		/* It seems to be necessary to make sure that the value is stable here somehow		   This is a rather odd pice of code here. */

⌨️ 快捷键说明

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