|
@@ -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) {
|