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

📄 simserial.c

📁 是关于linux2.5.1的完全源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Simulated Serial Driver (fake serial) * * This driver is mostly used for bringup purposes and will go away. * It has a strong dependency on the system console. All outputs * are rerouted to the same facility as the one used by printk which, in our * case means sys_sim.c console (goes via the simulator). The code hereafter * is completely leveraged from the serial.c driver. * * Copyright (C) 1999-2000 Hewlett-Packard Co * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com> * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> * * 02/04/00 D. Mosberger	Merged in serial.c bug fixes in rs_close(). * 02/25/00 D. Mosberger	Synced up with 2.3.99pre-5 version of serial.c. */#include <linux/config.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/malloc.h>#include <linux/console.h>#include <linux/module.h>#include <linux/serial.h>#include <linux/serialP.h>#include <asm/irq.h>#include <asm/uaccess.h>#ifdef CONFIG_KDB# include <linux/kdb.h>#endif#undef SIMSERIAL_DEBUG	/* define this to get some debug information */#define KEYBOARD_INTR	3	/* must match with simulator! */#define NR_PORTS	1	/* only one port for now */#define SERIAL_INLINE	1#ifdef SERIAL_INLINE#define _INLINE_ inline#endif#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endif#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)#define SSC_GETCHAR	21extern long ia64_ssc (long, long, long, long, int);extern void ia64_ssc_connect_irq (long intr, long irq);static char *serial_name = "SimSerial driver";static char *serial_version = "0.6";/* * This has been extracted from asm/serial.h. We need one eventually but * I don't know exactly what we're going to put in it so just fake one * for now. */#define BASE_BAUD ( 1843200 / 16 )#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)/* * Most of the values here are meaningless to this particular driver. * However some values must be preserved for the code (leveraged from serial.c * to work correctly). * port must not be 0 * type must not be UNKNOWN * So I picked arbitrary (guess from where?) values instead */static struct serial_state rs_table[NR_PORTS]={  /* UART CLK   PORT IRQ     FLAGS        */  { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 }  /* ttyS0 */};/* * Just for the fun of it ! */static struct serial_uart_config uart_config[] = {	{ "unknown", 1, 0 },	{ "8250", 1, 0 },	{ "16450", 1, 0 },	{ "16550", 1, 0 },	{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },	{ "cirrus", 1, 0 },	{ "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH },	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |		  UART_STARTECH },	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},	{ 0, 0}};static struct tty_driver serial_driver, callout_driver;static int serial_refcount;static struct async_struct *IRQ_ports[NR_IRQS];static struct tty_struct *serial_table[NR_PORTS];static struct termios *serial_termios[NR_PORTS];static struct termios *serial_termios_locked[NR_PORTS];static struct console *console;static unsigned char *tmp_buf;static DECLARE_MUTEX(tmp_buf_sem);extern struct console *console_drivers; /* from kernel/printk.c *//* * ------------------------------------------------------------ * 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){#ifdef SIMSERIAL_DEBUG	printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",		tty->stopped, tty->hw_stopped, tty->flow_stopped);#endif}static void rs_start(struct tty_struct *tty){#if SIMSERIAL_DEBUG	printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",		tty->stopped, tty->hw_stopped, tty->flow_stopped);#endif}static  void receive_chars(struct tty_struct *tty, struct pt_regs *regs){	unsigned char ch;	static unsigned char seen_esc = 0;	while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {		if ( ch == 27 && seen_esc == 0 ) {			seen_esc = 1;			continue;		} else {			if ( seen_esc==1 && ch == 'O' ) {				seen_esc = 2;				continue;			} else if ( seen_esc == 2 ) {				if ( ch == 'P' ) show_state();		/* F1 key */				if ( ch == 'Q' ) show_buffers();	/* F2 key */#ifdef CONFIG_KDB				if ( ch == 'S' )					kdb(KDB_REASON_KEYBOARD, 0, (kdb_eframe_t) regs);#endif				seen_esc = 0;				continue;			}		}		seen_esc = 0;		if (tty->flip.count >= TTY_FLIPBUF_SIZE) break;		*tty->flip.char_buf_ptr = ch;		*tty->flip.flag_buf_ptr = 0;		tty->flip.flag_buf_ptr++;		tty->flip.char_buf_ptr++;		tty->flip.count++;	}	tty_flip_buffer_push(tty);}/* * This is the serial driver's interrupt routine for a single port */static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs){	struct async_struct * info;	/*	 * I don't know exactly why they don't use the dev_id opaque data	 * pointer instead of this extra lookup table	 */	info = IRQ_ports[irq];	if (!info || !info->tty) {		printk("simrs_interrupt_single: info|tty=0 info=%p problem\n", info);		return;	}	/*	 * pretty simple in our case, because we only get interrupts	 * on inbound traffic	 */	receive_chars(info->tty, regs);}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- */#if 0/* * not really used in our situation so keep them commented out for now */static DECLARE_TASK_QUEUE(tq_serial); /* used to be at the top of the file */static void do_serial_bh(void){	run_task_queue(&tq_serial);	printk("do_serial_bh: called\n");}#endifstatic void do_softint(void *private_){	printk("simserial: do_softint called\n");}static void rs_put_char(struct tty_struct *tty, unsigned char ch){	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;	if (!tty || !info->xmit.buf) return;	save_flags(flags); cli();	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {		restore_flags(flags);		return;	}	info->xmit.buf[info->xmit.head] = ch;	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);	restore_flags(flags);}static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done){	int count;	unsigned long flags;	save_flags(flags); cli();	if (info->x_char) {		char c = info->x_char;		console->write(console, &c, 1);		info->state->icount.tx++;		info->x_char = 0;		goto out;	}	if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) {#ifdef SIMSERIAL_DEBUG		printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",		       info->xmit.head, info->xmit.tail, info->tty->stopped);#endif		goto out;	}	/*	 * We removed the loop and try to do it in to chunks. We need	 * 2 operations maximum because it's a ring buffer.	 *	 * First from current to tail if possible.	 * Then from the beginning of the buffer until necessary	 */	count = MIN(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),		    SERIAL_XMIT_SIZE - info->xmit.tail);	console->write(console, info->xmit.buf+info->xmit.tail, count);	info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);	/*	 * We have more at the beginning of the buffer	 */	count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);	if (count) {		console->write(console, info->xmit.buf, count);		info->xmit.tail += count;	}out:	restore_flags(flags);}static void rs_flush_chars(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped ||	    !info->xmit.buf)		return;	transmit_chars(info, NULL);}static int rs_write(struct tty_struct * tty, int from_user,		    const unsigned char *buf, int count){	int	c, ret = 0;	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;	if (!tty || !info->xmit.buf || !tmp_buf) return 0;	save_flags(flags);	if (from_user) {		down(&tmp_buf_sem);		while (1) {			int c1;			c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);			if (count < c)				c = count;			if (c <= 0)				break;			c -= copy_from_user(tmp_buf, buf, c);			if (!c) {				if (!ret)					ret = -EFAULT;				break;			}			cli();			c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);			if (c1 < c)				c = c1;			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);			info->xmit.head = ((info->xmit.head + c) &					   (SERIAL_XMIT_SIZE-1));			restore_flags(flags);			buf += c;			count -= c;			ret += c;		}		up(&tmp_buf_sem);	} else {		cli();		while (1) {			c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);			if (count < c)				c = count;			if (c <= 0) {				break;			}			memcpy(info->xmit.buf + info->xmit.head, buf, c);			info->xmit.head = ((info->xmit.head + c) &					   (SERIAL_XMIT_SIZE-1));			buf += c;			count -= c;			ret += c;		}		restore_flags(flags);	}	/*	 * Hey, we transmit directly from here in our case	 */	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)	    && !tty->stopped && !tty->hw_stopped) {		transmit_chars(info, NULL);	}	return ret;}static int rs_write_room(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);}static int rs_chars_in_buffer(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);}static void rs_flush_buffer(struct tty_struct *tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	unsigned long flags;	save_flags(flags); cli();	info->xmit.head = info->xmit.tail = 0;	restore_flags(flags);	wake_up_interruptible(&tty->write_wait);	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&	    tty->ldisc.write_wakeup)		(tty->ldisc.write_wakeup)(tty);}/* * This function is used to send a high-priority XON/XOFF character to * the device */static void rs_send_xchar(struct tty_struct *tty, char ch){	struct async_struct *info = (struct async_struct *)tty->driver_data;	info->x_char = ch;	if (ch) {		/*		 * I guess we could call console->write() directly but		 * let's do that for now.		 */		transmit_chars(info, NULL);	}}/* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void rs_throttle(struct tty_struct * tty){	if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty));	printk("simrs_throttle called\n");}static void rs_unthrottle(struct tty_struct * tty){	struct async_struct *info = (struct async_struct *)tty->driver_data;	if (I_IXOFF(tty)) {		if (info->x_char)			info->x_char = 0;		else			rs_send_xchar(tty, START_CHAR(tty));	}	printk("simrs_unthrottle called\n");}/* * rs_break() --- routine which turns the break handling on or off */static void rs_break(struct tty_struct *tty, int break_state){}static int rs_ioctl(struct tty_struct *tty, struct file * file,		    unsigned int cmd, unsigned long arg){	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {		if (tty->flags & (1 << TTY_IO_ERROR))		    return -EIO;	}	switch (cmd) {		case TIOCMGET:			printk("rs_ioctl: TIOCMGET called\n");			return -EINVAL;		case TIOCMBIS:		case TIOCMBIC:		case TIOCMSET:			printk("rs_ioctl: TIOCMBIS/BIC/SET called\n");			return -EINVAL;		case TIOCGSERIAL:			printk("simrs_ioctl TIOCGSERIAL called\n");			return 0;		case TIOCSSERIAL:			printk("simrs_ioctl TIOCSSERIAL called\n");			return 0;		case TIOCSERCONFIG:			printk("rs_ioctl: TIOCSERCONFIG called\n");			return -EINVAL;		case TIOCSERGETLSR: /* Get line status register */			printk("rs_ioctl: TIOCSERGETLSR called\n");			return  -EINVAL;		case TIOCSERGSTRUCT:			printk("rs_ioctl: TIOCSERGSTRUCT called\n");#if 0			if (copy_to_user((struct async_struct *) arg,					 info, sizeof(struct async_struct)))				return -EFAULT;#endif			return 0;		/*		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change		 * - mask passed in arg for lines of interest		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)		 * Caller should use TIOCGICOUNT to see which one it was		 */		case TIOCMIWAIT:			printk("rs_ioctl: TIOCMIWAIT: called\n");			return 0;		/*		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)		 * Return: write counters to the user passed counter struct		 * NB: both 1->0 and 0->1 transitions are counted except for		 *     RI where only 0->1 is counted.		 */		case TIOCGICOUNT:			printk("rs_ioctl: TIOCGICOUNT called\n");			return 0;		case TIOCSERGWILD:		case TIOCSERSWILD:			/* "setserial -W" is called in Debian boot */			printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");			return 0;		default:			return -ENOIOCTLCMD;		}	return 0;}#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios){	unsigned int cflag = tty->termios->c_cflag;	if (   (cflag == old_termios->c_cflag)	    && (   RELEVANT_IFLAG(tty->termios->c_iflag)		== RELEVANT_IFLAG(old_termios->c_iflag)))	  return;	/* Handle turning off CRTSCTS */	if ((old_termios->c_cflag & CRTSCTS) &&	    !(tty->termios->c_cflag & CRTSCTS)) {		tty->hw_stopped = 0;		rs_start(tty);	}}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on.

⌨️ 快捷键说明

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