Chinaunix首页 | 论坛 | 博客
  • 博客访问: 227672
  • 博文数量: 49
  • 博客积分: 2101
  • 博客等级: 大尉
  • 技术积分: 525
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-07 10:38
文章分类

全部博文(49)

文章存档

2010年(49)

我的朋友

分类: 嵌入式

2010-09-07 15:57:49

1 概述

       这里的uart设备是指物理设备,例如MPC8xxCPM中的SMC,这个设备可以工作在uart模式下。

       linux中,uart设备可以抽象成两类设备:serial设备和console设备。在cpm_uart_core.c中可以清楚的发现,对uart设备实现了两套驱动,分别针对这两类设备。Serial设备的抽象定义在serial_core.c中,而console设备的抽象定义在vt.c中。这两种抽象设备又统一于tty设备。tty设备是一种字符型设备。

       Console设备只用于内核空间的printk打印;而serial设备则可用于用户空间。 

2 serial设备驱动分析

2.1核心数据结构是:uart_driver

struct uart_driver {

       struct module         *owner;

       const char              *driver_name;

       const char              *dev_name;

       int                 major;

       int                 minor;

       int                 nr;

       struct console         *cons;

       struct uart_state      *state;

       struct tty_driver     *tty_driver;

};

其中tty_driver结构用于向tty层注册,而uart_state结构用于管理物理设备。

cpm_uart_core.c中有如下定义:

static struct uart_driver cpm_reg = {

       .owner           = THIS_MODULE,

       .driver_name  = "ttyCPM",

       .dev_name      = "ttyCPM",

       .major            = SERIAL_CPM_MAJOR,

       .minor            = SERIAL_CPM_MINOR,

       .cons              = CPM_UART_CONSOLE,

       .nr          = UART_NR,

};

2.2 cpm_uart_init

这个是模块的初始化函数,从这里开始分析。

2.2.1 uart_register_driver()

传入的参数就是上面提到的cpm_reg

1)申请数据结构

       申请UART_NRuart_state,并使cpm_reg.state指针指向新申请的空间;

       申请UART_NRtty_driver,并使cpm_reg.tty_driver指针指向新申请的空间;

       UART_NR,其值的实际大小定义在cpm_uart.h中:

#define UART_NR        fs_uart_nr

fs_uart_nr定义在fs_uart_pd.h中:

enum fs_uart_id {

       fsid_smc1_uart,

       fsid_smc2_uart,

       fsid_scc1_uart,

       fsid_scc2_uart,

       fsid_scc3_uart,

       fsid_scc4_uart,

       fs_uart_nr,

};

2)初始化cpm_reg.tty_driver

       1)结构中的变量;

       2)使cpm_reg.tty_driver->driver_state指针指向cpm_reg

       3tty_set_operations(normal, &uart_ops);

设定tty_driver的操作函数集为uart_opsUart_ops是定义在serial_core.c中静态变量:

       static const struct tty_operations uart_ops = {

       .open             = uart_open,

       .close             = uart_close,

       .write             = uart_write,

       .put_char = uart_put_char,

       ……

};

       从这里可以看出,很明显serial设备层是对各种uart物理设备的一种抽象,统一使用同样的操作函数。

3)初始化cpm_reg.state

       1)结构变量值

       2cpm_reg.state->uart_info

       3tasklet下半部注册

2.2.2 tty_register_driver

       传入的参数是cpm_reg->tty_driver

1)将tty_driver代表的tty设备注册为字符设备

2)设定字符设备的操作函数集为静态定义的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设备层已经完成了对所有tty设备的抽象,提供了统一的操作函数集。

3)将tty_driver加到系统的tty_driver链表

2.3 of_register_platform_driver

       2.2中,主要完成了uart层、tty层、chardev层三层的注册工作,但是,经过层层调用,最终一定要调用到具体的硬件设备操作函数才行。现在就是初始化这一部分内容。

       首先明确一点,硬件设备的驱动和cpm_reg->state->port相关连。很多硬件驱动函数的入参就是uart_port结构。数据结构的联系是:

       Uart_driver

              + uart_state

                     + uart_port

       2.2中,uart_port结构没有进行任何初始化,而这个工作就是下面要描述的过程。经过of_register_platform_driver的调用,会回调执行cpm_uart_probe函数,这个函数就是对uart_port数据结构的完善。

       Uart_port结构中有一个操作函数集指针:

       const struct uart_ops      *ops;

       这个指针应指向对应的物理设备的操作函数集,在cpm_uart_core.c中,就是指:

static struct uart_ops cpm_uart_pops = {

       .tx_empty       = cpm_uart_tx_empty,

