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

📄 serial.c

📁 基于16450/16550A的多串口驱动,估计很多人在找吧
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  linux/drivers/char/serial.c * *  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. *  * Added Support for PCI serial boards which contain 16x50 Chips * 31.10.1998 Henning P. Schmiedehausen <hps@tanstaafl.de> *  * This module exports the following rs232 io functions: * *	int rs_init(void); * 	int rs_open(struct tty_struct * tty, struct file * filp) */#include <linux/module.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/serial.h>#include <linux/serial_reg.h>#include <linux/config.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>#if defined(CONFIG_COLDFIRE) && defined(CONFIG_PCI)#define	CONFIG_SERIAL_PCI	1#endif#ifdef CONFIG_SERIAL_PCI#include <linux/pci.h>#include <linux/bios32.h>#endif#ifdef CONFIG_LEDMAN#include <linux/ledman.h>#endif#include <asm/system.h>#include <asm/io.h>#include <asm/segment.h>#include <asm/bitops.h>#ifdef CONFIG_COLDFIRE#include <asm/mcfpci.h>#endifstatic char *serial_name = "Serial driver";static char *serial_version = "4.13p1";DECLARE_TASK_QUEUE(tq_serial);struct tty_driver serial_driver, callout_driver;static int serial_refcount;/* serial subtype definitions */#define SERIAL_TYPE_NORMAL	1#define SERIAL_TYPE_CALLOUT	2/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256#ifdef CONFIG_COLDFIRE#undef	TTY_MAJOR#define	TTY_MAJOR	24#undef	TTYAUX_MAJOR#define	TTYAUX_MAJOR	25#endif/* * Serial driver configuration section.  Here are the various options: * * CONFIG_HUB6 *		Enables support for the venerable Bell Technologies *		HUB6 card. * * SERIAL_PARANOIA_CHECK * 		Check the magic number for the async_structure where * 		ever possible. */#define SERIAL_PARANOIA_CHECK#define CONFIG_SERIAL_NOPAUSE_IO#define SERIAL_DO_RESTART#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#ifdef CONFIG_SERIAL_PCI# undef SERIAL_DEBUG_PCI#endif#define RS_STROBE_TIME (10*HZ)#define RS_ISR_PASS_LIMIT 256#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)#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)#endif/* * IRQ_timeout		- How long the timeout should be for each IRQ * 				should be after the IRQ has been active. */static struct async_struct *IRQ_ports[16];static struct rs_multiport_struct rs_multiport[16];static int IRQ_timeout[16];static volatile int rs_irq_triggered;static volatile int rs_triggered;static int rs_wild_int_mask;#ifdef CONFIG_COLDFIRE#define	IRQMASK(i)	((i) & 0x0f)#endifstatic void autoconfig(struct async_struct * info);static void change_speed(struct async_struct *info);	/* * This assumes you have a 1.8432 MHz clock for your UART. * * It'd be nice if someone built a serial card with a 24.576 MHz * clock, since the 16550A is capable of handling a top speed of 1.5 * megabits/second; but this requires the faster clock. */#define BASE_BAUD ( 1843200 / 16 )/* * Well, it is not a 24,756 MHz clock but it is at least a start.  * This PCI board here has a 14,7456 MHz crystal oscillator which is  * eight times as fast as the standard serial clock... */#define PCI_BAUD  ( 14745600 / 16 )/* Standard COM flags (except for COM4, because of the 8514 problem) */#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF#define FOURPORT_FLAGS ASYNC_FOURPORT#define ACCENT_FLAGS 0#define BOCA_FLAGS 0#define HUB6_FLAGS 0	#ifdef CONFIG_SERIAL_PCI#define PCI_FLAGS (ASYNC_PCI|ASYNC_BOOT_AUTOCONF)#ifndef PCI_DEVICE_ID_PLX_SPCOM200#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103#endif/* * The chips we know about */#define PCISER_PLX9050  0    /* PLX 9050 local bus bridge as serial card */#define PCISER_PCCOM4   1    /* "PC COM PCI Bus 4 port serial Adapter" -- from Alvin Sim <alvin@alloycp.com.au> */struct pci_serial_boards pci_serial_tbl[] = {  { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, "SPCom 200", PCISER_PLX9050, pci_space_0|pci_space_1, 1, 0, 128, PCI_BAUD },  { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,   "PC COM 4",  PCISER_PCCOM4,  pci_space_0,             4, 8, 128, BASE_BAUD },  { 0x10b5, 0x9050, "RAStel", PCISER_PLX9050,  pci_space_0, 4, 8, 128, BASE_BAUD },  { 0x15aa, 0x2000, "RAStel", PCISER_PLX9050,  pci_space_0|pci_space_1, 1, 0, 128, BASE_BAUD },  {	0, 0, 0, 0, 0, 0, 0, 0, 0 }};#endif	/* * The following define the access methods for the HUB6 card. All * access is through two ports for all 24 possible chips. The card is * selected through the high 2 bits, the port on that card with the * "middle" 3 bits, and the register on that port with the bottom * 3 bits. * * While the access port and interrupt is configurable, the default * port locations are 0x302 for the port control register, and 0x303 * for the data read/write register. Normally, the interrupt is at irq3 * but can be anything from 3 to 7 inclusive. Note that using 3 will * require disabling com2. */#define C_P(card,port) (((card)<<6|(port)<<3) + 1)struct async_struct rs_table[] = {	/* UART CLK   PORT IRQ     FLAGS        */#if defined(CONFIG_NETtel)	{ 0, BASE_BAUD, 0x40000000, 28, STD_COM_FLAGS },/* ttyS0 */#endif#ifndef CONFIG_UCLINUX	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */	{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },	/* ttyS2 */	{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */	{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */	{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */	{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */	{ 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS },	/* ttyS7 */	{ 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS },	/* ttyS8 */	{ 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS },	/* ttyS9 */	{ 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS },	/* ttyS10 */	{ 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS },	/* ttyS11 */		{ 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS },	/* ttyS12 */	{ 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS },	/* ttyS13 */	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS14 (spare; user configurable) */	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS15 (spare; user configurable) */	{ 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS },	/* ttyS16 */	{ 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS },	/* ttyS17 */	{ 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS },	/* ttyS18 */	{ 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS },	/* ttyS19 */	{ 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS },	/* ttyS20 */	{ 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS },	/* ttyS21 */	{ 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS },	/* ttyS22 */	{ 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS },	/* ttyS23 */	{ 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS },	/* ttyS24 */	{ 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS },	/* ttyS25 */	{ 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS },	/* ttyS26 */	{ 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS },	/* ttyS27 */	{ 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS },	/* ttyS28 */	{ 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS },	/* ttyS29 */	{ 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS },	/* ttyS30 */	{ 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS },	/* ttyS31 *//* You can have up to four HUB6's in the system, but I've only * included two cards here for a total of twelve ports. */#ifdef CONFIG_HUB6	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) },	/* ttyS32 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) },	/* ttyS33 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) },	/* ttyS34 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) },	/* ttyS35 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) },	/* ttyS36 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) },	/* ttyS37 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },	/* ttyS38 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },	/* ttyS39 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },	/* ttyS40 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },	/* ttyS41 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS42 */	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS43 */#endif#endif /* CONFIG_UCLINUX */#ifdef CONFIG_SERIAL_PCI		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS32 or bigger... */		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS33 */		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS34 */		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS35 */		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS36 */		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS37 */		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS38 */		{ 0, BASE_BAUD, 0x0, 0, 0 },	/* ttyS39 */#endif};#define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))#ifdef CONFIG_SERIAL_PCI/* * currently you can have up to four PCI serial boards in your * system. Increase the size of this structure to have more */struct pci_struct pci_rs_chips[] = {                   {0, 0,},  {0, 0,},  {0, 0,},  {0, 0,},};#define PCI_NR_BOARDS	(sizeof(pci_rs_chips)/sizeof(struct pci_struct))#define PCI_NR_PORTS	8#define PCI_PORT_START (NR_PORTS - PCI_NR_PORTS)#endifstatic 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 = 0;static struct semaphore tmp_buf_sem = MUTEX;static inline int serial_paranoia_check(struct async_struct *info,					kdev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK	static const char *badmagic =		"Warning: bad magic number for serial struct (%s) in %s\n";	static const char *badinfo =		"Warning: null async_struct for (%s) in %s\n";	if (!info) {		printk(badinfo, kdevname(device), routine);		return 1;	}	if (info->magic != SERIAL_MAGIC) {		printk(badmagic, kdevname(device), routine);		return 1;	}#endif	return 0;}/* * This is used to figure out the divisor speeds and the timeouts */static int baud_table[] = {	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,	9600, 19200, 38400, 57600, 115200, 0 };static inline unsigned int serial_in(struct async_struct *info, int offset){#ifdef CONFIG_HUB6    if (info->hub6) {	outb(info->hub6 - 1 + offset, info->port);	return inb(info->port+1);    } else#endif	return inb(info->port + offset);}static inline unsigned int serial_inp(struct async_struct *info, int offset){#ifdef CONFIG_HUB6    if (info->hub6) {	outb(info->hub6 - 1 + offset, info->port);	return inb_p(info->port+1);    } else#endif#ifdef CONFIG_SERIAL_NOPAUSE_IO	return inb(info->port + offset);#else	return inb_p(info->port + offset);#endif}static inline void serial_out(struct async_struct *info, int offset, int value){#ifdef CONFIG_HUB6    if (info->hub6) {	outb(info->hub6 - 1 + offset, info->port);	outb(value, info->port+1);    } else#endif	outb(value, info->port+offset);}static inline void serial_outp(struct async_struct *info, int offset,			       int value){#ifdef CONFIG_HUB6    if (info->hub6) {	outb(info->hub6 - 1 + offset, info->port);	outb_p(value, info->port+1);    } else#endif#ifdef CONFIG_SERIAL_NOPAUSE_IO	outb(value, info->port+offset);#else    	outb_p(value, info->port+offset);#endif}/* * ------------------------------------------------------------ * 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 async_struct *info = (struct async_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_IER, info->IER);	}	restore_flags(flags);}static void rs_start(struct tty_struct *tty){	struct async_struct *info = (struct async_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_IER, 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 * ----------------------------------------------------------------------- */#ifndef CONFIG_COLDFIRE/* * This is the serial driver's interrupt routine while we are probing * for submarines. */static void rs_probe(int irq, void *dev_id, struct pt_regs * regs){	rs_irq_triggered = IRQMASK(irq);	rs_triggered |= 1 << IRQMASK(irq);	return;}

⌨️ 快捷键说明

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