免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: chenrvmldd

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

论坛徽章:
0
发表于 2011-03-16 15:49 |显示全部楼层
2011年2月24号
开始正式的驱动程序了,早上一来就仔细了研读了一下LDD3关于字符设备的驱动程序的步骤。看了两个小时左右就有了自己的思路,在具体的展开字符驱动设备编写之前,先来说说我思路:
1.        首先在驱动中首先一个写nor flash的操作,这里我决定先写第127扇区,安全起见
2.        用户态运行一个应用程序调用驱动中的写操作来写nor flash。
那么问题就来了:
第一:用户态的write操作怎么就能解析到驱动中的write操作?
第二:用户态的数据怎么能传给内核态?
第一个问题,在这我就不详细的解释了
http://dev.firnow.com/course/6_s ... 08831/139065_2.html
大家仔细看一下上面的帖子就明白了,也就是网上流传的write的奥秘,写的蛮不错的,为了搞清楚这个流程,我还去read the f***ing linux source code了。
关于第二个问题:起始网上也有,在这里简单的介绍四个函数:
copy_to_usr()—copy a block data into user space
copy_from_users   --copy a block data from user space
get_user()和put_user关于这四个函数具体的解释大家去google吧。
下面来讲解我们本帖的正式的主题,字符驱动设备的开发的步骤:
第一步:注册你的设备,由于本文是2.4的内核因此使用了register_chrdev这个函数,当然还有其他函数,可以参考LDD3的第三章,具体的注册方法如下:
dev_major=FLASH_DEV_MAJOR;
register_chrdev(dev_major, "NORFLASH", &flash_fops);
这里的dev_major是你注册的设备的主设备号:FLASH_DEV_MAJOR是我定义的一个宏:
#define FLASH_DEV_MAJOR 200,为什么我会使用200了?大家可以在自己的linux终端下输入如下的命令:ls  -l  /dev会发现有如下的输出:
-rwxr-xr-x    1 root     root         1620 Jan  5  2011 ELDK_FIXOWNER
-rwxr-xr-x    1 root     root         6252 Jan  5  2011 ELDK_MAKEDEV
crw-r--r--    1 root     root     218,   0 Jan  1 21:51 commode
crwxr-xr-x    1 root     root       5,   1 Jan  2 03:27 console
lrwxrwxrwx    1 root     root            3 Jan  5  2011 fb -> fb0
crwxr-xr-x    1 root     root      29,   0 Jan  5  2011 fb0
lrwxrwxrwx    1 root     root            7 Jan  5  2011 flash_environment -> flasha3
lrwxrwxrwx    1 root     root            7 Jan  5  2011 flash_kernel -> flasha2
lrwxrwxrwx    1 root     root            7 Jan  5  2011 flash_monitor -> flasha1
lrwxrwxrwx    1 root     root            7 Jan  5  2011 flash_reserved -> flasha4
crwxr-xr-x    1 root     root      60,   0 Jan  5  2011 flasha
crwxr-xr-x    1 root     root      60,   1 Jan  5  2011 flasha1
crwxr-xr-x    1 root     root      60,   2 Jan  5  2011 flasha2
crwxr-xr-x    1 root     root      60,   3 Jan  5  2011 flasha3
crwxr-xr-x    1 root     root      60,   4 Jan  5  2011 flasha4
crwxr-xr-x    1 root     root      60,   5 Jan  5  2011 flasha5
crwxr-xr-x    1 root     root      60,   6 Jan  5  2011 flasha6
crwxr-xr-x    1 root     root      60,   7 Jan  5  2011 flasha7
crwxr-xr-x    1 root     root      60,   8 Jan  5  2011 flashb
crwxr-xr-x    1 root     root      60,   9 Jan  5  2011 flashb1
crwxr-xr-x    1 root     root      60,  10 Jan  5  2011 flashb2
crwxr-xr-x    1 root     root      60,  11 Jan  5  2011 flashb3
crwxr-xr-x    1 root     root      60,  12 Jan  5  2011 flashb4
crwxr-xr-x    1 root     root      60,  13 Jan  5  2011 flashb5
crwxr-xr-x    1 root     root      60,  14 Jan  5  2011 flashb6
crwxr-xr-x    1 root     root      60,  15 Jan  5  2011 flashb7
crwxr-xr-x    1 root     root       1,   7 Jan  5  2011 full
brwxr-xr-x    1 root     root       3,   0 Jan  5  2011 hda
brwxr-xr-x    1 root     root       3,   1 Jan  5  2011 hda1
大家注意:第一列那一串英文最前面的字符有的是c,有的是b,有的是l,这里没有d,c表示是字符设备,b表示块设备,d表示是目录文件,l表示是链接文件,第4列表示设备的设备号,我发现200没有被占用,所以我就用了200,大家注意,在2.4中用这种方式,起始在2.6中已经不提倡这种方式,最好的是通过动态分配,具体的方法大家可以参考ldd3
第二步:定义驱动的方法:在ldd3中我们会看到这样的一个数据结构:
static struct file_operations  这个结构里面的每一个成员都是一个指向函数的指针,这个里面也就是填充的我们驱动对应的方法:本文中的这个数据结构的初始化如下:
static struct file_operations   flash_fops    =
{
    .owner = THIS_MODULE,
    .open = flash_open,
    .release = flash_release,
    .read = flash_read,
    .write = flash_write,
   
};
这边要继续阐述一个问题:在前面我们提到当在用户态调用一个write方法写nor flash的时候,那么这个方法会通过文件系统解析到nor flash驱动的write的方法,也就是说如果用户空间发生一个write(nor flash)经过文件系统解析后会调用flash_write(nor flash)操作。
第三步:实现具体的方法:在这里详细阐述一下flash_write的方法:
static ssize_t flash_write(struct file *filep,const char *buf,size_t count,loff_t *pstroff)这是flash_write的原型,具体的每个参数的含义请参考ldd3,下面是flash_write具体的实现:
kmalloc_mem=(char *)kmalloc(count,GFP_ATOMIC);首先通过kmalloc分配内存空间,在这里我请大家去google一下以下几个分配函数的区别,我相信google之后会得到很详细的解释在这里,我就不作解释了:
kmalloc(),
vmalloc(),
get_free_page(),
__get_free_pages(),
alloc_page()
alloc_pages()
malloc()
kmap()
这几个函数在我们驱动开发中除了malloc不用,其他的都会用到的,所以请大家自己去google一下吧。
分配好内存之后将用户的数据拷贝到内核空间:
copy_from_user(kmalloc_mem,buf,count);
拷贝结束之后调用write_to_flash将数据写到nor flash中去,写nor flash的方法这里和上一篇文章在内核模块中写nor flash是一样的
write_to_flash(kmalloc_mem,count);

下面是我的用户态程序:请大家仔细的看一下open操作后面的注释:
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <sys/mman.h>
  6. #include<stdarg.h>
  7. #include <fcntl.h>
  8. #include <assert.h>
  9. #include <sys/types.h>
  10. #include <sys/stat.h>


  11. #define   LEN    64

  12. static void printdata(char *buffer,int len);

  13. int main(int argc,char *argv[])
  14. {
  15.    
  16.     int i,fd;
  17.     char buf[64];
  18.     char *read_buf;
  19.     for(i=0;i<LEN;i++)
  20.     buf[i]='a';
  21.     fd=open("/dev/NORFLASH",O_RDWR);//打开nor flash设备文件,注意这是在终端下首先通过insmod将模块插入,然后mknod命令完成设备文件的创建,这个步骤很重要
  22.     if(fd<0)
  23.     {
  24.         printf("open error\n");
  25.         return -1;
  26.     }
  27.     if(write(fd,buf,LEN)<0)
  28.     {
  29.         printf("write fail\n");
  30.         return -1;
  31.     }
  32.     read_buf=(char *)malloc(LEN);
  33.     if(read(fd,read_buf,LEN)<0)
  34.     {
  35.         printf("read fail\n");
  36.         return -1;
  37.     }
  38.     printdata(read_buf,LEN);
  39.     return 0;
  40. }


  41. static void printdata(char *buffer,int len)
  42. {
  43.     int i;
  44.     for(i=0;i<len;i++)
  45.     {
  46.         printf("[%d]=%c ",i, buffer[i]);
  47.         if (i%8==7) printf("\n");
  48.     }
  49.    
  50.     printf("\n");
  51.     return;   
  52. }
复制代码
下面是我的驱动程序:
  1. #ifndef __KERNEL__
  2. #define __KERNEL__
  3. #endif
  4. #ifndef __MODULE__
  5. #define __MODULE__
  6. #endif


  7. #include <linux/module.h>
  8. #include <linux/kernel.h>
  9. #include <linux/init.h>
  10. #include <linux/fs.h>
  11. #include <linux/proc_fs.h>  // proc
  12. #include <linux/mm.h>
  13. #include <linux/wrapper.h>
  14. #include <linux/errno.h>
  15. #include <linux/delay.h>
  16. #include <linux/slab.h>
  17. #include <linux/vmalloc.h>
  18. #include <linux/time.h>
  19. #include <linux/interrupt.h>
  20. #include <asm/uaccess.h>
  21. #include <linux/poll.h>
  22. #include <asm/io.h>   /*for ioremap*/



  23. #define  FLASH_START_ADDR  0xff000000
  24. #define   MEM_START   0xff000000  /* the first phsical address of nor flash*/
  25. #define   MEM_SIZE    0x01000000   /* the total size of nor flash, 16M*/
  26. #define  SECT_SIZE            0x00020000  //128K
  27. #define  SECT_OFFSET_ADDR     (127*SECT_SIZE)//第127扇区相对于FLASH_START_ADDR的偏移地址
  28. #define  SECT_START_ADDR      (SECT_OFFSET_ADDR+FLASH_START_ADDR)//第127扇区的起始地址0xffe00000

  29. #define FLASH_DEV_MAJOR 200



  30. volatile unsigned char *iomap_flash_addr;
  31. static int dev_major;
  32. char *kmalloc_mem;


  33. static int write_to_flash(char *buf,ssize_t len);

  34. static void erase_flash(void);






  35. static int write_to_flash(char *buf,ssize_t len)
  36. {
  37. //        int left;/*bytes left */

  38.     ssize_t i;
  39.         unsigned short data;
  40.         volatile unsigned char *addr_ptr;
  41.         addr_ptr=iomap_flash_addr;
  42.        
  43. //        addr_ptr=iomap_flash_addr[file->f_pos];/*get the first address to write in */
  44.        
  45.        
  46.         erase_flash();/*erase flash before write*/
  47.        

  48.         for(i=0;i<len;i++)
  49.         {
  50.             
  51.             data=*buf++;
  52.             local_irq_disable();

  53.             *addr_ptr = 0x50;/* clear status register */
  54.             *addr_ptr = 0x40;/* write byte */
  55.             *addr_ptr = data;
  56.             *addr_ptr = 0x70;/* read status register */
  57.                     while((*addr_ptr & 0x80) != 0x80 )
  58.                     {
  59.                         *addr_ptr = 0x70;
  60.                     }
  61.                     
  62.                 local_irq_enable();
  63.                 addr_ptr++;
  64.         }
  65.        
  66.         *addr_ptr = 0xFF;/* reset to read mode        */
  67.         return 0;
  68. }

  69. static void erase_flash()
  70. {
  71.    
  72.     volatile unsigned char *addr;
  73.     addr=iomap_flash_addr;
  74.    
  75.     printk(KERN_ALERT"start to erase:\n");
  76.         local_irq_disable();
  77.         *addr = 0x50; /* clear status register */
  78.         *addr = 0x20; /* erase setup */
  79.         *addr = 0xD0; /* erase confirm */
  80.         *addr = 0x70; /* read status register */
  81.         while((*addr & 0x80) != 0x80 )
  82.         {
  83.                 *addr = 0x70;
  84.         }
  85.         local_irq_enable();
  86.         printk(KERN_ALERT"erase sector  ok!\n");
  87.         *addr = 0xFF;/* reset to read mode        */
  88.         return;
  89. }
  90.          

  91. static ssize_t flash_read(struct file *filep,
  92.                         char *buf,
  93.                         size_t count,
  94.                         loff_t *pstroff)
  95. {
  96.     copy_to_user(buf,(char *)iomap_flash_addr,count);
  97.     return count;
  98. }




  99. static ssize_t flash_write(struct file *filep,
  100.                          const char *buf,
  101.                          size_t count,
  102.                          loff_t *pstroff)
  103. {
  104.    
  105.     kmalloc_mem=(char *)kmalloc(count,GFP_ATOMIC);
  106.     copy_from_user(kmalloc_mem,buf,count);
  107.     write_to_flash(kmalloc_mem,count);
  108.     return count;
  109. }

  110. static int flash_open(struct inode *inode, struct file *filep)
  111. {
  112.     MOD_INC_USE_COUNT;
  113.     return 0;
  114. }

  115. static ssize_t flash_release(struct inode *inode, struct file *filep)
  116. {
  117.     MOD_DEC_USE_COUNT;
  118.     return 0;
  119. }




  120. static struct file_operations   flash_fops    =
  121. {
  122.     .owner = THIS_MODULE,
  123.     .open = flash_open,
  124.     .release = flash_release,
  125.     .read = flash_read,
  126.     .write = flash_write,
  127.    
  128. };



  129. int flash_init(void)
  130. {
  131.     int ret;
  132.     dev_major=FLASH_DEV_MAJOR;
  133.     ret = register_chrdev(dev_major, "NORFLASH", &flash_fops);
  134.     if (ret < 0)
  135.     {
  136.         printk("register fail!\n");
  137.         return ret;
  138.     }
  139.     if (dev_major == 0)
  140.     {
  141.         dev_major = ret;
  142.     }   /*random molloc device*/
  143.     printk("flash_driver device major number %d:\n",dev_major);
  144.     iomap_flash_addr=(volatile unsigned char *)ioremap(SECT_START_ADDR,SECT_SIZE);
  145.     if(iomap_flash_addr==NULL)
  146.      {
  147.         printk(KERN_ALERT"iomap fail\n");
  148.         unregister_chrdev(dev_major,"NORFLASH");
  149.         return -1;
  150.      }
  151.     printk(KERN_ALERT"iomap_flash_addr=%p\n",iomap_flash_addr);
  152.     return 0;
  153. }


  154. void flash_exit(void)
  155. {
  156.     if(iomap_flash_addr!=NULL)
  157.     iounmap((void*)iomap_flash_addr);
  158.     kfree(kmalloc_mem);
  159.     unregister_chrdev(dev_major, "NORFLASH");
  160.     return;
  161. }

  162. module_init(flash_init);
  163. module_exit(flash_exit);
  164. MODULE_LICENSE("GPL");
  165. EXPORT_NO_SYMBOLS;
复制代码
上面的两个程序经过测试,发现通过用户态调用底层驱动写nor flash最后127扇区的方案是可行的,那么明天我直接准备更换nor flash中的kernel文件了,好紧张啊!嘿嘿,这两天发现学了太多太多的东西了,看了不知道多少书,不知道用了多少次google和baidu,每天都是干活干到12点多,不过发现还是蛮充实

评分

参与人数 1可用积分 +5 收起 理由
bitmilong + 5 鼓励

查看全部评分

论坛徽章:
0
发表于 2011-03-16 15:52 |显示全部楼层
今天就写这么多了,明天继续整理吧

论坛徽章:
0
发表于 2011-03-16 16:11 |显示全部楼层
支持楼主的精神!

论坛徽章:
0
发表于 2011-03-16 17:00 |显示全部楼层
顶顶顶

论坛徽章:
0
发表于 2011-03-17 08:25 |显示全部楼层
不错的帖子,lz的学习思路值得学习

论坛徽章:
0
发表于 2011-03-17 15:37 |显示全部楼层
回复 15# wkq5325


    谢谢

论坛徽章:
0
发表于 2011-03-19 04:57 |显示全部楼层
hehe,真是不可多得的好帖子!顶一下!

还有请问楼主你的Nor Flash用的是什么驱动,怎么映射到physical memory上面去的?

论坛徽章:
0
发表于 2011-03-21 09:06 |显示全部楼层
这样也算公司技术泄密吧,呵呵。。。

论坛徽章:
2
2015年亚洲杯之阿曼
日期:2015-03-23 18:11:212015亚冠之大阪钢巴
日期:2015-09-07 13:54:16
发表于 2011-03-21 17:32 |显示全部楼层
LZ,首先很佩服你的精神。但是建议你去读一下drivers/mtd/maps/physmap.c。你应该可以用到这个分区映射文件。或者你可以阅读这个目录下的其他文件以确定如果改写分区。
这个里面你可以很轻松的分配分区,在用户层只需要flashcp就可完成在线更新。

论坛徽章:
0
发表于 2011-03-22 12:47 |显示全部楼层
呵呵,你的东西写得太多了,匆匆扫了一眼,说点自己的看法。实际的项目需求中,如果产品化,有一些东西不知道你有没有考虑:
       一个是何时触发升级的请求,除了盒子端要处理一些事情,服务器端如何配合?
       一个是备份的问题,如果升级中失败怎么办?(a 中途断电 b 所更新的内核或者文件系统本身就有问题)
     一个你的代码的移植性,如何后续换成nand flash你如何应对?
    呵呵,其实要考虑的问题很多,很明显你是走了弯路,因为使用mtd确实能很轻松完成你所完成的大部分工作,而你却弃之不用。

大家都是从新手走过来的,其实还是那句话,知之为知之,不知为不知,当你拿到一个项目需求时,不明白的地方,一定要找上级沟通,不要自己闷着脑袋搞。即使baidu/google后有思路了,也要找领导确认。还好目前一般都有概要设计和概要设计评审的机制。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP