chenrvmldd 发表于 2011-03-16 13:48

一个项目引起的7天写一个nor flash字符驱动程序的经历

仅以此文纪念自己的程序员生涯的开端!
故事源于一个刚毕业的学生,也就是本故事的主人公作者,废话少说,开始故事:
2011年2月21号:
也就是今天我带着喜悦和莫名的伤感来到一个牛逼的公司报道,开始我正式的程序员生涯,作为一个新人来讲,那个时候我觉得刚开始肯定会有很多人指导你的工作,可是报道之后才发现一切都靠自己,没有人主动指导你什么,只有你不懂的时候自己找适合的人去问,所以基本也是很重要的,好,开始说项目吧。


需求分析:
在嵌入式linux中一般我们更新内核和rootfs的时候,都需要再UBOOT的支持下才能更新,而且要输入一大串的命令,这样比较不方便,因此我们尝试在内核运行的时候直接将内核和rootfs更新掉,(在本文中Uboot,kernel,rootfs都存放在nor flash)
本文的nor flash容量为16M,其地址
ff000000          ~       ff03ffff               UBOOT
ff060000          ~       ff11ffff               Kernel
ff120000          ~       ffffffff               filesystem
看完需求分析,我的头脑一片空白,说实在话:因为我不知道什么叫nor flash?nand flash?当然刚开始最好的方法就是google and baidu. After google and baidu之后我大概了解nor flash是一种字符设备,按照我以前所学的知识来理解:既然作为一种设备,那么在linux下设备都可以作为文件存在,那么在Linux中写文件和都文件那岂不是很简单吗?说是迟那是快我就有了下面的想法:
1.        首先我可以把nor flash作为一个设备文件打开,通过open调用,返回一个设备文件描述符fd1
2.        将kernel文件或者filesystem文件下载到嵌入式系统中去(注意:这里的kernel文件和filesystem文件是要去更新nor flash中的文件)
3.        open下载进去的kernel文件或者filesystem文件,返回一个文件描述符fd2
4.        然后通过mmap调用将fd1中要替换的区域映射到进程地址空间,mmap返回一个虚拟地址addr1
5.        将第三步open的文件fd2也通过mmap映射到进程地址空间,mmap返回一个虚拟地址add2
6.        已经知道addr1和addr2,接下来更新文件的工作就相当于字符串拷贝的工作,很简单
为了安全起见,我并没有直接按照上面的步骤来,我怕如果此方法不通,会把kernel区域破坏掉,我想先试试能不能将nor flash中的kernel区域的前16字节 都出来,开和kernel文件中的前16字节是否一致,如果一致,那至少证明这种方式可以读Nor flash的区域,那就有了我的程序员生涯的第一个程序:
1.首先通过一个宏定义nor flash中kernel的地址和大小:
#define KERNEL_START_ADDR0xff060000
#define KERNEL_SIZE         0x000c0000 //768k
2.打开nor flash设备文件,在本系统中nor flash对应的设备文件名/dev/mem,然后将设备文件映射到进程地址空间中,返回的虚拟地址起始地址kernel_map_addr
kernel_fd =open("/dev/mem",O_RDWR|O_SYNC);
kernel_map_addr=(char*)mmap(NULL,KERNEL_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,kernel_fd,KERNEL_START_ADDR);
3.打印nor flash文件kernel区域的前16个字节,与kernel.img文件的前16个字节比较一下开是否读的对:
    for(i=0;i<16;i++)
    {
      printf("%x",rootfs_map_addr);
      printf("\n");
   }

这边小小的提示一下打印的时候要用”%x”,我一开始用的是”%c”,出了很多乱码,为什么会出乱码我相信读者朋友肯定知道为什么了?如果不知道大家google一下吧!
经过上面的步骤我发现;我读出的内容和kernel.img文件的前16个字节一致 ,那证明我的程序可以读nor flash任意区域的想法是对的。下面我把代码贴出来,代码中我是分别读三个区域都进行验证的。其他区域的方法和上面提到的方法是一致的。
补充一下:如果读者对Linux中设备可以作为文件来读写的机制不了解可以去google一下,或者对mmap系统调用不是很了解的也可以去google一下,其实mmap操作我也是临时google的,才了解的

