LINUX TTY驱动构架

再看Linux tty驱动过程中发现linux的驱动构架中,面向对象的思想已经根深蒂固。就比如这串口驱动,代码中经常有一些貌似和串口无关的代码,比如,tty_register_driver等。但我们却删它不得。因为正是这些代码实现了tty_core和具体的tty_driver(比如串口驱动)的联系和纽带。tty驱动中tty_core为最上层,tty_driver为最下层,线路规程层为中间层。tty_struct结构体为这三层交互的主要结构体。该结构体中包含了tty_core和线路规程层的操作方法。上层的操作首先到tty_core层,然后由tty_core层调用线路规程层的方法,再由线路规程层调用最终的tty_driver驱动,以下总结只是对tty构架的总体分析,希望对大家有所启发。重点理解tty_struct结构。

发送数据时,首先调用tty_core层的tty_write函数,然后tty_write调用do_tty_write,然后调用线路规程层的write_chan函数,它会调用具体的tty_driver中的write函数。


第一层:
tty_core

由上到下看:

上层应用首先使用open函数打开一个ttyS设备,open函数最后会调用static int tty_open(struct inode * inode, struct file * filp)函数。

该函数所做主要工作如下:

1、根据参数struct inode * inode(包含主设备号)在tty_drivers链表中找到对应的tty_driver驱动。

2、创建tty_struct结构体,初始化该结构体,增加tty_driver层的操作方法和线路规程层的操作方法ldiscs[N_TTY],初始化tty_driver接收缓冲区tty->flip.char_buf_ptr,调用线路规程层的open函数((tty->ldisc.open)(tty))申请read_buf。最主要的目的是将创建的tty_struct保存到struct file * filp中filp->private_data = tty;。代码分析如下:

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

{

struct tty_struct *tty;

int noctty, retval;

kdev_t device;

unsigned short saved_flags;

char buf[64];

...........

device = inode->i_rdev;

retval = init_dev(device, &tty);

if (retval)

return retval;

filp->private_data = tty;

if (tty->driver.open)//具体的驱动,会进一步初始化tty_struct。见下面的代码rs_open

retval = tty->driver.open(tty, filp); //为tty_struct指定具体的串口设备

else

retval = -ENODEV;

...........

return 0;

}

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

{

struct tty_struct *tty, *o_tty;

struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;

struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;

struct tty_driver *driver;

int retval=0;

int idx;

driver = get_tty_driver(device);

if (!driver)

return -ENODEV;

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];

if (tty) goto fast_track;

tty = alloc_tty_struct();

if(!tty)

goto fail_no_mem;

initialize_tty_struct(tty);

tty->device = device;

tty->driver = *driver;

/* 

 * 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;

(*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)

goto release_mem_out;

}

success:

*ret_tty = tty;

..........

}

struct tty_driver *get_tty_driver(kdev_t device)

{

int major, minor;

struct tty_driver *p;

minor = MINOR(device);

major = MAJOR(device);

for (p = tty_drivers; p; p = p->next) {

if (p->major != major)

continue;

if (minor < p->minor_start)

continue;

if (minor >= p->minor_start + p->num)

continue;

return p;

}

return NULL;

}

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_buffer flip ;tty_driver将接收到的字符放到缓冲区tty->flip.char_buf_ptr中,该缓冲区内容由此处指定的函数flush_to_ldisc调用线路规程层的tty->ldisc.receive_buf(tty, cp, fp, count);函数复制到tty_struct->read_buf中。tty_core层的tty_read函数调用线路规程中的(tty->ldisc.read)(tty,file,buf,count);函数。

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);

}

static int rs_open(struct tty_struct *tty, struct file * filp)

{

struct async_struct *info;

int  retval, line;

unsigned long page;

..........

MOD_INC_USE_COUNT;

line = MINOR(tty->device) - tty->driver.minor_start;

if ((line < 0) || (line >= NR_PORTS)) {

MOD_DEC_USE_COUNT;

return -ENODEV;

}

retval = get_async_struct(line, &info);//驱动中通过line区分不同的串口设备

tty->driver_data = info;

info->tty = tty;

.........

}

由下到上看:

static const struct file_operations tty_fops = {
    .llseek        = no_llseek,
    .read        = tty_read,
    .write        = tty_write,
    .poll        = tty_poll,
    .unlocked_ioctl    = tty_ioctl,
    .compat_ioctl    = tty_compat_ioctl,
    .open        = tty_open,
    .release    = tty_release,
    .fasync        = tty_fasync,
};

每个tty类型的驱动注册时都调用tty_register_driver函数

该函数将tty_driver添加到tty_drivers全局链表中,同时根据主设备号初始化chrdevs[major]将有关tty_core层中的tty_fops 以及tty_driver.name注册到chrdevs[major]结构体中。

int tty_register_driver(struct     tty_driver * driver)
{
     int error;

       int i;

  if (driver->flags & TTY_DRIVER_INSTALLED)

return 0;

  error = devfs_register_chrdev(driver->major, driver->name, &tty_fops);

  if (error < 0)

  return error;

  else if(driver->major == 0)

  driver->major = error;

  driver->prev = 0;

  driver->next = tty_drivers;

  if (tty_drivers) tty_drivers->prev = driver;

  tty_drivers = driver;    

...

  }

int devfs_register_chrdev (unsigned int major, const char *name,

   struct file_operations *fops)

{

    if (boot_options & OPTION_ONLY) return 0;

    return register_chrdev (major, name, fops);

}   /*  End Function devfs_register_chrdev  */

