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

📄 generic_serial.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  generic_serial.c * *  Copyright (C) 1998/1999 R.E.Wolff@BitWizard.nl * *  written for the SX serial driver. *     Contains the code that should be shared over all the serial drivers. * *  Credit for the idea to do it this way might go to Alan Cox.  * * *  Version 0.1 -- December, 1998. Initial version. *  Version 0.2 -- March, 1999.    Some more routines. Bugfixes. Etc. *  Version 0.5 -- August, 1999.   Some more fixes. Reformat for Linus. * *  BitWizard is actively maintaining this file. We sometimes find *  that someone submitted changes to this file. We really appreciate *  your help, but please submit changes through us. We're doing our *  best to be responsive.  -- REW * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/tty.h>#include <linux/serial.h>#include <linux/mm.h>#include <linux/generic_serial.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#define DEBUG static char *                  tmp_buf; static DECLARE_MUTEX(tmp_buf_sem);static int gs_debug;#ifdef DEBUG#define gs_dprintk(f, str...) if (gs_debug & f) printk (str)#else#define gs_dprintk(f, str...) /* nothing */#endif#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n")#define func_exit()  gs_dprintk (GS_DEBUG_FLOW, "gs: exit  " __FUNCTION__ "\n")#if NEW_WRITE_LOCKING#define DECL      /* Nothing */#define LOCKIT    down (& port->port_write_sem);#define RELEASEIT up (&port->port_write_sem);#else#define DECL      unsigned long flags;#define LOCKIT    save_flags (flags);cli ()#define RELEASEIT restore_flags (flags)#endif#define RS_EVENT_WRITE_WAKEUP	1MODULE_PARM(gs_debug, "i");void gs_put_char(struct tty_struct * tty, unsigned char ch){	struct gs_port *port;	DECL	func_enter (); 	if (!tty) return;	port = tty->driver_data;	if (!port) return;	if (! (port->flags & ASYNC_INITIALIZED)) return;	/* Take a lock on the serial tranmit buffer! */	LOCKIT;	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {		/* Sorry, buffer is full, drop character. Update statistics???? -- REW */		RELEASEIT;		return;	}	port->xmit_buf[port->xmit_head++] = ch;	port->xmit_head &= SERIAL_XMIT_SIZE - 1;	port->xmit_cnt++;  /* Characters in buffer */	RELEASEIT;	func_exit ();}#ifdef NEW_WRITE_LOCKING/*> Problems to take into account are:>       -1- Interrupts that empty part of the buffer.>       -2- page faults on the access to userspace. >       -3- Other processes that are also trying to do a "write". */int gs_write(struct tty_struct * tty, int from_user,                     const unsigned char *buf, int count){	struct gs_port *port;	int c, total = 0;	int t;	func_enter ();	if (!tty) return 0;	port = tty->driver;	if (!port) return 0;	if (! (port->flags & ASYNC_INITIALIZED))		return 0;	/* get exclusive "write" access to this port (problem 3) */	/* This is not a spinlock because we can have a disk access (page 		 fault) in copy_from_user */	down (& port->port_write_sem);	while (1) {		c = count; 		/* This is safe because we "OWN" the "head". Noone else can 		   change the "head": we own the port_write_sem. */		/* Don't overrun the end of the buffer */		t = SERIAL_XMIT_SIZE - port->xmit_head;		if (t < c) c = t; 		/* This is safe because the xmit_cnt can only decrease. This 		   would increase "t", so we might copy too little chars. */		/* Don't copy past the "head" of the buffer */		t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;		if (t < c) c = t; 		/* Can't copy more? break out! */		if (c <= 0) break;		if (from_user)			copy_from_user (port->xmit_buf + port->xmit_head, buf, c);		else			memcpy         (port->xmit_buf + port->xmit_head, buf, c);		port -> xmit_cnt += c;		port -> xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE -1);		buf += c;		count -= c;		total += c;	}	up (& port->port_write_sem);	gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n", 	            (port->flags & GS_TX_INTEN)?"enabled": "disabled"); 	if (port->xmit_cnt && 	    !tty->stopped && 	    !tty->hw_stopped &&	    !(port->flags & GS_TX_INTEN)) {		port->flags |= GS_TX_INTEN;		port->rd->enable_tx_interrupts (port);	}	func_exit ();	return total;}#else/*> Problems to take into account are:>       -1- Interrupts that empty part of the buffer.>       -2- page faults on the access to userspace. >       -3- Other processes that are also trying to do a "write". */int gs_write(struct tty_struct * tty, int from_user,                     const unsigned char *buf, int count){	struct gs_port *port;	int c, total = 0;	int t;	unsigned long flags;	func_enter ();	/* The standard serial driver returns 0 in this case. 	   That sounds to me as "No error, I just didn't get to writing any	   bytes. Feel free to try again." 	   The "official" way to write n bytes from buf is:		 for (nwritten = 0;nwritten < n;nwritten += rv) {			 rv = write (fd, buf+nwritten, n-nwritten);			 if (rv < 0) break; // Error: bail out. //		 } 	   which will loop endlessly in this case. The manual page for write	   agrees with me. In practise almost everybody writes 	   "write (fd, buf,n);" but some people might have had to deal with 	   incomplete writes in the past and correctly implemented it by now... 	 */	if (!tty) return -EIO;	port = tty->driver_data;	if (!port || !port->xmit_buf || !tmp_buf)		return -EIO;	save_flags(flags);	if (from_user) {		down(&tmp_buf_sem);		while (1) {			c = count;			/* This is safe because we "OWN" the "head". Noone else can 			   change the "head": we own the port_write_sem. */			/* Don't overrun the end of the buffer */			t = SERIAL_XMIT_SIZE - port->xmit_head;			if (t < c) c = t; 			/* This is safe because the xmit_cnt can only decrease. This 			   would increase "t", so we might copy too little chars. */			/* Don't copy past the "head" of the buffer */			t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;			if (t < c) c = t;	 			/* Can't copy more? break out! */			if (c <= 0) break;			c -= copy_from_user(tmp_buf, buf, c);			if (!c) {				if (!total)					total = -EFAULT;				break;			}			cli();			t = SERIAL_XMIT_SIZE - port->xmit_head;			if (t < c) c = t;			t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;			if (t < c) c = t;			memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);			port->xmit_head = ((port->xmit_head + c) &			                   (SERIAL_XMIT_SIZE-1));			port->xmit_cnt += c;			restore_flags(flags);			buf += c;			count -= c;			total += c;		}		up(&tmp_buf_sem);	} else {		while (1) {			cli();			c = count;			/* This is safe because we "OWN" the "head". Noone else can 			   change the "head": we own the port_write_sem. */			/* Don't overrun the end of the buffer */			t = SERIAL_XMIT_SIZE - port->xmit_head;			if (t < c) c = t; 			/* This is safe because the xmit_cnt can only decrease. This 			   would increase "t", so we might copy too little chars. */			/* Don't copy past the "head" of the buffer */			t = SERIAL_XMIT_SIZE - 1 - port->xmit_cnt;			if (t < c) c = t; 			/* Can't copy more? break out! */			if (c <= 0) {				restore_flags(flags);				break;			}			memcpy(port->xmit_buf + port->xmit_head, buf, c);			port->xmit_head = ((port->xmit_head + c) &			                   (SERIAL_XMIT_SIZE-1));			port->xmit_cnt += c;			restore_flags(flags);			buf += c;			count -= c;			total += c;		}	}	if (port->xmit_cnt && 	    !tty->stopped && 	    !tty->hw_stopped &&	    !(port->flags & GS_TX_INTEN)) {		port->flags |= GS_TX_INTEN;		port->rd->enable_tx_interrupts (port);	}	func_exit ();	return total;}#endifint gs_write_room(struct tty_struct * tty){	struct gs_port *port = tty->driver_data;	int ret;	func_enter ();	ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;	if (ret < 0)		ret = 0;	func_exit ();	return ret;}int gs_chars_in_buffer(struct tty_struct *tty){	struct gs_port *port = tty->driver_data;	func_enter ();	func_exit ();	return port->xmit_cnt;}int gs_real_chars_in_buffer(struct tty_struct *tty){	struct gs_port *port;	func_enter ();	if (!tty) return 0;	port = tty->driver_data;	if (!port->rd) return 0;	if (!port->rd->chars_in_buffer) return 0;	func_exit ();	return port->xmit_cnt + port->rd->chars_in_buffer (port);}static int gs_wait_tx_flushed (void * ptr, int timeout) {	struct gs_port *port = ptr;	long end_jiffies;	int jiffies_to_transmit, charsleft = 0, rv = 0;	int to, rcib;	func_enter();	gs_dprintk (GS_DEBUG_FLUSH, "port=%p.\n", port);	if (port) {		gs_dprintk (GS_DEBUG_FLUSH, "xmit_cnt=%x, xmit_buf=%p, tty=%p.\n", 		port->xmit_cnt, port->xmit_buf, port->tty);	}	if (!port || port->xmit_cnt < 0 || !port->xmit_buf) {		gs_dprintk (GS_DEBUG_FLUSH, "ERROR: !port, !port->xmit_buf or prot->xmit_cnt < 0.\n");		func_exit();		return -EINVAL;  /* This is an error which we don't know how to handle. */	}	rcib = gs_real_chars_in_buffer(port->tty);	if(rcib <= 0) {		gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n");		func_exit();		return rv;	}	/* stop trying: now + twice the time it would normally take +  seconds */	end_jiffies  = jiffies; 	if (timeout !=  MAX_SCHEDULE_TIMEOUT)		end_jiffies += port->baud?(2 * rcib * 10 * HZ / port->baud):0;	end_jiffies += timeout;	gs_dprintk (GS_DEBUG_FLUSH, "now=%lx, end=%lx (%ld).\n", 		    jiffies, end_jiffies, end_jiffies-jiffies); 	to = 100;	/* the expression is actually jiffies < end_jiffies, but that won't	   work around the wraparound. Tricky eh? */	while (to-- &&	       (charsleft = gs_real_chars_in_buffer (port->tty)) &&	        time_after (end_jiffies, jiffies)) {		/* Units check: 		   chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies!		   check! */		charsleft += 16; /* Allow 16 chars more to be transmitted ... */		jiffies_to_transmit = port->baud?(1 + charsleft * 10 * HZ / port->baud):0;		/*                                ^^^ Round up.... */		if (jiffies_to_transmit <= 0) jiffies_to_transmit = 1;		gs_dprintk (GS_DEBUG_FLUSH, "Expect to finish in %d jiffies "			    "(%d chars).\n", jiffies_to_transmit, charsleft); 		set_current_state (TASK_INTERRUPTIBLE);		schedule_timeout(jiffies_to_transmit);		if (signal_pending (current)) {			gs_dprintk (GS_DEBUG_FLUSH, "Signal pending. Bombing out: "); 			rv = -EINTR;			break;		}	}	gs_dprintk (GS_DEBUG_FLUSH, "charsleft = %d.\n", charsleft); 	set_current_state (TASK_RUNNING);	func_exit();	return rv;}void gs_flush_buffer(struct tty_struct *tty){	struct gs_port *port;	unsigned long flags;	func_enter ();	if (!tty) return;	port = tty->driver_data;	if (!port) return;	/* XXX Would the write semaphore do? */	save_flags(flags); cli();	port->xmit_cnt = port->xmit_head = port->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);	func_exit ();}void gs_flush_chars(struct tty_struct * tty){	struct gs_port *port;	func_enter ();	if (!tty) return;	port = tty->driver_data;	if (!port) return;	if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||	    !port->xmit_buf) {		func_exit ();		return;	}	/* Beats me -- REW */	port->flags |= GS_TX_INTEN;	port->rd->enable_tx_interrupts (port);	func_exit ();}void gs_stop(struct tty_struct * tty){	struct gs_port *port;	func_enter ();	if (!tty) return;	port = tty->driver_data;	if (!port) return;	if (port->xmit_cnt && 	    port->xmit_buf && 	    (port->flags & GS_TX_INTEN) ) {		port->flags &= ~GS_TX_INTEN;		port->rd->disable_tx_interrupts (port);	}	func_exit ();}void gs_start(struct tty_struct * tty){	struct gs_port *port;	if (!tty) return;	port = tty->driver_data;	if (!port) return;	if (port->xmit_cnt && 	    port->xmit_buf && 	    !(port->flags & GS_TX_INTEN) ) {		port->flags |= GS_TX_INTEN;		port->rd->enable_tx_interrupts (port);	}	func_exit ();}void gs_shutdown_port (struct gs_port *port){	long flags;	func_enter();		if (!port) return;		if (!(port->flags & ASYNC_INITIALIZED))		return;	save_flags (flags);	cli ();	if (port->xmit_buf) {		free_page((unsigned long) port->xmit_buf);		port->xmit_buf = 0;	}	if (port->tty)		set_bit(TTY_IO_ERROR, &port->tty->flags);	port->rd->shutdown_port (port);	port->flags &= ~ASYNC_INITIALIZED;	restore_flags (flags);	func_exit();}void gs_hangup(struct tty_struct *tty){

⌨️ 快捷键说明

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