免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 56959 | 回复: 67
打印 上一主题 下一主题

[驱动] 一个项目引起的7天写一个nor flash字符驱动程序的经历 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-03-16 13:48 |只看该作者 |正序浏览
仅以此文纪念自己的程序员生涯的开端!
故事源于一个刚毕业的学生,也就是本故事的主人公作者,废话少说,开始故事:
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_ADDR  0xff060000
#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点多钟才休息的,刚开始程序员的生涯,就这么惨了,因为第一天我都不知道什么是交叉编译,只是以前听说过,也不知道我开发的平台是什么,就为了搞开发平台我就搞了一上午,哎,新人就是惨啊,不过第一天抗过来了,有了这么大的收获还是蛮幸福的!兄弟姐妹肯定会怀疑:心里肯定会想,作者不是写字符驱动吗?怎么现在一点也没有,在这里我做个保证,到第三天肯定会有的,我之所以这样写的原因有三个:
第一:记录一下我做一个项目从无到有的经历
第二:让大家了解做这个项目的研究的思路
第三:与大家分享我遇到问题时候解决问题的思路,我觉得第三点是最重要的

  1. /**********************************  Include  ********************************/
  2. #include<stdarg.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<stdio.h>

  6. #include <unistd.h>
  7. #include <fcntl.h>
  8. #include <assert.h>
  9. #include <time.h>

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


  12. /**********************************  Define  *********************************/
  13. #define  UBOOT_START_ADDR   0xff000000
  14. #define  UBOOT_SIZE         0x00040000 //256k

  15. #define KERNEL_START_ADDR  0xff060000
  16. #define KERNEL_SIZE         0x000c0000 //768k

  17. #define  ROOTFS_START_ADDR    0xff120000
  18. #define   ROOTFS_SIZE         0x00ee0000 //15232k

  19. /******************************  Local Variable  *****************************/

  20. static int  uboot_fd;
  21. static int  kernel_fd;
  22. static int  rootfs_fd;

  23. volatile char *uboot_map_addr;//address from using mmap()
  24. volatile char *kernel_map_addr;
  25. volatile char *rootfs_map_addr;

  26. /******************************  Extern Variable  ****************************/

  27. /******************************  Local Function  *****************************/

  28. static int openMemRW(void);
  29. static void closeMemRW(void);

  30. /*********************************  Code  ************************************/



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

  33. //         if(argc<1)
  34. //      {
  35. //        printf("parameter error!\n");
  36. //        return -1;
  37. //       }   
  38.    // -----------------------------------------------------------
  39.    // Step 1, open /dev/mem to access NOR FLASH
  40.    // -----------------------------------------------------------

  41.      int len,i;
  42.      
  43.      len=strlen(argv[1])+1;
  44.      
  45.      for(i=0;i<len;i++)
  46.      printf("%c\n",argv[1][i]);
  47.    
  48.     if(openMemRW()<0)
  49.     {
  50.         printf("openMemRW fail\n");
  51.         return -1;
  52.     }
  53.     closeMemRW();
  54.     return 0;
  55. }

  56. static int openMemRW(void)

  57. {
  58.           int i;
  59.        
  60.              //将NOR FLASH的uboot区域映射到进程空间,以便以后直接通过访问映射的进程区域来访问uboot区域
  61.     uboot_fd =  open("/dev/mem",O_RDWR|O_SYNC);
  62.     if(uboot_fd < 0)
  63.     {
  64.         printf("open /dev/mem failed!\n");
  65.         return -1;
  66.     }
  67.     uboot_map_addr = (char *)mmap(NULL,UBOOT_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,uboot_fd,UBOOT_START_ADDR);
  68.     if(uboot_map_addr == NULL)
  69.     {
  70.         printf("openMemRW ERROR : mmap fd=%d failed!\n",uboot_fd);
  71.         close(uboot_fd);
  72.         return -1;
  73.      }
  74.     printf("uboot_map_addr=%x\n",uboot_map_addr);
  75.    
  76.     for(i=0;i<16;i++)
  77.     {
  78.         printf("%x ",uboot_map_addr[i]);
  79.         printf("\n");
  80.      }
  81.     printf("__uboot_map_addr=%x\n",&uboot_map_addr[i]);
  82.    // printf("usage:%s string\n",uboot_map_addr);
  83.                 //将NOR FLASH的kernel区域映射到进程空间,以便以后直接通过访问映射的进程区域来访问kernel区域
  84.     kernel_fd =  open("/dev/mem",O_RDWR|O_SYNC);
  85.     if(kernel_fd < 0)
  86.      {
  87.         printf("open /dev/mem failed!\n");
  88.         return -1;
  89.       }
  90.     kernel_map_addr = (char *)mmap(NULL,KERNEL_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,kernel_fd,KERNEL_START_ADDR);
  91.     if(kernel_map_addr == NULL)
  92.      {
  93.         printf("openMemRW ERROR : mmap fd=%d failed!\n",kernel_fd);
  94.         close(kernel_fd);
  95.         return -1;
  96.       }
  97.     printf("kernel_map_addr=%x\n",kernel_map_addr);
  98.    
  99.     for(i=0;i<16;i++)
  100.     {
  101.         printf("%x",kernel_map_addr[i]);
  102.         printf("\n");
  103.      }
  104.     printf("__kernel_map_addr=%x\n",&kernel_map_addr[i]);
  105.     //printf("usage:%s string\n",kernel_map_addr);
  106.                 //将NOR FLASH的ROOTFS区域映射到进程空间,以便以后直接通过访问映射的进程区域来访问ROOTFS区域
  107.     rootfs_fd =  open("/dev/mem",O_RDWR|O_SYNC);
  108.     if(rootfs_fd < 0)
  109.      {
  110.         printf("open /dev/mem failed!\n");
  111.         return -1;
  112.       }
  113.     rootfs_map_addr = (char *)mmap(NULL,ROOTFS_SIZE ,PROT_READ|PROT_WRITE,MAP_SHARED,rootfs_fd,ROOTFS_START_ADDR);
  114.     if(rootfs_map_addr == NULL)
  115.      {
  116.         printf("openMemRW ERROR : mmap fd=%d failed!\n",rootfs_fd);
  117.         close(rootfs_fd);
  118.         return -1;
  119.       }
  120.     printf("rootfs_map_addr=%x\n",rootfs_map_addr);
  121.     for(i=0;i<16;i++)
  122.     {
  123.         printf("%x",rootfs_map_addr[i]);
  124.         printf("\n");
  125.      }
  126.     printf("__rootfs_map_addr=%x\n",&rootfs_map_addr[i]);

  127.     //printf("usage:%s string\n",rootfs_map_addr);
  128.         return 0;
  129. }
  130. //关闭映射
  131. static void closeMemRW(void)
  132. {
  133.   
  134.      munmap((void*)uboot_map_addr,UBOOT_SIZE);
  135.               close(uboot_fd);
  136.   
  137.      munmap((void*)kernel_map_addr,KERNEL_SIZE);
  138.               close(kernel_fd);

  139.      munmap((void*)rootfs_map_addr,ROOTFS_SIZE);
  140.               close(rootfs_fd);
  141. }