int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)

{

......

chrdevs[major].name = name;

chrdevs[major].fops = fops;

        ........

return 0;

}


第二层:线路规程
不同的tty类型的设备,具有不同的线路规程。这一层也由内核实现,主要代码在drivers/char/n_tty.c文件中
从tty_write函数可以看出,他们最后调用到了线路规程的read/write函数

struct tty_ldisc_ops tty_ldisc_N_TTY = {
    .magic           = TTY_LDISC_MAGIC,
    .name            = "n_tty",
    .open            = n_tty_open,
    .close           = n_tty_close,
    .flush_buffer    = n_tty_flush_buffer,
    .chars_in_buffer = n_tty_chars_in_buffer,
    .read            = n_tty_read,
    .write           = n_tty_write,
    .ioctl           = n_tty_ioctl,
    .set_termios     = n_tty_set_termios,
    .poll            = n_tty_poll,
    .receive_buf     = n_tty_receive_buf,
    .write_wakeup    = n_tty_write_wakeup
};
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
               const unsigned char *buf, size_t nr)
{
    ...
    add_wait_queue(&tty->write_wait, &wait);//将当前进程放到等待队列中
    while (1) {
        set_current_state(TASK_INTERRUPTIBLE);
        if (signal_pending(current)) {
            retval = -ERESTARTSYS;
            break;
        }
        //进入此处继续执行的原因可能是被信号打断,而不是条件得到了满足。
        //只有条件得到了满足,我们才会继续,否则,直接返回!
        if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
            retval = -EIO;
            break;
        }
        if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
        } else {
            while (nr > 0) {
                c = tty->ops->write(tty, b, nr);
                //调用到具体的驱动中的write函数
                if (c < 0) {
                    retval = c;
                    goto break_out;
                }
                if (!c)
                    break;
                b += c;
                nr -= c;
            }
        }
        if (!nr)
            break;
        //全部写入,返回
        if (file->f_flags & O_NONBLOCK) {
            retval = -EAGAIN;
            break;
        }
        /*
        假如是以非阻塞的方式打开的,那么也直接返回。否则,让出cpu,等条件满足以后再继续执行。
        */        
        schedule();//执行到这里,当前进程才会真正让出cpu!!!
    }
break_out:
    __set_current_state(TASK_RUNNING);
    remove_wait_queue(&tty->write_wait, &wait);
    ...
}

这段代码中使用了wait等待队列,为什么要使用等待队列呢?大家想想看,我们在应用层打开一个设备文件的时候,有两种方式,阻塞和非阻塞,非阻塞很简单,不管结果怎样直接返回。但阻塞则有点死皮赖脸的意思,会一直等待,直到操作完成。那write函数的“阻塞”版本在内核里边是怎么实现的呢?就是使用等待队列,只要条件没有得到满足(驱动层调用write函数失败),那么就一直让出cpu,直到条件满足了才会继续执行,并将写操作的结果返回给上层。
通过以上分析,我们也可以得到如下结论:阻塞是在ldisc层也就是线路规程里边实现的。出于代价和操作性的考虑,我们不会再驱动里边实现阻塞类型的write/read函数
n_tty_read的操作比较复杂,tty_driver中当发生接收中断时中断处理函数调用receive_chars函数,该函数将调用核心层的tty->flip.tqueue.routine = flush_to_ldisc;即flush_to_ldisc函数,该函数最后调用线路规程的tty->ldisc.receive_buf(tty, cp, fp, count);函数,该函数将tty->flip.char_buf中的内容复制到tty->read_buf中,而tty_core中的tty_read函数调用线路规程层的read_chan函数将缓冲区内容复制到用户缓冲区中。
 

