tty driver(2)

来源:互联网 发布:淘宝商城二手电脑 编辑:程序博客网 时间:2024/05/16 12:45

how to initialize the tty subsystem ?

1.chr_dev_init -> tty_init

这个tty_init必须的?什么用途?

fs_initcall(chr_dev_init);
chr_dev_init -> tty_init
int __init tty_init(void)
{
    cdev_init(&tty_cdev, &tty_fops);
    cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1);
    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty");
    device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
    
    and /dev/console
/*crw-rw-rw- root     root       5,   0 2012-02-24 04:30 tty*/
/*crw------- root     root       5,   1 2012-02-24 04:32 console*/
}

2.tty是怎样工作的?写个 tty driver 从哪里开始?

2.1 create tty_driver, tty_port_init and tty_register_device

1)create a tty_driver结构体,初始化,调用函数tty_register_driver注册该tty driver;
2)tty_port_init(port operatoin);
3)tty_register_device;

2.2   通过设备文件节点,调用注册的file operations

后来的就是有char device文件调用引起的一连串行为

3 怎样写tty driver

从上面看出我们需要做的就是2.1部分,其他的就是系统提供的。下面看一下tty driver是怎样创建的?

3.1.对写驱动而言,首先接触的 tty相关的结构体是tty_driver

struct tty_driver {
    int    magic;        /* magic number for this structure */
    struct kref kref;    /* Reference management */
    struct cdev cdev;
    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    num;        /* number of devices allocated */
    short    type;        /* type of tty driver */
    short    subtype;    /* subtype of tty driver */
    struct ktermios init_termios; /* Initial termios */
    int    flags;        /* tty driver flags */
    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;
    struct ktermios **termios;
    void *driver_state;

    /*
     * Driver methods
     */

    const struct tty_operations *ops;
    struct list_head tty_drivers;
};

3.2 tty_driver 结构体的创建

static int smd_tty_init(struct smd_driver_info *info)
{
    int ret;
    int i;

    smd_tty_driver = alloc_tty_driver(32);
    if (smd_tty_driver == 0)
        return -ENOMEM;

    smd_tty_driver->owner = THIS_MODULE;
    smd_tty_driver->driver_name = "smd_tty_driver";
  /*数据成员name,major,minor虽然出现在tty_driver中,却是用于创建char device的?
   *为什么不出现在  tty_device 中?没有tty_device这个结构体?
   *是个没有tty_device这个结构体,作为输入只能放这里了
   */
    smd_tty_driver->name = "ttyN";
    smd_tty_driver->major = 0;
    smd_tty_driver->minor_start = 0;
    /*TTY_DRIVER_TYPE_SERIAL有什么用途?
     *从代码只有fs/proc/proc_tty.c:show_tty_range ->
     *    case TTY_DRIVER_TYPE_SERIAL:
     *    seq_puts(m, "serial");
     *除了 proc文件系统的显示输出“serial”没什么高深用途。
     */
    smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
  /*从代码没找到使用SERIAL_TYPE_NORMAL的地方*/
    smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
    
  /*ktermios: 串口的控制相关,比如baud rate 等*/
    smd_tty_driver->init_termios = tty_std_termios;
    smd_tty_driver->init_termios.c_iflag = 0;
    smd_tty_driver->init_termios.c_oflag = 0;
    smd_tty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD;
    smd_tty_driver->init_termios.c_lflag = 0;
    smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
                            TTY_DRIVER_REAL_RAW |
                            TTY_DRIVER_DYNAMIC_DEV;
    tty_set_operations(smd_tty_driver, &smd_tty_ops);
    ret = tty_register_driver(smd_tty_driver);
    if (ret)
        return ret;

    for (i = 0; i < SMD_TTY_MAX; i++)
    {
        tty_port_init(&info->smd_tty[i].port);
        info->smd_tty[i].id=i;
        info->smd_tty[i].port.ops = &smd_tty_port_ops;
        tty_register_device(smd_tty_driver, info->smd_tty[i].id, 0);
    }
    
    return 0;
}


3.2.1 有关tty flag

 /*
 * TTY_DRIVER_RESET_TERMIOS --- requests the tty layer to reset the
 *     termios setting when the last process has closed the device.
 *     Used for PTY's, in particular.
 *
 * TTY_DRIVER_REAL_RAW --- if set, indicates that the driver will
 *     guarantee never not to set any special character handling
 *     flags if ((IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR ||
 *     !INPCK)).  That is, if there is no reason for the driver to
 *     send notifications of parity and break characters up to the
 *     line driver, it won't do so.  This allows the line driver to
 *    optimize for this case if this flag is set.  (Note that there
 *     is also a promise, if the above case is true, not to signal
 *     overruns, either.)
 *
 *如果设置该flag,就需要单独调用tty_register_device创建cdev,
 *如果不set this flag,就根据tty_driver->num,创建num个cdev。
 * TTY_DRIVER_DYNAMIC_DEV --- if set, the individual tty devices need
 *    to be registered with a call to tty_register_device() when the
 *    device is found in the system and unregistered with a call to
 *    tty_unregister_device() so the devices will be show up
 *    properly in sysfs.  If not set, driver->num entries will be
 *    created by the tty core in sysfs when tty_register_driver() is
 *    called.  This is to be used by drivers that have tty devices
 *    that can appear and disappear while the main tty driver is
 *    registered with the tty core.
 */

3.2.2 tty_driver内存分配