复制代码

评分

参与人数 3可用积分 +51 收起 理由
bitmilong + 30 原创内容
amarant + 15 感谢分享
liying_gg + 6 原创内容

查看全部评分

论坛徽章:
0
68 [报告]
发表于 2016-03-01 16:41 |只看该作者
菜鸟表示看不懂啊

论坛徽章:
8
2015年辞旧岁徽章
日期:2015-03-03 16:54:15午马
日期:2015-02-04 12:00:07羊年新春福章
日期:2015-02-04 11:57:56双子座
日期:2014-12-02 11:44:59金牛座
日期:2014-10-08 16:47:08狮子座
日期:2014-08-29 13:37:46巳蛇
日期:2014-08-26 17:32:29NBA常规赛纪念章
日期:2015-05-04 22:32:03
67 [报告]
发表于 2013-10-22 19:40 |只看该作者
楼主不错,值得佩服  呵呵

论坛徽章:
0
66 [报告]
发表于 2013-10-07 10:17 |只看该作者
呵呵
向诸位学习了!:wink:

论坛徽章:
0
65 [报告]
发表于 2013-09-27 23:51 |只看该作者
太长等会看...

论坛徽章:
0
64 [报告]
发表于 2013-08-26 22:33 |只看该作者
挺楼主,期待更多文章

论坛徽章:
0
63 [报告]
发表于 2013-07-05 12:31 |只看该作者
很给力,升级镜像在bootloader中也可以吧,

论坛徽章:
0
62 [报告]
发表于 2013-07-03 20:42 |只看该作者
楼主很给力,希望能多多共享

论坛徽章:
0
61 [报告]
发表于 2013-04-25 17:43 |只看该作者
我觉得这不想是个刚工作的程序员写的
不管你们信不信,我反正信了。

论坛徽章:
0
60 [报告]
发表于 2013-04-17 11:22 |只看该作者
我的spi norflash 你这个思路我是用不了 ,不过很有借鉴意义。顶一个。
  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP