linux中uart设备名是什么,linux uart设备驱动结构分析

http://blog.chinaunix.net/u3/118615/showart_2320858.html

1概述

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

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

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

100907155710.jpg

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.1uart_register_driver()

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

(1)申请数据结构

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

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

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

#define UART_NRfs_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;

3)tty_set_operations(normal,

&uart_ops);

设定tty_driver的操作函数集为uart_ops。Uart_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)结构变量值

2)cpm_reg.state->uart_info

3)tasklet下半部注册

2.2.2tty_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.1cpm_uart_ports[UART_NR]

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

struct uart_cpm_port {

struct

uart_portport;

u16rx_nrfifos;

u16rx_fifosize;

u16tx_nrfifos;

…….

u32command;

intgpios[NUM_GPIOS];

};

struct uart_cpm_port

cpm_uart_ports[UART_NR];

2.3.2cpm_uart_init_port

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

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

2.3.3uart_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_console是console类型的静态数据结构变量:

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定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值