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