⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcxx.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  linux/drivers/char/pcxe.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 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 (clameter@fuller.edu) * Please contact the mailing list for problems first.  * * Sources of Information: * 1. The Linux Digiboard Page at http://private.fuller.edu/clameter/digi.html * 2. The Linux Digiboard Mailing list at digiboard@list.fuller.edu *    (Simply write a message to introduce yourself to subscribe) * *  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. * */#undef SPEED_HACK/* If you define SPEED_HACK then you get the following Baudrate translation   19200 = 57600   38400 = 115K   The driver supports the native 57.6K and 115K Baudrates under Linux, but   some distributions like Slackware 3.0 don't like these high baudrates.*/#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/malloc.h>#include <linux/init.h>#include <linux/version.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>#define VERSION 	"1.6.1"#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};# if (LINUX_VERSION_CODE > 0x020111)MODULE_AUTHOR("Bernhard Kaindl");MODULE_DESCRIPTION("Digiboard PC/X{i,e,eve} driver");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#endif MODULEstatic int numcards = 1;static int nbdevs = 0; static struct channel    *digi_channels;static struct tty_struct **pcxe_table;static struct termios    **pcxe_termios;static struct termios    **pcxe_termios_locked; int pcxx_ncook=sizeof(pcxx_cook);int pcxx_nbios=sizeof(pcxx_bios);#define MIN(a,b)	((a) < (b) ? (a) : (b))#define pcxxassert(x, msg)  if(!(x)) pcxx_error(__LINE__, msg)#define FEPTIMEOUT 200000  #define SERIAL_TYPE_NORMAL	1#define SERIAL_TYPE_CALLOUT	2#define PCXE_EVENT_HANGUP   1struct tty_driver pcxe_driver;struct tty_driver pcxe_callout;static int pcxe_refcount;DECLARE_TASK_QUEUE(tq_pcxx);static void pcxxpoll(void);static void pcxxdelay(int);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 do_pcxe_bh(void);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);#define TZ_BUFSZ 4096/* function definitions */#ifdef MODULE/* * pcxe_init() is our init_module(): */#define pcxe_init init_modulevoid	cleanup_module(void);/*****************************************************************************/void cleanup_module(){	unsigned long	flags;	int crd, i;	int e1, e2;	struct board_info *bd;	struct channel *ch;	printk(KERN_NOTICE "Unloading PC/Xx version %s\n", VERSION);	save_flags(flags);	cli();	timer_active &= ~(1 << DIGI_TIMER);	timer_table[DIGI_TIMER].fn = NULL;	timer_table[DIGI_TIMER].expires = 0;	remove_bh(DIGI_BH);	if ((e1 = tty_unregister_driver(&pcxe_driver)))		printk("SERIAL: failed to unregister serial driver (%d)\n", e1);	if ((e2 = tty_unregister_driver(&pcxe_callout)))		printk("SERIAL: failed to unregister callout driver (%d)\n",e2);	for(crd=0; crd < numcards; crd++) {		bd = &boards[crd];		ch = digi_channels+bd->first_minor;		for(i=0; i < bd->numports; i++, ch++) {			kfree(ch->tmp_buf);		}		release_region(bd->port, 4);	}	kfree(digi_channels);	kfree(pcxe_termios_locked);	kfree(pcxe_termios);	kfree(pcxe_table);	restore_flags(flags);}#endifstatic 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;	queue_task(&info->tqueue, &tq_pcxx);	mark_bh(DIGI_BH);}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){	struct wait_queue wait = { current, NULL };	int	retval = 0;	int	do_clocal = 0;	if (info->asyncflags & ASYNC_CALLOUT_ACTIVE) {		if (info->normal_termios.c_cflag & CLOCAL)			do_clocal = 1;	} else {		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();		if ((info->asyncflags & ASYNC_CALLOUT_ACTIVE) == 0) {			globalwinon(info);			info->omodem |= DTR|RTS;			fepcmd(info, SETMODEM, DTR|RTS, 0, 10, 1);			memoff(info);		}		sti();		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_CALLOUT_ACTIVE) == 0 &&		    (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 = MINOR(tty->device) - tty->driver.minor_start;	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);	}	/* flag the kernel that there is somebody using this guy */	MOD_INC_USE_COUNT;	/*	 * 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 this is a callout device, then just make sure the normal	 * device isn't being used.	 */	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {		if (ch->asyncflags & ASYNC_NORMAL_ACTIVE)			return -EBUSY;		if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) {			if ((ch->asyncflags & ASYNC_SESSION_LOCKOUT) &&		    		(ch->session != current->session))			    return -EBUSY;			if((ch->asyncflags & ASYNC_PGRP_LOCKOUT) &&			    (ch->pgrp != current->pgrp))			    return -EBUSY;		}		ch->asyncflags |= ASYNC_CALLOUT_ACTIVE;	}	else {		if (filp->f_flags & O_NONBLOCK) {			if(ch->asyncflags & ASYNC_CALLOUT_ACTIVE)				return -EBUSY;		}		else {			/* 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;	} 		save_flags(flags);	cli();	if((ch->count == 1) && (ch->asyncflags & ASYNC_SPLIT_TERMIOS)) {		if(tty->driver.subtype == SERIAL_TYPE_NORMAL)			*tty->termios = ch->normal_termios;		else 			*tty->termios = ch->callout_termios;		globalwinon(ch);		pcxxparam(tty,ch);		memoff(ch);	}	ch->session = current->session;	ch->pgrp = current->pgrp;	restore_flags(flags);	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 */			MOD_DEC_USE_COUNT;			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);			MOD_DEC_USE_COUNT;			return;		}		if (info->count < 0) {			info->count = 0;		}		info->asyncflags |= ASYNC_CLOSING;			/*		* Save the termios structure, since this port may have		* separate termios for callout and dialin.		*/		if(info->asyncflags & ASYNC_NORMAL_ACTIVE)			info->normal_termios = *tty->termios;		if(info->asyncflags & ASYNC_CALLOUT_ACTIVE)			info->callout_termios = *tty->termios;		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);		if(tty->ldisc.flush_buffer)			tty->ldisc.flush_buffer(tty);		shutdown(info);		tty->closing = 0;		info->event = 0;		info->tty = NULL;#ifndef MODULE/* ldiscs[] is not available in a MODULE** worth noting that while I'm not sure what this hunk of code is supposed** to do, it is not present in the serial.c driver.  Hmmm.  If you know,** please send me a note.  brian@ilinx.com** Don't know either what this is supposed to do clameter@waterf.org.*/

⌨️ 快捷键说明

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