/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/
#include "linux/irqreturn.h"
#include "linux/kd.h"
#include "linux/sched.h"
#include "linux/slab.h"
#include "chan.h"
#include "irq_kern.h"
#include "irq_user.h"
#include "kern_util.h"
#include "os.h"
#define LINE_BUFSIZE 4096
static irqreturn_t line_interrupt(int irq, void *data)
{
struct chan *chan = data;
struct line *line = chan->line;
if (line)
chan_interrupt(line, line->tty, irq);
return IRQ_HANDLED;
}
/*
* Returns the free space inside the ring buffer of this line.
*
* Should be called while holding line->lock (this does not modify data).
*/
static int write_room(struct line *line)
{
int n;
if (line->buffer == NULL)
return LINE_BUFSIZE - 1;
/* This is for the case where the buffer is wrapped! */
n = line->head - line->tail;
if (n <= 0)
n += LINE_BUFSIZE; /* The other case */
return n - 1;
}
int line_write_room(struct tty_struct *tty)
{
struct line *line = tty->driver_data;
unsigned long flags;
int room;
spin_lock_irqsave(&line->lock, flags);
room = write_room(line);
spin_unlock_irqrestore(&line->lock, flags);
return room;
}
int line_chars_in_buffer(struct tty_struct *tty)
{
struct line *line = tty->driver_data;
unsigned long flags;
int ret;
spin_lock_irqsave(&line->lock, flags);
/* write_room subtracts 1 for the needed NULL, so we readd it.*/
ret = LINE_BUFSIZE - (write_room(line) + 1);
spin_unlock_irqrestore(&line->lock, flags);
return ret;
}
/*
* This copies the content of buf into the circular buffer associated with
* this line.
* The return value is the number of characters actually copied, i.e. the ones
* for which there was space: this function is not supposed to ever flush out
* the circular buffer.
*
* Must be called while holding line->lock!
*/
static int buffer_data(struct line *line, const char *buf, int len)
{
int end, room;
if (line->buffer == NULL) {
line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC);
if (line->buffer == NULL) {
printk(KERN_ERR "buffer_data - atomic allocation "
"failed\n");
return 0;
}
line->head = line->buffer;
line->tail = line->buffer;
}
room = write_room(line);
len = (len > room) ? room : len;
end = line->buffer + LINE_BUFSIZE - line->tail;
if (len < end) {
memcpy(line->tail, buf, len);
line->tail += len;
}
else {
/* The circular buffer is wrapping */
memcpy(line->tail, buf, end);
buf += end;
memcpy(line->buffer, buf, len - end);
line->tail = line->buffer + len - end;
}
return len;
}
/*
* Flushes the ring buffer to the output channels. That is, write_chan is
* called, passing it line->head as buffer, and an appropriate count.
*
* On exit, returns 1 when the buffer is empty,
* 0 when the buffer is not empty on exit,
* and -errno when an error occurred.
*
* Must be called while holding line->lock!*/
static int flush_buffer(struct line *line)
{
int n, count;
if ((line->buffer == NULL) || (line->head == line->tail))
return 1;
if (line->tail < line->head) {
/* line->buffer + LINE_BUFSIZE is the end of the buffer! */
count = line->buffer + LINE_BUFSIZE - line->head;
n = write_chan(line->chan_out, line->head, count,
line->driver->write_irq);
if (n < 0)
return n;
if (n == count) {
/*
* We have flushed from ->head to buffer end, now we
* must flush only from the beginning to ->tail.
*/
line->head = line->buffer;
} else {
line->head += n;
return 0;
}
}
count = line->tail - line->head;
n = write_chan(line->chan_out, line->head, count,
line->driver->write_irq);
if (n < 0)
return n;
line->head += n;
return line->head == line->tail;
}
void line_flush_buffer(struct tty_struct *tty)
{
struct line *line = tty->driver_data;
unsigned long flags;
spin_lock_irqsave(&line->lock, flags);
flush_buffer(line);
spin_unlock_irqrestore(&line->lock, flags);
}
/*
* We map both ->flush_chars and ->put_char (which go in pair) onto
* ->flush_buffer and ->write. Hope it's not that bad.
*/
void line_flush_chars(struct tty_struct *tty)
{
line_flush_buffer(tty);
}
int line_put_char(struct tty_struct *tty, unsigned char ch)
{