static struct tty_driver *smd_tty_driver;

为这个指针变量指向的内容分配内存:
smd_tty_driver = alloc_tty_driver(32);
#define alloc_tty_driver(lines) __alloc_tty_driver(lines, THIS_MODULE)
/*__alloc_tty_driver为tty_driver分配内存,并初始化该driver管理的设备个数*/
struct tty_driver *__alloc_tty_driver(int lines, struct module *owner)
{
    struct tty_driver *driver;

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

3.2.3 tty_operations

/*tty_operations: 的函数指针很多,可以分为三类:
 *(1):输入参数包含tty_driver的:lookup,install and remove
 *(2):输入参数包含tty_struct的
 *(3):const struct file_operations *proc_fops;

 *这只多tty_operations赋值以下几个函数指针,其他的都是不用的?
 static const struct tty_operations smd_tty_ops = {
    .open = smd_tty_open,
    .close = smd_tty_close,
    .write = smd_tty_write,
    .write_room = smd_tty_write_room,
    .chars_in_buffer = smd_tty_chars_in_buffer,
  };
  check的方法是从crash中看一下这个全局变量,这是个const型,它的内容应该不会被改变
  也就是说不会在被赋值,其他函数应该是不用啦。

 */
struct tty_operations {
    struct tty_struct * (*lookup)(struct tty_driver *driver,
            struct inode *inode, int idx);
    int  (*install)(struct tty_driver *driver, struct tty_struct *tty);
    void (*remove)(struct tty_driver *driver, struct tty_struct *tty);

    int  (*open)(struct tty_struct * tty, struct file * filp);
    void (*close)(struct tty_struct * tty, struct file * filp);
    void (*shutdown)(struct tty_struct *tty);
    void (*cleanup)(struct tty_struct *tty);
    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,
            unsigned int cmd, unsigned long arg);
    long (*compat_ioctl)(struct tty_struct *tty,
                 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 (*tiocmget)(struct tty_struct *tty);
    int (*tiocmset)(struct tty_struct *tty,
            unsigned int set, unsigned int clear);
    int (*resize)(struct tty_struct *tty, struct winsize *ws);
    int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
    int (*get_icount)(struct tty_struct *tty,
                struct serial_icounter_struct *icount);

    const struct file_operations *proc_fops;
};


3.2.4 tty_register_driver

/*
 * Called by a tty driver to register itself.
 */
int tty_register_driver(struct tty_driver *driver)
{
    int i;
    dev_t dev;
    void **p = NULL;
    struct device *d;
    /*为指针指向的内容分配memory*/
    if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
        p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
    }
    /*动态得到dev_t*/
    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);
        }
    }
    /*赋值ttys and termios*/
    if (p) {
        driver->ttys = (struct tty_struct **)p;
        driver->termios = (struct ktermios **)(p + driver->num);
    }
    /*value the cdev,并初始化该设备结点的file operations: tty_fops*/
    cdev_init(&driver->cdev, &tty_fops);
    driver->cdev.owner = driver->owner;
    cdev_add(&driver->cdev, dev, driver->num);

    /*把成员tty_drivers的地址连接到list head:tty_drivers中
         *tty_drivers这个全局变量具有重要作用,从中可以得到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++) {
            d = tty_register_device(driver, i, NULL);
            if (IS_ERR(d)) {
                error = PTR_ERR(d);
                goto err;
            }
        }
    }
    proc_tty_register_driver(driver);
    driver->flags |= TTY_DRIVER_INSTALLED;
    return 0;

}

3.2.5 tty_register_device


/*tty_register_device赋值这 device_create的输入参数,然后调用 device_create*/
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
        tty_line_name(driver, index, name);
    pr_err("tty-->:tty_register_device: dev node\n");
    return device_create(tty_class, device, dev, NULL, name);
}

4. 这里还有一个 tty_port and tty_port_operation,

tty_port在那个层次上?什么时候起作用?
/*
 * Port level information. Each device keeps its own port level information
 * so provide a common structure for those ports wanting to use common support
 * routines.
 *
 * The tty port has a different lifetime to the tty so must be kept apart.
 * In addition be careful as tty -> port mappings are valid for the life
 * of the tty object but in many cases port -> tty mappings are valid only
 * until a hangup so don't use the wrong path.
 */
struct tty_port_operations {
    /* Return 1 if the carrier is raised */
    int (*carrier_raised)(struct tty_port *port);
    /* Control the DTR line */
    void (*dtr_rts)(struct tty_port *port, int raise);
    /* Called when the last close completes or a hangup finishes
       IFF the port was initialized. Do not use to free resources. Called
       under the port mutex to serialize against activate/shutdowns */
    void (*shutdown)(struct tty_port *port);
    void (*drop)(struct tty_port *port);
    /* Called under the port mutex from tty_port_open, serialized using
       the port mutex */
        /* FIXME: long term getting the tty argument *out* of this would be
           good for consoles */
    int (*activate)(struct tty_port *port, struct tty_struct *tty);
    /* Called on the final put of a port */
    void (*destruct)(struct tty_port *port);
};
static const struct tty_port_operations smd_tty_port_ops = {
    .shutdown = smd_tty_port_shutdown,
    .activate = smd_tty_port_activate,

};


总之, 实现tty_driver 就是创建 tty_driver并赋值。主要的工作是写tty_operations的实现,至于 tty_port_operations是不是必须的待查明。

原创粉丝点击