题外话:其实这天我一直搞到夜里1点多钟才休息的,刚开始程序员的生涯,就这么惨了,因为第一天我都不知道什么是交叉编译,只是以前听说过,也不知道我开发的平台是什么,就为了搞开发平台我就搞了一上午,哎,新人就是惨啊,不过第一天抗过来了,有了这么大的收获还是蛮幸福的!兄弟姐妹肯定会怀疑:心里肯定会想,作者不是写字符驱动吗?怎么现在一点也没有,在这里我做个保证,到第三天肯定会有的,我之所以这样写的原因有三个:
第一:记录一下我做一个项目从无到有的经历
第二:让大家了解做这个项目的研究的思路
第三:与大家分享我遇到问题时候解决问题的思路,我觉得第三点是最重要的
/**********************************Include********************************/
#include<stdarg.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>

#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <time.h>

#include <sys/ioctl.h>
#include <sys/mman.h>


/**********************************Define*********************************/
#defineUBOOT_START_ADDR   0xff000000
#defineUBOOT_SIZE         0x00040000 //256k

#define KERNEL_START_ADDR0xff060000
#define KERNEL_SIZE         0x000c0000 //768k

#defineROOTFS_START_ADDR    0xff120000
#define   ROOTFS_SIZE         0x00ee0000 //15232k

/******************************Local Variable*****************************/

static intuboot_fd;
static intkernel_fd;
static introotfs_fd;

volatile char *uboot_map_addr;//address from using mmap()
volatile char *kernel_map_addr;
volatile char *rootfs_map_addr;

/******************************Extern Variable****************************/

/******************************Local Function*****************************/

static int openMemRW(void);
static void closeMemRW(void);

/*********************************Code************************************/



int main(int argc, char* argv[])
{

//         if(argc<1)
//      {
//        printf("parameter error!\n");
//        return -1;
//       }   
   // -----------------------------------------------------------
   // Step 1, open /dev/mem to access NOR FLASH
   // -----------------------------------------------------------

   int len,i;
   
   len=strlen(argv)+1;
   
   for(i=0;i<len;i++)
   printf("%c\n",argv);
   
    if(openMemRW()<0)
    {
      printf("openMemRW fail\n");
      return -1;
    }
    closeMemRW();
    return 0;
}

static int openMemRW(void)