第三层:

该层使用serial_state表示一个具体的串口设备,下面的代码使用struct serial_state rs_table[2]表示了两个串口,内核启动时在setup_arch中调用register_serial(&__frv_uart0/1);函数注册两个串口设备到tty_driver驱动中。

tty_driver中没有read函数,发生中断时中断处理函数根据中断标识符会调用如下的函数:

if (status & UART_LSR_DR)

receive_chars(info, &status, regs);

tty->flip.tqueue.routine指向的tty_core中的flush_to_ldisc函数然后将缓冲区tty->flip.char_buf_ptr中字符复制到tty->read_buf中。同时纪录接收产生的错误、溢出错误的次数等。

static _INLINE_ void receive_chars(struct async_struct *info,

 int *status, struct pt_regs * regs)

{

struct tty_struct *tty = info->tty;

unsigned char ch;

struct async_icount *icount;

int max_count = 256;

icount = &info->state->icount;

do {

if (tty->flip.count >= TTY_FLIPBUF_SIZE) {

tty->flip.tqueue.routine((void *) tty);

if (tty->flip.count >= TTY_FLIPBUF_SIZE)

return; // if TTY_DONT_FLIP is set

}

ch = serial_inp(info, UART_RX);

*tty->flip.char_buf_ptr = ch;

icount->rx++;

*tty->flip.flag_buf_ptr = 0;

if (*status & (UART_LSR_BI | UART_LSR_PE |

       UART_LSR_FE | UART_LSR_OE)) {

/*

 * For statistics only

 */

if (*status & UART_LSR_BI) {

*status &= ~(UART_LSR_FE | UART_LSR_PE);

icount->brk++;

/*

 * We do the SysRQ and SAK checking

 * here because otherwise the break

 * may get masked by ignore_status_mask

 * or read_status_mask.

 */

if (info->flags & ASYNC_SAK)

do_SAK(tty);

} else if (*status & UART_LSR_PE)

icount->parity++;

else if (*status & UART_LSR_FE)

icount->frame++;

if (*status & UART_LSR_OE)

icount->overrun++;

/*

 * Mask off conditions which should be ignored.

 */

*status &= info->read_status_mask;

#ifdef CONFIG_SERIAL_CONSOLE

if (info->line == sercons.index) {

/* Recover the break flag from console xmit */

*status |= lsr_break_flag;

lsr_break_flag = 0;

}

#endif

if (*status & (UART_LSR_BI)) {

*tty->flip.flag_buf_ptr = TTY_BREAK;

} else if (*status & UART_LSR_PE)

*tty->flip.flag_buf_ptr = TTY_PARITY;

else if (*status & UART_LSR_FE)

*tty->flip.flag_buf_ptr = TTY_FRAME;

}

#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)

if (break_pressed && info->line == sercons.index) {

if (ch != 0 &&

    time_before(jiffies, break_pressed + HZ*5)) {

handle_sysrq(ch, regs, NULL, NULL);

break_pressed = 0;

goto ignore_char;

}

break_pressed = 0;

}

#endif

if ((*status & info->ignore_status_mask) == 0) {

tty->flip.flag_buf_ptr++;

tty->flip.char_buf_ptr++;

tty->flip.count++;

}

if ((*status & UART_LSR_OE) &&

    (tty->flip.count < TTY_FLIPBUF_SIZE)) {

/*

 * Overrun is special, since it's reported

 * immediately, and doesn't affect the current

 * character

 */

*tty->flip.flag_buf_ptr = TTY_OVERRUN;

tty->flip.count++;

tty->flip.flag_buf_ptr++;

tty->flip.char_buf_ptr++;

}

#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)

ignore_char:

#endif

*status = serial_inp(info, UART_LSR);

} while ((*status & UART_LSR_DR) && (max_count-- > 0));

#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */

tty_flip_buffer_push(tty);

#else

queue_task_irq_off(&tty->flip.tqueue, &tq_timer);

#endif

}

具体的tty类型的驱动,比如,以下是摘自serial.c的一段代码,描述的是串口驱动:
static struct serial_struct __frv_uart0 = {

.baud_base = 0,

.io_type = SERIAL_IO_MEMHI,

.iomem_base = (u8 *) UART0_BASE,

.iomem_reg_shift = 3,

.irq = IRQ_CPU_UART0,

.flags = STD_COM_FLAGS,

};

static struct serial_struct __frv_uart1 = {

.baud_base = 0,

.io_type = SERIAL_IO_MEMHI,

.iomem_base = (u8 *) UART1_BASE,

.iomem_reg_shift = 3,

.irq = IRQ_CPU_UART1,

.flags = STD_COM_FLAGS,

  };

int register_serial(struct serial_struct *req)

{

int i;

unsigned long flags;

struct serial_state *state;

struct async_struct *info;

unsigned long port;

port = req->port;

if (HIGH_BITS_OFFSET)

port += (unsigned long) req->port_high << HIGH_BITS_OFFSET;

save_flags(flags); cli();

state = &rs_table[i];

if (rs_table[i].count) {

restore_flags(flags);

printk("Couldn't configure serial #%d (port=%ld,irq=%d): "

       "device already open\n", i, port, req->irq);

return -1;

}

state->irq = req->irq;

state->port = port;

state->flags = req->flags;

state->io_type = req->io_type;

state->iomem_base = req->iomem_base;

state->iomem_reg_shift = req->iomem_reg_shift;

if (req->baud_base)

state->baud_base = req->baud_base;

if ((info = state->info) != NULL) {

info->port = port;

info->flags = req->flags;

info->io_type = req->io_type;

info->iomem_base = req->iomem_base;

info->iomem_reg_shift = req->iomem_reg_shift;

}

autoconfig(state);

if (state->type == PORT_UNKNOWN) {

restore_flags(flags);

printk("register_serial(): autoconfig failed\n");

return -1;

}

restore_flags(flags);

if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))

state->irq = detect_uart_irq(state);

     tty_register_devfs(&serial_driver, 0,

   serial_driver.minor_start + state->line); 

tty_register_devfs(&callout_driver, 0,

   callout_driver.minor_start + state->line);

return state->line + SERIAL_DEV_OFFSET;

}

初始化并注册tty_driver

static int __init rs_init(void)

{

int i;

struct serial_state * state;

memset(&serial_driver, 0, sizeof(struct tty_driver));

serial_driver.magic = TTY_DRIVER_MAGIC;

#if (LINUX_VERSION_CODE > 0x20100)

serial_driver.driver_name = "serial";

#endif

#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))

serial_driver.name = "tts/%d";

#else

serial_driver.name = "ttyS";

#endif

serial_driver.major = TTY_MAJOR;

serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET;

serial_driver.name_base = SERIAL_DEV_OFFSET;

serial_driver.num = NR_PORTS;

serial_driver.type = TTY_DRIVER_TYPE_SERIAL;

serial_driver.subtype = SERIAL_TYPE_NORMAL;

serial_driver.init_termios = tty_std_termios;

#ifdef CONFIG_SERIAL_TA7

serial_driver.init_termios.c_cflag =

B115200 | CS8 | CREAD | HUPCL | CLOCAL;

#else

serial_driver.init_termios.c_cflag =

B9600 | CS8 | CREAD | HUPCL | CLOCAL;

#endif

serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;

serial_driver.refcount = &serial_refcount;

serial_driver.table = serial_table;

serial_driver.termios = serial_termios;

serial_driver.termios_locked = serial_termios_locked;

serial_driver.open = rs_open;

serial_driver.close = rs_close;

serial_driver.write = rs_write;

serial_driver.put_char = rs_put_char;

serial_driver.flush_chars = rs_flush_chars;

serial_driver.write_room = rs_write_room;

serial_driver.chars_in_buffer = rs_chars_in_buffer;

serial_driver.flush_buffer = rs_flush_buffer;

serial_driver.ioctl = rs_ioctl;

serial_driver.throttle = rs_throttle;

serial_driver.unthrottle = rs_unthrottle;

serial_driver.set_termios = rs_set_termios;

serial_driver.stop = rs_stop;

serial_driver.start = rs_start;

serial_driver.hangup = rs_hangup;

#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */

serial_driver.break_ctl = rs_break;

#endif

#if (LINUX_VERSION_CODE >= 131343)

serial_driver.send_xchar = rs_send_xchar;

serial_driver.wait_until_sent = rs_wait_until_sent;

serial_driver.read_proc = rs_read_proc;

#endif

/*

 * The callout device is just like normal device except for

 * major number and the subtype code.

 */

callout_driver = serial_driver;

#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))

callout_driver.name = "cua/%d";

#else

callout_driver.name = "cua";

#endif

callout_driver.major = TTYAUX_MAJOR;

callout_driver.subtype = SERIAL_TYPE_CALLOUT;

#if (LINUX_VERSION_CODE >= 131343)

callout_driver.read_proc = 0;

callout_driver.proc_entry = 0;

#endif

if (tty_register_driver(&serial_driver))

panic("Couldn't register serial driver\n");

if (tty_register_driver(&callout_driver))

panic("Couldn't register callout driver\n");

.........

return 0;

}

