首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

4 linux tty驱动

2013-02-04 
四 linux tty驱动一. tty结构体1.tty_driverstruct tty_driver {intmagicstruct kref kref//参考计数str

四 linux tty驱动

4 linux tty驱动

一. tty结构体

1.tty_driver

struct tty_driver {intmagic;struct kref kref;//参考计数struct cdev cdev;//字符设备struct module*owner;//模块所有者const char*driver_name;//驱动名const char*name;//设备名intname_base;intmajor;//主设备号intminor_start;//起始次设备号intminor_num;//设备个数intnum;//分配了的tty设备个数shorttype;//tty设备的类型shortsubtype;//tty设备子类型struct ktermios init_termios;//初始化的ktermiosintflags;//tty驱动标志struct proc_dir_entry *proc_entry;//procfs入口struct tty_driver *other;struct tty_struct **ttys;struct ktermios **termios;struct ktermios **termios_locked;void *driver_state;const struct tty_operations *ops;//操作函数集struct list_head tty_drivers;//驱动链表};

1.1 tty->flag

#define TTY_DRIVER_INSTALLED0x0001#define TTY_DRIVER_RESET_TERMIOS0x0002#define TTY_DRIVER_REAL_RAW0x0004#define TTY_DRIVER_DYNAMIC_DEV0x0008#define TTY_DRIVER_DEVPTS_MEM0x0010#define TTY_DRIVER_HARDWARE_BREAK0x0020

1.2 tty->type tty设备类型

#define TTY_DRIVER_TYPE_SYSTEM0x0001#define TTY_DRIVER_TYPE_CONSOLE0x0002#define TTY_DRIVER_TYPE_SERIAL0x0003#define TTY_DRIVER_TYPE_PTY0x0004#define TTY_DRIVER_TYPE_SCC0x0005/* scc driver */#define TTY_DRIVER_TYPE_SYSCONS0x0006

2.ktermios结构体

struct ktermios {tcflag_t c_iflag;/* input mode flags *///输入模式标志tcflag_t c_oflag;/* output mode flags *///输出模式标志tcflag_t c_cflag;/* control mode flags *///控制模式标志tcflag_t c_lflag;/* local mode flags *///本地模式标志cc_t c_line;/* line discipline *///线路规程类型cc_t c_cc[NCCS];/* control characters *///控制字符speed_t c_ispeed;/* input speed *///输入速度speed_t c_ospeed;/* output speed *///输出速度};

3.tty_struct

struct tty_struct {intmagic;//魔数struct kref kref;//参考计数struct device *dev;//设备文件struct tty_driver *driver;//tty驱动const struct tty_operations *ops;//tty操作函数集int index;struct mutex ldisc_mutex;struct tty_ldisc *ldisc;//线路规程struct mutex termios_mutex;spinlock_t ctrl_lock;struct ktermios *termios, *termios_locked;struct termiox *termiox;char name[64];//名字struct pid *pgrp;struct pid *session;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;unsigned int receive_room;struct tty_struct *link;struct fasync_struct *fasync;struct tty_bufhead buf;int alt_speed;wait_queue_head_t write_wait;wait_queue_head_t read_wait;struct work_struct hangup_work;void *disc_data;void *driver_data;struct list_head tty_files;#define N_TTY_BUF_SIZE 4096unsigned int column;unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;unsigned char closing:1;unsigned char echo_overrun: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;int read_head;int read_tail;int read_cnt;unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];unsigned char *echo_buf;unsigned int echo_pos;unsigned int echo_cnt;int canon_data;unsigned long canon_head;unsigned int canon_column;struct mutex atomic_read_lock;struct mutex atomic_write_lock;struct mutex output_lock;struct mutex echo_lock;unsigned char *write_buf;int write_cnt;spinlock_t read_lock;struct work_struct SAK_work;struct tty_port *port;};

4.tty_ldisc线路规程

struct tty_ldisc {struct tty_ldisc_ops *ops;atomic_t users;};

4.1 线路规程操作函数集

struct tty_ldisc_ops {intmagic;char*name;intnum;intflags;int(*open)(struct tty_struct *);void(*close)(struct tty_struct *);void(*flush_buffer)(struct tty_struct *tty);ssize_t(*chars_in_buffer)(struct tty_struct *tty);ssize_t(*read)(struct tty_struct * tty, struct file * file,unsigned char __user * buf, size_t nr);ssize_t(*write)(struct tty_struct * tty, struct file * file,const unsigned char * buf, size_t nr);int(*ioctl)(struct tty_struct * tty, struct file * file,unsigned int cmd, unsigned long arg);long(*compat_ioctl)(struct tty_struct * tty, struct file * file,unsigned int cmd, unsigned long arg);void(*set_termios)(struct tty_struct *tty, struct ktermios * old);unsigned int (*poll)(struct tty_struct *, struct file *,struct poll_table_struct *);int(*hangup)(struct tty_struct *tty);void(*receive_buf)(struct tty_struct *, const unsigned char *cp,char *fp, int count);void(*write_wakeup)(struct tty_struct *);void(*dcd_change)(struct tty_struct *, unsigned int,struct timespec *);struct  module *owner;int refcount;};


二.系统初始化

设备类初始化

static int __init tty_class_init(void){tty_class = class_create(THIS_MODULE, "tty");//创建tty类if (IS_ERR(tty_class))return PTR_ERR(tty_class);tty_class->devnode = tty_devnode;//指定tty设备节点return 0;}

字符设备初始化

int __init tty_init(void){cdev_init(&tty_cdev, &tty_fops);//初始化tty字符设备if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||//添加tty字符设备    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)//注册/dev/tty字符设备panic("Couldn't register /dev/tty driver\n");device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,"tty");//注册tty设备cdev_init(&console_cdev, &console_fops);//初始化console字符设备if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||//添加console字符设备    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)//注册/dev/console设备panic("Couldn't register /dev/console driver\n");device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,"console");//创建console设备#ifdef CONFIG_VTvty_init(&console_fops);#endifreturn 0;}

虚拟终端/dev/tty0初始化vty_init

int __init vty_init(const struct file_operations *console_fops){cdev_init(&vc0_cdev, console_fops);if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)panic("Couldn't register /dev/tty0 driver\n");device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");//创建/dev/tty0 (4,0)vcs_init();console_driver = alloc_tty_driver(MAX_NR_CONSOLES);if (!console_driver)panic("Couldn't allocate console driver\n");console_driver->owner = THIS_MODULE;console_driver->name = "tty";console_driver->name_base = 1;console_driver->major = TTY_MAJOR;console_driver->minor_start = 1;console_driver->type = TTY_DRIVER_TYPE_CONSOLE;console_driver->init_termios = tty_std_termios;if (default_utf8)console_driver->init_termios.c_iflag |= IUTF8;console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;tty_set_operations(console_driver, &con_ops);if (tty_register_driver(console_driver))panic("Couldn't register console driver\n");kbd_init();console_map_init();#ifdef CONFIG_MDA_CONSOLEmda_console_init();#endifreturn 0;}


 

三.tty初始化步骤

1.分配tty结构体

struct tty_driver *alloc_tty_driver(int lines){struct tty_driver *driver;driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);//分配内存if (driver) {kref_init(&driver->kref);//引用计数+1driver->magic = TTY_DRIVER_MAGIC;//设置魔数0x5402driver->num = lines;//设置tty line个数}return driver;}


2.填充tty结构体成员

3.注册tty设备驱动

int tty_register_driver(struct tty_driver *driver){int error;int i;dev_t dev;void **p = NULL;struct device *d;if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);if (!p)return -ENOMEM;}if (!driver->major) {//指定了主设备号error = alloc_chrdev_region(&dev, driver->minor_start,driver->num, driver->name);//分配多个设备号if (!error) {driver->major = MAJOR(dev);//主设备号driver->minor_start = MINOR(dev);//次设备号}} else {dev = MKDEV(driver->major, driver->minor_start);//获取第一个设备号error = register_chrdev_region(dev, driver->num, driver->name);//分配多个设备号}if (error < 0) {kfree(p);return error;}if (p) {driver->ttys = (struct tty_struct **)p;//tty_structdriver->termios = (struct ktermios **)(p + driver->num);//ktermiosk} else {driver->ttys = NULL;driver->termios = NULL;}cdev_init(&driver->cdev, &tty_fops);//初始化字符设备driver->cdev.owner = driver->owner;//设置模块所有者error = cdev_add(&driver->cdev, dev, driver->num);//添加字符设备if (error) {unregister_chrdev_region(dev, driver->num);driver->ttys = NULL;driver->termios = NULL;kfree(p);return error;}mutex_lock(&tty_mutex);list_add(&driver->tty_drivers, &tty_drivers);//添加到全局tty_drivers链表mutex_unlock(&tty_mutex);if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {//标志动态创建设备标志for (i = 0; i < driver->num; i++) {d = tty_register_device(driver, i, NULL);//添加tty设备文件/dev/ttyXXif (IS_ERR(d)) {error = PTR_ERR(d);goto err;}}}proc_tty_register_driver(driver);//设置tty在/proc下的接口driver->flags |= TTY_DRIVER_INSTALLED;return 0;err:for (i--; i >= 0; i--)tty_unregister_device(driver, i);mutex_lock(&tty_mutex);list_del(&driver->tty_drivers);mutex_unlock(&tty_mutex);unregister_chrdev_region(dev, driver->num);driver->ttys = NULL;driver->termios = NULL;kfree(p);return error;}

4.创建tty设备文件/dev/ttyXXX

struct device *tty_register_device(struct tty_driver *driver, unsigned index,struct device *device){char name[64];dev_t dev = MKDEV(driver->major, driver->minor_start) + index;//获取设备号if (index >= driver->num) {printk(KERN_ERR "Attempt to register invalid tty line number (%d).\n", index);return ERR_PTR(-EINVAL);}if (driver->type == TTY_DRIVER_TYPE_PTY)//伪终端pty_line_name(driver, index, name);else//"/dev/ttyXXX"tty_line_name(driver, index, name);return device_create(tty_class, device, dev, NULL, name);//创建设备文件}

5.tty设备文件捆绑的操作函数集tty_fops

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的操作

当打开/dev/ttyXX的时候会调用tty_open函数

tty_open操作

static int tty_open(struct inode *inode, struct file *filp){struct tty_struct *tty = NULL;int noctty, retval;struct tty_driver *driver;int index;dev_t device = inode->i_rdev;//通过i节点获取设备号unsigned saved_flags = filp->f_flags;nonseekable_open(inode, filp);//设置打开关联文件的模式retry_open:noctty = filp->f_flags & O_NOCTTY;//设置O_NOCTTY标志index  = -1;retval = 0;mutex_lock(&tty_mutex);tty_lock();if (device == MKDEV(TTYAUX_MAJOR, 0)) {//打开的设备是/dev/tty (5,0)tty = get_current_tty();//取当前进程tty_structif (!tty) {tty_unlock();mutex_unlock(&tty_mutex);return -ENXIO;}driver = tty_driver_kref_get(tty->driver);//获取tty_driverindex = tty->index;filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */tty_kref_put(tty);//引用计数goto got_driver;}#ifdef CONFIG_VT//虚拟终端设备 /dev/tty0if (device == MKDEV(TTY_MAJOR, 0)) {//(4,0)extern struct tty_driver *console_driver;driver = tty_driver_kref_get(console_driver);//获取tty_structindex = fg_console;noctty = 1;goto got_driver;}#endifif (device == MKDEV(TTYAUX_MAJOR, 1)) {//打开的是/dev/console (5,1)struct tty_driver *console_driver = console_device(&index);//获取console对应的tty_driverif (console_driver) {driver = tty_driver_kref_get(console_driver);//获取tty_structif (driver) {filp->f_flags |= O_NONBLOCK;noctty = 1;goto got_driver;}}tty_unlock();mutex_unlock(&tty_mutex);return -ENODEV;}driver = get_tty_driver(device, &index);//获取tty_driver,设置次设备号(索引值) 非特殊的ttyif (!driver) {tty_unlock();mutex_unlock(&tty_mutex);return -ENODEV;}got_driver:if (!tty) {tty = tty_driver_lookup_tty(driver, inode, index);//获取tty_drivver的tty_structif (IS_ERR(tty)) {tty_unlock();mutex_unlock(&tty_mutex);return PTR_ERR(tty);}}if (tty) {//若tty_struct不为空retval = tty_reopen(tty);if (retval)tty = ERR_PTR(retval);} else//若tty_struct为空tty = tty_init_dev(driver, index, 0);//调用tty_init_dev函数(下面分析)mutex_unlock(&tty_mutex);tty_driver_kref_put(driver);//增加引用计数if (IS_ERR(tty)) {tty_unlock();return PTR_ERR(tty);}retval = tty_add_file(tty, filp);if (retval) {tty_unlock();return retval;}check_tty_count(tty, "tty_open");if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER)noctty = 1;#ifdef TTY_DEBUG_HANGUPprintk(KERN_DEBUG "opening %s...", tty->name);#endifif (!retval) {if (tty->ops->open)//若tty_struct存在open方法retval = tty->ops->open(tty, filp);//则调用其open方法elseretval = -ENODEV;}filp->f_flags = saved_flags;if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&!capable(CAP_SYS_ADMIN))retval = -EBUSY;if (retval) {#ifdef TTY_DEBUG_HANGUPprintk(KERN_DEBUG "error %d in opening %s...", retval,tty->name);#endiftty_unlock(); /* need to call tty_release without BTM */tty_release(inode, filp);if (retval != -ERESTARTSYS)return retvalif (signal_pending(current))return retval;schedule();tty_lock();if (filp->f_op == &hung_up_tty_fops)filp->f_op = &tty_fops;tty_unlock();goto retry_open;}tty_unlock();mutex_lock(&tty_mutex);tty_lock();spin_lock_irq(¤t->sighand->siglock);if (!noctty && current->signal->leader &&!current->signal->tty && tty->session == NULL)__proc_set_tty(current, tty);spin_unlock_irq(¤t->sighand->siglock);tty_unlock();mutex_unlock(&tty_mutex);return 0;}

tty_init_dev函数

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,int first_ok){struct tty_struct *tty;int retval;if (driver->subtype == PTY_TYPE_MASTER &&(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {return ERR_PTR(-EIO);}if (!try_module_get(driver->owner))return ERR_PTR(-ENODEV);tty = alloc_tty_struct();//分配tty_structif (!tty)goto fail_no_mem;initialize_tty_struct(tty, driver, idx);//初始化tty_structretval = tty_driver_install_tty(driver, tty);//调用tty_driver的install方法,并初始化termiosif (retval < 0) {free_tty_struct(tty);module_put(driver->owner);return ERR_PTR(retval);}retval = tty_ldisc_setup(tty, tty->link);//调用线路规程的open方法,及线路规程link的open方法if (retval)goto release_mem_out;return tty;fail_no_mem:module_put(driver->owner);return ERR_PTR(-ENOMEM);release_mem_out:if (printk_ratelimit())printk(KERN_INFO "tty_init_dev: ldisc open failed,clearing slot %d\n", idx);release_tty(tty, idx);return ERR_PTR(retval);}

tty_init_dev>>>initialize_tty_struct

void initialize_tty_struct(struct tty_struct *tty,struct tty_driver *driver, int idx){memset(tty, 0, sizeof(struct tty_struct));//初始化tty_structkref_init(&tty->kref);tty->magic = TTY_MAGIC;//魔数tty_ldisc_init(tty);//初始化线路规程tty->session = NULL;tty->pgrp = NULL;tty->overrun_time = jiffies;tty->buf.head = tty->buf.tail = NULL;tty_buffer_init(tty);mutex_init(&tty->termios_mutex);mutex_init(&tty->ldisc_mutex);init_waitqueue_head(&tty->write_wait);//初始化写等待队列头init_waitqueue_head(&tty->read_wait);//初始化读等待队列头INIT_WORK(&tty->hangup_work, do_tty_hangup);mutex_init(&tty->atomic_read_lock);mutex_init(&tty->atomic_write_lock);mutex_init(&tty->output_lock);mutex_init(&tty->echo_lock);spin_lock_init(&tty->read_lock);spin_lock_init(&tty->ctrl_lock);INIT_LIST_HEAD(&tty->tty_files);INIT_WORK(&tty->SAK_work, do_SAK_work);tty->driver = driver;//tty_struct->driver=tty_driver 捆绑tty_struct和tty_drivertty->ops = driver->ops;//tty_strcut的操作函数集=tty_driver的操作函数集tty->index = idx;//设置索引值tty_line_name(driver, idx, tty->name);tty->dev = tty_get_device(tty);//设置tty_struct的设备文件}

tty_init_dev>>>initialize_tty_struct>>>tty_ldisc_init

void tty_ldisc_init(struct tty_struct *tty){struct tty_ldisc *ld = tty_ldisc_get(N_TTY);//获取线路规程数组tty_ldiscs[N_TTY]项if (IS_ERR(ld))panic("n_tty: init_tty");tty_ldisc_assign(tty, ld);//设置线路规程}

在start_kernel-->console_init-->tty_ldisc_begin-->tty_register_ldisc将tty_ldiscs[N_TTY]=tty_ldisc_N_TTY

tty_driver_install_tty

static int tty_driver_install_tty(struct tty_driver *driver,struct tty_struct *tty){int idx = tty->index;int ret;if (driver->ops->install) {//tty驱动操作函数集存在install方法ret = driver->ops->install(driver, tty);//则调用install方法return ret;}if (tty_init_termios(tty) == 0) {//初始化termiostty_driver_kref_get(driver);tty->count++;driver->ttys[idx] = tty;return 0;}return -ENOMEM;}

tty_driver_install_tty>>>tty_init_termios

int tty_init_termios(struct tty_struct *tty){struct ktermios *tp;int idx = tty->index;tp = tty->driver->termios[idx];//获取termiosif (tp == NULL) {tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);if (tp == NULL)return -ENOMEM;memcpy(tp, &tty->driver->init_termios,sizeof(struct ktermios));tty->driver->termios[idx] = tp;}tty->termios = tp;tty->termios_locked = tp + 1;/* Compatibility until drivers always set this */tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);//设置接收波特率tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);//设置波特率return 0;}EXPORT_SYMBOL_GPL(tty_init_termios);

tty_ldisc_setup

int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty){struct tty_ldisc *ld = tty->ldisc;int retval;retval = tty_ldisc_open(tty, ld);//调用tty_struct的open方法if (retval)return retval;if (o_tty) {retval = tty_ldisc_open(o_tty, o_tty->ldisc);//调用tty_struct->link的open方法if (retval) {tty_ldisc_close(tty, ld);return retval;}tty_ldisc_enable(o_tty);//使能tty_struct->kink}tty_ldisc_enable(tty);//使能tty_structreturn 0;}

 

tty_read操作

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,loff_t *ppos){int i;struct inode *inode = file->f_path.dentry->d_inode;struct tty_struct *tty = file_tty(file);//获取tty_strcutstruct tty_ldisc *ld;if (tty_paranoia_check(tty, inode, "tty_read"))return -EIO;if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))return -EIO;ld = tty_ldisc_ref_wait(tty);//获取线路规程if (ld->ops->read)//线路规程存在读方法i = (ld->ops->read)(tty, file, buf, count);//调用其读方法elsei = -EIO;tty_ldisc_deref(ld);//引用计数if (i > 0)inode->i_atime = current_fs_time(inode->i_sb);return i;}

 

tty_write操作

static ssize_t tty_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos){struct inode *inode = file->f_path.dentry->d_inode;struct tty_struct *tty = file_tty(file);//获取tty_struct struct tty_ldisc *ld;ssize_t ret;if (tty_paranoia_check(tty, inode, "tty_write"))return -EIO;if (!tty || !tty->ops->write ||(test_bit(TTY_IO_ERROR, &tty->flags)))return -EIO;/* Short term debug to catch buggy drivers */if (tty->ops->write_room == NULL)printk(KERN_ERR "tty driver %s lacks a write_room method.\n",tty->driver->name);ld = tty_ldisc_ref_wait(tty);if (!ld->ops->write)//线路规程存在写方法ret = -EIO;elseret = do_tty_write(ld->ops->write, tty, file, buf, count);//调用线路规程写方法tty_ldisc_deref(ld);return ret;}

do_tty_write函数

do_tty_write(ld->ops->write,

                       tty, file, buf, count);

这个函数第一个参数是调用线路规程的写方法,写方法函数的参数是后面四个参数

该写方法将数据写入tty_struct相关的file里

static inline ssize_t do_tty_write(ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),struct tty_struct *tty,struct file *file,const char __user *buf,size_t count){ssize_t ret, written = 0;unsigned int chunk;ret = tty_write_lock(tty, file->f_flags & O_NDELAY);if (ret < 0)return ret;chunk = 2048;if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))chunk = 65536;if (count < chunk)chunk = count;/* write_buf/write_cnt is protected by the atomic_write_lock mutex */if (tty->write_cnt < chunk) {unsigned char *buf_chunk;if (chunk < 1024)chunk = 1024;buf_chunk = kmalloc(chunk, GFP_KERNEL);if (!buf_chunk) {ret = -ENOMEM;goto out;}kfree(tty->write_buf);tty->write_cnt = chunk;tty->write_buf = buf_chunk;}/* Do the write .. */for (;;) {size_t size = count;if (size > chunk)size = chunk;ret = -EFAULT;if (copy_from_user(tty->write_buf, buf, size))break;ret = write(tty, file, tty->write_buf, size);if (ret <= 0)break;written += ret;buf += ret;count -= ret;if (!count)break;ret = -ERESTARTSYS;if (signal_pending(current))break;cond_resched();}if (written) {struct inode *inode = file->f_path.dentry->d_inode;inode->i_mtime = current_fs_time(inode->i_sb);ret = written;}out:tty_write_unlock(tty);return ret;}

五.线路规程操作

tty_driver的open,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};

1.open方法

static int n_tty_open(struct tty_struct *tty){if (!tty)return -EINVAL;if (!tty->read_buf) {tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);if (!tty->read_buf)return -ENOMEM;}if (!tty->echo_buf) {tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);if (!tty->echo_buf)return -ENOMEM;}reset_buffer_flags(tty);tty->column = 0;n_tty_set_termios(tty, NULL);tty->minimum_to_wake = 1;tty->closing = 0;return 0;}

2.写方法

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,const unsigned char *buf, size_t nr){const unsigned char *b = buf;DECLARE_WAITQUEUE(wait, current);int c;ssize_t retval = 0;/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {retval = tty_check_change(tty);if (retval)return retval;}/* Write out any echoed characters that are still pending */process_echoes(tty);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))) {while (nr > 0) {ssize_t num = process_output_block(tty, b, nr);if (num < 0) {if (num == -EAGAIN)break;retval = num;goto break_out;}b += num;nr -= num;if (nr == 0)break;c = *b;if (process_output(c, tty) < 0)break;b++; nr--;}if (tty->ops->flush_chars)tty->ops->flush_chars(tty);//调用tty_struct操作函数集合的flush_chars方法} else {while (nr > 0) {c = tty->ops->write(tty, b, nr);//调用tty_struct操作函数集合的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;}schedule();}break_out:__set_current_state(TASK_RUNNING);remove_wait_queue(&tty->write_wait, &wait);if (b - buf != nr && tty->fasync)set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);return (b - buf) ? b - buf : retval;}

3.读方法

static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr){unsigned char __user *b = buf;DECLARE_WAITQUEUE(wait, current);int c;int minimum, time;ssize_t retval = 0;ssize_t size;long timeout;unsigned long flags;int packet;do_it_again:BUG_ON(!tty->read_buf);c = job_control(tty, file);if (c < 0)return c;minimum = time = 0;timeout = MAX_SCHEDULE_TIMEOUT;if (!tty->icanon) {time = (HZ / 10) * TIME_CHAR(tty);minimum = MIN_CHAR(tty);if (minimum) {if (time)tty->minimum_to_wake = 1;else if (!waitqueue_active(&tty->read_wait) || (tty->minimum_to_wake > minimum))tty->minimum_to_wake = minimum;} else {timeout = 0;if (time) {timeout = time;time = 0;}tty->minimum_to_wake = minimum = 1;}}if (file->f_flags & O_NONBLOCK) {if (!mutex_trylock(&tty->atomic_read_lock))return -EAGAIN;} else {if (mutex_lock_interruptible(&tty->atomic_read_lock))return -ERESTARTSYS;}packet = tty->packet;add_wait_queue(&tty->read_wait, &wait);while (nr) {/* First test for status change. */if (packet && tty->link->ctrl_status) {unsigned char cs;if (b != buf)break;spin_lock_irqsave(&tty->link->ctrl_lock, flags);cs = tty->link->ctrl_status;tty->link->ctrl_status = 0;spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);if (tty_put_user(tty, cs, b++)) {retval = -EFAULT;b--;break;}nr--;break;}set_current_state(TASK_INTERRUPTIBLE);if (((minimum - (b - buf)) < tty->minimum_to_wake) &&    ((minimum - (b - buf)) >= 1))tty->minimum_to_wake = (minimum - (b - buf));if (!input_available_p(tty, 0)) {if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {retval = -EIO;break;}if (tty_hung_up_p(file))break;if (!timeout)break;if (file->f_flags & O_NONBLOCK) {retval = -EAGAIN;break;}if (signal_pending(current)) {retval = -ERESTARTSYS;break;}/* FIXME: does n_tty_set_room need locking ? */n_tty_set_room(tty);timeout = schedule_timeout(timeout);continue;}__set_current_state(TASK_RUNNING);/* Deal with packet mode. */if (packet && b == buf) {if (tty_put_user(tty, TIOCPKT_DATA, b++)) {retval = -EFAULT;b--;break;}nr--;}if (tty->icanon && !L_EXTPROC(tty)) {/* N.B. avoid overrun if nr == 0 */while (nr && tty->read_cnt) {int eol;eol = test_and_clear_bit(tty->read_tail,tty->read_flags);c = tty->read_buf[tty->read_tail];spin_lock_irqsave(&tty->read_lock, flags);tty->read_tail = ((tty->read_tail+1) &(N_TTY_BUF_SIZE-1));tty->read_cnt--;if (eol) {if (--tty->canon_data < 0)tty->canon_data = 0;}spin_unlock_irqrestore(&tty->read_lock, flags);if (!eol || (c != __DISABLED_CHAR)) {if (tty_put_user(tty, c, b++)) {retval = -EFAULT;b--;break;}nr--;}if (eol) {tty_audit_push(tty);break;}}if (retval)break;} else {int uncopied;uncopied = copy_from_read_buf(tty, &b, &nr);uncopied += copy_from_read_buf(tty, &b, &nr);if (uncopied) {retval = -EFAULT;break;}}if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {n_tty_set_room(tty);check_unthrottle(tty);}if (b - buf >= minimum)break;if (time)timeout = time;}mutex_unlock(&tty->atomic_read_lock);remove_wait_queue(&tty->read_wait, &wait);if (!waitqueue_active(&tty->read_wait))tty->minimum_to_wake = minimum;__set_current_state(TASK_RUNNING);size = b - buf;if (size) {retval = size;if (nr)clear_bit(TTY_PUSH, &tty->flags);} else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again;n_tty_set_room(tty);return retval;}

六.线路规程的操作函数集会调用对应的tty_struct的操作函数集
在initialize_tty_struct中tty->ops = driver->ops也就是说tty_struct的操作函数集就是tty_driver的操作函数集合

tty_driver的操作函数集设置在其register之前,例如串口的注册函数uart_register_driver中调用tty_set_operations(normal, &uart_ops)

将tty_driver->ops设置为uart_ops。

这样我们对/dev/ttyXXX的操作的调用顺序:字符设备的操作函数集tty_fops-->线路规程的操作函数集tty_ldisc_N_TTY-->串口核心或其他类型设备的操作函数集uart_ops
五 linux 串口驱动




 

热点排行