Linux内核读写文件以及从SD卡读文件并处理的方法
2012-03-09 15:13
429 查看
在Linux的应用平台上,很多时候我们需要用到从SD卡读数据来使用,比如升级触摸屏固件,比如载入调试文本等,都需要用到Linux的文件系统来操作。
(1)基础函数
这些函数主 要有: filp_open() filp_close(), vfs_read() vfs_write(),set_fs(),get_fs()等,这些函数在linux/fs.h和asm/uaccess.h头文件中声明。
a,打开文件。filp_open():在kernel中可以打开文件,其原形如下:strcut file* filp_open(const char* filename, int open_mode, int mode); 该函数返回strcut file*结构指针,供后继函数操作使用,该返回值用IS_ERR()来检验其有效性。 filename:表明要打开或创建文件的名称(包括路径部分)。在内核中打开的文件时需要注意打开的时机,比如需要打开的文件所在设备还没有挂载到文件系统,会导致打开失败。open_mode:
文件的打开方式,可以取O_CREAT,O_RDWR,O_RDONLY等。 mode:自己实验时必设为0777,方具有完全的读写权限,否则filp_open会返回失败。
b,读写文件。kernel中文件的读写操作可以使用vfs_read()和vfs_write,两函数的原形如下:ssize_t vfs_read(struct file* filp, char __user* buffer, size_t len, loff_t* pos); ssize_t vfs_write(struct file* filp, const char __user* buffer, size_t len, loff_t* pos); 在使用vfs_read()和vfs_write()最后需要注意的一点是最后的参数loff_t
* pos,pos所指向的值要初始化,表明从文件的什么地方开始读写,一般设0。注意buffer是指针。
c,权限函数get_fs()和 set_fs()这两个函数。b所讲的两个函数的第二个参数buffer,前面都有__user修饰符,这就要求这两个buffer指针都应该指向用户空间的内存,如果对该参数传递kernel空间的指针,这两个函数都会返回失败-EFAULT。要使这两个读写函数使用kernel空间的buffer指针也能正确工作,需要使用set_fs()函数,其原形如下:void set_fs(mm_segment_t fs);该函数的作用是改变kernel对内存地址检查的处理方式,其实该函数的参数fs只有两个取值:USER_DS,KERNEL_DS,分别代表用户空间和内核空间,默认情况下,kernel取值为USER_DS,即对用户空间地址检查并做变换。那么要在这种对内存地址做检查变换的函数中使用内核
空间地址,就需要使用set_fs(KERNEL_DS)进行设置。这两个函数的一般用法为:
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
...... //与内存有关的操作
set_fs(old_fs);
还有一些其它的内核函数也有用__user修饰的参数,在kernel中需要用kernel空间的内存代替时,都可以使用类似办法。
d, 关闭读写文件。int filp_close(struct file*filp, fl_owner_t id); 该函数的使用很简单,第二个参数一般传递NULL值。
注意点:其实Linux Kernel组成员不赞成在kernel中独立的读写文件(这样做可能会影响到策略和安全问题),对内核需要操作的文件内容,最好由应用层完成。样例代码:
(2)经常会用到的读写一个文件作为存储标志的做法
很多时候,系统关机时需要底层一个文件保存0或者1,以备下次开机后系统直接读取之前的状态值。同时,这个标志位在系统运行时也是可以随时改变,并影响流程。
需要注意的是,show和store通常只是操作了attr(查看或者单击checkbox)之后才会跑到,默认开机是不会跑show当然也不会去读之前保存的值作为实时变量的,那么必须在变量判断的地方前执行一次read操作,来获得关机前保存的变量值作为实时变量。
另一个注意的地方是,show和store才会创建文件,如果是重新格式化第一次开机,这个文件是没有的,如果没有文件没有初始化值(有时也客户要求初始化成某个值),会导致异常。此时在read中添加一个若文件为空则初始化某个值的过程即可。
(4)从T卡读文件并处理(cred处理是多余的)
按以上方式升级完后不用重启手机即可正常操作。验证成功与否的话,进入工厂菜单中即可看到正确的版本号F6,则说明升级成功。需要注意的是:该种方式只能用BIN固件升级,不能用C格式固件,因为读入的是流文件,也解释了之前升级C固件却导致TP不能用的原因。
(5)以上是升级TP固件,如果是从T卡中读文件并解析文件,则需要编写对应的流文件解析代码段即可。
(1)基础函数
这些函数主 要有: filp_open() filp_close(), vfs_read() vfs_write(),set_fs(),get_fs()等,这些函数在linux/fs.h和asm/uaccess.h头文件中声明。
a,打开文件。filp_open():在kernel中可以打开文件,其原形如下:strcut file* filp_open(const char* filename, int open_mode, int mode); 该函数返回strcut file*结构指针,供后继函数操作使用,该返回值用IS_ERR()来检验其有效性。 filename:表明要打开或创建文件的名称(包括路径部分)。在内核中打开的文件时需要注意打开的时机,比如需要打开的文件所在设备还没有挂载到文件系统,会导致打开失败。open_mode:
文件的打开方式,可以取O_CREAT,O_RDWR,O_RDONLY等。 mode:自己实验时必设为0777,方具有完全的读写权限,否则filp_open会返回失败。
b,读写文件。kernel中文件的读写操作可以使用vfs_read()和vfs_write,两函数的原形如下:ssize_t vfs_read(struct file* filp, char __user* buffer, size_t len, loff_t* pos); ssize_t vfs_write(struct file* filp, const char __user* buffer, size_t len, loff_t* pos); 在使用vfs_read()和vfs_write()最后需要注意的一点是最后的参数loff_t
* pos,pos所指向的值要初始化,表明从文件的什么地方开始读写,一般设0。注意buffer是指针。
c,权限函数get_fs()和 set_fs()这两个函数。b所讲的两个函数的第二个参数buffer,前面都有__user修饰符,这就要求这两个buffer指针都应该指向用户空间的内存,如果对该参数传递kernel空间的指针,这两个函数都会返回失败-EFAULT。要使这两个读写函数使用kernel空间的buffer指针也能正确工作,需要使用set_fs()函数,其原形如下:void set_fs(mm_segment_t fs);该函数的作用是改变kernel对内存地址检查的处理方式,其实该函数的参数fs只有两个取值:USER_DS,KERNEL_DS,分别代表用户空间和内核空间,默认情况下,kernel取值为USER_DS,即对用户空间地址检查并做变换。那么要在这种对内存地址做检查变换的函数中使用内核
空间地址,就需要使用set_fs(KERNEL_DS)进行设置。这两个函数的一般用法为:
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
...... //与内存有关的操作
set_fs(old_fs);
还有一些其它的内核函数也有用__user修饰的参数,在kernel中需要用kernel空间的内存代替时,都可以使用类似办法。
d, 关闭读写文件。int filp_close(struct file*filp, fl_owner_t id); 该函数的使用很简单,第二个参数一般传递NULL值。
注意点:其实Linux Kernel组成员不赞成在kernel中独立的读写文件(这样做可能会影响到策略和安全问题),对内核需要操作的文件内容,最好由应用层完成。样例代码:
#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/uaccess.h> static char buf[] = "你好"; static char buf1[10]; int __init hello_init(void) { struct file *fp; mm_segment_t fs; loff_t pos; printk("hello enter\n"); fp = filp_open("/home/niutao/kernel_file", O_RDWR | O_CREAT, 0644); if (IS_ERR(fp)) { printk("create file error\n"); return -1; } fs = get_fs(); set_fs(KERNEL_DS); pos = 0; vfs_write(fp, buf, sizeof(buf), &pos); pos = 0; vfs_read(fp, buf1, sizeof(buf), &pos); printk("read: %s\n", buf1); filp_close(fp, NULL); set_fs(fs); return 0; }
(2)经常会用到的读写一个文件作为存储标志的做法
很多时候,系统关机时需要底层一个文件保存0或者1,以备下次开机后系统直接读取之前的状态值。同时,这个标志位在系统运行时也是可以随时改变,并影响流程。
u8 tpwakeupswitch = 1; //运行中他处需要判断的变量 static ssize_t fts_tpwakeupswitch_show(struct device *dev,struct device_attribute *attr,char *buf) { //上层读 ssize_t num_read_chars = 0; tpwakeupswitch_read(); //会读取保存的文件并改变tpwakeupswitch的值 num_read_chars = snprintf(buf, PAGE_SIZE, "%d\n", tpwakeupswitch); return num_read_chars; } static ssize_t fts_tpwakeupswitch_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count) { //上层写 if('1' == *buf) tpwakeupswitch = 1; else tpwakeupswitch = 0; tpwakeupswitch_write(); //会根据tpwakeupswitch的值写入到文件中 return count; }读写文件函数如下:
static int tpwakeupswitch_read() { char temp; struct file *fp = NULL; mm_segment_t fs; loff_t pos; if (NULL == fp) fp = filp_open("/data/tpwakeupswitch.txt", O_RDWR | O_CREAT, 0777); if (IS_ERR(fp)) { printk("tpwakeupswitch_read create or open file error\n"); return -1; } fs = get_fs(); set_fs(KERNEL_DS); pos = 0; vfs_read(fp, &temp, sizeof(temp), &pos); if('1' == temp ) tpwakeupswitch = 1; else tpwakeupswitch = 0; filp_close(fp, NULL); set_fs(fs); return 0; } static int tpwakeupswitch_write() { char temp; struct file *fp = NULL; mm_segment_t fs; loff_t pos; if (NULL == fp) fp = filp_open("/data/tpwakeupswitch.txt", O_RDWR | O_CREAT, 0777); if (IS_ERR(fp)) { printk("tpwakeupswitch_write create or open file error\n"); return -1; } fs = get_fs(); set_fs(KERNEL_DS); pos = 0; if(1 == tpwakeupswitch) temp = '1'; else temp = '0'; vfs_write(fp, &temp, sizeof(temp), &pos); filp_close(fp, NULL); set_fs(fs); return 0; }
需要注意的是,show和store通常只是操作了attr(查看或者单击checkbox)之后才会跑到,默认开机是不会跑show当然也不会去读之前保存的值作为实时变量的,那么必须在变量判断的地方前执行一次read操作,来获得关机前保存的变量值作为实时变量。
另一个注意的地方是,show和store才会创建文件,如果是重新格式化第一次开机,这个文件是没有的,如果没有文件没有初始化值(有时也客户要求初始化成某个值),会导致异常。此时在read中添加一个若文件为空则初始化某个值的过程即可。
loff_t file_size; struct inode *inode = NULL; .... inode = fp->f_dentry->d_inode; file_size = inode->i_size; if(0 == file_size )//文件长度为0,肯定是初建文件,默认写个值 { pos = 0; temp = '1'; vfs_write(fp, &temp, sizeof(temp), &pos); }(3)通过举一个实例来说明VFS FILE操作细节,即在ADB中通过echo 到DEVICE_ATTR的store来升级TP固件,相关升级函数可以参见博文/article/1948700.html。store属性函数
static ssize_t melfas_fwupdate_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count) { int UpResult; mt65xx_eint_mask(CUST_EINT_TOUCH_PANEL_NUM); printk("run here \r\n"); UpResult = ms6000_firmware_upgrade_from_TCard(); //T卡升级主函数 if(UpResult== MS6000_RET_SUCCESS){ printk("MFS6000 DOWNLOAD SUCCESS \r\n"); msleep(100); } else mfs6000_print_fail_result(UpResult); i2c_client->addr = MS6000_8BIT_I2CADDR; mt_set_gpio_mode(GPIO_I2C0_SCA_PIN, GPIO_I2C0_SCA_PIN_M_SCL); mt_set_gpio_mode(GPIO_I2C0_SDA_PIN, GPIO_I2C0_SDA_PIN_M_SDA); mt65xx_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); //恢复中断和I2C脚的使用 return count; }
(4)从T卡读文件并处理(cred处理是多余的)
static int ms6000_firmware_upgrade_from_TCard() { unsigned char NewFwVersion,OldFwVersion; UINT16 rdlen; //实际读入内存的文件长度 loff_t file_size; //文件大小 UINT8 *buf = NULL; //分配的内存指针 struct file *fp = NULL; struct inode *inode = NULL; int ret = MS6000_RET_FILE_ACCESS_FAILED; mm_segment_t old_fs; const struct cred *old_cred; struct cred *override_cred; override_cred = prepare_creds(); if (!override_cred) return -ENOMEM; override_cred->fsuid = 0; override_cred->fsgid = 0; old_cred = (struct cred *)override_creds(override_cred); //权限属性 old_fs = get_fs(); set_fs(KERNEL_DS); //权限属性 if(mfs_i2c_read_single_reg(0x21,&OldFwVersion) == true){ NewFwVersion = ms6000_GetLibVer(); //从T卡中读文件,并取出存于特定字节的版本号值 printk("mfs6000 NewFwVersionis 0x%x,OldFwVersion is 0x%x \r\n",NewFwVersion,OldFwVersion); } i2c_client->addr = MS6000_8BIT_DOWNLOAD_ADDR; if(1NewFwVersion != OldFwVersion){ fp = filp_open("/mnt/sdcard/MCH_U5(S)_R01_VF6.bin", O_RDONLY, 0); //载入BIN格式的固件 if (IS_ERR(fp)) { printk("ERROR opening file \r\n"); return MS6000_RET_OPENFILE_ERROR; } else { if (!(fp->f_op)) { printk("File has no file operations registered!\r\n"); filp_close(fp, NULL); return MS6000_RET_OPENFILE_ERROR; } if (!fp->f_op->read) { printk(" File has no READ operations registered!\r\n"); filp_close(fp, NULL); return MS6000_RET_OPENFILE_ERROR; } } inode = fp->f_dentry->d_inode; file_size = inode->i_size; buf = (UINT8 *)kmalloc(file_size, GFP_KERNEL); if(buf < 0) return -ENOMEM; rdlen = vfs_read(fp, buf, file_size, &(fp->f_pos)); printk("start download, file_size is %d,rdlen is %d\r\n",file_size,rdlen); mtk_wdt_disable(); ret = ms6000_download(buf,rdlen); //升级主函数参见文章头部博文 mtk_wdt_get_en_setting(); kfree(buf); filp_close(fp, NULL); revert_creds(old_cred); put_cred(override_cred); set_fs(old_fs); //恢复权限 } else{ printk("because of the same lib, update abort!\r\n"); } return ret; }
按以上方式升级完后不用重启手机即可正常操作。验证成功与否的话,进入工厂菜单中即可看到正确的版本号F6,则说明升级成功。需要注意的是:该种方式只能用BIN固件升级,不能用C格式固件,因为读入的是流文件,也解释了之前升级C固件却导致TP不能用的原因。
(5)以上是升级TP固件,如果是从T卡中读文件并解析文件,则需要编写对应的流文件解析代码段即可。
相关文章推荐
- Linux平台从SD卡读文件并处理的方法
- Android的SD卡文件读写的帮助类——FileHelper,以及查询SD卡是否存在及其容量的方法
- C语言对文件的读写操作以及处理CSV文件的方法
- Linux-2.6 open()打开文件涉及的内核处理和数据结构分析 http://blog.csdn.net/ordeder/article/details/24420637
- linux在多文件中查找关键词的位置以及替换关键词的方法
- Windows 以及 Linux 使用文件作为程序测试输入的方法
- 从Unix分层内核栈以及中断处理看Linux内核的另类
- linux初学-内核编译以及文件系统2
- [Linux] 内核的 /proc 文件系统介绍及使用方法
- 建配置文件方法以及处理编码集为UTF-8步骤 Oracle数据库的连接配置内含读取配置类
- linux 内核 下 读写 文件
- 在编写最简单的linux 内核驱动, 编译后出现的问题以及解决方法。
- java中对图象文件的处理(读写以及转换)
- 编译升级linux由2.4.20-8到2.6.10内核时,遇到的错误,以及解决方法
- C#文件/目录处理 以及 文件的读写
- 从Unix分层内核栈以及中断处理看Linux内核的另类
- linux内核读写文件
- linux 内核启动过程以及挂载android 根文件系统的过程 ( 转)
- linux 内核启动过程以及挂载android 根文件系统的过程
- u-boot向2.6之后的linux内核传递启动参数的方法以及常用的参数