终端设备驱动之tty_open函数

《linux内核源代码情景分析》读书笔记。

tty_open函数源码:


static int tty_open(struct inode * inode, struct file * filp)
{

......
retval = init_dev(device, &tty);调用此函数,为需要打开的终端设备建立一个(或找到其)tty_struct数据结构。每个已打开的终端设备都有一个tty_struct数据结构代表。在tty_struct数据结构中有个struct tty_flip_buffer flip

struct tty_flip_buffer {
struct tq_struct tqueue;
struct semaphore pty_sem;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int count;
int buf_num;
unsigned char char_buf[2*TTY_FLIPBUF_SIZE];
char flag_buf[2*TTY_FLIPBUF_SIZE];
unsigned char slop[4]; /* N.B. bug overwrites buffer by 1 */
};

是终端设备的输入缓冲区,底层的中断服务程序将接受到字符存储于这个缓冲区中,供其上层读取。init_dev函数代码如下:

static int init_dev(kdev_t device, struct tty_struct **ret_tty)
{
........


driver = get_tty_driver(device);
if (!driver)
return -ENODEV;

根据设备号从tty_drivers链表中找到相应的tty_driver数据结构,这个结构中有个指针table,指向一个tty_struct结构指针数组。数组中包含了所有该种终端设备的tty_struct结构指针。


idx = MINOR(device) - driver->minor_start;


/* 
* Check whether we need to acquire the tty semaphore to avoid
* race conditions.  For now, play it safe.
*/
down_tty_sem(idx);


/* check whether we're reopening an existing tty */
tty = driver->table[idx];

如果tty_struct结构存在,就不需从新创建
if (tty) goto fast_track;


/*
* First time open is complex, especially for PTY devices.
* This code guarantees that either everything succeeds and the
* TTY is ready for operation, or else the table slots are vacated
* and the allocated memory released.  (Except that the termios 
* and locked termios may be retained.)
*/


o_tty = NULL;
tp = o_tp = NULL;
ltp = o_ltp = NULL;


tty = alloc_tty_struct();分配空间
if(!tty)
goto fail_no_mem;
initialize_tty_struct(tty);

初始化,源码如下:

/*
 * This subroutine initializes a tty structure.
 */
static void initialize_tty_struct(struct tty_struct *tty)
{
memset(tty, 0, sizeof(struct tty_struct));
tty->magic = TTY_MAGIC;
tty->ldisc = ldiscs[N_TTY];
tty->pgrp = -1;
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
init_MUTEX(&tty->flip.pty_sem);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
tty->tq_hangup.routine = do_tty_hangup;
tty->tq_hangup.data = tty;
sema_init(&tty->atomic_read, 1);
sema_init(&tty->atomic_write, 1);
spin_lock_init(&tty->read_lock);
INIT_LIST_HEAD(&tty->tty_files);
INIT_TQUEUE(&tty->SAK_tq, 0, 0);
}

回到init_dev中
tty->device = device;
tty->driver = *driver;


tp_loc = &driver->termios[idx];
if (!*tp_loc) {
tp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!tp)
goto free_mem_out;
*tp = driver->init_termios;
}


ltp_loc = &driver->termios_locked[idx];
if (!*ltp_loc) {
ltp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!ltp)
goto free_mem_out;
memset(ltp, 0, sizeof(struct termios));
}


if (driver->type == TTY_DRIVER_TYPE_PTY) {

伪终端时,进行一些特殊的处理
........

}


/* 
* All structures have been allocated, so now we install them.
* Failures after this point use release_mem to clean up, so 
* there's no need to null out the local pointers.
*/
driver->table[idx] = tty;

将新创建的tty_struct结构“安装”在其所属tty_driver结构的table[]数组中的相应位置

if (!*tp_loc)
*tp_loc = tp;
if (!*ltp_loc)
*ltp_loc = ltp;
tty->termios = *tp_loc;
tty->termios_locked = *ltp_loc;

安装新创建的termios结构
(*driver->refcount)++;
tty->count++;


/* 
* Structures all installed ... call the ldisc open routines.
* If we fail here just call release_mem to clean up.  No need
* to decrement the use counts, as release_mem doesn't care.
*/
if (tty->ldisc.open) {
retval =
(tty->ldisc.open)(tty);
if (retval)

不管是哪一种终端设备,开始时总是采用与下标N_TTY对应的tty_ldisc结构。源码如下:

struct tty_ldisc tty_ldisc_N_TTY = {
TTY_LDISC_MAGIC, /* magic */
"n_tty", /* name */
0, /* num */
0, /* flags */
n_tty_open, /* open */
n_tty_close, /* close */
n_tty_flush_buffer, /* flush_buffer */
n_tty_chars_in_buffer, /* chars_in_buffer */
read_chan, /* read */
write_chan, /* write */
n_tty_ioctl, /* ioctl */
n_tty_set_termios, /* set_termios */
normal_poll, /* poll */
n_tty_receive_buf, /* receive_buf */
n_tty_receive_room, /* receive_room */
n_tty_write_wakeup /* write_wakeup */
};

n_tty_open函数源码:

static int n_tty_open(struct tty_struct *tty)
{
if (!tty)
return -EINVAL;


if (!tty->read_buf) {
tty->read_buf = alloc_buf();
if (!tty->read_buf)
return -ENOMEM;
}分配read_buf缓冲区

memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
reset_buffer_flags(tty);设置一些缓冲区需使用的标志等等
tty->column = 0;
n_tty_set_termios(tty, 0);根据终端设备的termios数据结构设置其tty_struct结构中的字符位图process_char_map和其他几个标志。
tty->minimum_to_wake = 1;
tty->closing = 0;
return 0;
}

回到tty_open中
.......

#ifdef CONFIG_UNIX98_PTYS
init_dev_done:
#endif
filp->private_data = tty;

用file结构中的private_data指针指向目标设备的tty_struct数据结构,从当前进程到目标终端设备的连接就建立起来了。
file_move(filp, &tty->tty_files);

将指向该终端设备的file结构挂入到tty_struct结构中的tty_files队列中。
check_tty_count(tty, "tty_open");
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
   tty->driver.subtype == PTY_TYPE_MASTER)
noctty = 1;
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "opening %s...", tty_name(tty, buf));
#endif
if (tty->driver.open)
retval = tty->driver.open(tty, filp);

具体终端设备的open函数
else

retval = -ENODEV;
filp->f_flags = saved_flags;

.........
return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值