{
          int i;
       
             //将NOR FLASH的uboot区域映射到进程空间,以便以后直接通过访问映射的进程区域来访问uboot区域
    uboot_fd =open("/dev/mem",O_RDWR|O_SYNC);
    if(uboot_fd < 0)
    {
      printf("open /dev/mem failed!\n");
      return -1;
    }
    uboot_map_addr = (char *)mmap(NULL,UBOOT_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,uboot_fd,UBOOT_START_ADDR);
    if(uboot_map_addr == NULL)
    {
      printf("openMemRW ERROR : mmap fd=%d failed!\n",uboot_fd);
      close(uboot_fd);
      return -1;
   }
    printf("uboot_map_addr=%x\n",uboot_map_addr);
   
    for(i=0;i<16;i++)
    {
      printf("%x ",uboot_map_addr);
      printf("\n");
   }
    printf("__uboot_map_addr=%x\n",&uboot_map_addr);
   // printf("usage:%s string\n",uboot_map_addr);
                //将NOR FLASH的kernel区域映射到进程空间,以便以后直接通过访问映射的进程区域来访问kernel区域
    kernel_fd =open("/dev/mem",O_RDWR|O_SYNC);
    if(kernel_fd < 0)
   {
      printf("open /dev/mem failed!\n");
      return -1;
      }
    kernel_map_addr = (char *)mmap(NULL,KERNEL_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,kernel_fd,KERNEL_START_ADDR);
    if(kernel_map_addr == NULL)
   {
      printf("openMemRW ERROR : mmap fd=%d failed!\n",kernel_fd);
      close(kernel_fd);
      return -1;
      }
    printf("kernel_map_addr=%x\n",kernel_map_addr);
   
    for(i=0;i<16;i++)
    {
      printf("%x",kernel_map_addr);
      printf("\n");
   }
    printf("__kernel_map_addr=%x\n",&kernel_map_addr);
    //printf("usage:%s string\n",kernel_map_addr);
                //将NOR FLASH的ROOTFS区域映射到进程空间,以便以后直接通过访问映射的进程区域来访问ROOTFS区域
    rootfs_fd =open("/dev/mem",O_RDWR|O_SYNC);
    if(rootfs_fd < 0)
   {
      printf("open /dev/mem failed!\n");
      return -1;
      }
    rootfs_map_addr = (char *)mmap(NULL,ROOTFS_SIZE ,PROT_READ|PROT_WRITE,MAP_SHARED,rootfs_fd,ROOTFS_START_ADDR);
    if(rootfs_map_addr == NULL)
   {
      printf("openMemRW ERROR : mmap fd=%d failed!\n",rootfs_fd);
      close(rootfs_fd);
      return -1;
      }
    printf("rootfs_map_addr=%x\n",rootfs_map_addr);
    for(i=0;i<16;i++)
    {
      printf("%x",rootfs_map_addr);
      printf("\n");
   }
    printf("__rootfs_map_addr=%x\n",&rootfs_map_addr);

    //printf("usage:%s string\n",rootfs_map_addr);
        return 0;
}
//关闭映射
static void closeMemRW(void)
{

   munmap((void*)uboot_map_addr,UBOOT_SIZE);
            close(uboot_fd);

   munmap((void*)kernel_map_addr,KERNEL_SIZE);
            close(kernel_fd);

   munmap((void*)rootfs_map_addr,ROOTFS_SIZE);
            close(rootfs_fd);
}

chenrvmldd 发表于 2011-03-16 13:52

2011年2月22号:
今天一大早就来到了公司,开始我昨天的想法,通过open和mmap的方法来更新nor flash的kernel区域或者rootfs区域。(忘了补充一下我的开发平台是linux 2.4版本的,嵌入式平台是Motolora PPC平台)。我仔细了思考了一下,如果直接写kernel或者rootfs区域的话万一不行怎么办(呵呵,我做人比较谨慎,思考问题也一样,我相信做程序的做事就应该谨慎一点好),我可以试试写nor flash的空白区域,如果空白区域能写进去东西,那再往kernel或者rootfs区域也不迟啊。经过分析,我发现nor flash总共128个sector,每个sector的大小128K,编号是从0~127,那么我想最后的一个扇区应该是空的,我就准备写最后一个扇区:
我的思路如下:
1.        char buf[]="eeeeeeeeeee!"要写入到nor flash第127扇区的数据
2.        rootfs_fd =open("/dev/mem",O_RDWR|O_SYNC);打开nor flash设备文件
3.        rootfs_map_addr=(char*)mmap(NULL,ROOTFS_SIZE ,PROT_READ|PROT_WRITE,MAP_SHARED,rootfs_fd,ROOTFS_START_ADDR);将nor flash的rootfs区域映射到进程地址空间
4.        打印出前第127扇区的前11个字节的内容,与char buf[]中的数据进行比较
5.        将cha buf[]的内容写入到底127扇区,直接通过字符串拷贝的方式,然后再将第127扇区的前11个字节打印出来与写入得数据进行比较
下面是我的代码:/**********************************Include********************************/
#include<stdarg.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>

#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <time.h>

#include <sys/ioctl.h>
#include <sys/mman.h>

/**********************************Define*********************************/
#defineUBOOT_START_ADDR   0xff000000
#defineUBOOT_SIZE         0x00040000 //256k

#define KERNEL_START_ADDR0xff060000
#define KERNEL_SIZE         0x000c0000 //768k

#defineROOTFS_START_ADDR    0xff120000
#define   ROOTFS_SIZE         0x00ee0000 //15232k
#define START_READ_OFFSET   0x00800000   //8M

#defineSTART_WRITE_OFFSET   0x00C00000//12M

/******************************Local Variable*****************************/
static intuboot_fd;
static intkernel_fd;
static introotfs_fd;