我们主要实现这一层的功能,前两层是kernel中已经实现的,我们仅仅需要套用之。当我们按照tty driver的格式书写这一层驱动,并实现几个必要的函数,这个驱动就可以成功运转了。

使用的结构体如下:

/*

 * This structure defines the interface between the low-level tty

 * driver and the tty routines.  The following routines can be

 * defined; unless noted otherwise, they are optional, and can be

 * filled in with a null pointer.

 *

 * int  (*open)(struct tty_struct * tty, struct file * filp);

 *

 *  This routine is called when a particular tty device is opened.

 *  This routine is mandatory; if this routine is not filled in,

 *  the attempted open will fail with ENODEV.

 *     

 * void (*close)(struct tty_struct * tty, struct file * filp);

 *

 *  This routine is called when a particular tty device is closed.

 *

 * int (*write)(struct tty_struct * tty, int from_user,

 *   const unsigned char *buf, int count);

 *

 *  This routine is called by the kernel to write a series of

 *  characters to the tty device.  The characters may come from

 *  user space or kernel space.  This routine will return the

 * number of characters actually accepted for writing.  This

 * routine is mandatory.

 *

 * void (*put_char)(struct tty_struct *tty, unsigned char ch);

 *

 *  This routine is called by the kernel to write a single

 *  character to the tty device.  If the kernel uses this routine,

 *  it must call the flush_chars() routine (if defined) when it is

 *  done stuffing characters into the driver.  If there is no room

 *  in the queue, the character is ignored.

 *

 * void (*flush_chars)(struct tty_struct *tty);

 *

 *  This routine is called by the kernel after it has written a

 *  series of characters to the tty device using put_char().  

 * 

 * int  (*write_room)(struct tty_struct *tty);

 *

 *  This routine returns the numbers of characters the tty driver

 *  will accept for queuing to be written.  This number is subject

 *  to change as output buffers get emptied, or if the output flow

 * control is acted.

 * 

 * int  (*ioctl)(struct tty_struct *tty, struct file * file,

 *      unsigned int cmd, unsigned long arg);

 *

 *  This routine allows the tty driver to implement

 * device-specific ioctl's.  If the ioctl number passed in cmd

 *  is not recognized by the driver, it should return ENOIOCTLCMD.

 * 

 * void (*set_termios)(struct tty_struct *tty, struct termios * old);

 *

 *  This routine allows the tty driver to be notified when

 *  device's termios settings have changed.  Note that a

 *  well-designed tty driver should be prepared to accept the case

 *  where old == NULL, and try to do something rational.

 *

 * void (*set_ldisc)(struct tty_struct *tty);

 *

 *  This routine allows the tty driver to be notified when the

 *  device's termios settings have changed.

 * 

 * void (*throttle)(struct tty_struct * tty);

 *

 *  This routine notifies the tty driver that input buffers for

 *  the line discipline are close to full, and it should somehow

 *  signal that no more characters should be sent to the tty.

 * 

 * void (*unthrottle)(struct tty_struct * tty);

 *

 *  This routine notifies the tty drivers that it should signals

 *  that characters can now be sent to the tty without fear of

 *  overrunning the input buffers of the line disciplines.

 * 

 * void (*stop)(struct tty_struct *tty);

 *

 *  This routine notifies the tty driver that it should stop

 *  outputting characters to the tty device.  

 * 

 * void (*start)(struct tty_struct *tty);

 *

 *  This routine notifies the tty driver that it resume sending

 * characters to the tty device.

 * 

 * void (*hangup)(struct tty_struct *tty);

 *

 *  This routine notifies the tty driver that it should hangup the

 *  tty device.

 *

 * void (*break_ctl)(struct tty_stuct *tty, int state);

 *

 *  This optional routine requests the tty driver to turn on or

 *  off BREAK status on the RS-232 port.  If state is -1,

 *  then the BREAK status should be turned on; if state is 0, then

 *  BREAK should be turned off.

 *

 *  If this routine is implemented, the high-level tty driver will

 *  handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK,

 *  TIOCCBRK.  Otherwise, these ioctls will be passed down to the

 *  driver to handle.

 *

 * void (*wait_until_sent)(struct tty_struct *tty, int timeout);

 * 

 *  This routine waits until the device has written out all of the

 *  characters in its transmitter FIFO.

 *

 * void (*send_xchar)(struct tty_struct *tty, char ch);

 *

 *  This routine is used to send a high-priority XON/XOFF

 *  character to the device.

 */

