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后有思路了,也要找领导确认。还好目前一般都有概要设计和概要设计评审的机制。