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

📄 amiserial.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  linux/drivers/char/amiserial.c * * Serial driver for the amiga builtin port. * * This code was created by taking serial.c version 4.30 from kernel * release 2.3.22, replacing all hardware related stuff with the * corresponding amiga hardware actions, and removing all irrelevant * code. As a consequence, it uses many of the constants and names * associated with the registers and bits of 16550 compatible UARTS - * but only to keep track of status, etc in the state variables. It * was done this was to make it easier to keep the code in line with * (non hardware specific) changes to serial.c. * * The port is registered with the tty driver as minor device 64, and * therefore other ports should should only use 65 upwards. * * Richard Lucock 28/12/99 * *  Copyright (C) 1991, 1992  Linus Torvalds *  Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997,  * 		1998, 1999  Theodore Ts'o * *//* * Serial driver configuration section.  Here are the various options: * * SERIAL_PARANOIA_CHECK * 		Check the magic number for the async_structure where * 		ever possible. */#include <linux/config.h>#include <linux/version.h>#undef SERIAL_PARANOIA_CHECK#define SERIAL_DO_RESTART/* Set of debugging defines */#undef SERIAL_DEBUG_INTR#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT/* Sanity checks */#define SERIAL_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/* * End of serial driver configuration section. */#ifdef MODVERSIONS#include <linux/modversions.h>#endif#include <linux/module.h>#include <linux/types.h>#include <linux/serial.h>#include <linux/serialP.h>#include <linux/serial_reg.h>static char *serial_version = "4.30";#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/console.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/malloc.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/setup.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/bitops.h>#include <asm/amigahw.h>#include <asm/amigaints.h>#ifdef SERIAL_INLINE#define _INLINE_ inline#endifstatic char *serial_name = "Amiga-builtin serial driver";static DECLARE_TASK_QUEUE(tq_serial);static struct tty_driver serial_driver, callout_driver;static int serial_refcount;/* serial subtype definitions */#ifndef SERIAL_TYPE_NORMAL#define SERIAL_TYPE_NORMAL	1#define SERIAL_TYPE_CALLOUT	2#endif/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256static struct async_struct *IRQ_ports;static unsigned char current_ctl_bits;static void change_speed(struct async_struct *info, struct termios *old);static void rs_wait_until_sent(struct tty_struct *tty, int timeout);static struct serial_state rs_table[1];#define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))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 copy_from_user 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);#include <asm/uaccess.h>#define serial_isroot()	(capable(CAP_SYS_ADMIN))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;}/* some serial hardware definitions */#define SDR_OVRUN   (1<<15)#define SDR_RBF     (1<<14)#define SDR_TBE     (1<<13)#define SDR_TSRE    (1<<12)#define SERPER_PARENB    (1<<15)#define AC_SETCLR   (1<<15)#define AC_UARTBRK  (1<<11)#define SER_DTR     (1<<7)#define SER_RTS     (1<<6)#define SER_DCD     (1<<5)#define SER_CTS     (1<<4)#define SER_DSR     (1<<3)static __inline__ void rtsdtr_ctrl(int bits){    ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR));}/* * ------------------------------------------------------------ * 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;		/* disable Tx interrupt and remove any pending interrupts */		custom.intena = IF_TBE;		mb();		custom.intreq = IF_TBE;		mb();	}	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.head != info->xmit.tail	    && info->xmit.buf	    && !(info->IER & UART_IER_THRI)) {		info->IER |= UART_IER_THRI;		custom.intena = IF_SETCLR | IF_TBE;		mb();		/* set a pending Tx Interrupt, transmitter should restart now */		custom.intreq = IF_SETCLR | IF_TBE;		mb();	}	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 async_struct *info,				  int event){	info->event |= 1 << event;	queue_task(&info->tqueue, &tq_serial);	mark_bh(SERIAL_BH);}static _INLINE_ void receive_chars(struct async_struct *info){        int status;	int serdatr;	struct tty_struct *tty = info->tty;	unsigned char ch;	struct	async_icount *icount;	icount = &info->state->icount;	status = UART_LSR_DR; /* We obviously have a character! */	serdatr = custom.serdatr;	mb();	custom.intreq = IF_RBF;	mb();	if((serdatr & 0x1ff) == 0)	    status |= UART_LSR_BI;	if(serdatr & SDR_OVRUN)	    status |= UART_LSR_OE;	ch = serdatr & 0xff;	if (tty->flip.count >= TTY_FLIPBUF_SIZE)	  goto ignore_char;	*tty->flip.char_buf_ptr = ch;	icount->rx++;#ifdef SERIAL_DEBUG_INTR	printk("DR%02x:%02x...", ch, status);#endif	*tty->flip.flag_buf_ptr = 0;	/*	 * We don't handle parity or frame errors - but I have left	 * the code in, since I'm not sure that the errors can't be	 * detected.	 */	if (status & (UART_LSR_BI | UART_LSR_PE |		      UART_LSR_FE | UART_LSR_OE)) {	  /*	   * For statistics only	   */	  if (status & UART_LSR_BI) {	    status &= ~(UART_LSR_FE | UART_LSR_PE);	    icount->brk++;	  } else if (status & UART_LSR_PE)	    icount->parity++;	  else if (status & UART_LSR_FE)	    icount->frame++;	  if (status & UART_LSR_OE)	    icount->overrun++;	  /*	   * Now check to see if character should be	   * ignored, and mask off conditions which	   * should be ignored.	   */	  if (status & info->ignore_status_mask)	    goto ignore_char;	  status &= info->read_status_mask;	  if (status & (UART_LSR_BI)) {#ifdef SERIAL_DEBUG_INTR	    printk("handling break....");#endif	    *tty->flip.flag_buf_ptr = TTY_BREAK;	    if (info->flags & ASYNC_SAK)	      do_SAK(tty);	  } else if (status & UART_LSR_PE)	    *tty->flip.flag_buf_ptr = TTY_PARITY;	  else if (status & UART_LSR_FE)	    *tty->flip.flag_buf_ptr = TTY_FRAME;	  if (status & UART_LSR_OE) {	    /*	     * Overrun is special, since it's	     * reported immediately, and doesn't	     * affect the current character	     */	    if (tty->flip.count < TTY_FLIPBUF_SIZE) {	      tty->flip.count++;	      tty->flip.flag_buf_ptr++;	      tty->flip.char_buf_ptr++;	      *tty->flip.flag_buf_ptr = TTY_OVERRUN;	    }	  }	}	tty->flip.flag_buf_ptr++;	tty->flip.char_buf_ptr++;	tty->flip.count++; ignore_char:	tty_flip_buffer_push(tty);}static _INLINE_ void transmit_chars(struct async_struct *info){	custom.intreq = IF_TBE;	mb();	if (info->x_char) {	        custom.serdat = info->x_char | 0x100;		mb();		info->state->icount.tx++;		info->x_char = 0;		return;	}	if (info->xmit.head == info->xmit.tail	    || info->tty->stopped	    || info->tty->hw_stopped) {		info->IER &= ~UART_IER_THRI;	        custom.intena = IF_TBE;		mb();		return;	}	custom.serdat = info->xmit.buf[info->xmit.tail++] | 0x100;	mb();	info->xmit.tail = info->xmit.tail & (SERIAL_XMIT_SIZE-1);	info->state->icount.tx++;	if (CIRC_CNT(info->xmit.head,		     info->xmit.tail,		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR	printk("THRE...");#endif	if (info->xmit.head == info->xmit.tail) {	        custom.intena = IF_TBE;		mb();		info->IER &= ~UART_IER_THRI;	}}static _INLINE_ void check_modem_status(struct async_struct *info){	unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);	unsigned char dstatus;	struct	async_icount *icount;	/* Determine bits that have changed */	dstatus = status ^ current_ctl_bits;	current_ctl_bits = status;	if (dstatus) {		icount = &info->state->icount;		/* update input line counters */		if (dstatus & SER_DSR)			icount->dsr++;		if (dstatus & SER_DCD) {			icount->dcd++;#ifdef CONFIG_HARD_PPS			if ((info->flags & ASYNC_HARDPPS_CD) &&			    !(status & SER_DCD))				hardpps();#endif		}		if (dstatus & SER_CTS)			icount->cts++;		wake_up_interruptible(&info->delta_msr_wait);	}	if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))		printk("ttyS%02d CD now %s...", info->line,		       (!(status & SER_DCD)) ? "on" : "off");#endif		if (!(status & SER_DCD))			wake_up_interruptible(&info->open_wait);		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&			   (info->flags & ASYNC_CALLOUT_NOHUP))) {#ifdef SERIAL_DEBUG_OPEN			printk("doing serial hangup...");#endif			if (info->tty)				tty_hangup(info->tty);		}	}	if (info->flags & ASYNC_CTS_FLOW) {		if (info->tty->hw_stopped) {			if (!(status & SER_CTS)) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))				printk("CTS tx start...");#endif				info->tty->hw_stopped = 0;				info->IER |= UART_IER_THRI;				custom.intena = IF_SETCLR | IF_TBE;				mb();				/* set a pending Tx Interrupt, transmitter should restart now */				custom.intreq = IF_SETCLR | IF_TBE;				mb();				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);				return;			}		} else {			if ((status & SER_CTS)) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))				printk("CTS tx stop...");#endif				info->tty->hw_stopped = 1;				info->IER &= ~UART_IER_THRI;				/* disable Tx interrupt and remove any pending interrupts */				custom.intena = IF_TBE;				mb();				custom.intreq = IF_TBE;				mb();			}		}	}}static void ser_vbl_int( int irq, void *data, struct pt_regs *regs){        /* vbl is just a periodic interrupt we tie into to update modem status */	struct async_struct * info = IRQ_ports;	/*	 * TBD - is it better to unregister from this interrupt or to	 * ignore it if MSI is clear ?	 */	if(info->IER & UART_IER_MSI)	  check_modem_status(info);}static void ser_rx_int(int irq, void *dev_id, struct pt_regs * regs){	struct async_struct * info;#ifdef SERIAL_DEBUG_INTR	printk("ser_rx_int...");#endif	info = IRQ_ports;	if (!info || !info->tty)		return;	receive_chars(info);	info->last_active = jiffies;#ifdef SERIAL_DEBUG_INTR	printk("end.\n");#endif}static void ser_tx_int(int irq, void *dev_id, struct pt_regs * regs){	struct async_struct * info;	if (custom.serdatr & SDR_TBE) {#ifdef SERIAL_DEBUG_INTR	  printk("ser_tx_int...");#endif	  info = IRQ_ports;	  if (!info || !info->tty)	    return;	  transmit_chars(info);	  info->last_active = jiffies;#ifdef SERIAL_DEBUG_INTR	  printk("end.\n");#endif	}}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh(void){	run_task_queue(&tq_serial);}static void do_softint(void *private_){	struct async_struct	*info = (struct async_struct *) private_;	struct tty_struct	*tty;	tty = info->tty;	if (!tty)		return;	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&		    tty->ldisc.write_wakeup)			(tty->ldisc.write_wakeup)(tty);		wake_up_interruptible(&tty->write_wait);	}}/* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver:  routines to

⌨️ 快捷键说明

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