tools: env: Add support for direct read/write UBI volumes
Up to now we were able to read/write environment data from/to UBI volumes only indirectly by gluebi driver. This driver creates NAND MTD on top of UBI volumes, which is quite a workaroung for this use case. Add support for direct read/write UBI volumes in order to not use obsolete gluebi driver. Forward-ported from this patch: http://patchwork.ozlabs.org/patch/619305/ Original patch: Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com> Forward port: Signed-off-by: S. Lockwood-Childs <sjl@vctlabs.com>
This commit is contained in:
		
							parent
							
								
									d36a27adbb
								
							
						
					
					
						commit
						34255b92e6
					
				|  | @ -25,6 +25,7 @@ | |||
| #include <sys/ioctl.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
| #include <dirent.h> | ||||
| 
 | ||||
| #ifdef MTD_OLD | ||||
| # include <stdint.h> | ||||
|  | @ -34,6 +35,8 @@ | |||
| # include <mtd/mtd-user.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <mtd/ubi-user.h> | ||||
| 
 | ||||
| #include "fw_env_private.h" | ||||
| #include "fw_env.h" | ||||
| 
 | ||||
|  | @ -58,6 +61,7 @@ struct envdev_s { | |||
| 	ulong erase_size;		/* device erase size */ | ||||
| 	ulong env_sectors;		/* number of environment sectors */ | ||||
| 	uint8_t mtd_type;		/* type of the MTD device */ | ||||
| 	int is_ubi;			/* set if we use UBI volume */ | ||||
| }; | ||||
| 
 | ||||
| static struct envdev_s envdevices[2] = | ||||
|  | @ -76,6 +80,7 @@ static int dev_current; | |||
| #define DEVESIZE(i)   envdevices[(i)].erase_size | ||||
| #define ENVSECTORS(i) envdevices[(i)].env_sectors | ||||
| #define DEVTYPE(i)    envdevices[(i)].mtd_type | ||||
| #define IS_UBI(i)     envdevices[(i)].is_ubi | ||||
| 
 | ||||
| #define CUR_ENVSIZE ENVSIZE(dev_current) | ||||
| 
 | ||||
|  | @ -120,6 +125,228 @@ static unsigned char obsolete_flag = 0; | |||
| #define DEFAULT_ENV_INSTANCE_STATIC | ||||
| #include <env_default.h> | ||||
| 
 | ||||
| #define UBI_DEV_START "/dev/ubi" | ||||
| #define UBI_SYSFS "/sys/class/ubi" | ||||
| #define UBI_VOL_NAME_PATT "ubi%d_%d" | ||||
| 
 | ||||
| static int is_ubi_devname(const char *devname) | ||||
| { | ||||
| 	return !strncmp(devname, UBI_DEV_START, sizeof(UBI_DEV_START) - 1); | ||||
| } | ||||
| 
 | ||||