#include <linux/fs.h>

struct tty_driver {

int magic; /* magic number for this structure */

const char *driver_name;

const char *name;

int name_base; /* offset of printed name */

short major; /* major device number */

short minor_start; /* start of minor device number*/

short num; /* number of devices */

short type; /* type of tty driver */

short subtype; /* subtype of tty driver */

struct termios init_termios; /* Initial termios */

int flags; /* tty driver flags */

int *refcount; /* for loadable tty drivers */

struct proc_dir_entry *proc_entry; /* /proc fs entry */

struct tty_driver *other; /* only used for the PTY driver */

/*

 * Pointer to the tty data structures

 */

struct tty_struct **table;

struct termios **termios;

struct termios **termios_locked;

void *driver_state; /* only used for the PTY driver */

/*

 * Interface routines from the upper tty layer to the tty

 * driver.

 */

int  (*open)(struct tty_struct * tty, struct file * filp);

void (*close)(struct tty_struct * tty, struct file * filp);

int  (*write)(struct tty_struct * tty, int from_user,

      const unsigned char *buf, int count);

void (*put_char)(struct tty_struct *tty, unsigned char ch);

void (*flush_chars)(struct tty_struct *tty);

int  (*write_room)(struct tty_struct *tty);

int  (*chars_in_buffer)(struct tty_struct *tty);

int  (*ioctl)(struct tty_struct *tty, struct file * file,

    unsigned int cmd, unsigned long arg);

void (*set_termios)(struct tty_struct *tty, struct termios * old);

void (*throttle)(struct tty_struct * tty);

void (*unthrottle)(struct tty_struct * tty);

void (*stop)(struct tty_struct *tty);

void (*start)(struct tty_struct *tty);

void (*hangup)(struct tty_struct *tty);

void (*break_ctl)(struct tty_struct *tty, int state);

void (*flush_buffer)(struct tty_struct *tty);

void (*set_ldisc)(struct tty_struct *tty);

void (*wait_until_sent)(struct tty_struct *tty, int timeout);

void (*send_xchar)(struct tty_struct *tty, char ch);

int (*read_proc)(char *page, char **start, off_t off,

  int count, int *eof, void *data);

int (*write_proc)(struct file *file, const char *buffer,

  unsigned long count, void *data);

/*

 * linked list pointers

 */

struct tty_driver *next;

struct tty_driver *prev;

};

###########################################################################