volatile char *uboot_map_addr;//address from using mmap()
volatile char *kernel_map_addr;
volatile char *rootfs_map_addr;


/******************************Extern Variable****************************/

/******************************Local Function*****************************/
static int openMemRW(void);
static void closeMemRW(void);

/*********************************Code************************************/


int main(int argc, char* argv[])
{
   
   
   
    if(openMemRW()<0)
    {
      printf("openMemRW fail\n");
      return -1;
    }
    closeMemRW();
    return 0;
}



static int openMemRW(void)
{
    int i,len;
    char buf[]="eeeeeeeeeee!";//准备写入到rootfs_map_addr开始的地方
    rootfs_fd =open("/dev/mem",O_RDWR|O_SYNC);
    if(rootfs_fd < 0)
   {
      printf("open /dev/mem failed!\n");
      return -1;
      }
    rootfs_map_addr = (char *)mmap(NULL,ROOTFS_SIZE ,PROT_READ|PROT_WRITE,MAP_SHARED,rootfs_fd,ROOTFS_START_ADDR);
    if(rootfs_map_addr == NULL)
   {
      printf("openMemRW ERROR : mmap fd=%d failed!\n",rootfs_fd);
      close(rootfs_fd);
      return -1;
   }
    printf("rootfs_map_addr=%x\n",rootfs_map_addr);//在进程地址空间为映射文件分配的起始地址
    len=strlen(buf)+1;
   
    //将rootfs_map_addr打印出来与rootfs_map_addr未写入信息之前的值比较
   
    printf("start from START_READ_OFFSET:\n");
    for(i=0;i<len;i++)
    {
      printf("%x",rootfs_map_addr);
      printf("\n");
    }
   
   
    printf("before write:\n");
    //在往rootfs区域写之前,先将原来的内容打印出来
   
    for(i=0;i<len;i++)
    {
      printf("%x",rootfs_map_addr);
      printf("\n");
    }
    printf("after write:\n");
    //将写入到rootfs区域的信息打印出来
    for(i=0;i<len;i++)
    {
      rootfs_map_addr=buf;
      printf("%x",rootfs_map_addr);
      printf("\n");
      
    }
    printf("%s\n",buf);
    printf("rootfs_map_addr=%x\n",&rootfs_map_addr);
    printf("len=%d\n",len);
    printf("rootfs_map_addr=%x\n",&rootfs_map_addr);
    return 0;
      

}

//关闭映射
static void closeMemRW(void)
{

   munmap((void*)rootfs_map_addr,ROOTFS_SIZE);
            close(rootfs_fd);
}


