随笔 - 89  文章 - 141  trackbacks - 0
<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

常用链接

留言簿(9)

随笔分类

随笔档案

文章分类

文章档案

相册

Gis

OpenSource

搜索

  •  

最新评论

阅读排行榜

评论排行榜


bh处理

1.三种旧式的bottom half 处理类型

IMMEDIATE_BH:  driver注册入tq_immediate队列,等待调度

TQUEUE_BH:       执行tq_timer,到系统tick产生时触发

TIMER_BH:    直接绑定到do_timer()处理函数

 

2.mark_bh()

       将激活32BH中的某一个,触发软中断来进入被调度状态

       此种软中断优先级为 HI_SOFTIRQ

      

3.tasklet

       此方法将触发软中断,使tasklet t被进入调度状态

       此方式触发的软中断优先级为TASKLET_SOFTIRQ

       使用方式:

              1. 声明tasklet:

                     DECLARE_TASKLET

              2. 执行tasklet

                     tasklet_schedule(t)

 

 

 

       init_bh(TIMER_BH, timer_bh);     定时器中断将执行 timer_bh任务队列

       init_bh(TQUEUE_BH, tqueue_bh);

       init_bh(IMMEDIATE_BH, immediate_bh);

BH后半部处理类型

interrupts.h

 

enum {

       TIMER_BH = 0, /*时钟*/ 

       TQUEUE_BH,   定时器队列

       DIGI_BH,

       SERIAL_BH,

       RISCOM8_BH,

       SPECIALIX_BH,

       AURORA_BH,

       ESP_BH,

       SCSI_BH,

       IMMEDIATE_BH,

       CYCLADES_BH,

       CM206_BH,

       JS_BH,

       MACSERIAL_BH,

       ISICOM_BH

};

 

bonfs

 

1.bonfs 是一种简易的只读文件系统,在2440项目中,bonfs是一个可选的块设备。

 bonfs是一种设备,他位于mtd管理层的上端,所以其可以 register_mtd_user()注册一个mtd_user.

此刻如果已经存在mtd设备或者mtd分区设备,在register_mtd_user()时,每个mtd设备都会通过 notifier_add()通知bonfs,然后bonfs便可以进行初始化操作。

 

vivi在进行分区时可以将分区格式化为若干bon分区( read_partition_info() )

bonnotifier_add()处理中,调用read_partition_info()读取分区开头8字节是否是bon分区的MAGIC标志

 

**bon.c代码,发现bon是将整个mtd设备(也就是整个nand flash)作为操作对象,而并不是一个mtd分区一个bon分区。

       似乎有点好理解了,(bon设备不依赖mtd分区),vivi 在用bon分区时,将分区信息写入nand flash 头部的512字节区域内,bon视整个flash是一个mtd设备,

       然后通过read_partition_info()bon分区信息读出,在内存中构建分区信息,所以在mtdblock.c代码中没有使用到mtd设备的分区信息,所以也就是不能用 root =/dev/mtdblock/0方式加载.

 

 

**vivi的代码中已经写死了整个nand flash的分区使用规则,如果不使用bonfs的话,就没有必要进行bon分区,分区只是搽除数据和写入标志,还有其他一些信息

 

 

       bon设备支持 add_mtd_device(s3c2440_mtd) 方式添加的mtd设备

 

如何读取和校验bon分区的:

 

       read_partition_info(){

              mtd->read_oob(( mtd->size - mtd->erasesize),8) erasesize开始读8字节oob数据

              if( oob[5] == 0xff){

                     buf = mtd->read( ( mtd->size - mtd->erasesize),512)  512字节,bon分区信息存储在这512字节区间内,比如分区偏移,大小,标志等等

                     if( buf[0~7]!=BON_MAGIC){

                            非法bon分区

                     }

              }

              devfs_mk_dir("bon")                   创建 /dev/bon

              mtd(nand flash)中读取bon分区表信息,构建分区数组

              devfs_register()            注册子设备文件 /dev/bon/0,1,2,3 (子设备号根据vivinand flash的分区)

              统计bad_block信息

       }

      

      

       bon_sizes[]     分区大小  ,k为计算单位

       bon_blksizes[] 分区块大小

      

      

      

