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

📄 esp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  esp.c - driver for Hayes ESP serial cards * *  --- Notices from serial.c, upon which this driver is based --- * *  Copyright (C) 1991, 1992  Linus Torvalds * *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now *  much more extensible to support other serial cards based on the *  16450/16550A UART's.  Added support for the AST FourPort and the *  Accent Async board.   * *  set_serial_info fixed to set the flags, custom divisor, and uart * 	type fields.  Fix suggested by Michael K. Johnson 12/12/92. * *  11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah@doc.ic.ac.uk> * *  03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk> * *  rs_set_termios fixed to look also for changes of the input *      flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. *                                            Bernd Anhäupl 05/17/96. * * --- End of notices from serial.c --- * * Support for the ESP serial card by Andrew J. Robinson *     <arobinso@nyx.net> (Card detection routine taken from a patch *     by Dennis J. Boylan).  Patches to allow use with 2.1.x contributed *     by Chris Faylor. * * Most recent changes: (Andrew J. Robinson) *   Support for PIO mode.  This allows the driver to work properly with *     multiport cards. * * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - * several cleanups, use module_init/module_exit, etc * * This module exports the following rs232 io functions: * *	int espserial_init(void); */#include <linux/module.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial.h>#include <linux/serialP.h>#include <linux/serial_reg.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/slab.h>#include <asm/uaccess.h>#include <linux/hayesesp.h>#define NR_PORTS 64	/* maximum number of ports */#define NR_PRIMARY 8	/* maximum number of primary ports */#define REGION_SIZE 8   /* size of io region to request *//* The following variables can be set by giving module options */static int irq[NR_PRIMARY];	/* IRQ for each base port */static unsigned int divisor[NR_PRIMARY]; /* custom divisor for each port */static unsigned int dma = ESP_DMA_CHANNEL; /* DMA channel */static unsigned int rx_trigger = ESP_RX_TRIGGER;static unsigned int tx_trigger = ESP_TX_TRIGGER;static unsigned int flow_off = ESP_FLOW_OFF;static unsigned int flow_on = ESP_FLOW_ON;static unsigned int rx_timeout = ESP_RX_TMOUT;static unsigned int pio_threshold = ESP_PIO_THRESHOLD;MODULE_LICENSE("GPL");module_param_array(irq, int, NULL, 0);module_param_array(divisor, uint, NULL, 0);module_param(dma, uint, 0);module_param(rx_trigger, uint, 0);module_param(tx_trigger, uint, 0);module_param(flow_off, uint, 0);module_param(flow_on, uint, 0);module_param(rx_timeout, uint, 0);module_param(pio_threshold, uint, 0);/* END */static char *dma_buffer;static int dma_bytes;static struct esp_pio_buffer *free_pio_buf;#define DMA_BUFFER_SZ 1024#define WAKEUP_CHARS 1024static char serial_name[] __initdata = "ESP serial driver";static char serial_version[] __initdata = "2.2";static struct tty_driver *esp_driver;/* serial subtype definitions */#define SERIAL_TYPE_NORMAL	1/* * Serial driver configuration section.  Here are the various options: * * SERIAL_PARANOIA_CHECK * 		Check the magic number for the esp_structure where * 		ever possible. */#undef SERIAL_PARANOIA_CHECK#define SERIAL_DO_RESTART#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ tty->name, (info->flags), serial_driver.refcount,info->count,tty->count,s)#else#define DBG_CNT(s)#endifstatic struct esp_struct *ports;static void change_speed(struct esp_struct *info);static void rs_wait_until_sent(struct tty_struct *, int);/* * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE * times the normal 1.8432 Mhz clock of most serial boards). */#define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))/* Standard COM flags (except for COM4, because of the 8514 problem) */#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)static inline int serial_paranoia_check(struct esp_struct *info,					char *name, const char *routine){#ifdef SERIAL_PARANOIA_CHECK	static const char badmagic[] = KERN_WARNING		"Warning: bad magic number for serial struct (%s) in %s\n";	static const char badinfo[] = KERN_WARNING		"Warning: null esp_struct for (%s) in %s\n";	if (!info) {		printk(badinfo, name, routine);		return 1;	}	if (info->magic != ESP_MAGIC) {		printk(badmagic, name, routine);		return 1;	}#endif	return 0;}static inline unsigned int serial_in(struct esp_struct *info, int offset){	return inb(info->port + offset);}static inline void serial_out(struct esp_struct *info, int offset,			      unsigned char value){	outb(value, info->port+offset);}/* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void rs_stop(struct tty_struct *tty){	struct esp_struct *info = (struct esp_struct *)tty->driver_data;	unsigned long flags;	if (serial_paranoia_check(info, tty->name, "rs_stop"))		return;	spin_lock_irqsave(&info->lock, flags);	if (info->IER & UART_IER_THRI) {		info->IER &= ~UART_IER_THRI;		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);		serial_out(info, UART_ESI_CMD2, info->IER);	}	spin_unlock_irqrestore(&info->lock, flags);}static void rs_start(struct tty_struct *tty){	struct esp_struct *info = (struct esp_struct *)tty->driver_data;	unsigned long flags;		if (serial_paranoia_check(info, tty->name, "rs_start"))		return;		spin_lock_irqsave(&info->lock, flags);	if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {		info->IER |= UART_IER_THRI;		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);		serial_out(info, UART_ESI_CMD2, info->IER);	}	spin_unlock_irqrestore(&info->lock, flags);}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines.  All of the following * subroutines are declared as inline and are folded into * rs_interrupt().  They were separated out for readability's sake. * * Note: rs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off.  People who may want to modify * rs_interrupt() should try to keep the interrupt handler as fast as * possible.  After you are done making modifications, it is not a bad * idea to do: *  * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c * * and look at the resulting assemble code in serial.s. * * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93 * ----------------------------------------------------------------------- *//* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static inline void rs_sched_event(struct esp_struct *info,				  int event){	info->event |= 1 << event;	schedule_work(&info->tqueue);}static DEFINE_SPINLOCK(pio_lock);static inline struct esp_pio_buffer *get_pio_buffer(void){	struct esp_pio_buffer *buf;	unsigned long flags;	spin_lock_irqsave(&pio_lock, flags);	if (free_pio_buf) {		buf = free_pio_buf;		free_pio_buf = buf->next;	} else {		buf = kmalloc(sizeof(struct esp_pio_buffer), GFP_ATOMIC);	}	spin_unlock_irqrestore(&pio_lock, flags);	return buf;}static inline void release_pio_buffer(struct esp_pio_buffer *buf){	unsigned long flags;	spin_lock_irqsave(&pio_lock, flags);	buf->next = free_pio_buf;	free_pio_buf = buf;	spin_unlock_irqrestore(&pio_lock, flags);}static inline void receive_chars_pio(struct esp_struct *info, int num_bytes){	struct tty_struct *tty = info->tty;	int i;	struct esp_pio_buffer *pio_buf;	struct esp_pio_buffer *err_buf;	unsigned char status_mask;	pio_buf = get_pio_buffer();	if (!pio_buf)		return;	err_buf = get_pio_buffer();	if (!err_buf) {		release_pio_buffer(pio_buf);		return;	}	status_mask = (info->read_status_mask >> 2) & 0x07;			for (i = 0; i < num_bytes - 1; i += 2) {		*((unsigned short *)(pio_buf->data + i)) =			inw(info->port + UART_ESI_RX);		err_buf->data[i] = serial_in(info, UART_ESI_RWS);		err_buf->data[i + 1] = (err_buf->data[i] >> 3) & status_mask;		err_buf->data[i] &= status_mask;	}	if (num_bytes & 0x0001) {		pio_buf->data[num_bytes - 1] = serial_in(info, UART_ESI_RX);		err_buf->data[num_bytes - 1] =			(serial_in(info, UART_ESI_RWS) >> 3) & status_mask;	}	/* make sure everything is still ok since interrupts were enabled */	tty = info->tty;	if (!tty) {		release_pio_buffer(pio_buf);		release_pio_buffer(err_buf);		info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;		return;	}	status_mask = (info->ignore_status_mask >> 2) & 0x07;	for (i = 0; i < num_bytes; i++) {		if (!(err_buf->data[i] & status_mask)) {			int flag = 0;			if (err_buf->data[i] & 0x04) {				flag = TTY_BREAK;				if (info->flags & ASYNC_SAK)					do_SAK(tty);			}			else if (err_buf->data[i] & 0x02)				flag = TTY_FRAME;			else if (err_buf->data[i] & 0x01)				flag = TTY_PARITY;			tty_insert_flip_char(tty, pio_buf->data[i], flag);		}	}	tty_schedule_flip(tty);	info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;	release_pio_buffer(pio_buf);	release_pio_buffer(err_buf);}static inline void receive_chars_dma(struct esp_struct *info, int num_bytes){	unsigned long flags;	info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;	dma_bytes = num_bytes;	info->stat_flags |= ESP_STAT_DMA_RX;		flags=claim_dma_lock();        disable_dma(dma);        clear_dma_ff(dma);        set_dma_mode(dma, DMA_MODE_READ);        set_dma_addr(dma, isa_virt_to_bus(dma_buffer));        set_dma_count(dma, dma_bytes);        enable_dma(dma);        release_dma_lock(flags);                serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX);}static inline void receive_chars_dma_done(struct esp_struct *info,					    int status){	struct tty_struct *tty = info->tty;	int num_bytes;	unsigned long flags;		flags=claim_dma_lock();	disable_dma(dma);	clear_dma_ff(dma);	info->stat_flags &= ~ESP_STAT_DMA_RX;	num_bytes = dma_bytes - get_dma_residue(dma);	release_dma_lock(flags);		info->icount.rx += num_bytes;	if (num_bytes > 0) {		tty_insert_flip_string(tty, dma_buffer, num_bytes - 1);		status &= (0x1c & info->read_status_mask);				/* Is the status significant or do we throw the last byte ? */		if (!(status & info->ignore_status_mask)) {			int statflag = 0;			if (status & 0x10) {				statflag = TTY_BREAK;				(info->icount.brk)++;				if (info->flags & ASYNC_SAK)					do_SAK(tty);			} else if (status & 0x08) {				statflag = TTY_FRAME;				(info->icount.frame)++;			}			else if (status & 0x04) {				statflag = TTY_PARITY;				(info->icount.parity)++;			}			tty_insert_flip_char(tty, dma_buffer[num_bytes - 1], statflag);		}		tty_schedule_flip(tty);	}	if (dma_bytes != num_bytes) {		num_bytes = dma_bytes - num_bytes;		dma_bytes = 0;		receive_chars_dma(info, num_bytes);	} else		dma_bytes = 0;}/* Caller must hold info->lock */static inline void transmit_chars_pio(struct esp_struct *info,					int space_avail){	int i;	struct esp_pio_buffer *pio_buf;	pio_buf = get_pio_buffer();	if (!pio_buf)		return;	while (space_avail && info->xmit_cnt) {		if (info->xmit_tail + space_avail <= ESP_XMIT_SIZE) {			memcpy(pio_buf->data,			       &(info->xmit_buf[info->xmit_tail]),			       space_avail);		} else {			i = ESP_XMIT_SIZE - info->xmit_tail;			memcpy(pio_buf->data,			       &(info->xmit_buf[info->xmit_tail]), i);			memcpy(&(pio_buf->data[i]), info->xmit_buf,			       space_avail - i);		}		info->xmit_cnt -= space_avail;		info->xmit_tail = (info->xmit_tail + space_avail) &			(ESP_XMIT_SIZE - 1);		for (i = 0; i < space_avail - 1; i += 2) {			outw(*((unsigned short *)(pio_buf->data + i)),			     info->port + UART_ESI_TX);		}		if (space_avail & 0x0001)			serial_out(info, UART_ESI_TX,				   pio_buf->data[space_avail - 1]);		if (info->xmit_cnt) {			serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);			serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);			space_avail = serial_in(info, UART_ESI_STAT1) << 8;			space_avail |= serial_in(info, UART_ESI_STAT2);			if (space_avail > info->xmit_cnt)				space_avail = info->xmit_cnt;		}	}	if (info->xmit_cnt < WAKEUP_CHARS) {		rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR		printk("THRE...");#endif		if (info->xmit_cnt <= 0) {			info->IER &= ~UART_IER_THRI;			serial_out(info, UART_ESI_CMD1,

⌨️ 快捷键说明

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