经过程序测试:发现读出来的东西和写进去的东西不一致,而且秩序执行几次打印出来的结果也不一致,我当时就崩溃了。一个人之所以崩溃,是因为他面临的情况超出了他的知识范围领域,在这种情况下,大多数人都是去问别人或者是google,我选择了两者的结合,通过google和询问我发现:这边之所以失败的原因如下(搞过nor flash的可以跳过下面的分析):
Nor flash的写必须遵守一种规则,大家注意,这里只提到了写操作,而我们上面的写操作没有遵守这种规则,具体的规则在这里我不详细阐述,这边有一边文章写的不错:
http://www.cnblogs.com/yytblog/archive/2009/09/02/1558943.html
不懂的兄弟姐妹去看一下就明白为什么了,如果不想看的话,下面还会大概介绍一下写规则的
既然上面的方法不行:根据那个链接里提示的规则,我把程序进行了修改,思路如下:
1.        在写之前先将第127扇区前面的一些字节打印出来,我是通过printFlashData()来实现的
2.        然后擦除第127扇区,我是通过eraseFlashSector函数实现的
3.        printFlashData()一下,比较擦除前与擦除后数据是否一致,在这里我要大概介绍一下nor flash的写规则:nor flash在写之前先要erase,然后再写,nor flash erase的单位是以一个sector也就是128K,也就是说一次erase是一个扇区,erase之后的扇区都填充着0xff,那么这一步通过printFlashData()打印出来的内容都应该是0xff
4.        writeFlashWord()函数往第127扇区写数据。
下面来分析一下我上面提到的两个函数:eraseFlashSector和writeFlashWord()
static int eraseFlashSector(void)
{
    volatile unsigned char *addr;
    printf("start to erase:\n");
    addr = rootfs_map_addr; //要擦除的扇区的起始地址,记住这必须是128k的整数倍
    *addr =0x50; /* clear status register */
    *addr =0x20; /* erase setup */
    *addr =0xD0; /* erase confirm */
    *addr =0x70; /* read status register */
    while((*addr & 0x80) != 0x80 )
    {
      *addr = 0x70;
    }
    printf("erase sectorok!\n");
//    sync();
    *addr = 0xFF;//最后要记住回到read mode那个链接里面也讲到的
//    printf("flash_reset!\n");
    return 0;
}
static int writeFlashWord(unsigned short data)
{
    volatile unsigned char *addr_ptr;
    int i;

    addr_ptr =rootfs_map_addr;
    for(i=0;i<16;i++)
        {
          *addr_ptr=0x60;/* clear status register */
          *addr_ptr =0x40;/* write byte */
          *addr_ptr =data;//要写入扇区的数据,记住这里一次只能写一个byte
          *addr_ptr =0x70;/* read status register */
          while((*addr_ptr & 0x80) != 0x80 )
          {
                *addr_ptr = 0x70;
          }
          addr_ptr++;
          
        }
        *rootfs_map_addr = 0xFF;        //还是要回到read mode
    return 0;
}
不知道为什么上面的程序经过一翻调试和验证发现还是出现很多奇怪的问题:数据还是不能写入,每次执行的结果都有可能有差别,我就不停得找原因,找来找去就是找不到原因,后来没办法只能去问别人,别人给了我一个手册就是关于Nor flash的英文手册,网上都可以下的到,花了一个晚上的时间我仔细的去研读了这个手册,突然眼前一亮,发现有这么一句话:我就把它的大概意思翻译出来:就是erase的时候要保证一次erase序列的完整性,中间不能被打断。大家可以看一下eraseFlashSector()这个函数中对*addr赋值的那几个步骤就是一个完整的序列。如果这个序列被打断的话,有两个寄存器SR4和SR5会报被SET了。在我的程序执行完的时候经常会报SR4和SR5被SET,而且erase后,通过printFlashData()打印出来的值也不是0xff,从这两点我推测,应该是我的应用程序在执行的时候被某个中断打断了,尤其是在erase的时候,那个序列肯定被打断了,所以我的erase总是不成功。那么下面我们就来分析一下:为什么程序在erase的时候会被打断了,这个问题在我们操作系统理论中就讲过,那是由于我们现代多道程序设计的理念才会产生的并发的操作。那么如何避免并发操作给我们erase序列带来的问题了?这个问题纠结了我很久,到这个时候其实已经夜里12点多了,女朋友还在等我回家了,那个时候我大脑已经不运转了,算了,还是明天找原因吧。下面是我的源代码,细心的朋友会发现,这次的源代码从结构上变的很清晰了,打印方面我做了一个简单的函数做处理了,简化了main函数,erase和write操作都单独做了一个函数。聪明的读者会发现明天注定我们要与驱动接触了!#include<stdarg.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>

#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <time.h>

#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>


#defineFLASH_START_ADDR0xff000000
#definesectno   127
#defineROOTFS_START_ADDR    0xff120000
#define   ROOTFS_SIZE         0x00ee0000 //15232k
#defineSECT_SIZE            0x00020000//128K
#defineSECT_OFFSET_ADDR   (127*SECT_SIZE)//第127扇区相对于FLASH_START_ADDR的偏移地址
#defineSECT_START_ADDR      (SECT_OFFSET_ADDR+FLASH_START_ADDR)//第127扇区的起始地址


#defineFLASH_TEST_ADDR0xffFE0000


static int eraseFlashSector(void);
static int writeFlashWord(unsigned short data);
static int openMemRW(void);
static void closeMemRW(void);

