app中通过mtd flash的接口对 mtd5分区进行升级
//通过mtd实现linux下只读文件系统中的flash应用层读写文件
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#define MTD_FACTORY "/dev/mtd5"
#define FIBERHOME_MAGIC_NUM 0x13579246
#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
#define MEMERASE _IOW('M', 2, struct erase_info_user)
#define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
struct erase_info_user {
unsigned int start;
unsigned int length;
};
struct mtd_info_user {
unsigned char type;
unsigned int flags;
unsigned int size;
unsigned int erasesize;
unsigned int oobblock;
unsigned int oobsize;
unsigned int ecctype;
unsigned int eccsize;
};
void print_buf (unsigned char* p, int len)
{
int i = 0;
if (p == NULL || len < 0)
return;
for (i = 0; i < len; i++)
{
printf ("0x%x ", p[i]);
if ((i % 16 == 0) && (i != 0))
printf ("\n");
}
}
int fhdrv_kdrv_set_led_control_data(int flag)
{
FILE *fp = NULL;
char buf[128] = {0};
fp = fopen("/proc/driver/led_data", "w");
if (fp == NULL)
{
return -1;
}
snprintf(buf, sizeof(buf), "%x %d", FIBERHOME_MAGIC_NUM, flag);
fputs(buf,fp);
fclose(fp);
return 0;
}
int mtd_read(int off, int len)
{
int i = 0;
int fd = 0;
unsigned char* buf = NULL;
buf = malloc (len);
if (buf == 0)
{
printf ("malloc err\n");
return -1;
}
fd = open(MTD_FACTORY, O_RDWR | O_SYNC);
if(fd < 0) {
printf("Could not open mtd device: %s\n", MTD_FACTORY);
free (buf);
return -1;
}
lseek(fd, 0, SEEK_SET);
if(read(fd, buf, len) != len){
printf("read() failed\n");
free(buf);
close(fd);
return -1;
}
print_buf (buf, len);
close(fd);
free(buf);
return 0;
}
int mtd_erase ()
{
struct mtd_info_user mtdInfo;
struct erase_info_user mtdEraseInfo;
int fd = 0;
fd = open(MTD_FACTORY, O_RDWR | O_SYNC);
if(fd < 0) {
fprintf(stderr, "Could not open mtd device: %s\n", MTD_FACTORY);
return -1;
}
if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
fprintf(stderr, "Could not get MTD device info from %s\n", MTD_FACTORY);
close(fd);
return -1;
}
lseek(fd, 0, SEEK_SET);
mtdEraseInfo.length = mtdInfo.erasesize;
mtdEraseInfo.start = 0x0;
for (mtdEraseInfo.start; mtdEraseInfo.start < mtdInfo.size; mtdEraseInfo.start += mtdInfo.erasesize) {
ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
if(ioctl(fd, MEMERASE, &mtdEraseInfo)){
fprintf(stderr, "Failed to erase block on %s at 0x%x\n", MTD_FACTORY, mtdEraseInfo.start);
close(fd);
return -1;
}
}
close(fd);
return 0;
}
int mtd_write(int off, int len, unsigned char* data)
{
struct mtd_info_user mtdInfo;
struct erase_info_user mtdEraseInfo;
int fd = 0;
int offset;
FILE *fp = NULL;
char buf[2048] = {0};
char buf_bak[2048] = {0};
int read_len = 0;
/* 打开镜像文件 */
fp = fopen(data, "r");
if (fp == NULL)
{
return -1;
}
printf("open image file :%s \n", data);
/* 写入镜像文件 */
fd = open(MTD_FACTORY, O_RDWR | O_SYNC);
if(fd < 0) {
printf("write fail \n");
return -1;
}
if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
printf("write fail \n");
close(fd);
return -1;
}
lseek(fd, 0, SEEK_SET);
fseek(fp, 0, SEEK_SET);
printf("prepare updata len:0x%x \n", len);
for (offset = 0; (offset + 1024) < len;)
{
memset(&buf, 0, sizeof(buf));
printf("write addr :0x%x per:%d\n", offset, offset*100/len);
if (0 == fread(buf, 1024, 1, fp))
{
printf("read image err !!! offset:0x%x\n", offset);
}
if (write(fd, buf, 1024) != 1024) {
printf("write image err !!! offset:0x%x\n", offset);
goto write_fail;
}
offset += 1024;
}
if (offset + 1024 > len)
{
memset(&buf, 0, sizeof(buf));
printf("left data :%d \n", len - offset);
printf("write addr :0x%x per:%d\n", offset, offset*100/len);
if (0 == fread(buf, len - offset, 1, fp))
{
printf("left read image err !!! offset:%x\n", offset);
}
if (write(fd, buf, 1024) != 1024) {
printf("left write image err !!! offset:0x:%x\n", offset);
goto write_fail;
}
}
fclose(fp);
close(fd);
printf("updata success \n");
return 0;
write_fail:
printf("write fail \n");
fclose(fp);
close(fd);
return -1;
}
void usage(char **str)
{
printf("How to use:\n");
printf("\tread: %s r offset length\n", str[0]);
printf("\twrite: %s w offset length data...\n", str[0]);
printf ("\terase: %s e\n", str[0]);
printf ("\tfor read, offset should >= 0 && offset + length should < 10240\n");
printf ("\tfor write, offset should >= 0 && offset + length should < 10240 && length should <= 10\n");
printf ("\tfor erase, off 0 to 10240(not included) will be erased to 0xFF\n");
}
int main(int argc,char **argv)
{
char op;
int off = 0;
int len = 0;
if (argc < 2)
goto CmdFail;
fhdrv_kdrv_set_led_control_data(1);
op = *(argv[1]);
if (op != 'e')
{
off = atoi (argv[2]);
len = atoi (argv[3]);
printf("op:%c, off:0x%x, len:0x%x \n", op, off, len);
}
switch (op) {
case 'r':
if (mtd_read(off, len) < 0)
goto Fail;
break;
case 'w':
if (mtd_write(off, len, argv[4]) < 0)
goto Fail;
break;
case 'e':
if (mtd_erase() < 0)
goto Fail;
break;
default:
goto CmdFail;
}
return 0;
CmdFail:
usage(argv);
Fail:
return -1;
}
#tftp -g -r a.out 10.35.27.168
a.out 100% |*******************************| a.out 100% |*******************************| 14764 0:00:00 ETA
#chmod +x a.out
#./a.out e
#./a.out w 0 3964928 rootfs.squashfs.img
op:w, off:0x0, len:0x3c8000
open image file :rootfs.squashfs.img
prepare updata len:0x3c8000
write addr :0x0 per:0
横向对比,重要总结
linux 驱动为什么分为,字符设备,块设备,网络设备。这个和gpio,i2c,等子系统有什么关系呢。目前我认为,众多子系统都是根据总线不同,在字符设备上进行了封装,使得子系统可以支持很多的,通用的驱动外设的方法。所以这些子系统的设备都包含重要结构体fileoperation。
关键问题
read和write都是常见的文件操作,那么像erase这种文件操作,使用ioctl的这个结构体为什么是这么定义的呢?
ioctl其实就是驱动层和业务层,约定好的处理文件操作之外的命令,处理方式。所以说为什么这么定义,这个需要看驱动层代码。在mtdchar.c这个文件里面有介绍:
static const struct file_operations mtd_fops = {
.owner = THIS_MODULE,
.llseek = mtdchar_lseek,
.read = mtdchar_read,
.write = mtdchar_write,
.unlocked_ioctl = mtdchar_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mtdchar_compat_ioctl,
#endif
.open = mtdchar_open,
.release = mtdchar_close,
.mmap = mtdchar_mmap,
#ifndef CONFIG_MMU
.get_unmapped_area = mtdchar_get_unmapped_area,
.mmap_capabilities = mtdchar_mmap_capabilities,
#endif
};
继续看ioctl这个里面函数的定义:
static long mtdchar_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
{
int ret;
mutex_lock(&mtd_mutex);
ret = mtdchar_ioctl(file, cmd, arg);
mutex_unlock(&mtd_mutex);
return ret;
}
在这个函数中,就是加了互斥锁,然后直接调用mtdchar_ioctl。
static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
void __user *argp = (void __user *)arg;
int ret = 0;
u_long size;
struct mtd_info_user info;
pr_debug("MTD_ioctl\n");
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (cmd & IOC_IN) {
if (!access_ok(VERIFY_READ, argp, size))
return -EFAULT;
}
if (cmd & IOC_OUT) {
if (!access_ok(VERIFY_WRITE, argp, size))
return -EFAULT;
}
switch (cmd) {
case MEMGETREGIONCOUNT:
if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
return -EFAULT;
break;
case MEMGETREGIONINFO:
。。。
}
重点关注传递命令,对应的参数的结构体,继续查找:
#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
#define MEMERASE _IOW('M', 2, struct erase_info_user)
#define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
struct mtd_info_user {
__u8 type;
__u32 flags;
__u32 size; /* Total size of the MTD */
__u32 erasesize;
__u32 writesize;
__u32 oobsize; /* Amount of OOB data per block (e.g. 16) */
__u64 padding; /* Old obsolete field; do not use */
};
可以看到这个结构体信息,定义的。因此使用的才会这么使用