struct tty_struct {

int magic;

struct tty_driver driver;//tty_driver操作

struct tty_ldisc ldisc;//线路规程操作

struct termios *termios, *termios_locked;

int pgrp;

int session;

kdev_t device;

unsigned long flags;

int count;

struct winsize winsize;

unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;

unsigned char low_latency:1, warned:1;

unsigned char ctrl_status;

struct tty_struct *link;

struct fasync_struct *fasync;

struct tty_flip_buffer flip;//tty_driver接收缓冲区,该缓冲区内容由线路规程层复制到tty_core层

int max_flip_cnt;

int alt_speed; /* For magic substitution of 38400 bps */

wait_queue_head_t write_wait;

wait_queue_head_t read_wait;

struct tq_struct tq_hangup;

void *disc_data;

void *driver_data;//保存async_struct,async_struct为一个串口实例

struct list_head tty_files;

#define N_TTY_BUF_SIZE 4096

/*

 * The following is data for the N_TTY line discipline.  For

 * historical reasons, this is included in the tty structure.

 */

unsigned int column;

unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;

unsigned char closing:1;

unsigned short minimum_to_wake;

unsigned long overrun_time;

int num_overrun;

unsigned long process_char_map[256/(8*sizeof(unsigned long))];

char *read_buf;//tty线路规程层接收缓冲区

int read_head;

int read_tail;

int read_cnt;

unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];

int canon_data;

unsigned long canon_head;

unsigned int canon_column;

struct semaphore atomic_read;

struct semaphore atomic_write;

spinlock_t read_lock;

/* If the tty has a pending do_SAK, queue it here - akpm */

struct tq_struct SAK_tq;

};

#define TTY_FLIPBUF_SIZE 512

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 */

};

struct tq_struct {

struct list_head list; /* linked list of active bh's */

unsigned long sync; /* must be initialized to zero */

void (*routine)(void *); /* function to call */

void *data; /* argument to function */

};

###########################################################################

//代表一个串口端口状态

struct serial_state {

int magic;

int baud_base;

unsigned long port;

int irq;

int flags;

int hub6;

int type;//串口类型 如16450、16550、16550A

int line;//驱动中通过line区分不同的串口设备

int revision; /* Chip revision (950) */

int xmit_fifo_size;//FIFO大小 

int custom_divisor;

intcount;//打开次数

u8 *iomem_base;//设备寄存器基址

u16 iomem_reg_shift;//寄存器间隔

unsigned short close_delay;

unsigned short closing_wait; /* time to wait before closing */

struct async_icount icount; //发送接收统计

struct termios normal_termios;

struct termios callout_termios;

int io_type;//串口地址内存类型

struct async_struct *info;//一般为空。指向驱动程序中串口端口实例。

struct pci_dev *dev;

};

struct async_icount {

__u32 cts, dsr, rng, dcd, tx, rx;

__u32 frame, parity, overrun, brk;

__u32 buf_overrun;

};

###########################################################################struct async_struct {

int magic;

unsigned long port;

int hub6;

int flags;

int xmit_fifo_size;

struct serial_state *state;

struct tty_struct  *tty;

int read_status_mask;

int ignore_status_mask;

int timeout;

int quot;

int x_char; /* xon/xoff character */

int close_delay;

unsigned short closing_wait;

unsigned short closing_wait2;

int IER;  /* Interrupt Enable Register */

int MCR;  /* Modem control register */

int LCR;  /* Line control register */

int ACR;  /* 16950 Additional Control Reg. */

unsigned long event;

unsigned long last_active;

int line;

int blocked_open; /* # of blocked opens */

long session; /* Session of opening process */

long pgrp; /* pgrp of opening process */

  struct circ_bufxmit;//发送缓冲区

  spinlock_t xmit_lock;

u8 *iomem_base;

u16 iomem_reg_shift;

int io_type;

struct tq_struct tqueue;

#ifdef DECLARE_WAITQUEUE

wait_queue_head_t open_wait;

wait_queue_head_t close_wait;

wait_queue_head_t delta_msr_wait;

#else

struct wait_queue *open_wait;

struct wait_queue *close_wait;

struct wait_queue *delta_msr_wait;

#endif

struct async_struct *next_port; /* For the linked list */

struct async_struct *prev_port;

};

struct circ_buf {

char *buf;

int head;

int tail;

};

###########################################################################

struct serial_struct {

int type;

int line;

unsigned int port;

int irq;

int flags;

int xmit_fifo_size;

int custom_divisor;

int baud_base;

unsigned short close_delay;

char io_type;

char reserved_char[1];

int hub6;

unsigned short closing_wait; /* time to wait before closing */

unsigned short closing_wait2; /* no longer used... */

unsigned char *iomem_base;

unsigned short iomem_reg_shift;

unsigned int port_high;

int reserved[1];

};

猜你喜欢

转载自blog.csdn.net/pan0755/article/details/108254981
今日推荐