linux UART驱动

来源:互联网 发布:如何清理mac磁盘空间 编辑:程序博客网 时间:2024/06/08 08:59

module_init(s3c24xx_serial_modinit);static int __init s3c24xx_serial_modinit(void){int ret;ret = uart_register_driver(&s3c24xx_uart_drv);if (ret < 0) {printk(KERN_ERR "failed to register UART driver\n");return -1;}return 0;}

我们先看一下 uart_register_driver 函数的原型

int uart_register_driver(struct uart_driver *drv);

该函数只有一个参数 struct uart_driver *drv
struct uart_driver 结构在uart驱动开发中是一个非常重要的结构,先看一下这个结构体的定义:

struct uart_driver {struct module*owner;const char*driver_name;const char*dev_name;int major;int minor;int nr;struct console*cons;/* * these are private; the low level driver should not * touch these; they should be initialised to NULL */struct uart_state*state;struct tty_driver*tty_driver;};

前面几个成员都比较简单,规定了驱动的名称和设备的名称,还有主、次设备号和uart设备的个数;
最后两个结构体成员比较重要,看上面的注释说这是私有的数据,驱动开发者不应该去操作这两个成
员,它们应该被初始化为空。
事实上,uart_register_driver 函数主要就是在操作这两个结构体成员,下面的代码将会分析。作为
驱动开发者,我们应该像下面这样定义一个 struct uart_driver 结构体的变量:

static struct uart_driver s3c24xx_uart_drv = {.owner= THIS_MODULE,.dev_name= "s3c2410_serial",.nr= CONFIG_SERIAL_SAMSUNG_UARTS,.cons= S3C24XX_SERIAL_CONSOLE,.driver_name= S3C24XX_SERIAL_NAME,.major= S3C24XX_SERIAL_MAJOR,.minor= S3C24XX_SERIAL_MINOR,};int uart_register_driver(struct uart_driver *drv){struct tty_driver *normal = NULL;int i, retval;BUG_ON(drv->state);/* * Maybe we should be using a slab cache for this, especially if * we have a large number of ports to handle. *///说明1drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);retval = -ENOMEM;if (!drv->state)goto out;//说明2normal  = alloc_tty_driver(drv->nr);if (!normal)goto out;drv->tty_driver = normal;normal->owner= drv->owner;normal->driver_name= drv->driver_name;normal->name= drv->dev_name;normal->major= drv->major;normal->minor_start= drv->minor;normal->type= TTY_DRIVER_TYPE_SERIAL;normal->subtype= SERIAL_TYPE_NORMAL;normal->init_termios= tty_std_termios;normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;normal->flags= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;normal->driver_state    = drv;tty_set_operations(normal, &uart_ops);/* * Initialise the UART state(s). *///说明3for (i = 0; i < drv->nr; i++) {struct uart_state *state = drv->state + i;struct tty_port *port = &state->port;tty_port_init(port);port->close_delay     = 500;/* .5 seconds */port->closing_wait    = 30000;/* 30 seconds */tasklet_init(&state->tlet, uart_tasklet_action,     (unsigned long)state);}//说明4retval = tty_register_driver(normal); out:if (retval < 0) {put_tty_driver(normal);kfree(drv->state);}return retval;}

说明1:
分配drv->nr个struct uart_state结构体大小的内存,把地址赋给drv->state,
struct uart_state也是一个很重要的结构体,其中的struct uart_port *uart_port成员
将在我们自己写的驱动probe函数中进行初始化,后面会分析。


说明2:
这里主要为成员变量 drv->tty_driver 分配内存,并且初始化 tty_driver。
可以看出前面几个成员的值都是通过drv传递进来的;最后将成员driver_state指向drv;
tty_set_operations(normal, &uart_ops)这个函数很简单,主要是为成员ops赋值。
变量 uart_ops 是一个全局变量。

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);driver->magic = TTY_DRIVER_MAGIC;driver->num = lines;/* later we'll move allocation of tables here */}return driver;}void tty_set_operations(struct tty_driver *driver,const struct tty_operations *op){driver->ops = op;};