| static int ubi_check_volume_sysfs_name(const char *volume_sysfs_name, | ||||
| 				       const char *volname) | ||||
| { | ||||
| 	char path[256]; | ||||
| 	FILE *file; | ||||
| 	char *name; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	strcpy(path, UBI_SYSFS "/"); | ||||
| 	strcat(path, volume_sysfs_name); | ||||
| 	strcat(path, "/name"); | ||||
| 
 | ||||
| 	file = fopen(path, "r"); | ||||
| 	if (!file) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	ret = fscanf(file, "%ms", &name); | ||||
| 	fclose(file); | ||||
| 	if (ret <= 0 || !name) { | ||||
| 		fprintf(stderr, | ||||
| 			"Failed to read from file %s, ret = %d, name = %s\n", | ||||
| 			path, ret, name); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!strcmp(name, volname)) { | ||||
| 		free(name); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	free(name); | ||||
| 
 | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static int ubi_get_volnum_by_name(int devnum, const char *volname) | ||||
| { | ||||
| 	DIR *sysfs_ubi; | ||||
| 	struct dirent *dirent; | ||||
| 	int ret; | ||||
| 	int tmp_devnum; | ||||
| 	int volnum; | ||||
| 
 | ||||
| 	sysfs_ubi = opendir(UBI_SYSFS); | ||||
| 	if (!sysfs_ubi) | ||||
| 		return -1; | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| 	fprintf(stderr, "Looking for volume name \"%s\"\n", volname); | ||||
| #endif | ||||
| 
 | ||||
| 	while (1) { | ||||
| 		dirent = readdir(sysfs_ubi); | ||||
| 		if (!dirent) | ||||
| 			return -1; | ||||
| 
 | ||||
| 		ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT, | ||||
| 			     &tmp_devnum, &volnum); | ||||
| 		if (ret == 2 && devnum == tmp_devnum) { | ||||
| 			if (ubi_check_volume_sysfs_name(dirent->d_name, | ||||
| 							volname) == 0) | ||||
| 				return volnum; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static int ubi_get_devnum_by_devname(const char *devname) | ||||
| { | ||||
| 	int devnum; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = sscanf(devname + sizeof(UBI_DEV_START) - 1, "%d", &devnum); | ||||
| 	if (ret != 1) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	return devnum; | ||||
| } | ||||
| 
 | ||||
| static const char *ubi_get_volume_devname(const char *devname, | ||||
| 					  const char *volname) | ||||
| { | ||||
| 	char *volume_devname; | ||||
| 	int volnum; | ||||
| 	int devnum; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	devnum = ubi_get_devnum_by_devname(devname); | ||||
| 	if (devnum < 0) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	volnum = ubi_get_volnum_by_name(devnum, volname); | ||||
| 	if (volnum < 0) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	ret = asprintf(&volume_devname, "%s_%d", devname, volnum); | ||||
| 	if (ret < 0) | ||||
| 		return NULL; | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| 	fprintf(stderr, "Found ubi volume \"%s:%s\" -> %s\n", | ||||
| 		devname, volname, volume_devname); | ||||
| #endif | ||||
| 
 | ||||
| 	return volume_devname; | ||||
| } | ||||
| 
 | ||||
| static void ubi_check_dev(unsigned int dev_id) | ||||
| { | ||||
| 	char *devname = (char *)DEVNAME(dev_id); | ||||
| 	char *pname; | ||||
| 	const char *volname = NULL; | ||||
| 	const char *volume_devname; | ||||
| 
 | ||||
| 	if (!is_ubi_devname(DEVNAME(dev_id))) | ||||
| 		return; | ||||
| 
 | ||||
| 	IS_UBI(dev_id) = 1; | ||||
| 
 | ||||
| 	for (pname = devname; *pname != '\0'; pname++) { | ||||
| 		if (*pname == ':') { | ||||
| 			*pname = '\0'; | ||||
| 			volname = pname + 1; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (volname) { | ||||
| 		/* Let's find real volume device name */ | ||||
| 		volume_devname = ubi_get_volume_devname(devname, volname); | ||||
| 		if (!volume_devname) { | ||||
| 			fprintf(stderr, "Didn't found ubi volume \"%s\"\n", | ||||
| 				volname); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		free(devname); | ||||
| 		DEVNAME(dev_id) = volume_devname; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int ubi_update_start(int fd, int64_t bytes) | ||||
| { | ||||
| 	if (ioctl(fd, UBI_IOCVOLUP, &bytes)) | ||||
| 		return -1; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ubi_read(int fd, void *buf, size_t count) | ||||
| { | ||||
| 	ssize_t ret; | ||||
| 
 | ||||
| 	while (count > 0) { | ||||
| 		ret = read(fd, buf, count); | ||||
| 		if (ret > 0) { | ||||
| 			count -= ret; | ||||
| 			buf += ret; | ||||
| 
 | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (ret == 0) { | ||||
| 			/*
 | ||||
| 			 * Happens in case of too short volume data size. If we | ||||
| 			 * return error status we will fail it will be treated | ||||
| 			 * as UBI device error. | ||||
| 			 * | ||||
| 			 * Leave catching this error to CRC check. | ||||
| 			 */ | ||||
| 			fprintf(stderr, "Warning: end of data on ubi volume\n"); | ||||
| 			return 0; | ||||
| 		} else if (errno == EBADF) { | ||||
| 			/*
 | ||||
| 			 * Happens in case of corrupted volume. The same as | ||||
| 			 * above, we cannot return error now, as we will still | ||||
| 			 * be able to successfully write environment later. | ||||
| 			 */ | ||||
| 			fprintf(stderr, "Warning: corrupted volume?\n"); | ||||
| 			return 0; | ||||
| 		} else if (errno == EINTR) { | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		fprintf(stderr, "Cannot read %u bytes from ubi volume, %s\n", | ||||
| 			(unsigned int)count, strerror(errno)); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int ubi_write(int fd, const void *buf, size_t count) | ||||
| { | ||||
| 	ssize_t ret; | ||||
| 
 | ||||
| 	while (count > 0) { | ||||
| 		ret = write(fd, buf, count); | ||||
| 		if (ret <= 0) { | ||||
| 			if (ret < 0 && errno == EINTR) | ||||
| 				continue; | ||||
| 
 | ||||
| 			fprintf(stderr, "Cannot write %u bytes to ubi volume\n", | ||||
| 				(unsigned int)count); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		count -= ret; | ||||
| 		buf += ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int flash_io (int mode); | ||||
| static int parse_config(struct env_opts *opts); | ||||
| 
 | ||||
|  | @ -960,6 +1187,12 @@ static int flash_write (int fd_current, int fd_target, int dev_target) | |||
| 		DEVOFFSET (dev_target), DEVNAME (dev_target)); | ||||
| #endif | ||||
| 
 | ||||
| 	if (IS_UBI(dev_target)) { | ||||
| 		if (ubi_update_start(fd_target, CUR_ENVSIZE) < 0) | ||||
| 			return 0; | ||||
| 		return ubi_write(fd_target, environment.image, CUR_ENVSIZE); | ||||
| 	} | ||||
| 
 | ||||
| 	rc = flash_write_buf(dev_target, fd_target, environment.image, | ||||
| 			     CUR_ENVSIZE); | ||||
| 	if (rc < 0) | ||||
|  | @ -984,6 +1217,12 @@ static int flash_read (int fd) | |||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	if (IS_UBI(dev_current)) { | ||||
| 		DEVTYPE(dev_current) = MTD_ABSENT; | ||||
| 
 | ||||
| 		return ubi_read(fd, environment.image, CUR_ENVSIZE); | ||||
| 	} | ||||
| 
 | ||||
| 	rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE, | ||||
| 			    DEVOFFSET(dev_current)); | ||||
| 	if (rc != CUR_ENVSIZE) | ||||
|  | @ -1165,7 +1404,8 @@ int fw_env_open(struct env_opts *opts) | |||
| 			   DEVTYPE(!dev_current) == MTD_UBIVOLUME) { | ||||
| 			environment.flag_scheme = FLAG_INCREMENTAL; | ||||
| 		} else if (DEVTYPE(dev_current) == MTD_ABSENT && | ||||
| 			   DEVTYPE(!dev_current) == MTD_ABSENT) { | ||||
| 			   DEVTYPE(!dev_current) == MTD_ABSENT && | ||||
| 			   IS_UBI(dev_current) == IS_UBI(!dev_current)) { | ||||
| 			environment.flag_scheme = FLAG_INCREMENTAL; | ||||
| 		} else { | ||||
| 			fprintf (stderr, "Incompatible flash types!\n"); | ||||
|  | @ -1271,8 +1511,12 @@ int fw_env_close(struct env_opts *opts) | |||
| static int check_device_config(int dev) | ||||
| { | ||||
| 	struct stat st; | ||||
| 	int32_t lnum = 0; | ||||
| 	int fd, rc = 0; | ||||
| 
 | ||||
| 	/* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */ | ||||
| 	ubi_check_dev(dev); | ||||
| 
 | ||||
| 	fd = open(DEVNAME(dev), O_RDONLY); | ||||
| 	if (fd < 0) { | ||||
| 		fprintf(stderr, | ||||
|  | @ -1288,7 +1532,14 @@ static int check_device_config(int dev) | |||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	if (S_ISCHR(st.st_mode)) { | ||||
| 	if (IS_UBI(dev)) { | ||||
| 		rc = ioctl(fd, UBI_IOCEBISMAP, &lnum); | ||||
| 		if (rc < 0) { | ||||
| 			fprintf(stderr, "Cannot get UBI information for %s\n", | ||||
| 				DEVNAME(dev)); | ||||
| 			goto err; | ||||
| 		} | ||||
| 	} else if (S_ISCHR(st.st_mode)) { | ||||
| 		struct mtd_info_user mtdinfo; | ||||
| 		rc = ioctl(fd, MEMGETINFO, &mtdinfo); | ||||
| 		if (rc < 0) { | ||||
|  |  | |||
|  | @ -28,3 +28,11 @@ | |||
| 
 | ||||
| # VFAT example | ||||
| #/boot/uboot.env	0x0000          0x4000 | ||||
| 
 | ||||
| # UBI volume | ||||
| #/dev/ubi0_0		0x0		0x1f000		0x1f000 | ||||
| #/dev/ubi0_1		0x0		0x1f000		0x1f000 | ||||
| 
 | ||||
| # UBI volume by name | ||||
| #/dev/ubi0:env		0x0		0x1f000		0x1f000 | ||||
| #/dev/ubi0:env-redund	0x0		0x1f000		0x1f000 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue