chenrvmldd 发表于 2011-03-16 15:49

2011年2月24号
开始正式的驱动程序了,早上一来就仔细了研读了一下LDD3关于字符设备的驱动程序的步骤。看了两个小时左右就有了自己的思路,在具体的展开字符驱动设备编写之前,先来说说我思路:
1.        首先在驱动中首先一个写nor flash的操作,这里我决定先写第127扇区,安全起见
2.        用户态运行一个应用程序调用驱动中的写操作来写nor flash。
那么问题就来了:
第一:用户态的write操作怎么就能解析到驱动中的write操作?
第二:用户态的数据怎么能传给内核态?
第一个问题,在这我就不详细的解释了
http://dev.firnow.com/course/6_system/linux/Linuxjs/2008831/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 Jan52011 ELDK_FIXOWNER
-rwxr-xr-x    1 root   root         6252 Jan52011 ELDK_MAKEDEV
crw-r--r--    1 root   root   218,   0 Jan1 21:51 commode
crwxr-xr-x    1 root   root       5,   1 Jan2 03:27 console
lrwxrwxrwx    1 root   root            3 Jan52011 fb -> fb0
crwxr-xr-x    1 root   root      29,   0 Jan52011 fb0
lrwxrwxrwx    1 root   root            7 Jan52011 flash_environment -> flasha3
lrwxrwxrwx    1 root   root            7 Jan52011 flash_kernel -> flasha2
lrwxrwxrwx    1 root   root            7 Jan52011 flash_monitor -> flasha1
lrwxrwxrwx    1 root   root            7 Jan52011 flash_reserved -> flasha4
crwxr-xr-x    1 root   root      60,   0 Jan52011 flasha
crwxr-xr-x    1 root   root      60,   1 Jan52011 flasha1
crwxr-xr-x    1 root   root      60,   2 Jan52011 flasha2
crwxr-xr-x    1 root   root      60,   3 Jan52011 flasha3
crwxr-xr-x    1 root   root      60,   4 Jan52011 flasha4
crwxr-xr-x    1 root   root      60,   5 Jan52011 flasha5
crwxr-xr-x    1 root   root      60,   6 Jan52011 flasha6
crwxr-xr-x    1 root   root      60,   7 Jan52011 flasha7
crwxr-xr-x    1 root   root      60,   8 Jan52011 flashb
crwxr-xr-x    1 root   root      60,   9 Jan52011 flashb1
crwxr-xr-x    1 root   root      60,10 Jan52011 flashb2
crwxr-xr-x    1 root   root      60,11 Jan52011 flashb3
crwxr-xr-x    1 root   root      60,12 Jan52011 flashb4
crwxr-xr-x    1 root   root      60,13 Jan52011 flashb5
crwxr-xr-x    1 root   root      60,14 Jan52011 flashb6
crwxr-xr-x    1 root   root      60,15 Jan52011 flashb7
crwxr-xr-x    1 root   root       1,   7 Jan52011 full
brwxr-xr-x    1 root   root       3,   0 Jan52011 hda
brwxr-xr-x    1 root   root       3,   1 Jan52011 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操作后面的注释:#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include<stdarg.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>


#define   LEN    64

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

int main(int argc,char *argv[])
{
   
    int i,fd;
    char buf;
    char *read_buf;
    for(i=0;i<LEN;i++)
    buf='a';
    fd=open("/dev/NORFLASH",O_RDWR);//打开nor flash设备文件,注意这是在终端下首先通过insmod将模块插入,然后mknod命令完成设备文件的创建,这个步骤很重要
    if(fd<0)
    {
      printf("open error\n");
      return -1;
    }
    if(write(fd,buf,LEN)<0)
    {
      printf("write fail\n");
      return -1;
    }
    read_buf=(char *)malloc(LEN);
    if(read(fd,read_buf,LEN)<0)
    {
      printf("read fail\n");
      return -1;
    }
    printdata(read_buf,LEN);
    return 0;
}


static void printdata(char *buffer,int len)
{
    int i;
    for(i=0;i<len;i++)
    {
      printf("[%d]=%c ",i, buffer);
      if (i%8==7) printf("\n");
    }
   
    printf("\n");
    return;   
}
下面是我的驱动程序:#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef __MODULE__
#define __MODULE__
#endif


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>// proc
#include <linux/mm.h>
#include <linux/wrapper.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <asm/io.h>   /*for ioremap*/



#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

#define FLASH_DEV_MAJOR 200



volatile unsigned char *iomap_flash_addr;
static int dev_major;
char *kmalloc_mem;


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

static void erase_flash(void);






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

    ssize_t i;
        unsigned short data;
        volatile unsigned char *addr_ptr;
        addr_ptr=iomap_flash_addr;
       
//        addr_ptr=iomap_flash_addr;/*get the first address to write in */
       
       
        erase_flash();/*erase flash before write*/
       

        for(i=0;i<len;i++)
        {
          
          data=*buf++;
          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 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;
}
         

static ssize_t flash_read(struct file *filep,
                        char *buf,
                        size_t count,
                        loff_t *pstroff)
{
    copy_to_user(buf,(char *)iomap_flash_addr,count);
    return count;
}




static ssize_t flash_write(struct file *filep,
                         const char *buf,
                         size_t count,
                         loff_t *pstroff)
{
   
    kmalloc_mem=(char *)kmalloc(count,GFP_ATOMIC);
    copy_from_user(kmalloc_mem,buf,count);
    write_to_flash(kmalloc_mem,count);
    return count;
}

static int flash_open(struct inode *inode, struct file *filep)
{
    MOD_INC_USE_COUNT;
    return 0;
}

static ssize_t flash_release(struct inode *inode, struct file *filep)
{
    MOD_DEC_USE_COUNT;
    return 0;
}




static struct file_operations   flash_fops    =
{
    .owner = THIS_MODULE,
    .open = flash_open,
    .release = flash_release,
    .read = flash_read,
    .write = flash_write,
   
};



int flash_init(void)
{
    int ret;
    dev_major=FLASH_DEV_MAJOR;
    ret = register_chrdev(dev_major, "NORFLASH", &flash_fops);
    if (ret < 0)
    {
      printk("register fail!\n");
      return ret;
    }
    if (dev_major == 0)
    {
      dev_major = ret;
    }   /*random molloc device*/
    printk("flash_driver device major number %d:\n",dev_major);
    iomap_flash_addr=(volatile unsigned char *)ioremap(SECT_START_ADDR,SECT_SIZE);
    if(iomap_flash_addr==NULL)
   {
      printk(KERN_ALERT"iomap fail\n");
      unregister_chrdev(dev_major,"NORFLASH");
      return -1;
   }
    printk(KERN_ALERT"iomap_flash_addr=%p\n",iomap_flash_addr);
    return 0;
}


void flash_exit(void)
{
    if(iomap_flash_addr!=NULL)
    iounmap((void*)iomap_flash_addr);
    kfree(kmalloc_mem);
    unregister_chrdev(dev_major, "NORFLASH");
    return;
}

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

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

今天就写这么多了,明天继续整理吧

armips 发表于 2011-03-16 16:11

支持楼主的精神!

阿尔发828 发表于 2011-03-16 17:00

顶顶顶

wkq5325 发表于 2011-03-17 08:25

不错的帖子,lz的学习思路值得学习

chenrvmldd 发表于 2011-03-17 15:37

回复 15# wkq5325


    谢谢

miis 发表于 2011-03-19 04:57

hehe,真是不可多得的好帖子!顶一下!

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

king370 发表于 2011-03-21 09:06

这样也算公司技术泄密吧,呵呵。。。

CN薰様 发表于 2011-03-21 17:32

LZ,首先很佩服你的精神。但是建议你去读一下drivers/mtd/maps/physmap.c。你应该可以用到这个分区映射文件。或者你可以阅读这个目录下的其他文件以确定如果改写分区。
这个里面你可以很轻松的分配分区,在用户层只需要flashcp就可完成在线更新。

wangfeifeiwc 发表于 2011-03-22 12:47

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

大家都是从新手走过来的,其实还是那句话,知之为知之,不知为不知,当你拿到一个项目需求时,不明白的地方,一定要找上级沟通,不要自己闷着脑袋搞。即使baidu/google后有思路了,也要找领导确认。还好目前一般都有概要设计和概要设计评审的机制。
页: 1 [2] 3 4 5 6 7
查看完整版本: 一个项目引起的7天写一个nor flash字符驱动程序的经历