Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1811671
  • 博文数量: 272
  • 博客积分: 1272
  • 博客等级: 少尉
  • 技术积分: 1866
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-09 15:51
文章分类

全部博文(272)

文章存档

2016年(16)

2015年(28)

2014年(97)

2013年(59)

2012年(25)

2011年(47)

分类: LINUX

2013-11-15 16:13:12

linux tty 核心是构建在标准字符设备驱动之上,提供一系列功能,作为接口为终端类型设备驱动所使用。在前面的分析中我们看到 tty核心控制了通过tty设备的数据流,格式化了这些数据流,所以常规tty驱动的工作不必考虑常规操作方法与用户空间的交互,同时不同的线路规程可以虚拟的插入到任何tty设备上。从前面的各种方法分析上,我们知道tty核心从用户空间得到数据把数据发送到tty线路规程,线路规程驱动把数据传递给tty驱动程序,同时tty设备从硬件那里接收到的数据传递给tty线路规程,线路规程再传递给tty核心。所以tty驱动程序的主要任务放在处理流向或流出硬件的数据上。

      tty驱动程序在内核中用struct tty_driver表示,在tty核心中有一个tty_drivers链表,任何tty设备驱动程序都挂在在这个链表上。从tty_open的操作中就有一个get_tty_driver函数就是根据设备号在tty_drivers链表中找到相应的设备驱动程序。

      tty类设备主要分为三类:控制台、伪终端、串口。其中前两者前面已经介绍了内核为我们实现好了,所以以后我们写tty设备驱动程序主要就是写串口的设备驱动程序。tty驱动程序通俗的说就是分配一个tty_driver结构初始化后并把其挂载到tty_dirvers链表上。下面我们看看struct tty_driver结构及其成员。


extern struct list_head tty_drivers;


struct tty_driver {
int magic;  /* magic number for this structure */
struct cdev cdev;                       //tty驱动是一个字符设备驱动
struct module *owner;
const char *driver_name;          //驱动程序名字
const char *name;                    //设备名称的一部分
int name_base; /* offset of printed name */
int major;  /* major device number */                 //主设备号
int minor_start; /* start of minor device number */  //次设备号的起始
int minor_num; /* number of *possible* devices */   //设备号的个数
int num;  /* number of devices allocated */                //设备个数
short type;  /* type of tty driver */                            
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */       //初始化设备的struct ktermmios结构
int flags;  /* tty driver flags */
int refcount; /* for loadable tty drivers */                //引用计数
struct proc_dir_entry *proc_entry; /* /proc fs entry */ 
struct tty_driver *other; /* only used for the PTY driver */   //伪终端时指向通信另一端

/*
  * Pointer to the tty data structures
  */
struct tty_struct **ttys;       //驱动所属设备的tty_struct 结构
struct ktermios **termios;   //每个设备相应的
struct ktermios **termios_locked;
void *driver_state;             //驱动状态

/*
  * Driver methods
  */

const struct tty_operations *ops; //驱动程序的操作函数(驱动的主要工作)
struct list_head tty_drivers;         
};

      下面介绍实现一个tty设备驱动的主要流程:

       1)首先调用alloc_tty_driver动态分配一个tty_driver结构,lines表示驱动中设备的个数

struct tty_driver *alloc_tty_driver(int lines)
{
struct tty_driver *driver;

driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (driver) {
  driver->magic = TTY_DRIVER_MAGIC;
  driver->num = lines;
  /* later we'll move allocation of tables here */
}
return driver;
}

      2)初始化tty_driver中各个成员 其中主要是 init_termios成员和ops成员 。如果用户在端口初始化以前使用端口,该变量用来提供一系列安全的设置值,成员的具体含义在termios手册中能找到这里就不具体介绍。其次就是tty设备驱动最根本的操作函数集ops,并用set_tty_operations和驱动关联在前面的分析时我们已经知道这些回调函数的具体调用位置。

struct tty_operations {
int  (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
int  (*write)(struct tty_struct * tty,
        const unsigned char *buf, int count);
int  (*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);
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);
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);
int (*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 (*tiocmget)(struct tty_struct *tty, struct file *file);
int (*tiocmset)(struct tty_struct *tty, struct file *file,
   unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty,
    struct winsize *ws);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *driver, int line, char *options);
int (*poll_get_char)(struct tty_driver *driver, int line);
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
};

3)初始化好tty_driver就调用tty_register_driver在内核中注册一个字符设备驱动并把tty_driver注册到tty_drivers链表中

/*
* Called by a tty driver to register itself.
*/
int tty_register_driver(struct tty_driver *driver)
{
int error;
int i;
dev_t dev;
void **p = NULL;

if (driver->flags & TTY_DRIVER_INSTALLED)
  return 0;

if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
  p = kzalloc(driver->num * 3 * 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;
}

//为每个设备的及其ktermios分配指针空间

if (p) {
  driver->ttys = (struct tty_struct **)p;
  driver->termios = (struct ktermios **)(p + driver->num);
  driver->termios_locked = (struct ktermios **)
       (p + driver->num * 2);
} else {
  driver->ttys = NULL;
  driver->termios = NULL;
  driver->termios_locked = 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 = driver->termios_locked = NULL;
  kfree(p);
  return error;
}

//tty_driver加入链表

mutex_lock(&tty_mutex);
list_add(&driver->tty_drivers, &tty_drivers);
mutex_unlock(&tty_mutex);

//动态加载设备时,注册驱动控制的设备

if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
  for (i = 0; i < driver->num; i++)
      tty_register_device(driver, i, NULL);
}
proc_tty_register_driver(driver);
return 0;
}


至此设备驱动注册成功,当卸载模块时调用tty_unregister_driver完成相反的工作

阅读(1355) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~