Beispiel #1
0
void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
{
	/*
	 * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
	 * kill any delayed work. As this is the final close it does not
	 * race with the set_ldisc code path.
	 */

	tty_ldisc_halt(tty);
	flush_scheduled_work();

	mutex_lock(&tty->ldisc_mutex);
	/*
	 * Now kill off the ldisc
	 */
	tty_ldisc_close(tty, tty->ldisc);
	tty_ldisc_put(tty->ldisc);
	/* Force an oops if we mess this up */
	tty->ldisc = NULL;

	/* Ensure the next open requests the N_TTY ldisc */
	tty_set_termios_ldisc(tty, N_TTY);
	mutex_unlock(&tty->ldisc_mutex);

	/* This will need doing differently if we need to lock */
	if (o_tty)
		tty_ldisc_release(o_tty, NULL);

	/* And the memory resources remaining (buffers, termios) will be
	   disposed of when the kref hits zero */
}
static void tty_ldisc_kill(struct tty_struct *tty)
{
	/*
	 * Now kill off the ldisc
	 */
	tty_ldisc_close(tty, tty->ldisc);
	tty_ldisc_put(tty->ldisc);
	/* Force an oops if we mess this up */
	tty->ldisc = NULL;

	/* Ensure the next open requests the N_TTY ldisc */
	tty_set_termios_ldisc(tty, N_TTY);
}
Beispiel #3
0
static void tty_ldisc_reinit(struct tty_struct *tty)
{
	struct tty_ldisc *ld;

	tty_ldisc_close(tty, tty->ldisc);
	tty_ldisc_put(tty->ldisc);
	tty->ldisc = NULL;
	/*
	 *	Switch the line discipline back
	 */
	ld = tty_ldisc_get(N_TTY);
	BUG_ON(IS_ERR(ld));
	tty_ldisc_assign(tty, ld);
	tty_set_termios_ldisc(tty, N_TTY);
}
static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
{
	struct tty_ldisc *ld = tty_ldisc_get(tty, ldisc);

	if (IS_ERR(ld))
		return -1;

	tty_ldisc_close(tty, tty->ldisc);
	tty_ldisc_put(tty->ldisc);
	/*
	 *	Switch the line discipline back
	 */
	tty->ldisc = ld;
	tty_set_termios_ldisc(tty, ldisc);

	return 0;
}
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
	struct tty_ldisc *ld = tty->ldisc;
	int retval;

	retval = tty_ldisc_open(tty, ld);
	if (retval)
		return retval;

	if (o_tty) {
		retval = tty_ldisc_open(o_tty, o_tty->ldisc);
		if (retval) {
			tty_ldisc_close(tty, ld);
			return retval;
		}
	}
	return 0;
}
Beispiel #6
0
/**
 *	tty_ldisc_change	-	change line discipline
 *	@tty: tty to change
 *	@disc: new line discipine number
 *
 *	Change the line discipline of the tty. The tty must have an old
 *	ldisc attached to, we must handle it properly. Sandix implement
 *	it quite simple, but please look at linux, see how complex it is!
 *	XXX tty shoud be locked
 */
int tty_ldisc_change(struct tty_struct *tty, int disc)
{
	int ret;
	struct tty_ldisc *old_ldisc, *new_ldisc;

	BUG_ON(!tty->ldisc);
	if (tty->ldisc->ops->num == disc)
		return 0;

	tty_lock(tty);
	new_ldisc = tty_ldisc_get(tty, disc);
	if (IS_ERR(new_ldisc)) {
		tty_unlock(tty);
		return PTR_ERR(new_ldisc);
	}

	old_ldisc = tty->ldisc;

	/* Close the old one */
	tty_ldisc_close(tty, old_ldisc);

	/* Set up the new one */
	tty->ldisc = new_ldisc;
	tty_set_termios_ldisc(tty, disc);

	ret = tty_ldisc_open(tty, new_ldisc);
	if (ret) {
		/* Back to the old one or N_TTY */
		tty_ldisc_put(new_ldisc);
		tty_ldisc_restore(tty, old_ldisc);
	}

	/*
	 * At this point we hold a reference to the new ldisc and a
	 * reference to the old ldisc, or we hold two references to
	 * the old ldisc (if it was restored as part of error cleanup
	 * above). In either case, releasing a single reference from
	 * the old ldisc is correct.
	 */
	tty_ldisc_put(old_ldisc);

	tty_unlock(tty);
	return ret;
}
Beispiel #7
0
static void tty_ldisc_kill(struct tty_struct *tty)
{
	/* There cannot be users from userspace now. But there still might be
	 * drivers holding a reference via tty_ldisc_ref. Do not steal them the
	 * ldisc until they are done. */
	tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT);

	mutex_lock(&tty->ldisc_mutex);
	/*
	 * Now kill off the ldisc
	 */
	tty_ldisc_close(tty, tty->ldisc);
	tty_ldisc_put(tty->ldisc);
	/* Force an oops if we mess this up */
	tty->ldisc = NULL;

	/* Ensure the next open requests the N_TTY ldisc */
	tty_set_termios_ldisc(tty, N_TTY);
	mutex_unlock(&tty->ldisc_mutex);
}
static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
{
	struct tty_ldisc *ld = tty_ldisc_get(ldisc);

	if (IS_ERR(ld))
		return -1;

	WARN_ON_ONCE(tty_ldisc_wait_idle(tty));

	tty_ldisc_close(tty, tty->ldisc);
	tty_ldisc_put(tty->ldisc);
	tty->ldisc = NULL;
	/*
	 *	Switch the line discipline back
	 */
	tty_ldisc_assign(tty, ld);
	tty_set_termios_ldisc(tty, ldisc);

	return 0;
}
Beispiel #9
0
static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
#else
//static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
#endif
//20110402 [email protected] [LS855] handle exception for tty daemon [END]
{

//20110402 [email protected] [LS855] handle exception for tty daemon [START]
#if 1
	struct tty_ldisc *ld = tty_ldisc_get(ldisc); 
#else
//	struct tty_ldisc *ld;
#endif
//20110402 [email protected] [LS855] handle exception for tty daemon [END]

	 if (IS_ERR(ld)) 
		 return -1; 

	tty_ldisc_close(tty, tty->ldisc);
	tty_ldisc_put(tty->ldisc);
	tty->ldisc = NULL;
	/*
	 *	Switch the line discipline back
	 */
//20110402 [email protected] [LS855] handle exception for tty daemon [START]	 
#if 0	 
	ld = tty_ldisc_get(ldisc);
	BUG_ON(IS_ERR(ld));
#endif
//20110402 [email protected] [LS855] handle exception for tty daemon [END]


	tty_ldisc_assign(tty, ld);
	tty_set_termios_ldisc(tty, ldisc);

	return 0;  //20110402 [email protected] [LS855] handle exception for tty daemon
}
int tty_set_ldisc(struct tty_struct *tty, int ldisc)
{
	int retval;
	struct tty_ldisc *old_ldisc, *new_ldisc;
	struct tty_struct *o_tty = tty->link;

	new_ldisc = tty_ldisc_get(tty, ldisc);
	if (IS_ERR(new_ldisc))
		return PTR_ERR(new_ldisc);

	retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ);
	if (retval) {
		tty_ldisc_put(new_ldisc);
		return retval;
	}

	/*
	 *	Check the no-op case
	 */

	if (tty->ldisc->ops->num == ldisc) {
		tty_ldisc_enable_pair(tty, o_tty);
		tty_ldisc_put(new_ldisc);
		return 0;
	}

	old_ldisc = tty->ldisc;
	tty_lock(tty);

	if (test_bit(TTY_HUPPING, &tty->flags) ||
	    test_bit(TTY_HUPPED, &tty->flags)) {
		/* We were raced by the hangup method. It will have stomped
		   the ldisc data and closed the ldisc down */
		tty_ldisc_enable_pair(tty, o_tty);
		tty_ldisc_put(new_ldisc);
		tty_unlock(tty);
		return -EIO;
	}

	/* Shutdown the old discipline. */
	tty_ldisc_close(tty, old_ldisc);

	/* Now set up the new line discipline. */
	tty->ldisc = new_ldisc;
	tty_set_termios_ldisc(tty, ldisc);

	retval = tty_ldisc_open(tty, new_ldisc);
	if (retval < 0) {
		/* Back to the old one or N_TTY if we can't */
		tty_ldisc_put(new_ldisc);
		tty_ldisc_restore(tty, old_ldisc);
	}

	if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc)
		tty->ops->set_ldisc(tty);

	/* At this point we hold a reference to the new ldisc and a
	   reference to the old ldisc, or we hold two references to
	   the old ldisc (if it was restored as part of error cleanup
	   above). In either case, releasing a single reference from
	   the old ldisc is correct. */

	tty_ldisc_put(old_ldisc);

	/*
	 *	Allow ldisc referencing to occur again
	 */
	tty_ldisc_enable_pair(tty, o_tty);

	/* Restart the work queue in case no characters kick it off. Safe if
	   already running */
	schedule_work(&tty->port->buf.work);
	if (o_tty)
		schedule_work(&o_tty->port->buf.work);

	tty_unlock(tty);
	return retval;
}
Beispiel #11
0
int tty_set_ldisc(struct tty_struct *tty, int ldisc)
{
	int retval;
	struct tty_ldisc *o_ldisc, *new_ldisc;
	int work, o_work = 0;
	struct tty_struct *o_tty;

	new_ldisc = tty_ldisc_get(ldisc);
	if (IS_ERR(new_ldisc))
		return PTR_ERR(new_ldisc);

	/*
	 *	We need to look at the tty locking here for pty/tty pairs
	 *	when both sides try to change in parallel.
	 */

	o_tty = tty->link;	/* o_tty is the pty side or NULL */


	/*
	 *	Check the no-op case
	 */

	if (tty->ldisc->ops->num == ldisc) {
		tty_ldisc_put(new_ldisc);
		return 0;
	}

	/*
	 *	Problem: What do we do if this blocks ?
	 *	We could deadlock here
	 */

	tty_wait_until_sent(tty, 0);

	mutex_lock(&tty->ldisc_mutex);

	/*
	 *	We could be midstream of another ldisc change which has
	 *	dropped the lock during processing. If so we need to wait.
	 */

	while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
		mutex_unlock(&tty->ldisc_mutex);
		wait_event(tty_ldisc_wait,
			test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
		mutex_lock(&tty->ldisc_mutex);
	}
	set_bit(TTY_LDISC_CHANGING, &tty->flags);

	/*
	 *	No more input please, we are switching. The new ldisc
	 *	will update this value in the ldisc open function
	 */

	tty->receive_room = 0;

	o_ldisc = tty->ldisc;
	/*
	 *	Make sure we don't change while someone holds a
	 *	reference to the line discipline. The TTY_LDISC bit
	 *	prevents anyone taking a reference once it is clear.
	 *	We need the lock to avoid racing reference takers.
	 *
	 *	We must clear the TTY_LDISC bit here to avoid a livelock
	 *	with a userspace app continually trying to use the tty in
	 *	parallel to the change and re-referencing the tty.
	 */

	work = tty_ldisc_halt(tty);
	if (o_tty)
		o_work = tty_ldisc_halt(o_tty);

	/*
	 * Wait for ->hangup_work and ->buf.work handlers to terminate.
	 * We must drop the mutex here in case a hangup is also in process.
	 */

	mutex_unlock(&tty->ldisc_mutex);

	flush_scheduled_work();

	mutex_lock(&tty->ldisc_mutex);
	if (test_bit(TTY_HUPPED, &tty->flags)) {
		/* We were raced by the hangup method. It will have stomped
		   the ldisc data and closed the ldisc down */
		clear_bit(TTY_LDISC_CHANGING, &tty->flags);
		mutex_unlock(&tty->ldisc_mutex);
		tty_ldisc_put(new_ldisc);
		return -EIO;
	}

	/* Shutdown the current discipline. */
	tty_ldisc_close(tty, o_ldisc);

	/* Now set up the new line discipline. */
	tty_ldisc_assign(tty, new_ldisc);
	tty_set_termios_ldisc(tty, ldisc);

	retval = tty_ldisc_open(tty, new_ldisc);
	if (retval < 0) {
		/* Back to the old one or N_TTY if we can't */
		tty_ldisc_put(new_ldisc);
		tty_ldisc_restore(tty, o_ldisc);
	}

	/* At this point we hold a reference to the new ldisc and a
	   a reference to the old ldisc. If we ended up flipping back
	   to the existing ldisc we have two references to it */

	if (tty->ldisc->ops->num != o_ldisc->ops->num && tty->ops->set_ldisc)
		tty->ops->set_ldisc(tty);

	tty_ldisc_put(o_ldisc);

	/*
	 *	Allow ldisc referencing to occur again
	 */

	tty_ldisc_enable(tty);
	if (o_tty)
		tty_ldisc_enable(o_tty);

	/* Restart the work queue in case no characters kick it off. Safe if
	   already running */
	if (work)
		schedule_delayed_work(&tty->buf.work, 1);
	if (o_work)
		schedule_delayed_work(&o_tty->buf.work, 1);
	mutex_unlock(&tty->ldisc_mutex);
	return retval;
}
Beispiel #12
0
int tty_set_ldisc(struct tty_struct *tty, int ldisc)
{
	int retval;
	struct tty_ldisc *o_ldisc, *new_ldisc;
	struct tty_struct *o_tty = tty->link;

	new_ldisc = tty_ldisc_get(tty, ldisc);
	if (IS_ERR(new_ldisc))
		return PTR_ERR(new_ldisc);

	retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ);
	if (retval) {
		tty_ldisc_put(new_ldisc);
		return retval;
	}

	/*
	 *	Check the no-op case
	 */

	if (tty->ldisc->ops->num == ldisc) {
		tty_ldisc_enable_pair(tty, o_tty);
		tty_ldisc_put(new_ldisc);
		return 0;
	}

	/* FIXME: why 'shutoff' input if the ldisc is locked? */
	tty->receive_room = 0;

	o_ldisc = tty->ldisc;
	tty_lock(tty);

	/* FIXME: for testing only */
	WARN_ON(test_bit(TTY_HUPPED, &tty->flags));

	if (test_bit(TTY_HUPPING, &tty->flags)) {
		/* We were raced by the hangup method. It will have stomped
		   the ldisc data and closed the ldisc down */
		tty_ldisc_enable_pair(tty, o_tty);
		tty_ldisc_put(new_ldisc);
		tty_unlock(tty);
		return -EIO;
	}

	/* Shutdown the current discipline. */
	tty_ldisc_close(tty, o_ldisc);

	/* Now set up the new line discipline. */
	tty->ldisc = new_ldisc;
	tty_set_termios_ldisc(tty, ldisc);

	retval = tty_ldisc_open(tty, new_ldisc);
	if (retval < 0) {
		/* Back to the old one or N_TTY if we can't */
		tty_ldisc_put(new_ldisc);
		tty_ldisc_restore(tty, o_ldisc);
	}

	/* At this point we hold a reference to the new ldisc and a
	   a reference to the old ldisc. If we ended up flipping back
	   to the existing ldisc we have two references to it */

	if (tty->ldisc->ops->num != o_ldisc->ops->num && tty->ops->set_ldisc)
		tty->ops->set_ldisc(tty);

	tty_ldisc_put(o_ldisc);

	/*
	 *	Allow ldisc referencing to occur again
	 */
	tty_ldisc_enable_pair(tty, o_tty);

	/* Restart the work queue in case no characters kick it off. Safe if
	   already running */
	schedule_work(&tty->port->buf.work);
	if (o_tty)
		schedule_work(&o_tty->port->buf.work);

	tty_unlock(tty);
	return retval;
}