//volatile unsigned short *sect_start_addr= (volatile unsigned short *)(FLASH_START_ADDR + sectno*0x20000);
static introotfs_fd;
volatile unsigned char *rootfs_map_addr;

//char *buf[];


void printFlashData(void);
//void printFile(void);


int main(int argc, char* argv[])
{
    unsigned char data;
    data=2;
    int i;
    char buf;
    for(i=0;i<8;i++)
    buf='a';
    //在写之前先读数据
   if(openMemRW()<0)
    {
      printf("openMemRW fail\n");
      return -1;
    }

    printFlashData();
//    if(write(rootfs_fd,buf,8)<0)
//    {
//      printf("write fail\n");
//      return -1;
//    }
//    printFlashData();
      
//    printFile();
//    write_to_buff();

//   umount("dev/mtdblock2");
// #if 0   
    if(eraseFlashSector()<0)
    {
      printf(" erase fail\n");
      return -1;
    }
    printFlashData();

   
    if(writeFlashWord(data)<0)
    {
      printf("write fail\n");
      return -1;
    }
    data = *(unsigned char *)rootfs_map_addr;
    printf("read data=%d\n", data);
    printFlashData();
    //写完之后再读数据,看是否写入
//    if(openMemRW()<0)
//    {
//      printf("openMemRW fail\n");
//      return -1;
//    }
//#endif   
    closeMemRW();
   
    return 0;
}



//FLASH 16M,128K/SECTOR, TOTAL 128, 0~127
static int eraseFlashSector(void)
{
    volatile unsigned char *addr;
    printf("start to erase:\n");
    addr = rootfs_map_addr;
    *addr =0x50; /* clear status register */
    *addr =0x20; /* erase setup */
    *addr =0xD0; /* erase confirm */
    *addr =0x70; /* read status register */
    while((*addr & 0x80) != 0x80 )
    {
      *addr = 0x70;
    }
    printf("erase sectorok!\n");
//    sync();
    *addr = 0xFF;
//    printf("flash_reset!\n");
    return 0;
}

static int writeFlashWord(unsigned short data)
{
    volatile unsigned char *addr_ptr;
    int i;

    addr_ptr =rootfs_map_addr;
    for(i=0;i<16;i++)
        {
          *addr_ptr=0x60;/* clear status register */
          *addr_ptr =0x40;/* write byte */
          *addr_ptr =data;
          *addr_ptr =0x70;/* read status register */
          while((*addr_ptr & 0x80) != 0x80 )
          {
                *addr_ptr = 0x70;
          }
          addr_ptr++;
          
        }
        *rootfs_map_addr = 0xFF;       
    return 0;
}


static int openMemRW(void)
{
    rootfs_fd =open("/dev/mem",O_RDWR|O_SYNC);
    if(rootfs_fd < 0)
   {
      printf("open /dev/mem failed!\n");
      return -1;
      }
    printf("start to map flash %x\n", SECT_START_ADDR);
    //把第127扇区映射到进程地址空间
    rootfs_map_addr = (volatile unsigned short *)mmap(NULL,SECT_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,rootfs_fd,SECT_START_ADDR);
//    if(lseek(rootfs_fd,0,SEEK_SET)==-1)
//    {
//      printf("cannot seek\n");
//      return -1;
//    }
    if(rootfs_map_addr == NULL)
    {
      printf("openMemRW ERROR : mmap fd=%d failed!\n",rootfs_fd);
      close(rootfs_fd);
      return -1;
    }
    return 0;
}


static void closeMemRW(void)
{

    munmap((void*)rootfs_map_addr,SECT_SIZE );
    close(rootfs_fd);
}

void printFlashData(void)
{
    int i;
    printf("rootfs_map_addr=%x\n",rootfs_map_addr);//在进程地址空间为映射文件分配的起始地址
//    *rootfs_map_addr = 0xFF;
   
    for(i=0;i<16;i++)
    {
      printf("[%d]=%x ",i, rootfs_map_addr);
      if (i%8==7) printf("\n");
    }
   
    printf("\n");
}
//
//void printFile(void)
//{
//    char buf;
//    int i;
//    printf("rootfs_fd=%d\n",rootfs_fd);
//    if(read(rootfs_fd,buf,32)<0)
//    {
//      printf("read fail\n");
//      return;
//    }
//    for(i=0;i<32;i++)
//    {
//      printf("[%d]=%x",i,buf);
//      if(i%16==15)printf("\n");
//    }
//   
//}   

chenrvmldd 发表于 2011-03-16 14:21

2011年2月23号:
今天早上又很早的就过来了,继续思考昨天的问题,还是回到大学的教材上找答案,在操作系统书上有这么一句话:可以通过关中断的方式来保证执行的序列不被打断。所以解决昨天遗留的问题只能通过关中断!可是问题又来了,怎么关中断,在哪关?
LDD3那本书上详细的阐述了怎么关中断:本文中使用了下面的函数来关中断和开中断:
local_irq_disable()和local_irq_enable(),可是在哪关了?
用户态显然是不能关中断的,那必然要在内核态关,看来我们离驱动有近了一步。说实在话上来就做个驱动心里还是抗拒的,虽然很兴奋,但是还是觉得比较难,心里没有底。
经过仔细的思考,直接上来做驱动风险太大:第一:不知道是不是由于erase的序列被打断而引起不能erase扇区。第二:驱动以前从来没有接触过,搞起来很难搞。因此,我打算做一个内核模块,通过这个模块先往第127扇区写东西,看能不能写进去。思路定下来开始动手了,这边遇到两个问题:
第一:模块编程从来没有接触过。
第二:怎么让你编写的一个模块加载到内核中去
好,既然没接触过那就google,发现网上最多的关于模块方面入门的就是hello world模块,我发现这个模块入门依旧那么的经典。第二个问题网上也有
与是乎就有了我的第一个内核模块程序:这个程序其实和前面的应用程序没有太大的区别:
只不在内核模块下打印不能用printf只能用printk,关于这两个函数打印格式的区别建议不熟悉的人去google一下。同时在erase和write的时候加了关中断,细心的朋友会发现程序中多了一个ioremap而不是原来的mmap函数,这里我为什么会用ioremap,其实这里完全可以不用,但是我当时觉得应该用(因为一开始理解的不是很深刻,在后来的程序中我把ioremap函数取消掉了),那关于ioremap函数,在什么情况下用:这边有一篇文章讲的非常好,建议大家看一下看http://blog.csdn.net/do2jiang/archive/2010/04/05/5450839.aspx

还有在erase和write的时候都进行了关中断#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mm.h>   
#include <asm/io.h>   /*for ioremap*/
#include <linux/slab.h>/*for kmalloc and kfree*/
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/segment.h>
#include <asm/uaccess.h> /*for copy_from_user() and copy_to_user()*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>

#defineFLASH_START_ADDR0xff000000
#define   MEM_START   0xff000000/* the first phsical address of nor flash*/
#define   MEM_SIZE    0x01000000   /* the total size of nor flash, 16M*/
#defineSECT_SIZE            0x00020000//128K
#defineSECT_OFFSET_ADDR   (127*SECT_SIZE)//第127扇区相对于FLASH_START_ADDR的偏移地址
#defineSECT_START_ADDR      (SECT_OFFSET_ADDR+FLASH_START_ADDR)//第127扇区的起始地址0xffe00000


//static int major;/* major device number*/
volatile unsigned char *iomap_flash_addr;/*address return from iommap for nor flash*/

static int write_to_flash(void);
static void printData(void);
static void erase_flash(void);





