2.10 Linux 串口、CF卡、MTD、I2C驱动分析

本文以风河linux代码为例,解析Linux 串口、CF卡、flash MTD、I2C驱动的具体实现。

1、串口驱动

1.1、原理简介

系统A 的xlr732cpu提供了两个串口,我们使用了其中一个串口作为控制台。串口虽然简单,但是作用是非常重要的。在内核调试的初期一切都未正常,只能向串口直接输出调试信息来调试;在系统的启动阶段也只能向串口打印启动信息。

在Linux系统中,串口驱动的架构如下:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

1.2、直接向串口打印

在内核调试的初始阶段,一切都是混沌,我们可以使用直接向串口输出数据的方法来调试。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

1.3、串口驱动的注册

直接向串口输出数据是在系统初始阶段干的事情。如果要把串口当成可以输入输出的交互接口,串口必须要向系统注册成标准的串口设备。

上面串口层次结构图中串口的实现有很多层,但是用户串口驱动层来看,注册一个串口驱动需要调用的接口就两个:

  • 1、uart_register_driver() //注册uart串口驱动
  • 2、uart_add_one_port() //增加实体串口

1.4、8250串口驱动的注册

wr3.0的串口驱动代码在drivers\serial\8250.c文件当中,我们分析其具体实现,它是利用了“platform”总线的device、driver架构来调用uart_register_driver()、uart_add_one_port(),以实现串口的驱动注册。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

上面说到实际的 “serial8250” platform device在arch\mips\rmi\ptr\setup.c中定义,我们看看其具体的实现:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

再看看“serial8250” platform driver的probe函数的实现:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

1.5、串口的数据收发

从上往下整个串口的收发层次比较复杂,我们只看8250串口驱动内的数据收发关键函数。receive_chars()、transmit_chars()。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

1.6、Console串口的注册

串口不但被注册成了uart字符驱动,还注册成了console。Console的作用就是使用printk输入内核打印时,会打印到每个注册console上。串口console功能和字符设备功能是独立的。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

2、CF卡驱动

2.1、原理简介

系统A 的local bus cs4上挂接了一个cf卡,我们的cf卡使用的是ide接口,在linux系统下被注册成了标准的ide块设备。可以通过命令查看:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

在linux系统中,ide块设备驱动的架构如下:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

我们分析wr.14的cf卡ide驱动代码,看看其实现原理。Ide驱动也是采取linux通用的驱动模型:bus、device、driver。

2.2、Ide bus注册

系统首先注册一个“ide”总线,以供ide driver和ide device向上挂载。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

在dirvers\ide\ide.c中可以看到“ide”总线注册的过程:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

ide_bus_type数据结构中需要关注的是“.match”函数ide_bus_match(),其是用来匹配driver和device的。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

ide_bus_match()固定的返回1,说明“ide”总线对所有的driver和device进行适配。

另外需要注意的是ide_init()中调用的init_ide_data()和probe_for_hwifs()函数。init_ide_data()函数初始化设备数组ide_hwifs[];probe_for_hwifs()函数把各个ide设备的参数填进ide设备数组ide_hwifs[]中,系统会根据ide_hwifs[]来逐个注册ide设备。后面详细描述其实现。

2.3、Ide driver注册

我们可以看到内核中引用ide_bus_type的驱动有idedisk_driver、ide_cdrom_driver、idefloppy_driver、idescsi_driver、idetape_driver,但是我们cf卡只使用其中的disk驱动,所以我们只分析idedisk_driver。

在drivers\ide\ide-disk.c中,我们可以看到idedisk_driver的注册过程:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

驱动注册完成后,可以在“ide”总线的driver目录中看到:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

idedisk_driver数据结构中需要关注的是 “.gen_driver/.probe”函数ide_disk_probe(),该函数的目的是在有ide device插入并且适配idedisk_driver驱动,就调用probe函数执行初始化和注册块设备。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

2.4、Ide device注册

有了“ide”总线,有了“ide-disk”驱动,还需要向系统添加我们的ide设备。

入口在drivers\ide\ide-generic.c中的ide_generic_init:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

继续调用ideprobe_init()函数:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

