📄 esp.c
字号:
/* * 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鋟pl 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 <asm/system.h>#include <asm/io.h>#include <asm/segment.h>#include <asm/bitops.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 *//* 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_PARM(irq, "1-8i");MODULE_PARM(divisor, "1-8i");MODULE_PARM(dma, "i");MODULE_PARM(rx_trigger, "i");MODULE_PARM(tx_trigger, "i");MODULE_PARM(flow_off, "i");MODULE_PARM(flow_on, "i");MODULE_PARM(rx_timeout, "i");MODULE_PARM(pio_threshold, "i");/* 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 DECLARE_TASK_QUEUE(tq_esp);static struct tty_driver esp_driver, esp_callout_driver;static int serial_refcount;/* serial subtype definitions */#define SERIAL_TYPE_NORMAL 1#define SERIAL_TYPE_CALLOUT 2/* * 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#define _INLINE_ inline #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ kdevname(tty->device), (info->flags), serial_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 struct tty_struct *serial_table[NR_PORTS];static struct termios *serial_termios[NR_PORTS];static struct termios *serial_termios_locked[NR_PORTS];#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif/* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the memcpy_fromfs blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. */static unsigned char *tmp_buf;static DECLARE_MUTEX(tmp_buf_sem);static inline int serial_paranoia_check(struct esp_struct *info, kdev_t device, 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, kdevname(device), routine); return 1; } if (info->magic != ESP_MAGIC) { printk(badmagic, kdevname(device), 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->device, "rs_stop")) return; save_flags(flags); cli(); 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); } restore_flags(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->device, "rs_start")) return; save_flags(flags); cli(); 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); } restore_flags(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; queue_task(&info->tqueue, &tq_esp); mark_bh(ESP_BH);}static _INLINE_ struct esp_pio_buffer *get_pio_buffer(void){ struct esp_pio_buffer *buf; if (free_pio_buf) { buf = free_pio_buf; free_pio_buf = buf->next; } else { buf = kmalloc(sizeof(struct esp_pio_buffer), GFP_ATOMIC); } return buf;}static _INLINE_ void release_pio_buffer(struct esp_pio_buffer *buf){ buf->next = free_pio_buf; free_pio_buf = buf;}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; } sti(); 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; } cli(); /* 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)) { *(tty->flip.char_buf_ptr++) = pio_buf->data[i]; if (err_buf->data[i] & 0x04) { *(tty->flip.flag_buf_ptr++) = TTY_BREAK; if (info->flags & ASYNC_SAK) do_SAK(tty); } else if (err_buf->data[i] & 0x02) *(tty->flip.flag_buf_ptr++) = TTY_FRAME; else if (err_buf->data[i] & 0x01) *(tty->flip.flag_buf_ptr++) = TTY_PARITY; else *(tty->flip.flag_buf_ptr++) = 0; tty->flip.count++; } } queue_task(&tty->flip.tqueue, &tq_timer); 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, 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; memcpy(tty->flip.char_buf_ptr, dma_buffer, num_bytes); tty->flip.char_buf_ptr += num_bytes; tty->flip.count += num_bytes; memset(tty->flip.flag_buf_ptr, 0, num_bytes); tty->flip.flag_buf_ptr += num_bytes; if (num_bytes > 0) { tty->flip.flag_buf_ptr--; status &= (0x1c & info->read_status_mask); if (status & info->ignore_status_mask) { tty->flip.count--; tty->flip.char_buf_ptr--; tty->flip.flag_buf_ptr--; } else if (status & 0x10) { *tty->flip.flag_buf_ptr = TTY_BREAK; (info->icount.brk)++; if (info->flags & ASYNC_SAK) do_SAK(tty); } else if (status & 0x08) { *tty->flip.flag_buf_ptr = TTY_FRAME; (info->icount.frame)++; } else if (status & 0x04) { *tty->flip.flag_buf_ptr = TTY_PARITY; (info->icount.parity)++; } tty->flip.flag_buf_ptr++; queue_task(&tty->flip.tqueue, &tq_timer); } if (dma_bytes != num_bytes) { num_bytes = dma_bytes - num_bytes; dma_bytes = 0; receive_chars_dma(info, num_bytes); } else dma_bytes = 0;}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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -