rt-thread/components/drivers/tty/tty_ldisc.c

180 lines
3.7 KiB
C

#include <tty.h>
#include <tty_ldisc.h>
#define DBG_TAG "TTY_LDISC"
#ifdef RT_TTY_DEBUG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_INFO
#endif /* RT_TTY_DEBUG */
#include <rtdbg.h>
extern struct tty_ldisc_ops n_tty_ops;
static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS] = {
&n_tty_ops, /* N_TTY = 0 */
};
static struct tty_ldisc_ops *get_ldops(int disc)
{
struct tty_ldisc_ops *ldops = RT_NULL;
int level = 0;
level = rt_hw_interrupt_disable();
ldops = tty_ldiscs[disc];
if (ldops)
{
ldops->refcount++;
}
rt_hw_interrupt_enable(level);
return ldops;
}
static void put_ldops(struct tty_ldisc_ops *ldops)
{
int level = 0;
level = rt_hw_interrupt_disable();
ldops->refcount--;
rt_hw_interrupt_enable(level);
}
static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
{
struct tty_ldisc *ld = RT_NULL;
struct tty_ldisc_ops *ldops = RT_NULL;
if (disc < N_TTY || disc >= NR_LDISCS)
{
return RT_NULL;
}
ldops = get_ldops(disc);
if (ldops == RT_NULL)
{
LOG_E("tty ldisc get error\n");
return RT_NULL;
}
ld = rt_malloc(sizeof(struct tty_ldisc));
if (ld == RT_NULL)
{
ldops->refcount--;
return RT_NULL;
}
ld->ops = ldops;
ld->tty = tty;
return ld;
}
/**
* tty_ldisc_put - release the ldisc
*
* Complement of tty_ldisc_get().
*/
static void tty_ldisc_put(struct tty_ldisc *ld)
{
if (ld)
{
put_ldops(ld->ops);
rt_free(ld);
}
}
/**
* tty_ldisc_close - close a line discipline
* @tty: tty we are opening the ldisc on
* @ld: discipline to close
*
* A helper close method. Also a convenient debugging and check
* point.
*/
static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
{
if (ld && ld->ops->close)
{
ld->ops->close(tty);
}
}
/**
* tty_ldisc_kill - teardown ldisc
* @tty: tty being released
*
* Perform final close of the ldisc and reset tty->ldisc
*/
void tty_ldisc_kill(struct tty_struct *tty)
{
if (tty && tty->ldisc)
{
/*
* 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;
}
}
int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
{
int ret = 0;
int level = 0;
if (disc < N_TTY || disc >= NR_LDISCS)
{
return -EINVAL;
}
level = rt_hw_interrupt_disable();
tty_ldiscs[disc] = new_ldisc;
new_ldisc->num = disc;
new_ldisc->refcount = 0;
rt_hw_interrupt_enable(level);
return ret;
}
/**
* tty_ldisc_release - release line discipline
* @tty: tty being shut down (or one end of pty pair)
*
* Called during the final close of a tty or a pty pair in order to shut
* down the line discpline layer. On exit, each tty's ldisc is NULL.
*/
void tty_ldisc_release(struct tty_struct *tty)
{
int level = 0;
struct tty_struct *o_tty = tty->other_struct;
/*
* Shutdown this line discipline. As this is the final close,
* it does not race with the set_ldisc code path.
*/
level = rt_hw_interrupt_disable();
tty_ldisc_kill(tty);
if (o_tty)
{
tty_ldisc_kill(o_tty);
}
rt_hw_interrupt_enable(level);
}
/**
* tty_ldisc_init - ldisc setup for new tty
* @tty: tty being allocated
*
* Set up the line discipline objects for a newly allocated tty. Note that
* the tty structure is not completely set up when this call is made.
*/
void tty_ldisc_init(struct tty_struct *tty)
{
if (tty)
{
tty->ldisc = tty_ldisc_get(tty, N_TTY);
}
}