当前位置: 首页 > 工具软件 > tty > 使用案例 >

linux tty结构体,linux tty驱动架构分析

向和歌
2023-12-01

再看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;

charbuf[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)

{

intmajor, 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 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;

intretval, line;

unsigned longpage;

..........

MOD_INC_USE_COUNT;

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

if ((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_operationstty_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

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

structasync_icount *icount;

intmax_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

/*

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

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

struct tty_driver {

intmagic;/* magic number for this structure */

const char*driver_name;

const char*name;

intname_base;/* offset of printed name */

shortmajor;/* major device number */

shortminor_start;/* start of minor device number*/

shortnum;/* number of devices */

shorttype;/* type of tty driver */

shortsubtype;/* subtype of tty driver */

struct termios init_termios; /* Initial termios */

intflags;/* 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 {

intmagic;

struct tty_driver driver;//tty_driver操作

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

struct termios *termios, *termios_locked;

int pgrp;

int session;

kdev_tdevice;

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;

intcount;//接收缓冲区中的字符数

intbuf_num;

unsigned charchar_buf[2*TTY_FLIPBUF_SIZE];

charflag_buf[2*TTY_FLIPBUF_SIZE];

unsigned charslop[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 {

intmagic;

intbaud_base;

unsigned longport;

intirq;

intflags;

inthub6;

inttype;//串口类型 如16450、16550、16550A

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

intrevision;/* Chip revision (950) */

intxmit_fifo_size;//FIFO大小

intcustom_divisor;

intcount;//打开次数

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

u16iomem_reg_shift;//寄存器间隔

unsigned shortclose_delay;

unsigned shortclosing_wait; /* time to wait before closing */

struct async_icounticount;//发送接收统计

struct termiosnormal_termios;

struct termioscallout_termios;

intio_type;//串口地址内存类型

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

struct pci_dev*dev;

};

struct async_icount {

__u32cts, dsr, rng, dcd, tx, rx;

__u32frame, parity, overrun, brk;

__u32buf_overrun;

};

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

intmagic;

unsigned longport;

inthub6;

intflags;

intxmit_fifo_size;

struct serial_state*state;

struct tty_struct*tty;

intread_status_mask;

intignore_status_mask;

inttimeout;

intquot;

intx_char;/* xon/xoff character */

intclose_delay;

unsigned shortclosing_wait;

unsigned shortclosing_wait2;

intIER;/* Interrupt Enable Register */

intMCR;/* Modem control register */

intLCR;/* Line control register */

intACR;/* 16950 Additional Control Reg. */

unsigned longevent;

unsigned longlast_active;

intline;

intblocked_open; /* # of blocked opens */

longsession; /* Session of opening process */

longpgrp; /* pgrp of opening process */

struct circ_bufxmit;//发送缓冲区

spinlock_txmit_lock;

u8*iomem_base;

u16iomem_reg_shift;

intio_type;

struct tq_structtqueue;

#ifdef DECLARE_WAITQUEUE

wait_queue_head_topen_wait;

wait_queue_head_tclose_wait;

wait_queue_head_tdelta_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 {

inttype;

intline;

unsigned intport;

intirq;

intflags;

intxmit_fifo_size;

intcustom_divisor;

intbaud_base;

unsigned shortclose_delay;

chario_type;

charreserved_char[1];

inthub6;

unsigned shortclosing_wait; /* time to wait before closing */

unsigned shortclosing_wait2; /* no longer used... */

unsigned char*iomem_base;

unsigned shortiomem_reg_shift;

unsigned intport_high;

intreserved[1];

};

 类似资料: