966 lines · 23.0 KB · C
RawDownload
/* *  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 NEW_WRITE_LOCKING 1#if NEW_WRITE_LOCKING#define DECL      /* Nothing */#define LOCKIT    mutex_lock(& port->port_write_mutex);#define RELEASEIT mutex_unlock(&port->port_write_mutex);#else#define DECL      unsigned long flags;#define LOCKIT    save_flags (flags);cli ()#define RELEASEIT restore_flags (flags)#endif #define RS_EVENT_WRITE_WAKEUP	1 module_param(gs_debug, int, 0644);  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,                     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;}#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,                    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)		return -EIO; 	local_save_flags(flags);	while (1) {		cli();		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) {			local_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;		local_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;} #endif   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);

Lines 1–500 of 966.