generic_serial.c

来自「linux 内核源代码」· C语言 代码 · 共 853 行 · 第 1/2 页

C
853
字号
/* *  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 <linux/interrupt.h>#include <linux/tty_flip.h>#include <linux/delay.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#define DEBUG 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 %s\n", __FUNCTION__)#define func_exit()  gs_dprintk (GS_DEBUG_FLOW, "gs: exit  %s\n", __FUNCTION__)#define RS_EVENT_WRITE_WAKEUP	1module_param(gs_debug, int, 0644);void gs_put_char(struct tty_struct * tty, unsigned char ch){	struct gs_port *port;	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! */	mutex_lock(& port->port_write_mutex);	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {		/* Sorry, buffer is full, drop character. Update statistics???? -- REW */		mutex_unlock(&port->port_write_mutex);		return;	}	port->xmit_buf[port->xmit_head++] = ch;	port->xmit_head &= SERIAL_XMIT_SIZE - 1;	port->xmit_cnt++;  /* Characters in buffer */	mutex_unlock(&port->port_write_mutex);	func_exit ();}/*> 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,                     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_data;	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 */	mutex_lock(& port->port_write_mutex);	while (1) {		c = count; 		/* This is safe because we "OWN" the "head". Noone else can 		   change the "head": we own the port_write_mutex. */		/* 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;		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;	}	mutex_unlock(& port->port_write_mutex);	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;}int 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;}static 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, unsigned long timeout) {	struct gs_port *port = ptr;	unsigned long end_jiffies;	int jiffies_to_transmit, charsleft = 0, rv = 0;	int 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 */	if (timeout == 0) timeout = MAX_SCHEDULE_TIMEOUT;	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); 	/* the expression is actually jiffies < end_jiffies, but that won't	   work around the wraparound. Tricky eh? */	while ((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); 		msleep_interruptible(jiffies_to_msecs(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? */	spin_lock_irqsave (&port->driver_lock, flags);	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;	spin_unlock_irqrestore (&port->driver_lock, flags);	tty_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 ();}static void gs_shutdown_port (struct gs_port *port){	unsigned long flags;	func_enter();		if (!port) return;		if (!(port->flags & ASYNC_INITIALIZED))		return;	spin_lock_irqsave(&port->driver_lock, flags);	if (port->xmit_buf) {		free_page((unsigned long) port->xmit_buf);		port->xmit_buf = NULL;	}	if (port->tty)		set_bit(TTY_IO_ERROR, &port->tty->flags);	port->rd->shutdown_port (port);	port->flags &= ~ASYNC_INITIALIZED;	spin_unlock_irqrestore(&port->driver_lock, flags);	func_exit();}void gs_hangup(struct tty_struct *tty){	struct gs_port   *port;	func_enter ();	if (!tty) return;	port = tty->driver_data;	tty = port->tty;	if (!tty) 		return;	gs_shutdown_port (port);	port->flags &= ~(ASYNC_NORMAL_ACTIVE|GS_ACTIVE);	port->tty = NULL;	port->count = 0;	wake_up_interruptible(&port->open_wait);	func_exit ();}int gs_block_til_ready(void *port_, struct file * filp){	struct gs_port *port = port_;	DECLARE_WAITQUEUE(wait, current);	int    retval;	int    do_clocal = 0;	int    CD;	struct tty_struct *tty;	unsigned long flags;	func_enter ();	if (!port) return 0;	tty = port->tty;

⌨️ 快捷键说明

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