需要关注ideprobe_init()->hwif_init()->init_irq()->ide_init_queue()函数,ide_init_queue()中初始化了ide块设备的队列,真正的数据读写都是在这里实现的。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

2.5、用户Ide设备的注册

从上面ide驱动的架构分析可以看到,ide总线、ide驱动、ide块设备的注册都是标准接口,关于块设备操作ide命令接口也是标准的。用户新添加ide设备,只需要向ide设备数组ide_hwifs[]中添加本设备的io基地址和中断,系统就会自动的将你的ide设备添加上。

我们看到wr1.4中的cf卡驱动drivers\ide\mips\ide-cfcard.c中的核心实现就是把本设备添加到ide_hwifs[]中:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

2.6、Wr3.0中Ide device的注册

以上几小节描述的都是wr1.4内核中的ide驱动架构,在wr3.0中ide驱动架构有稍许修改。关于ide bus和ide driver的注册没有改变,关于ide device的注册方法有变动。在wr1.4中,用户把所有ide设备的参数加到ide_hwifs[]数组中,由系统统一注册成ide device;而在wr3.0中是每个ide设备独立注册的。

我们来研究wr3.0 cf卡驱动的实现过程,在drivers\ide\mips\phoenix_ide.c中:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

可以看到用户ide驱动向“/sys/bus/platform”总线注册了一个驱动和一个设备。我们看看platform_bus_type的“.match”函数的实现。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

看到只要驱动和设备的名称一致,就认为适配。所以platform_bus_type总线只是简单提供了一个bus、driver、device的架构,只要driver、device的名字一致就启动适配。

phoenix_ide_init_module()函数最终的目的调用到了phoenix_ide_driver的“.probe” phoenix_ide_probe()函数。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

可以看到phoenix_ide_probe()的目的就是根据用户参数初始化一个hw_regs_t数据结构,再根据这个数据结构向系统注册ide设备。调用过程为ide_host_add() ->ide_host_register() ->hwif_register_devices() ->device_register()。

3、I2C驱动

3.1、原理简介

系统A 的cpu xlr732自带两条i2c总线:一条用来挂接eeprom,内部存储网口mac地址等信息;一条作为ipmi总线和bmc通讯。
I2c总线在linux下被注册成i2c_adapter,并且被 “i2c-dev”驱动用字符驱动的形式呈现给用户。可以通过命令查看:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

在讲解linux i2c驱动架构之前,先熟悉几个概念:

  • i2c_adapter:对应一个物理上的适配器;
  • i2c_algorithm:是对适配器的支持的算法。就是通信协议,用来驱动i2c_adapter;
  • i2c_client:对应一个物理上的设备;
  • i2c_driver:设备驱动,用来驱动i2c_client;

在linux系统中,i2c设备驱动的架构如下:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

i2c驱动也遵循一般linux驱动的bus、driver、device架构:

  • 首先驱动会注册一个i2c总线i2c_bus_type,即“/sys/bus/i2c”;
  • i2c总线上的driver为i2c_driver类型,即“/sys/bus/i2c/drivers”下的驱动;
  • i2c总线上的device为i2c_adapter类型,即i2c的物理适配器(也是i2c物理总线),对应 “/sys/class/i2c-adapter/”下的设备。
  • i2c总线上另一种device为i2c_client类型,物理上对应的是i2c总线上的设备,每一个i2c_client需要绑定i2c_adapter。

I2c驱动中driver和device的适配方法有两种:

  • 一种是Adapter方式(LEGACY),用于i2c_driver和i2c_adapter的适配,调用i2c_driver的driver->attach_adapter()函数来进行适配和初始化;
  • 另一种是Probe方式(new style),用于i2c_driver和i2c_client的适配,使用i2c_bus_type总线的“.match”函数i2c_device_match()来进行适配,适配成功调用i2c_driver的driver->probe()函数来进行初始化。

我们来分析wr3.0内核中i2c驱动的具体实现:

3.2、I2c bus注册

在drivers\i2c\i2c-core.c文件i2c_init()函数中注册“/sys/bus/i2c/”总线:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

3.3、I2c device注册(i2c_adapter)

