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

};

linux tty结构体,linux tty驱动架构分析相关推荐

  1. linux utmp结构体,Linux C编程如何使用联机帮助来解决编程问题?

    1.背景 多次学习C语言一直无法踏入C语言的大门,每次都是在学习C语言中的那些系统调用库函数等望而却只,linux下的系统调用需要我们去记忆一些没有规律的结构体和一些大写的宏定义并且还有一个函数多种形 ...

  2. linux windows 结构体,Linux下C语言——结构体对齐

    结构体对齐的步骤: 1.结构体各成员对齐 2.整个结构体圆整 结构体对齐的特定对齐值: 1.自身对齐值: 自身对齐值就是结构体变量里每个成员的自身大小; 2.指定对齐值: 指定对齐值是由宏#pragm ...

  3. linux sockaddr结构体,linux网络编程笔记 sockaddr_in结构体[转]

    struct sockaddr { unsigned short sa_family; char sa_data[14]; }; 此数据结构用做bind.connect.recvfrom.sendto ...

  4. linux 进程 结构体,Linux中进程控制块PCB-------task_struct结构体结构

    Linux中task_struct用来控制管理进程,结构如下: struct task_struct { //说明了该进程是否可以执行,还是可中断等信息 volatile long state; // ...

  5. linux 触摸结构体,xboot-x4412ibox项目实战54-Linux触摸屏驱动之I2C驱动实验 - Powered by Discuz!...

    前面我们分析了linux触摸屏驱动的input子系统机制,本章节分析linux触摸屏驱动的i2c机制. 驱动源码路径: kernel/drivers/input/touchscreen/ft5x06_ ...

  6. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的I2C驱动架构分析

    PowerPC + Linux2.6.25平台下的I2C驱动架构分析 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人b ...

  7. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的SPI驱动架构分析

    PowerPC + Linux2.6.25平台下的SPI驱动架构分析 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人b ...

  8. Linux网卡驱动(1)-网卡驱动架构分析

    1.Linux网络子系统 网络子系统采用分层的结构: 我们这里研究内核空间即可,在内核空间分成5层,分别是: 1.系统调用接口,它面向的客户是应用层序,为应用程序提供访问网络子系统的统一方法,比如说s ...

  9. linux网络设备驱动结构体,Linux网络设备驱动之设备驱动的注册与注销(二)

    网络设备驱动的注册与注销由 register_netdev( ) 和 unregister_netdev( ) 函数完成,这两个函数的原型为: int register_netdev(struct n ...

最新文章

  1. 会python可以从事什么工作-Python入门后,想要从事自由职业可以做哪方面工作?...
  2. Java系列笔记(1) - Java 类加载与初始化
  3. BXNA在调试显示新闻的JS?
  4. MYSQL中最基础的的聚合函数(avg求平均值及count求和)
  5. 全渠道java b2b b2c o2o平台
  6. 初学C语言没有项目练手怎么行,这17个小项目收下不谢
  7. flash源文件_Animate/FLASH如何将多个源文件合并
  8. python sql逐行读取数据库数据,使用python读取数据库中的内容 把爬虫爬到的内容,存储在mysql数据库中...
  9. APACHE 403 FORBIDDEN错误的解决办法之一
  10. JAVA使用摄像头录制_JavaCV开发详解之1:调用本机摄像头视频(建议使用javaCV最新版本)...
  11. libudev-dev : Depends: libudev0 (= 175-0ubuntu9) but 175-0ubuntu9.3 is to be installed 错误解决方案...
  12. GB-T 15834-2011《标点符号用法》文档的格式问题
  13. KindEditor
  14. android 安装在笔记本,笔记本电脑怎么装安卓系统_笔记本安装安卓教程-系统城...
  15. 演化策略(Evolutionary Strategies)
  16. python中sinxcosy_∫e^x[(1-cosy)dx-(y-siny)dy],其中c为区域 0≤x≤π,0≤y≤sinx的边界曲线取正向.求曲...
  17. Android中补间动画相关知识
  18. 纸短情长用计算机破出来的数字,纸短情长简谱数字大全 一看就会的简谱
  19. 求细胞数量:一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。
  20. C语言探索之旅 | 第一部分第三课:你的第一个程序

热门文章

  1. 基于netty-socketio的web聊天--发送图片
  2. Chrome浏览器出现“由贵单位管理”原因及解决去除方法
  3. 男人刮胡子学问 不能随便刮
  4. Python——12306图片验证码
  5. 数字信号处理相关1(卷积(convolution)的几种解释)
  6. WPF TextBox限制只能输入数字的两种方法
  7. 【Python】最长公共子序列LCS
  8. uniqueResult和list
  9. Vue JavaScript heap out of memory
  10. 面试——异步FIFO详解