line.c

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

C
894
字号
/* * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */#include "linux/irqreturn.h"#include "linux/kd.h"#include "chan_kern.h"#include "irq_kern.h"#include "irq_user.h"#include "os.h"#define LINE_BUFSIZE 4096static irqreturn_t line_interrupt(int irq, void *data){	struct chan *chan = data;	struct line *line = chan->line;	struct tty_struct *tty = line->tty;	if (line)		chan_interrupt(&line->chan_list, &line->task, tty, irq);	return IRQ_HANDLED;}static void line_timer_cb(struct work_struct *work){	struct line *line = container_of(work, struct line, task.work);	if (!line->throttled)		chan_interrupt(&line->chan_list, &line->task, line->tty,			       line->driver->read_irq);}/* * 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 + n; /* 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;	if (tty->stopped)		return 0;	spin_lock_irqsave(&line->lock, flags);	room = write_room(line);	spin_unlock_irqrestore(&line->lock, flags);	/*XXX: Warning to remove */	if (0 == room)		printk(KERN_DEBUG "%s: %s: no room left in buffer\n",		       __FUNCTION__,tty->name);	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_list, 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_list, 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;	int err;	/*XXX: copied from line_write, verify if it is correct!*/	if (tty->stopped)		return;	spin_lock_irqsave(&line->lock, flags);	err = 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);}void line_put_char(struct tty_struct *tty, unsigned char ch){	line_write(tty, &ch, sizeof(ch));}int line_write(struct tty_struct *tty, const unsigned char *buf, int len){	struct line *line = tty->driver_data;	unsigned long flags;	int n, ret = 0;	if (tty->stopped)		return 0;	spin_lock_irqsave(&line->lock, flags);	if (line->head != line->tail)		ret = buffer_data(line, buf, len);	else {		n = write_chan(&line->chan_list, buf, len,			       line->driver->write_irq);		if (n < 0) {			ret = n;			goto out_up;		}		len -= n;		ret += n;		if (len > 0)			ret += buffer_data(line, buf + n, len);	}out_up:	spin_unlock_irqrestore(&line->lock, flags);	return ret;}void line_set_termios(struct tty_struct *tty, struct ktermios * old){	/* nothing */}static const struct {	int  cmd;	char *level;	char *name;} tty_ioctls[] = {	/* don't print these, they flood the log ... */	{ TCGETS,      NULL,       "TCGETS"      },	{ TCSETS,      NULL,       "TCSETS"      },	{ TCSETSW,     NULL,       "TCSETSW"     },	{ TCFLSH,      NULL,       "TCFLSH"      },	{ TCSBRK,      NULL,       "TCSBRK"      },	/* general tty stuff */	{ TCSETSF,     KERN_DEBUG, "TCSETSF"     },	{ TCGETA,      KERN_DEBUG, "TCGETA"      },	{ TIOCMGET,    KERN_DEBUG, "TIOCMGET"    },	{ TCSBRKP,     KERN_DEBUG, "TCSBRKP"     },	{ TIOCMSET,    KERN_DEBUG, "TIOCMSET"    },	/* linux-specific ones */	{ TIOCLINUX,   KERN_INFO,  "TIOCLINUX"   },	{ KDGKBMODE,   KERN_INFO,  "KDGKBMODE"   },	{ KDGKBTYPE,   KERN_INFO,  "KDGKBTYPE"   },	{ KDSIGACCEPT, KERN_INFO,  "KDSIGACCEPT" },};int line_ioctl(struct tty_struct *tty, struct file * file,	       unsigned int cmd, unsigned long arg){	int ret;	int i;	ret = 0;	switch(cmd) {#ifdef TIOCGETP	case TIOCGETP:	case TIOCSETP:	case TIOCSETN:#endif#ifdef TIOCGETC	case TIOCGETC:	case TIOCSETC:#endif#ifdef TIOCGLTC	case TIOCGLTC:	case TIOCSLTC:#endif	case TCGETS:	case TCSETSF:	case TCSETSW:	case TCSETS:	case TCGETA:	case TCSETAF:	case TCSETAW:	case TCSETA:	case TCXONC:	case TCFLSH:	case TIOCOUTQ:	case TIOCINQ:	case TIOCGLCKTRMIOS:	case TIOCSLCKTRMIOS:	case TIOCPKT:	case TIOCGSOFTCAR:	case TIOCSSOFTCAR:		return -ENOIOCTLCMD;#if 0	case TCwhatever:		/* do something */		break;#endif	default:		for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++)			if (cmd == tty_ioctls[i].cmd)				break;		if (i == ARRAY_SIZE(tty_ioctls)) {			printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n",			       __FUNCTION__, tty->name, cmd);		}		ret = -ENOIOCTLCMD;		break;	}	return ret;}void line_throttle(struct tty_struct *tty){	struct line *line = tty->driver_data;	deactivate_chan(&line->chan_list, line->driver->read_irq);	line->throttled = 1;}void line_unthrottle(struct tty_struct *tty){	struct line *line = tty->driver_data;	line->throttled = 0;	chan_interrupt(&line->chan_list, &line->task, tty,		       line->driver->read_irq);	/*	 * Maybe there is enough stuff pending that calling the interrupt	 * throttles us again.  In this case, line->throttled will be 1	 * again and we shouldn't turn the interrupt back on.	 */	if (!line->throttled)		reactivate_chan(&line->chan_list, line->driver->read_irq);}static irqreturn_t line_write_interrupt(int irq, void *data){	struct chan *chan = data;	struct line *line = chan->line;	struct tty_struct *tty = line->tty;	int err;	/*	 * Interrupts are disabled here because we registered the interrupt with	 * IRQF_DISABLED (see line_setup_irq).	 */	spin_lock(&line->lock);	err = flush_buffer(line);	if (err == 0) {		return IRQ_NONE;	} else if (err < 0) {		line->head = line->buffer;		line->tail = line->buffer;	}	spin_unlock(&line->lock);	if (tty == NULL)		return IRQ_NONE;	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&	   (tty->ldisc.write_wakeup != NULL))		(tty->ldisc.write_wakeup)(tty);	/*	 * BLOCKING mode	 * In blocking mode, everything sleeps on tty->write_wait.	 * Sleeping in the console driver would break non-blocking	 * writes.	 */	if (waitqueue_active(&tty->write_wait))		wake_up_interruptible(&tty->write_wait);	return IRQ_HANDLED;}int line_setup_irq(int fd, int input, int output, struct line *line, void *data){	const struct line_driver *driver = line->driver;	int err = 0, flags = IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM;	if (input)		err = um_request_irq(driver->read_irq, fd, IRQ_READ,				       line_interrupt, flags,				       driver->read_irq_name, data);	if (err)		return err;	if (output)		err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,					line_write_interrupt, flags,					driver->write_irq_name, data);	line->have_irq = 1;	return err;}/* * Normally, a driver like this can rely mostly on the tty layer * locking, particularly when it comes to the driver structure. * However, in this case, mconsole requests can come in "from the * side", and race with opens and closes. * * mconsole config requests will want to be sure the device isn't in * use, and get_config, open, and close will want a stable * configuration.  The checking and modification of the configuration * is done under a spinlock.  Checking whether the device is in use is * line->tty->count > 1, also under the spinlock. * * tty->count serves to decide whether the device should be enabled or * disabled on the host.  If it's equal to 1, then we are doing the * first open or last close.  Otherwise, open and close just return. */int line_open(struct line *lines, struct tty_struct *tty){	struct line *line = &lines[tty->index];	int err = -ENODEV;	spin_lock(&line->count_lock);	if (!line->valid)		goto out_unlock;	err = 0;	if (tty->count > 1)		goto out_unlock;	spin_unlock(&line->count_lock);	tty->driver_data = line;	line->tty = tty;

⌨️ 快捷键说明

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