/*
* 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 char * tmp_buf;
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. //
} <