说明3:
这里主要初始化struct tty_driver结构体中的struct tty_port成员

说明4:
调用 tty_register_driver 函数注册一个字符设备

int tty_register_driver(struct tty_driver *driver){int error;int i;dev_t dev;void **p = NULL;////TTY_DRIVER_DEVPTS_MEM:使用devpts进行动态内存映射//driver->flags= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;//说明1if (!(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;}//说明2if (p) {driver->ttys = (struct tty_struct **)p;driver->termios = (struct ktermios **)(p + driver->num);} 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);//说明3: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);driver->flags |= TTY_DRIVER_INSTALLED;return 0;}

说明:
在这个函数里我们可以看见这几个很熟悉的函数:
1,register_chrdev_region
2,cdev_init
3,cdev_add
以上这些都表明这是在向内核注册一个字符设备。


说明1和说明2:
先看一下 struct tty_driver 结构体中的两个成员:

struct tty_driver {.../* * Pointer to the tty data structures */struct tty_struct **ttys;struct ktermios **termios;...};

这两个成员都是二级指针,注释说明它们是指向tty数据结构的指针,这里首先为它们
分配地址,其中的ttys变量将会在调用tty_open的时候用到。


说明3:
将该驱动挂载到全局变量tty_drivers中,将来我们调用tty_open的时候会从这个全局变量
中查找该驱动。




附记:
A:
cdev_init函数中用到的变量 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,};

B:
uart_register_driver -> tty_set_operations(normal, &uart_ops)中
的 uart_ops 也是一个全局变量,定义如下:

static const struct tty_operations uart_ops = {.open= uart_open,.close= uart_close,.write= uart_write,.put_char= uart_put_char,.flush_chars= uart_flush_chars,.write_room= uart_write_room,.chars_in_buffer= uart_chars_in_buffer,.flush_buffer= uart_flush_buffer,.ioctl= uart_ioctl,.throttle= uart_throttle,.unthrottle= uart_unthrottle,.send_xchar= uart_send_xchar,.set_termios= uart_set_termios,.set_ldisc= uart_set_ldisc,.stop= uart_stop,.start= uart_start,.hangup= uart_hangup,.break_ctl= uart_break_ctl,.wait_until_sent= uart_wait_until_sent,#ifdef CONFIG_PROC_FS.proc_fops= &uart_proc_fops,#endif.tiocmget= uart_tiocmget,.tiocmset= uart_tiocmset,#ifdef CONFIG_CONSOLE_POLL.poll_init= uart_poll_init,.poll_get_char= uart_poll_get_char,.poll_put_char= uart_poll_put_char,#endif};

C: 
struct uart_state 结构的声明如下:

struct uart_state {struct tty_portport;intpm_state;struct circ_bufxmit;struct tasklet_structtlet;struct uart_port*uart_port;};

D: 
struct tty_driver 结构的声明如下:

struct tty_driver {intmagic;/* magic number for this structure */struct kref kref;/* Reference management */struct cdev cdev;struct module*owner;const char*driver_name;const char*name;intname_base;/* offset of printed name */intmajor;/* major device number */intminor_start;/* start of minor device number */intminor_num;/* number of *possible* devices */intnum;/* number of devices allocated */shorttype;/* type of tty driver */shortsubtype;/* subtype of tty driver */struct ktermios init_termios; /* Initial termios */intflags;/* 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;struct ktermios **termios_locked;void *driver_state;/* * Driver methods */const struct tty_operations *ops;struct list_head tty_drivers;};

总结:
uart_register_driver函数主要围绕着结构体 struct uart_driver 展开,
其中该结构中的前几个成员需要我们自己定义,最后两个成员由该函数分配内存
并初始化。
uart_register_driver函数最后调用tty_register_driver函数向内核中注册
一个字符设备。
struct uart_driver结构起着非常重要的作用,在后面其它的操作中,基本上
都是围绕着该结构进行的














0 0