static int init_module(void)
{


    iomap_flash_addr=(volatile unsigned char *)ioremap(SECT_START_ADDR,SECT_SIZE);
    if(iomap_flash_addr==NULL)
   {
      printk(KERN_ALERT"iomap fail\n");

      return -1;
   }
    printk(KERN_ALERT"iomap_flash_addr=%p\n",iomap_flash_addr);
    printData();
    write_to_flash();
    printData();
    return 0;
}
static void exit_module(void)
{
    if(iomap_flash_addr!=NULL)
    iounmap((void*)iomap_flash_addr);

    return;
}
static int write_to_flash(void)
{
//        int left;/*bytes left */

    int i;
        unsigned short data=1;
        volatile unsigned char *addr_ptr;
        addr_ptr=iomap_flash_addr;
       

       
       
        erase_flash();/*erase flash before write*/
       

        for(i=0;i<16;i++)
        {
          local_irq_disable();

          *addr_ptr = 0x50;/* clear status register */
          *addr_ptr = 0x40;/* write byte */
          *addr_ptr = data;
          *addr_ptr = 0x70;/* read status register */
                  while((*addr_ptr & 0x80) != 0x80 )
                  {
                        *addr_ptr = 0x70;
                  }
                  
                local_irq_enable();
                addr_ptr++;
        }
       
        *addr_ptr = 0xFF;/* reset to read mode        */
        return 0;
}
static void printData()
{
    int i;
    volatile unsigned char *addr;
    addr=iomap_flash_addr;
    for(i=0;i<16;i++)
    {   
      printk(KERN_ALERT"[%02d]=%04x ",i,iomap_flash_addr);
      if(i%8==7)
      printk("\n");
    }   
    return;

}

static void erase_flash()
{
   
    volatile unsigned char *addr;
    addr=iomap_flash_addr;
   
    printk(KERN_ALERT"start to erase:\n");
        local_irq_disable(); // 注意此时要关中断啦
        *addr = 0x50; /* clear status register */
        *addr = 0x20; /* erase setup */
        *addr = 0xD0; /* erase confirm */
        *addr = 0x70; /* read status register */
        while((*addr & 0x80) != 0x80 )
        {
                *addr = 0x70;
        }
        local_irq_enable();
        printk(KERN_ALERT"erase sectorok!\n");
        *addr = 0xFF;/* reset to read mode        */
        return;
}
         

//module_init(init_module);
//
//module_exit(exit_module);
//
这个模块当加载到内核的时候验证了我前面的所有想法,确实在前面erase和write的时候是由于序列被打断了。那么接下来我们真的要开始写驱动了。这点内容起始我琢磨了一天,整整一天,

chenrvmldd 发表于 2011-03-16 14:23

发现整理做过的东西还是蛮痛苦的

chenrvmldd 发表于 2011-03-16 14:24

为了论坛的活跃还是要继续的整理,整理,哈哈

sep 发表于 2011-03-16 14:24

赞一个,可惜我不能给你加分。
有个问题:为什么不用open /dev/mtd<no.>,read,write的方式?这样mtd层会帮你搞定时序问题。只要你的mtd分区offset和size符合边界条件,应该都可写的。这样程序应该十分简单的。

chenrvmldd 发表于 2011-03-16 14:30

本帖最后由 chenrvmldd 于 2011-03-16 14:33 编辑

回复 6# sep


    兄弟你慢慢往下看就会明白了,在这里先说一下主要有两个原因:
第一:在我的这个系统中MTD框架下没有nor flash的驱动,当时我试了一下确定没有
第二:以后我要更新文件系统,你说的这个框架是基于文件系统去解析的,就算MTD下有nor flash的驱动也尽量不要用,如果我要去文件系统,在这里我经历避免跟文件系统去打交道的
我不知道这么说兄弟你能不能明白?

sep 发表于 2011-03-16 14:42

回复 7# chenrvmldd


    对于第一点我有点好奇,没有mtd,norflash以什么形式存在的?
你的是2.4内核?这个我还真没了解

chenrvmldd 发表于 2011-03-16 14:54

回复 8# sep


    呵呵,其实这个也不难理解,我自己做的nor flash就没有基于MTD,MTD其实就有一点类似于VFS一样的东西,它只不过把FLASH驱动的共同点进行抽象,简化了增加新的驱动的麻烦,我是这么理解的,不知道对不对,兄弟如果你还是不太理解的话,可以看看<<linux内核分析及编程>>倪继利编的,第11章,我是看了那个之后理解的

amarant 发表于 2011-03-16 15:25

呵呵支持一下
页: [1] 2 3 4 5 6 7
查看完整版本: 一个项目引起的7天写一个nor flash字符驱动程序的经历