.bon分区作为启动root分区

       cmd : char linux_cmd[] = "noinitrd root=/dev/bon/2 init=/linuxrc console=ttyS0";

       察看 main.c root_dev_names[]列表中并不存在bon的设备类型,原因在于bon是注册进入devfs,fs加载之前便已经初始化完毕了,

       所以传递以上内核参数是可以找到对应的设备的.

       root_dev_names[]定义的是固定的设备名称和设备编号,此时就需要实现此设备的驱动程序必须与设备编号进行匹配对应了,也就是靠devfs_register(MAJOR)注册的设备主编号

      

      

.bon分区启动

       start_kernel(){

              1.parse_command() 分析命令行参数 root=/dev/bon/2

              2.查找启动设备的主设备编号

              初始化所有设备驱动__initcall

              在设备驱动中,bon注册一个devfs的块设备名称 /dev/bon,然后注册为mtd user ,register_mtd_user()

              mtd设备驱动被初始话时,启动nand_chip驱动去搜索flash设备,通过一系列的寄存器的交互,发现了mtd设备,

              如果当前没有把分区开关打开的话则将整个flash当作一个mtd设备加入mtd_table[],同时通知mtd user,也就是告知

              bon,当前已经有mtd设备挂入,bon得到控制权了,他就读取mtd设备的开头512字节的数据,构造分区表信息,和校验分区是否合法,

              bon将每个分区注册进入devfs作为子设备(/dev/bon/0)

              3.回到步骤2,由于bon没有存在root_dev_names[]静态表中,所以内核不知道其major编号,所以在跳过了nfs,floppy启动判别之后,

              内核拿着bon名字到devfs进行查询此设备的主次设备编号,如果找到了则读取超级块,然后进行文件系统的识别

             

       }

devfs

devfs 文件系统在内存中建立,而不是存储在块设备或者字符设备之上。

但是为了符合vfs文件系统的接口规范,以 devfs_entry来包装 dentry,以devfs_inode来包装 inode

 

devfs文件系统中的devfs_entry存放在一个叫做 fs_info的结构变量中,以inode->ino节点编号作为索引可以查询fs_info中的devfs_entry

 

数据结构

===============

1. devfs_entry : 描述devfs中的目录项

       typedef struct devfs_entry * devfs_handle_t;

       struct fcb_type  : 此结构类型概括了 字符设备/块设备/常规文件的信息,包括了设备的特性,安全管理和最重要的驱动函数入口

       devfs_entry存放在fs_info->table[]

 

2.devfsd

       此为一服务进程,观察devfs 目录项条目的运行状态,比如加入新的entry devfs_register()会触发DEVFSD_NOTIFY_REGISTEREDdevfsd便可以进行一些新增设备的处理工作

      

 

函数

==============

1.get_devfs_entry_from_vfs_inode()   

       根据inode->ino,在fs_info->table中搜寻devfs_entry对象

 

 

+++++++++++

1.访问devfs块设备文件

命令: cat /dev/mtdblock/0

              mtdblock/0 的主设备号31,次设备号 12 ,之前是由mtd驱动调用 devfs_register()注册进入内核,并在devfsfs_info->table[]中占有一席之地。

              用户在打开设备文件进入内核空间,在内核空间中,fs层通过path_walk()等函数找到/dev/mtdblock/0对应的dentry信息,

              然后构建出file结构,将dentry->inode file作为参数,传入devfs文件系统的 dev_open()函数。

              devfs文件系统根据inode->ino编号在fs_info->table[]中找出mtd设备的devfs_entry对象,在devfs_entry->fcb->ops中存放着

              设备驱动入口函数,驱动入口函数原型可以是file_operations或者block_device_operations. 然后将 devfs_entry->fcb->ops传给inode->i_bdev->bd_op作为块设备驱动入口。

              devfs_open()中,如果当前打开的是块设备,就把缺省的块设备访问接口(def_blk_fops)付给file->f_op,然后调用file->f_open->open(inode,file)打开块设备.

              **所以对比字符设备和块设备,块设备的访问比字符设备多了一层 block_device_operation. 字符设备可以将驱动写在file_operations中,但块设备必须写在block_device_operation, 但要访问设备的话都是通过 file_operation访问,块设备利用def_blk_fops进行了file_operationblock_device_operation的转换。

             

2.块设备的读写

       ll_rw_block                 

      

      

devfs_register

===================

embedded_arch

1. bootloader :  vivi/u-boot

2. kernel_image

3. root_fs

 

bootloader通过jtag烧写到flash rom

bootloader通过rs232kernel_image写入flash,或者bootloaderrs232/ethernet动态加载kernel_image

kernel_image加载fs,可以采取nfs/mtd方式加载

kernel支持mtd/nand文件系统驱动

 

bootloader通过参数决定从何处挂载root-fs

 

 

fs

1. 新的文件系统注册

       register_filesystem()

       find_filesystem()

       kern_mount()

数据结构:

       1.file_system_type

              struct file_system_type {

              const char *name;

              int fs_flags;

              struct super_block *(*read_super) (struct super_block *, void *, int);

              struct module *owner;/*动态加载的模块,编译进内核则为0*/

              struct vfsmount *kern_mnt; /* For kernel mount, if it's FS_SINGLE fs */

              struct file_system_type * next;

              };

       2.file_systems(super.c)定义了文件系统类型的链表

 

IO访问

1.外设访问的方式

       *IO端口访问 提供特殊的端口访问指令,比如inb,outb

       *端口映射:  将外设register映射到cpu内存空间,用普通操作指令便可访问外设register 。非常方便,cpu开销比较小,指令简单

2.防止优化

       对于register映射到内存的方式,将碰到两种麻烦的可能。其一,硬件采用cache的方式来加快对内存地址的访问;其二,编译器会优化

       操作指令,比如写入内存数据可能会被放入寄存器。以上两种情况都将导致访问外射IO内存地址空间失败。

       所以为了避免这种情况,linux初始化时禁用访问io地址空间时硬件cache功能;对于编译器的解决方法就是采用barrier()

      

3.io端口地址的请求和释放

       release_region

       request_region

4. io读写

       inb(),insb(),outb(),outsb()

       inb_p 暂停读

 

io资源分配

1. request_region()

       请求io映射端口内存地址范围.内存区域用resource数据结构进行表示。

       resource表现一段内存地址,以树型展示不同的区域。也就是简单的理解为一段内存地址可以又分隔为许多内存地址,所以resource具有了parent,sibling,child属性

 

ipc

source/ipc目录内的ipc机制都是system V 新增的通信机制,包括 共享内存,消息队列和信号量

 

sys_pipe的实现在arch/i386/kernel中,似乎跟具体的平台有某种关系,这是何故呢?

*pipe的实现比较简单,但是具有所有文件具有的特征,区别在于pipeinode节点中存在一个 pipe_operation的操作跳转,两个进程一头写一头读,数据靠内核中申请的一页内存进行中转。

夫进程在创建pipe之后,可以将pipe文件句柄传递到两个子进程去,使其子进程之间可以进行单向的数据通信。

 

**fifo 命名管道

      支持在无进程关系的不同进程之间通过打开命名管道名称进行通信

==管道都是基于流,效率和使用方式都不是很好,比如不能提供有效的消息分割,导致要到应用层进行数据的解析,类似于tcp流的处理.

 

system-V ipc sys_ipc()函数内分流处理,类型有 semophera/message/share-memory

 

?sys_ipc()存在于 /arch/i386/kernel,又不知何故难道不可移植的吗?

kernel运行参数加载

include/asm-arm/mach/arch.h

arch/arm/mach-s3c2440/smdk.c

       定义平台信息结构

 

MACHINE_START(SMDK2440, "Samsung-SMDK2440")

       BOOT_MEM(0x30000000, 0x48000000, 0xe8000000)

       BOOT_PARAMS(0x30000100)     //定义linux启示参数存放区域

       FIXUP(fixup_smdk)

       MAPIO(smdk_map_io)

       INITIRQ(s3c2440_init_irq)

MACHINE_END

 

start_kernel(){

       setup_arch(){

              setup_machine() //获取平台配置信息结构 struct machine_desc

              {

                     检索__arch_info_begin开始的位置的平台信息是否匹配平台类型

                     vmlinux-armv.lds.in 包含__arch_info_begin 定义*(.arch.info)

                    

              }

       }

      

}

Linux启动过程中硬件模块的加载

设备驱动程序都用module_init(xx)进行登记

module_init()函数就是将 xxx函数地址负值到 initcall(void*) 的函数指针变量,并将其连接如.initcall.init节中. __attribute__ ((unused,__section__ (".initcall.init")))

       kernel启动时,在函数do_initcalls() initcall.init节中的所有函数地址都执行一便,这样就完成了静态连接到kernel的驱动的初始化调用

mtd_nand

1.nand支持结构层次

 

       fs (user mode)

       mtd

       nand(driver)

 

** mtd/0              字符设备       driver/mtd/mtdchar.c

** mtdblock/0 块设备  driver/mtd/mtdblock.c

      

3层结构,nand驱动提供访问flash 的底层接口函数,mtd抽象访问接口,最上层就是文件系统层,目前可以使用的是jiffs,yaffs2等等

 

2. 基本数据结构

       struct mtd_info

              定义一堆的访问nand驱动的接口和数据

       add_mtd_device()   将一个nand 设备注册为一个mtd设备

       add_mtd_partitions()      将分区注册为mtd设备 (这种方式不能用于bon块设备,因为这样bon无法读取正确的设备分区信息,bon是面向整个flash设备的)

       struct mtd_part 代表一个nand分区 , mtd_part::mtd指向 mtd_info

      

       struct mtd_notifier {

        void (*add)(struct mtd_info *mtd);  注册时回调fs的函数

        void (*remove)(struct mtd_info *mtd);

        struct mtd_notifier *next;

    };

    user-mode fs要提供mtd_notifier,nand设备插拔时将进行通知(remove)fs

      

       register_mtd_user (struct mtd_notifier *new);

       unregister_mtd_user (struct mtd_notifier *old);

       以上这两个方法供具体的fs调用

      

       CONFIG_MTD_SMC_S3C2440_SMDK_PARTITION 决定了是否将一个mtd分区注册为一个mtd设备

      

1. s3c2440 nand驱动

       driver\mtd\nand\smc_sc2440.c

       smc_insert()登记 nand partition信息

       smc_scan()    扫描nand类型,并安装nand处理函数到 mtd_info中去

       add_mtd_partitions() 注册mtd_info,支持partition

       add_mtd_device(){

              把每一个分区注册为一个mtd设备

              把每个mtd分区注册进每一个mtd_notifier中去

       }

      

2.

       mtd_partition  定义mtd设备分区表

       mtd_part        定义分区信息结构

                                   每个mtd_part分区都视为mtd设备,所以都具有 mtd_info数据结构。并将mtd_part通过add_mtd_device()注册进入mtd_table[]数组

      

       mtd_part 拥有mtd_info的理由:

              mtd_info定义了访问mtd设备的接口,比如 mtd.read().  mtd_part作为mtd设备的一个区间,但也被看作是一个mtd设备,所以读取mtd的起始位置与直接访问mtd主设备不一致,多了一个偏移量。mtd_part.mtd.read()函数中计算出mtd主设备中自己的绝对位置,然后调用主mtd设备的read()接口函数,所以在访问带有分区的mtd设备时调用绕了一个小圈子。

              mtd_info中的诸多函数在主设备中定义为  nand_xxx(),而对应着在mtd_part.mtd中也对应定义了 part_xxx(),但调用还是最终要进入 nand_xxx(). part_xxx()接口只是在为真正调用nand_xxx()而计算自己在主设备中的偏移量而已

       所以对mtd来讲,一个分区就是一个mtd设备,如果没有划分分区则此设备就是一个mtd设备

      

       smc_insert

mtdblock

1. 初始化

 

       init_mtdblock(){

              devfs_register_blkdev()  注册mtd块设备到devfs文件系统

              register_mtd_user()              注册mtd设备插拔事件接收者

              blk_init_queue()            初始化数据队列

              mtdblock_thread()         启动工作线程

       }

      

/drivers/mtd/mtdcore.c 代码中通过add_mtd_device()mtd/nand/smc_s3c2440.c中定义的分区mtd_part作为mtd_info设备注册进系统mtd设备链表

       修改分区信息:

              smc_s3c2440.c::smc_partitions[],

              开放 宏开关 CONFIG_MTD_SMC_S3C2440_SMDK_PARTITION ,系统将所有定义的分区作为mtd设备登记入mtd_table[]数组.(通过/dev/mtdblock/n便穿越mtdblock驱动到达mtd驱动层获取mtd分区信息)

 

64M K9F1208U0M Flash

15bit 32k pages = 16M

nand flash = 16M*4Plane = 64M

 

column Address 用于选择 ABC区地址

 

出厂时如果是坏块,在第一或者第二页的C区的第6字节不为0xff

 

Flash由于在使用时会产生新的坏块,所以必须采用replacement策略,即在写入检测为坏块则跳跃到下一个块进行处理。

 

//擦出操作时产生失败,则有必要将此块标示为坏块

       检测到操作失败

       定位到页所在块的第一个页位置

       写入C517位置的值为非0xff

 

在读写之前判别好/坏块的方法:

       从块编号换算到页编号

       NF_CMD(CMD_READ2);           // 0x50

       NF_ADDR(VALIDADDR)            //column 6个字节,517位置,      #define VALIDADDR     0x05

       NF_ADDR()          //PAGE raws

       NF_RDDATA()  如果是0xff,表示为好块,否则为坏块

      

ECC校验:

       Samsung 2440nandflash controller在读写flash之后将自动产生ecc校验码存放在ECC寄存器,所以在执行写页操作之后,必须把ECC校验码写入到512~527这个C区间内,一般都放置在512+8的位置;在读操作时,将存储的校验码读出并与ECC寄存器的值进行比对即可

Flash 读写代码:

 

BOOL FMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)

{

    BYTE Status;

    ULONG SectorAddr = (ULONG)startSectorAddr;

    ULONG MECC;             

    if (!pSectorBuff && !pSectorInfoBuff)

    {

        RETAILMSG(1,(TEXT("FMD_WriteSector: Failed sector write 01. \r\n")));

        return(FALSE);

    }

   

    NF_RSTECC();                            // Initialize ECC.

    NF_nFCE_L();                             // Select the flash chip.

    NF_CMD(CMD_RESET);        // Send reset command.

    NF_WAITRB();            // Wait for flash to complete command.

 

    while (dwNumSectors--)

    {

        ULONG blockPage = (((SectorAddr / NAND_PAGE_CNT) * NAND_PAGE_CNT) | (SectorAddr % NAND_PAGE_CNT));

 

        if (!pSectorBuff) //如果只是准备写入page信息(512~517)

        {

            // If we are asked just to write the SectorInfo, we will do that separately

            NF_CMD(CMD_READ2);                 // Send read command. 读第C区命令

            NF_CMD(CMD_WRITE);                    // Send write command.

            NF_ADDR(0);                            // Column = 0.  第一个字节开始(512)

            NF_ADDR(blockPage         & 0xff);    // Page address. 页行地址,15位宽,所以共 2^15=32K pages = 16M

            NF_ADDR((blockPage >>  8) & 0xff);

            NF_ADDR((blockPage >> 16) & 0xff); 

            WrPageInfo((PBYTE)pSectorInfoBuff);      //写入信息

            pSectorInfoBuff++;

        }

        else

        {

          NF_RSTECC();

          NF_MECC_UnLock();

         

            NF_CMD(CMD_READ);                     // Send read command. 0x00

            NF_CMD(CMD_WRITE);                    // Send write command.

            NF_ADDR(0);                            // Column = 0.

            NF_ADDR(blockPage         & 0xff);    // Page address.

            NF_ADDR((blockPage >>  8) & 0xff);

            NF_ADDR((blockPage >> 16) & 0xff); 

 

            //  Special case to handle un-aligned buffer pointer.

            if( ((DWORD) pSectorBuff) & 0x3)

            {

                WrPage512Unalign (pSectorBuff);

            }

            else

            {

                WrPage512(pSectorBuff);                // Write page/sector data.

            }

                     NF_MECC_Lock();

                    

            // Write the SectorInfo data to the media.

            //

            if(pSectorInfoBuff)

            {

                WrPageInfo((PBYTE)pSectorInfoBuff);  //写入尾部信息 512~519

                pSectorInfoBuff++;

 

            }

            else    // Make sure we advance the Flash's write pointer (even though we aren't writing the SectorInfo data)

            {

                BYTE TempInfo[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

                WrPageInfo(TempInfo);

            }

            pSectorBuff += NAND_PAGE_SIZE;

 

            MECC = NF_RDMECC0();

            NF_WRDATA((MECC      ) & 0xff);

            NF_WRDATA((MECC >>  8) & 0xff);

            NF_WRDATA((MECC >> 16) & 0xff);

            NF_WRDATA((MECC >> 24) & 0xff);

 

        }

           

        //NF_CLEAR_RB();

        NF_CMD(CMD_WRITE2);                    // Send write confirm command.