I2c总线适配器,i2c_adapter类型device的注册,使用drivers\i2c\i2c-core.c文件中的i2c_add_adapter ()函数。

i2c_adapter类型的device的注册并没有指定bus为i2c_bus_type,而是指定class为i2c_adapter_class,所以i2c_adapter类型的device要和i2c_bus_type上挂载的驱动适配,需要手工遍历i2c_bus_type上挂载的驱动,调用driver->attach_adapter()函数来进行适配和初始化。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

3.4、I2c device注册(i2c_client)

I2c总线上挂载i2c设备,i2c_client类型device的注册,使用drivers\i2c\i2c-core.c文件中的i2c_attach_client ()函数。

i2c_client类型的device的注册到了i2c_bus_type总线上,所以i2c_driver和i2c_client的适配,使用i2c_bus_type总线的“.match”函数i2c_device_match()来进行适配,适配成功调用i2c_driver的driver->probe()函数来进行初始化。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

3.5、I2c driver注册

i2c_driver类型driver的注册,使用drivers\i2c\i2c-core.c文件中的i2c_register_driver ()函数。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

i2c_bus_type总线的“.match”函数i2c_device_match() :

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

i2c_bus_type总线的“.probe”函数i2c_device_probe () :

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

3.6、I2c驱动的数据读写

我们来分析一下I2c驱动的数据读写。

I2c驱动的数据读写是站在i2c_client基础之上的,普通的数据读写使用i2c_master_recv()和i2c_master_send()对i2c_client进行操作。基于smbus的数据读写使用i2c_smbus_read_byte()、i2c_smbus_write_byte()等一系列函数对i2c_client进行操作。

如果没有注册i2c_client,只注册了i2c_adapter,可以新建一个匿名的i2c_client来绑定i2c_adapter,以实现I2c的读写,i2c_adapter的字符设备i2c-dev.c就是这样干的。

drivers\i2c\i2c-core.c文件中可以看到i2c内核读写的实现过程:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

i2c_master_recv()和i2c_master_send()最后都是调用i2c_transfer()来对i2c_client绑定的i2c_adapter进行数据传输。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

可以看到i2c_transfer()进一步调用到i2c_adapter的i2c_algorithm->master_xfer()函数。i2c_adapter的i2c_algorithm是i2c适配器的支持的算法,即是i2c适配器是哪种类型的适配器,怎么样操作i2c适配器的控制寄存器来发起i2c数据读写。

i2c_algorithm在i2c_adapter使用i2c_add_adapter()注册前初始化。例如我们使用的palm类型algorithm的i2c_adapter的注册,drivers\i2c\algos\ i2c-algo-palm.c文件中可以看到其实现过程:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

palm_algo中的“.master_xfer”函数palm_xfer()会调用到palm_tx()、palm_tx() —> palm_write()、palm_read()—> algo_data->write()、algo_data->read()—> adap->algo_data->write()、adap->algo_data-> read ()。

可见最终调用到了i2c_adapter的algo_data->write()和algo_data-> read ()函数,这两个函数在drivers\i2c\ busses\i2c-bk3220.c文件中定义,这两个函数提供了对i2c适配器的控制寄存器的基本读写函数。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

3.7、I2c client的字符设备呈现

向系统注册成功i2c_client类型的设备,可以在内核态通过i2c_master_recv()和i2c_master_send()对i2c_client进行读写操作。但是如果想在用户态能够对i2c_client类型的设备进行读写访问的话,还需要建立字符设备,把i2c_master_recv()和i2c_master_send()操作封装成字符设备的cdev->file_operations->read()和cdev->file_operations->write()操作。

下面是一个实例,怎么样把i2c_client类型的设备封装成字符设备:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

3.8、I2c adapter的字符设备呈现

上面看到读写i2c数据需要通过i2c_client来操作。但是如果没有注册i2c_client,只注册了i2c_adapter,怎么来启动读写呢?

可以新建一个匿名的i2c_client来绑定i2c_adapter,以实现I2c的读写。i2c-dev.c就是这样干的,其目的是把i2c_adapter封装成字符设备呈现给用户态使用。drivers\i2c\i2c-dev.c文件中可以看到其实现过程:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

4、Flash MTD驱动

4.1、原理简介

系统A cpu上挂载了一个bootrom和一个flash芯片,bootrom芯片用来存储基本bootrom,flash芯片用来存储扩展bootrom和lbp。
其中flash芯片在linux下面被注册成了基于mtd的块设备和字符设备:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

在linux系统中,flash mtd设备驱动的架构如下:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

Flash驱动的注册定义在drivers\mtd\maps\mrp_flash.c中,其本身并不复杂,我们通过分析其过程来研究wr3.0 linux mtd驱动原理。

4.2、用户flash设备注册

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

4.3、探测flash的mtd_info信息

do_map_probe()函数用来探测生成flash的mtd_info信息。参数为:探测方法名和flash的地址信息map_info。函数定义在drivers\mtd\chips\chipreg.c中:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

我们可以看到,通过驱动名来查找探测驱动,其实质就是在chip_drvs_list链表中查找探测驱动。而探测驱动的注册函数register_mtd_chip_driver()就是将驱动加入到chip_drvs_list链表中。

我们查看有谁来调用register_mtd_chip_driver()来判断系统有哪些探测驱动,可以看到有cfi、jedec、map_absent、map_ram、map_rom等探测驱动。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

最常用的就是cfi探测,我们下面只分析cfi的探测方法。

4.4、CFI接口flash信息探测

cfi全称为Common Flash Memory Interface,多种不同类型flash芯片都支持,是一种通用flash信息查询接口。

一般进入cfi查询模式的命令为向地址0x55写数据0x98:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

Cfi提供的信息中包括了:

  • 1、 芯片使用的读、写、擦除的命令cmdset;即使用哪种风格的命令组合来启动读、写、擦除;
  • 2、 芯片的时序参数;
  • 3、 芯片的大小;

等等信息,如下图:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

cfi接口的flash探测驱动定义在drivers\mtd\chips\cfi_probe.c中:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

Cfi探测驱动的“.probe”函数为cfi_probe():

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

.5、AMD 命令集的flash操作方法

从上节cfi读出的信息中,有两个字段可以表示flash的操作命令集id,即flash读、写、擦除的方法。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

对命令集id的解析如下:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

可以看到flash的命令集有很多种,但是我们现在应用的是amd命令集,我们具体介绍这一种。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

4.6、添加flash分区

探测完flash芯片的mtd_info信息以后,回到用户init_mrp6600flash()函数中,下一步调用add_mtd_partitions()函数来增加flash的分区。定义在drivers\mtd\mtdpart.c中:

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

我们可以看到添加分区add_mtd_partitions()函数最终调用到了使用mtd_notifiers链表中的函数来进行实际的分区注册。
而向mtd_notifiers链表中注册是使用register_mtd_user():

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

通过对register_mtd_user()函数调用关系的分析,可以可以看到mtd_blk、mtdchar、mtdoops三种注册了自己的flash分区注册函数。我们只使用了mtd块设备和字符设备,所以我们只分析这两种的实现。

2.10 Linux 串口、CF卡、MTD、I2C驱动分析

4.7、添加flash分区的mtd块设备

Mtd块设备添加分区操作,定义在drivers\mtd\mtdblock.c中 :

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

4.8、mtd块设备的读写

块设备的读写函数实现在读写队列中,mtdblock的块设备读写队列在drivers\mtd\mtd_blkdevs.c中初始化:
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

Mtdblock的readsect()和writesect()函数,定义在drivers\mtd\mtdblock.c中 :

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

4.9、添加flash分区的mtd字符设备

Mtd字符设备添加分区操作,定义在drivers\mtd\mtdchar.c中 :

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析

我们可以看到mtd字符设备关于flash分区添加的钩子函数中,并没有做什么实际的事情。有关mtd字符设备的实现核心全都在mtdchar创建的mtd字符设备中。

4.10、mtd字符设备的操作函数

Mtd字符设备操作,定义在drivers\mtd\mtdchar.c中 :

2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析
2.10 Linux 串口、CF卡、MTD、I2C驱动分析