       .set_mctrl       = cpm_uart_set_mctrl,

       .get_mctrl       = cpm_uart_get_mctrl,

       .stop_tx   = cpm_uart_stop_tx,

       .start_tx   = cpm_uart_start_tx,

       .stop_rx   = cpm_uart_stop_rx,

       .enable_ms     = cpm_uart_enable_ms,

       .break_ctl       = cpm_uart_break_ctl,

       .startup    = cpm_uart_startup,

       .shutdown       = cpm_uart_shutdown,

       .set_termios    = cpm_uart_set_termios,

       .type              = cpm_uart_type,

       .release_port   = cpm_uart_release_port,

       .request_port   = cpm_uart_request_port,

       .config_port    = cpm_uart_config_port,

       .verify_port    = cpm_uart_verify_port,

#ifdef CONFIG_CONSOLE_POLL

       .poll_get_char = cpm_get_poll_char,

       .poll_put_char = cpm_put_poll_char,

#endif

};

2.3.1 cpm_uart_ports[UART_NR]

这个数据结构代表了物理设备,它在uart_port结构的基础上,增加很多物理设备自有的特性。cpm_reg->state->port最终会指向这里面的uart_port port。这样uart_driver层就和物理驱动层建立了联系。

struct uart_cpm_port {

       struct uart_port      port;

       u16                rx_nrfifos;

       u16                rx_fifosize;

       u16                tx_nrfifos;

       …….

       u32                command;

       int                 gpios[NUM_GPIOS];

};

struct uart_cpm_port cpm_uart_ports[UART_NR];

2.3.2 cpm_uart_init_port

       这个函数用于初始化cpm_uart_ports及其中的uart_port结构。这个地方会根据open firmware中的fdt树对uart接口的描述来进行初始化。

       值得注意的是,用户对uart的一些初始化就可以写在这里。

2.3.3 uart_add_one_port

       uart_add_one_port(&cpm_reg, &pinfo->port);

       这样其入参,cpm_reg自然是uart_driver,而pinfo是指cpm_uart_ports,其内部的port自然是uart_port

       这个函数最大的作用就是使cpm_reg->state->port指向这里面的uart_port port

3 console设备驱动分析

3.1 vty_init

3.1.1 入参console_fops

tty_io.c中的tty_init函数会调用vt.c中的vty_init函数。所以初始化的起点可以定为vty_init函数。

       该函数的入参是

static const struct file_operations console_fops = {

       .llseek            = no_llseek,

       .read              = tty_read,

       .write             = redirected_tty_write,

       .poll        = tty_poll,

       .unlocked_ioctl      = tty_ioctl,

       .compat_ioctl  = tty_compat_ioctl,

       .open             = tty_open,

       .release    = tty_release,

       .fasync           = tty_fasync,

};

 

3.1.2 注册为字符设备

注册时,使用的操作函数集正是console_fops。这个console_fops和前面提到的tty_fops同样定义在tty_io.c中。仔细对比就会发现二者几乎相同。所以说,serial设备和console设备统一与tty设备。

3.1.3 设定tty驱动操作函数集

       tty_set_operations(console_driver, &con_ops);

这个con_ops定义在vt.c

static const struct tty_operations con_ops = {

       .open = con_open,

       .close = con_close,

       .write = con_write,

       .write_room = con_write_room,

       .put_char = con_put_char,

       .flush_chars = con_flush_chars,

       .chars_in_buffer = con_chars_in_buffer,

       .ioctl = vt_ioctl,

       .stop = con_stop,

       .start = con_start,

       .throttle = con_throttle,

       .unthrottle = con_unthrottle,

       .resize = vt_resize,

       .shutdown = con_shutdown

};

其可类比于定义于serial_core.c中的uart_ops。很明显这是两个不同的tty驱动集。

3.2 cpm_uart_console_init

cpm_uart_core.c中,通过声明

console_initcall(cpm_uart_console_init);

那么,cpm_uart_console_init函数就会被调用。cpm_uart_console_init调用了register_console函数,调用形式如下:

register_console(&cpm_scc_uart_console);

其中cpm_scc_uart_consoleconsole类型的静态数据结构变量:

static struct console cpm_scc_uart_console = {

       .name             = "ttyCPM",

       .write             = cpm_uart_console_write,

       .device           = uart_console_device,

       .setup             = cpm_uart_console_setup,

       .flags             = CON_PRINTBUFFER,

       .index            = -1,

       .data              = &cpm_reg,

};

很明显,里面的驱动函数是对uart硬件的又一套驱动。可以类比于static struct uart_ops cpm_uart_pops 定义。

 

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