static ssize_t n_tty_read()

in n_tty.c [2028:2196]


static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
			  unsigned char *kbuf, size_t nr,
			  void **cookie, unsigned long offset)
{
	struct n_tty_data *ldata = tty->disc_data;
	unsigned char *kb = kbuf;
	DEFINE_WAIT_FUNC(wait, woken_wake_function);
	int c;
	int minimum, time;
	ssize_t retval = 0;
	long timeout;
	bool packet;
	size_t tail;

	/*
	 * Is this a continuation of a read started earler?
	 *
	 * If so, we still hold the atomic_read_lock and the
	 * termios_rwsem, and can just continue to copy data.
	 */
	if (*cookie) {
		if (ldata->icanon && !L_EXTPROC(tty)) {
			if (canon_copy_from_read_buf(tty, &kb, &nr))
				return kb - kbuf;
		} else {
			if (copy_from_read_buf(tty, &kb, &nr))
				return kb - kbuf;
		}

		/* No more data - release locks and stop retries */
		n_tty_kick_worker(tty);
		n_tty_check_unthrottle(tty);
		up_read(&tty->termios_rwsem);
		mutex_unlock(&ldata->atomic_read_lock);
		*cookie = NULL;
		return kb - kbuf;
	}

	c = job_control(tty, file);
	if (c < 0)
		return c;

	/*
	 *	Internal serialization of reads.
	 */
	if (file->f_flags & O_NONBLOCK) {
		if (!mutex_trylock(&ldata->atomic_read_lock))
			return -EAGAIN;
	} else {
		if (mutex_lock_interruptible(&ldata->atomic_read_lock))
			return -ERESTARTSYS;
	}

	down_read(&tty->termios_rwsem);

	minimum = time = 0;
	timeout = MAX_SCHEDULE_TIMEOUT;
	if (!ldata->icanon) {
		minimum = MIN_CHAR(tty);
		if (minimum) {
			time = (HZ / 10) * TIME_CHAR(tty);
		} else {
			timeout = (HZ / 10) * TIME_CHAR(tty);
			minimum = 1;
		}
	}

	packet = tty->ctrl.packet;
	tail = ldata->read_tail;

	add_wait_queue(&tty->read_wait, &wait);
	while (nr) {
		/* First test for status change. */
		if (packet && tty->link->ctrl.pktstatus) {
			unsigned char cs;
			if (kb != kbuf)
				break;
			spin_lock_irq(&tty->link->ctrl.lock);
			cs = tty->link->ctrl.pktstatus;
			tty->link->ctrl.pktstatus = 0;
			spin_unlock_irq(&tty->link->ctrl.lock);
			*kb++ = cs;
			nr--;
			break;
		}

		if (!input_available_p(tty, 0)) {
			up_read(&tty->termios_rwsem);
			tty_buffer_flush_work(tty->port);
			down_read(&tty->termios_rwsem);
			if (!input_available_p(tty, 0)) {
				if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
					retval = -EIO;
					break;
				}
				if (tty_hung_up_p(file))
					break;
				/*
				 * Abort readers for ttys which never actually
				 * get hung up.  See __tty_hangup().
				 */
				if (test_bit(TTY_HUPPING, &tty->flags))
					break;
				if (!timeout)
					break;
				if (tty_io_nonblock(tty, file)) {
					retval = -EAGAIN;
					break;
				}
				if (signal_pending(current)) {
					retval = -ERESTARTSYS;
					break;
				}
				up_read(&tty->termios_rwsem);

				timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
						timeout);

				down_read(&tty->termios_rwsem);
				continue;
			}
		}

		if (ldata->icanon && !L_EXTPROC(tty)) {
			if (canon_copy_from_read_buf(tty, &kb, &nr))
				goto more_to_be_read;
		} else {
			/* Deal with packet mode. */
			if (packet && kb == kbuf) {
				*kb++ = TIOCPKT_DATA;
				nr--;
			}

			/*
			 * Copy data, and if there is more to be had
			 * and we have nothing more to wait for, then
			 * let's mark us for retries.
			 *
			 * NOTE! We return here with both the termios_sem
			 * and atomic_read_lock still held, the retries
			 * will release them when done.
			 */
			if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) {
more_to_be_read:
				remove_wait_queue(&tty->read_wait, &wait);
				*cookie = cookie;
				return kb - kbuf;
			}
		}

		n_tty_check_unthrottle(tty);

		if (kb - kbuf >= minimum)
			break;
		if (time)
			timeout = time;
	}
	if (tail != ldata->read_tail)
		n_tty_kick_worker(tty);
	up_read(&tty->termios_rwsem);

	remove_wait_queue(&tty->read_wait, &wait);
	mutex_unlock(&ldata->atomic_read_lock);

	if (kb - kbuf)
		retval = kb - kbuf;

	return retval;
}