diff mbox series

[libubootenv,RFC] Move MTD related function to separate file

Message ID 20231128180938.532132-1-stefano.babic@swupdate.org
State Accepted
Headers show
Series [libubootenv,RFC] Move MTD related function to separate file | expand

Commit Message

Stefano Babic Nov. 28, 2023, 6:09 p.m. UTC
This is to support other OS that do not have MTD support like Linux.

Signed-off-by: Stefano Babic <stefano.babic@swupdate.org>
---
 src/CMakeLists.txt  |   1 +
 src/uboot_env.c     | 485 ++-----------------------------------------
 src/uboot_mtd.c     | 487 ++++++++++++++++++++++++++++++++++++++++++++
 src/uboot_private.h |  69 ++++++-
 4 files changed, 568 insertions(+), 474 deletions(-)
 create mode 100644 src/uboot_mtd.c

Comments

Storm, Christian Nov. 29, 2023, 9 p.m. UTC | #1
Hi Stefano,

> This is to support other OS that do not have MTD support like Linux.
> 
> Signed-off-by: Stefano Babic <stefano.babic@swupdate.org>

With the two patches I just sent applied, my local testing was successful.
If it passes tests on Linux (haven't tested), I deem it ready..


Thanks!


Kind regards,
  Christian
diff mbox series

Patch

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ababe0f..1291a79 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@  cmake_minimum_required (VERSION 2.6)
 # Sources and private headers
 SET(libubootenv_SOURCES
   uboot_env.c
+  uboot_mtd.c
   uboot_private.h
 )
 
diff --git a/src/uboot_env.c b/src/uboot_env.c
index ae85c7e..367d349 100644
--- a/src/uboot_env.c
+++ b/src/uboot_env.c
@@ -21,7 +21,12 @@ 
 #include <dirent.h>
 #include <unistd.h>
 #include <limits.h>
+#ifdef __FreeBSD__
+#include <sys/disk.h>
+#define BLKGETSIZE64 DIOCGMEDIASIZE
+#else
 #include <linux/fs.h>
+#endif
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -34,32 +39,9 @@ 
 #include <sys/ioctl.h>
 #include <zlib.h>
 #include <yaml.h>
-#include <mtd/mtd-user.h>
-#include <mtd/ubi-user.h>
 
 #include "uboot_private.h"
 
-#define UBI_MAX_VOLUME			128
-
-#define DEVICE_MTD_NAME 		"/dev/mtd"
-#define DEVICE_UBI_NAME 		"/dev/ubi"
-#define DEVICE_UBI_CTRL 		"/dev/ubi_ctrl"
-#define SYS_UBI				"/sys/class/ubi"
-#define SYS_UBI_MTD_NUM			"/sys/class/ubi/ubi%d/mtd_num"
-#define SYS_UBI_VOLUME_COUNT		"/sys/class/ubi/ubi%d/volumes_count"
-#define SYS_UBI_VOLUME_NAME		"/sys/class/ubi/ubi%d/ubi%d_%d/name"
-
-#define	LIST_FOREACH_SAFE(var, head, field, tvar)			\
-	for ((var) = LIST_FIRST((head));				\
-	    (var) != NULL &&						\
-	    ((tvar) = LIST_NEXT((var), field), 1);			\
-	    (var) = (tvar))
-
-#define MTDLOCK(dev, psector)	\
-	if (!dev->disable_mtd_lock) ioctl (dev->fd, MEMLOCK, psector)
-#define MTDUNLOCK(dev, psector) \
-	if (!dev->disable_mtd_lock) ioctl (dev->fd, MEMUNLOCK, psector)
-
 /* yaml_* functions return 1 on success and 0 on failure. */
 enum yaml_status {
     SUCCESS = 0,
@@ -212,248 +194,6 @@  static enum device_type get_device_type(char *device)
 	return type;
 }
 
-static int ubi_get_dev_id(char *device)
-{
-	int dev_id = -1;
-	char *sep;
-
-	sep = strrchr(device, 'i');
-	if (sep)
-		sscanf(sep + 1, "%d", &dev_id);
-
-	return dev_id;
-}
-
-static int mtd_get_dev_id(char *device)
-{
-	int dev_id = -1;
-	char *sep;
-
-	sep = strrchr(device, 'd');
-	if (sep)
-		sscanf(sep + 1, "%d", &dev_id);
-
-	return dev_id;
-}
-
-static int ubi_get_dev_id_from_mtd(char *device)
-{
-	DIR *sysfs_ubi;
-	struct dirent *dirent;
-	int mtd_id;
-
-	mtd_id = mtd_get_dev_id(device);
-	if (mtd_id < 0)
-		return -1;
-
-	sysfs_ubi = opendir(SYS_UBI);
-	if (!sysfs_ubi)
-		return -1;
-
-	while (1) {
-		int ubi_num, ret;
-
-		dirent = readdir(sysfs_ubi);
-		if (!dirent)
-			break;
-
-		if (strlen(dirent->d_name) >= 255) {
-			closedir(sysfs_ubi);
-			return -1;
-		}
-
-		ret = sscanf(dirent->d_name, "ubi%d", &ubi_num);
-		if (ret == 1) {
-			char filename[DEVNAME_MAX_LENGTH];
-			char data[DEVNAME_MAX_LENGTH];
-			int fd, n, num_mtd = -1;
-
-			snprintf(filename, sizeof(filename), SYS_UBI_MTD_NUM, ubi_num);
-			fd = open(filename, O_RDONLY);
-			if (fd < 0)
-				continue;
-
-			n = read(fd, data, sizeof(data));
-			close(fd);
-			if (n < 0)
-				continue;
-
-			if (sscanf(data, "%d", &num_mtd) != 1)
-				num_mtd = -1;
-
-			if (num_mtd < 0)
-				continue;
-
-			if (num_mtd == mtd_id) {
-				closedir(sysfs_ubi);
-				return ubi_num;
-			}
-		}
-	}
-
-	closedir(sysfs_ubi);
-	return -1;
-}
-
-static int ubi_get_num_volume(char *device)
-{
-	char filename[DEVNAME_MAX_LENGTH];
-	char data[DEVNAME_MAX_LENGTH];
-	int dev_id, fd, n, num_vol = -1;
-
-	dev_id = ubi_get_dev_id(device);
-	if (dev_id < 0)
-		return -1;
-
-	snprintf(filename, sizeof(filename), SYS_UBI_VOLUME_COUNT, dev_id);
-	fd = open(filename, O_RDONLY);
-	if (fd < 0)
-		return -1;
-
-	n = read(fd, data, sizeof(data));
-	if (n < 0)
-		goto out;
-
-	if (sscanf(data, "%d", &num_vol) != 1)
-		num_vol = -1;
-
-out:
-	close(fd);
-	return num_vol;
-}
-
-static int ubi_get_volume_name(char *device, int vol_id, char vol_name[DEVNAME_MAX_LENGTH])
-{
-	char filename[80];
-	char data[DEVNAME_MAX_LENGTH];
-	int dev_id, fd, n, ret = -1;
-
-	dev_id = ubi_get_dev_id(device);
-	if (dev_id < 0)
-		return -1;
-
-	snprintf(filename, sizeof(filename), SYS_UBI_VOLUME_NAME, dev_id, dev_id, vol_id);
-	fd = open(filename, O_RDONLY);
-	if (fd < 0)
-		return -1;
-
-	memset(data, 0, DEVNAME_MAX_LENGTH);
-	n = read(fd, data, DEVNAME_MAX_LENGTH);
-	if (n < 0)
-		goto out;
-
-	memset(vol_name, 0, DEVNAME_MAX_LENGTH);
-	if (sscanf(data, "%s", vol_name) != 1)
-		goto out;
-
-	ret = 0;
-
-out:
-	close(fd);
-	return ret;
-}
-
-static int ubi_get_vol_id(char *device, char *volname)
-{
-	int i, n, ret, num_vol, vol_id = -1;
-
-	num_vol = ubi_get_num_volume(device);
-	if (num_vol < 0)
-		goto out;
-
-	i = 0;
-	n = 0;
-	while ((n < num_vol) && (i < UBI_MAX_VOLUME))
-	{
-		char name[DEVNAME_MAX_LENGTH];
-
-		ret = ubi_get_volume_name(device, i, name);
-		if (!ret && !strcmp(name, volname)) {
-			vol_id = i;
-			break;
-		}
-
-		i++;
-		if (!ret)
-			n++;
-	}
-
-out:
-	return vol_id;
-}
-
-static int ubi_update_name(struct uboot_flash_env *dev)
-{
-	char device[DEVNAME_MAX_LENGTH];
-	char volume[DEVNAME_MAX_LENGTH];
-	int dev_id, vol_id, fd, ret = -EBADF;
-	struct stat st;
-	char *sep;
-
-	if (!strncmp(dev->devname, DEVICE_MTD_NAME, strlen(DEVICE_MTD_NAME)))
-	{
-		sep = strchr(dev->devname, DEVNAME_SEPARATOR);
-		if (sep)
-		{
-			memset(device, 0, DEVNAME_MAX_LENGTH);
-			memcpy(device, dev->devname, sep - dev->devname);
-
-			memset(volume, 0, DEVNAME_MAX_LENGTH);
-			sscanf(sep + 1, "%s", &volume[0]);
-
-			ret = ubi_get_dev_id_from_mtd(device);
-			if (ret < 0) {
-				struct ubi_attach_req req;
-
-				memset(&req, 0, sizeof(struct ubi_attach_req));
-				req.ubi_num = UBI_DEV_NUM_AUTO;
-				req.mtd_num = mtd_get_dev_id(device);
-				req.vid_hdr_offset = 0;
-
-				fd = open(DEVICE_UBI_CTRL, O_RDONLY);
-				if (fd == -1)
-					return -EBADF;
-
-				ret = ioctl(fd, UBI_IOCATT, &req);
-				close(fd);
-				if (ret == -1)
-					return -EBADF;
-
-				sprintf(dev->devname, DEVICE_UBI_NAME"%d:%s", req.ubi_num, volume);
-			} else {
-				sprintf(dev->devname, DEVICE_UBI_NAME"%d:%s", ret, volume);
-			}
-		} else {
-			return -EBADF;
-		}
-	}
-
-	sep = strchr(dev->devname, DEVNAME_SEPARATOR);
-	if (sep)
-	{
-		memset(device, 0, DEVNAME_MAX_LENGTH);
-		memcpy(device, dev->devname, sep - dev->devname);
-
-		memset(volume, 0, DEVNAME_MAX_LENGTH);
-		sscanf(sep + 1, "%s", &volume[0]);
-
-		dev_id = ubi_get_dev_id(device);
-		if (dev_id < 0)
-			goto out;
-
-		vol_id = ubi_get_vol_id(device, volume);
-		if (vol_id < 0)
-			goto out;
-
-		sprintf(dev->devname, "%s_%d", device, vol_id);
-	}
-
-	ret = 0;
-
-out:
-	return ret;
-}
-
 static int normalize_device_path(char *path, struct uboot_flash_env *dev)
 {
 	char *sep = NULL, *normalized = NULL;
@@ -512,7 +252,7 @@  static int check_env_device(struct uboot_flash_env *dev)
 		return -EBADF;
 
 	if (dev->device_type == DEVICE_UBI) {
-		ret = ubi_update_name(dev);
+		ret = libubootenv_ubi_update_name(dev);
 		if (ret)
 			return ret;
 	}
@@ -526,7 +266,7 @@  static int check_env_device(struct uboot_flash_env *dev)
 
 	if (S_ISCHR(st.st_mode)) {
 		if (dev->device_type == DEVICE_MTD) {
-			ret = ioctl(fd, MEMGETINFO, &dev->mtdinfo);
+			ret = libubootenv_mtdgetinfo(fd, dev);
 			if (ret < 0 || (dev->mtdinfo.type != MTD_NORFLASH &&
 					dev->mtdinfo.type != MTD_NANDFLASH)) {
 				close(fd);
@@ -596,15 +336,6 @@  static bool check_compatible_devices(struct uboot_ctx *ctx)
 	return true;
 }
 
-static int is_nand_badblock(struct uboot_flash_env *dev, loff_t start)
-{
-	int bad;
-	if (dev->mtdinfo.type != MTD_NANDFLASH)
-		return 0;
-	bad = ioctl(dev->fd, MEMGETBADBLOCK, &start);
-	return bad;
-}
-
 static int fileread(struct uboot_flash_env *dev, void *data)
 {
 	int ret = 0;
@@ -638,81 +369,6 @@  static int fileread(struct uboot_flash_env *dev, void *data)
 	return ret;
 }
 
-static int mtdread(struct uboot_flash_env *dev, void *data)
-{
-	size_t count;
-	size_t blocksize;
-	loff_t start;
-	int sectors, skip;
-	int ret = 0;
-
-	switch (dev->mtdinfo.type) {
-	case MTD_ABSENT:
-	case MTD_NORFLASH:
-		if (dev->offset)
-			if (lseek(dev->fd, dev->offset, SEEK_SET) < 0) {
-				ret = -EIO;
-				break;
-			}
-		ret = read(dev->fd, data, dev->envsize);
-		break;
-	case MTD_NANDFLASH:
-		if (dev->offset)
-			if (lseek(dev->fd, dev->offset, SEEK_SET) < 0) {
-				ret = -EIO;
-				break;
-			}
-
-		count = dev->envsize;
-		start = dev->offset;
-		blocksize = dev->envsize;
-		sectors = dev->envsectors ? dev->envsectors : 1;
-
-		while (count > 0) {
-			skip = is_nand_badblock(dev, start);
-			if (skip < 0) {
-				return -EIO;
-			}
-
-			if (skip > 0) {
-				start += dev->sectorsize;
-				sectors--;
-				if (sectors > 0)
-					continue;
-				return  -EIO;
-			}
-
-			if (count > dev->sectorsize)
-				blocksize = dev->sectorsize;
-			else
-				blocksize = count;
-
-			if (lseek(dev->fd, start, SEEK_SET) < 0) {
-				return -EIO;
-			}
-			if (read(dev->fd, data, blocksize) != blocksize) {
-				return -EIO;
-			}
-			start += dev->sectorsize;
-			data += blocksize;
-			count -= blocksize;
-			ret += blocksize;
-		}
-		break;
-	}
-
-	return ret;
-}
-
-static int ubiread(struct uboot_flash_env *dev, void *data)
-{
-	int ret = 0;
-
-	ret = read(dev->fd, data, dev->envsize);
-
-	return ret;
-}
-
 static int devread(struct uboot_ctx *ctx, unsigned int copy, void *data)
 {
 	int ret;
@@ -732,10 +388,10 @@  static int devread(struct uboot_ctx *ctx, unsigned int copy, void *data)
 		ret = fileread(dev, data);
 		break;
 	case DEVICE_MTD:
-		ret = mtdread(dev, data);
+		ret = libubootenv_mtdread(dev, data);
 		break;
 	case DEVICE_UBI:
-		ret = ubiread(dev, data);
+		ret = libubootenv_ubiread(dev, data);
 		break;
 	default:
 		ret = -1;
@@ -856,95 +512,6 @@  static int filewrite(struct uboot_flash_env *dev, void *data)
 	return ret;
 }
 
-static int mtdwrite(struct uboot_flash_env *dev, void *data)
-{
-	int ret = 0;
-	struct erase_info_user erase;
-	size_t count;
-	size_t blocksize;
-	loff_t start;
-	void *buf;
-	int sectors, skip;
-
-	switch (dev->mtdinfo.type) {
-	case MTD_NORFLASH:
-	case MTD_NANDFLASH:
-		count = dev->envsize;
-		start = dev->offset;
-		blocksize = dev->envsize;
-		erase.length = dev->sectorsize;
-		sectors = dev->envsectors ? dev->envsectors : 1;
-		buf = data;
-		while (count > 0) {
-			erase.start = start;
-
-			skip = is_nand_badblock(dev, start);
-			if (skip < 0) {
-				ret = -EIO;
-				goto devwrite_out;
-			}
-
-			if (skip > 0) {
-				start += dev->sectorsize;
-				sectors--;
-				if (sectors > 0)
-					continue;
-				ret = -EIO;
-				goto devwrite_out;
-			}
-
-			if (count > dev->sectorsize)
-				blocksize = dev->sectorsize;
-			else
-				blocksize = count;
-
-			/*
-			 * unlock could fail, no check
-			 */
-			MTDUNLOCK(dev, &erase);
-			if (ioctl(dev->fd, MEMERASE, &erase) != 0) {
-				ret =-EIO;
-				goto devwrite_out;
-			}
-			if (lseek(dev->fd, start, SEEK_SET) < 0) {
-				ret =-EIO;
-				goto devwrite_out;
-			}
-			if (write(dev->fd, buf, blocksize) != blocksize) {
-				ret =-EIO;
-				goto devwrite_out;
-			}
-			MTDLOCK(dev, &erase);
-			start += dev->sectorsize;
-			buf += blocksize;
-			count -= blocksize;
-			ret += blocksize;
-		}
-		break;
-	}
-
-devwrite_out:
-	return ret;
-}
-
-static int ubi_update_volume(struct uboot_flash_env *dev)
-{
-	int64_t envsize = dev->envsize;
-	return ioctl(dev->fd, UBI_IOCVOLUP, &envsize);
-}
-
-static int ubiwrite(struct uboot_flash_env *dev, void *data)
-{
-	int ret;
-
-	if (ubi_update_volume(dev) < 0)
-		return -1;
-
-	ret = write(dev->fd, data, dev->envsize);
-
-	return ret;
-}
-
 static int devwrite(struct uboot_ctx *ctx, unsigned int copy, void *data)
 {
 	int ret;
@@ -963,10 +530,10 @@  static int devwrite(struct uboot_ctx *ctx, unsigned int copy, void *data)
 		ret = filewrite(dev, data);
 		break;
 	case DEVICE_MTD:
-		ret = mtdwrite(dev, data);
+		ret = libubootenv_mtdwrite(dev, data);
 		break;
 	case DEVICE_UBI:
-		ret = ubiwrite(dev, data);
+		ret = libubootenv_ubiwrite(dev, data);
 		break;
 	default:
 		ret = -1;
@@ -978,34 +545,6 @@  static int devwrite(struct uboot_ctx *ctx, unsigned int copy, void *data)
 	return ret;
 }
 
-static int set_obsolete_flag(struct uboot_flash_env *dev)
-{
-	uint8_t offsetflags = offsetof(struct uboot_env_redund, flags);
-	unsigned char flag = 0;
-	struct erase_info_user erase;
-	int ret = 0;
-
-	dev->fd = open(dev->devname, O_RDWR);
-	if (dev->fd < 0)
-		return -EBADF;
-	if (lseek(dev->fd, dev->offset + offsetflags, SEEK_SET) < 0) {
-		close(dev->fd);
-		return -EBADF;
-	}
-	erase.start = dev->offset;
-	erase.length = dev->sectorsize;
-	MTDUNLOCK(dev, &erase);
-	ret = write(dev->fd, &flag, sizeof(flag));
-	if (ret == sizeof(flag))
-		ret = 0;
-	else if (ret >= 0)
-		ret = -EIO;
-	MTDLOCK(dev, &erase);
-	close(dev->fd);
-
-	return ret;
-}
-
 const struct uboot_version_info *libuboot_version_info(void)
 {
 	int i;
@@ -1114,7 +653,7 @@  int libuboot_env_store(struct uboot_ctx *ctx)
 
 	if (ctx->redundant && !ret) {
 		if (ctx->envdevs[ctx->current].flagstype == FLAGS_BOOLEAN)
-			ret = set_obsolete_flag(&ctx->envdevs[ctx->current]);
+			ret = libubootenv_set_obsolete_flag(&ctx->envdevs[ctx->current]);
 	}
 
 	if (!ret)
diff --git a/src/uboot_mtd.c b/src/uboot_mtd.c
new file mode 100644
index 0000000..27b63ef
--- /dev/null
+++ b/src/uboot_mtd.c
@@ -0,0 +1,487 @@ 
+/*
+ * (C) Copyright 2023
+ * Stefano Babic, stefano.babic@swupdate.org
+ *
+ * SPDX-License-Identifier:     LGPL-2.1-or-later
+ */
+
+/**
+ * @file uboot_mtd.c
+ *
+ * @brief function used with MTD subsystem
+ *
+ */
+
+#if !defined(__FreeBSD__)
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <limits.h>
+#include <linux/fs.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+#include <mtd/ubi-user.h>
+#include "uboot_private.h"
+
+static int ubi_get_dev_id(char *device)
+{
+	int dev_id = -1;
+	char *sep;
+
+	sep = strrchr(device, 'i');
+	if (sep)
+		sscanf(sep + 1, "%d", &dev_id);
+
+	return dev_id;
+}
+
+static int mtd_get_dev_id(char *device)
+{
+	int dev_id = -1;
+	char *sep;
+
+	sep = strrchr(device, 'd');
+	if (sep)
+		sscanf(sep + 1, "%d", &dev_id);
+
+	return dev_id;
+}
+
+static int is_nand_badblock(struct uboot_flash_env *dev, loff_t start)
+{
+	int bad;
+	if (dev->mtdinfo.type != MTD_NANDFLASH)
+		return 0;
+	bad = ioctl(dev->fd, MEMGETBADBLOCK, &start);
+	return bad;
+}
+
+
+static int ubi_get_dev_id_from_mtd(char *device)
+{
+	DIR *sysfs_ubi;
+	struct dirent *dirent;
+	int mtd_id;
+
+	mtd_id = mtd_get_dev_id(device);
+	if (mtd_id < 0)
+		return -1;
+
+	sysfs_ubi = opendir(SYS_UBI);
+	if (!sysfs_ubi)
+		return -1;
+
+	while (1) {
+		int ubi_num, ret;
+
+		dirent = readdir(sysfs_ubi);
+		if (!dirent)
+			break;
+		if (strlen(dirent->d_name) >= 255) {
+			closedir(sysfs_ubi);
+			return -1;
+		}
+
+		ret = sscanf(dirent->d_name, "ubi%d", &ubi_num);
+		if (ret == 1) {
+			char filename[DEVNAME_MAX_LENGTH];
+			char data[DEVNAME_MAX_LENGTH];
+			int fd, n, num_mtd = -1;
+
+			snprintf(filename, sizeof(filename), SYS_UBI_MTD_NUM, ubi_num);
+			fd = open(filename, O_RDONLY);
+			if (fd < 0)
+				continue;
+
+			n = read(fd, data, sizeof(data));
+			close(fd);
+			if (n < 0)
+				continue;
+
+			if (sscanf(data, "%d", &num_mtd) != 1)
+				num_mtd = -1;
+
+			if (num_mtd < 0)
+				continue;
+
+			if (num_mtd == mtd_id) {
+				closedir(sysfs_ubi);
+				return ubi_num;
+			}
+		}
+	}
+
+	closedir(sysfs_ubi);
+	return -1;
+}
+
+static int ubi_get_num_volume(char *device)
+{
+	char filename[DEVNAME_MAX_LENGTH];
+	char data[DEVNAME_MAX_LENGTH];
+	int dev_id, fd, n, num_vol = -1;
+
+	dev_id = ubi_get_dev_id(device);
+	if (dev_id < 0)
+		return -1;
+
+	snprintf(filename, sizeof(filename), SYS_UBI_VOLUME_COUNT, dev_id);
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	n = read(fd, data, sizeof(data));
+	if (n < 0)
+		goto out;
+
+	if (sscanf(data, "%d", &num_vol) != 1)
+		num_vol = -1;
+
+out:
+	close(fd);
+	return num_vol;
+}
+
+static int ubi_get_volume_name(char *device, int vol_id, char vol_name[DEVNAME_MAX_LENGTH])
+{
+	char filename[80];
+	char data[DEVNAME_MAX_LENGTH];
+	int dev_id, fd, n, ret = -1;
+
+	dev_id = ubi_get_dev_id(device);
+	if (dev_id < 0)
+		return -1;
+
+	snprintf(filename, sizeof(filename), SYS_UBI_VOLUME_NAME, dev_id, dev_id, vol_id);
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	memset(data, 0, DEVNAME_MAX_LENGTH);
+	n = read(fd, data, DEVNAME_MAX_LENGTH);
+	if (n < 0)
+		goto out;
+
+	memset(vol_name, 0, DEVNAME_MAX_LENGTH);
+	if (sscanf(data, "%s", vol_name) != 1)
+		goto out;
+
+	ret = 0;
+
+out:
+	close(fd);
+	return ret;
+}
+
+static int ubi_get_vol_id(char *device, char *volname)
+{
+	int i, n, ret, num_vol, vol_id = -1;
+
+	num_vol = ubi_get_num_volume(device);
+	if (num_vol < 0)
+		goto out;
+
+	i = 0;
+	n = 0;
+	while ((n < num_vol) && (i < UBI_MAX_VOLUME))
+	{
+		char name[DEVNAME_MAX_LENGTH];
+
+		ret = ubi_get_volume_name(device, i, name);
+		if (!ret && !strcmp(name, volname)) {
+			vol_id = i;
+			break;
+		}
+
+		i++;
+		if (!ret)
+			n++;
+	}
+
+out:
+	return vol_id;
+}
+
+int libubootenv_ubi_update_name(struct uboot_flash_env *dev)
+{
+	char device[DEVNAME_MAX_LENGTH];
+	char volume[DEVNAME_MAX_LENGTH];
+	int dev_id, vol_id, fd, ret = -EBADF;
+	struct stat st;
+	char *sep;
+
+	if (!strncmp(dev->devname, DEVICE_MTD_NAME, strlen(DEVICE_MTD_NAME)))
+	{
+		sep = strchr(dev->devname, DEVNAME_SEPARATOR);
+		if (sep)
+		{
+			memset(device, 0, DEVNAME_MAX_LENGTH);
+			memcpy(device, dev->devname, sep - dev->devname);
+
+			memset(volume, 0, DEVNAME_MAX_LENGTH);
+			sscanf(sep + 1, "%s", &volume[0]);
+
+			ret = ubi_get_dev_id_from_mtd(device);
+			if (ret < 0) {
+				struct ubi_attach_req req;
+
+				memset(&req, 0, sizeof(struct ubi_attach_req));
+				req.ubi_num = UBI_DEV_NUM_AUTO;
+				req.mtd_num = mtd_get_dev_id(device);
+				req.vid_hdr_offset = 0;
+
+				fd = open(DEVICE_UBI_CTRL, O_RDONLY);
+				if (fd == -1)
+					return -EBADF;
+
+				ret = ioctl(fd, UBI_IOCATT, &req);
+				close(fd);
+				if (ret == -1)
+					return -EBADF;
+
+				sprintf(dev->devname, DEVICE_UBI_NAME"%d:%s", req.ubi_num, volume);
+			} else {
+				sprintf(dev->devname, DEVICE_UBI_NAME"%d:%s", ret, volume);
+			}
+		} else {
+			return -EBADF;
+		}
+	}
+
+	sep = strchr(dev->devname, DEVNAME_SEPARATOR);
+	if (sep)
+	{
+		memset(device, 0, DEVNAME_MAX_LENGTH);
+		memcpy(device, dev->devname, sep - dev->devname);
+
+		memset(volume, 0, DEVNAME_MAX_LENGTH);
+		sscanf(sep + 1, "%s", &volume[0]);
+
+		dev_id = ubi_get_dev_id(device);
+		if (dev_id < 0)
+			goto out;
+
+		vol_id = ubi_get_vol_id(device, volume);
+		if (vol_id < 0)
+			goto out;
+
+		sprintf(dev->devname, "%s_%d", device, vol_id);
+	}
+
+	ret = 0;
+
+out:
+	return ret;
+}
+
+int libubootenv_mtdread(struct uboot_flash_env *dev, void *data)
+{
+	size_t count;
+	size_t blocksize;
+	loff_t start;
+	int sectors, skip;
+	int ret = 0;
+
+	switch (dev->mtdinfo.type) {
+	case MTD_ABSENT:
+	case MTD_NORFLASH:
+		if (dev->offset)
+			if (lseek(dev->fd, dev->offset, SEEK_SET) < 0) {
+				ret = -EIO;
+				break;
+			}
+		ret = read(dev->fd, data, dev->envsize);
+		break;
+	case MTD_NANDFLASH:
+		if (dev->offset)
+			if (lseek(dev->fd, dev->offset, SEEK_SET) < 0) {
+				ret = -EIO;
+				break;
+			}
+
+		count = dev->envsize;
+		start = dev->offset;
+		blocksize = dev->envsize;
+		sectors = dev->envsectors ? dev->envsectors : 1;
+
+		while (count > 0) {
+			skip = is_nand_badblock(dev, start);
+			if (skip < 0) {
+				return -EIO;
+			}
+
+			if (skip > 0) {
+				start += dev->sectorsize;
+				sectors--;
+				if (sectors > 0)
+					continue;
+				return  -EIO;
+			}
+
+			if (count > dev->sectorsize)
+				blocksize = dev->sectorsize;
+			else
+				blocksize = count;
+
+			if (lseek(dev->fd, start, SEEK_SET) < 0) {
+				return -EIO;
+			}
+			if (read(dev->fd, data, blocksize) != blocksize) {
+				return -EIO;
+			}
+			start += dev->sectorsize;
+			data += blocksize;
+			count -= blocksize;
+			ret += blocksize;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+int libubootenv_ubiread(struct uboot_flash_env *dev, void *data)
+{
+	int ret = 0;
+
+	ret = read(dev->fd, data, dev->envsize);
+
+	return ret;
+}
+
+int libubootenv_mtdwrite(struct uboot_flash_env *dev, void *data)
+{
+	int ret = 0;
+	struct erase_info_user erase;
+	size_t count;
+	size_t blocksize;
+	loff_t start;
+	void *buf;
+	int sectors, skip;
+
+	switch (dev->mtdinfo.type) {
+	case MTD_NORFLASH:
+	case MTD_NANDFLASH:
+		count = dev->envsize;
+		start = dev->offset;
+		blocksize = dev->envsize;
+		erase.length = dev->sectorsize;
+		sectors = dev->envsectors ? dev->envsectors : 1;
+		buf = data;
+		while (count > 0) {
+			erase.start = start;
+
+			skip = is_nand_badblock(dev, start);
+			if (skip < 0) {
+				ret = -EIO;
+				goto devwrite_out;
+			}
+
+			if (skip > 0) {
+				start += dev->sectorsize;
+				sectors--;
+				if (sectors > 0)
+					continue;
+				ret = -EIO;
+				goto devwrite_out;
+			}
+
+			if (count > dev->sectorsize)
+				blocksize = dev->sectorsize;
+			else
+				blocksize = count;
+
+			/*
+			 * unlock could fail, no check
+			 */
+			MTDUNLOCK(dev, &erase);
+			if (ioctl(dev->fd, MEMERASE, &erase) != 0) {
+				ret =-EIO;
+				goto devwrite_out;
+			}
+			if (lseek(dev->fd, start, SEEK_SET) < 0) {
+				ret =-EIO;
+				goto devwrite_out;
+			}
+			if (write(dev->fd, buf, blocksize) != blocksize) {
+				ret =-EIO;
+				goto devwrite_out;
+			}
+			MTDLOCK(dev, &erase);
+			start += dev->sectorsize;
+			buf += blocksize;
+			count -= blocksize;
+			ret += blocksize;
+		}
+		break;
+	}
+
+devwrite_out:
+	return ret;
+}
+
+int libubootenv_ubi_update_volume(struct uboot_flash_env *dev)
+{
+	int64_t envsize = dev->envsize;
+	return ioctl(dev->fd, UBI_IOCVOLUP, &envsize);
+}
+
+int libubootenv_ubiwrite(struct uboot_flash_env *dev, void *data)
+{
+	int ret;
+
+	if (libubootenv_ubi_update_volume(dev) < 0)
+		return -1;
+
+	ret = write(dev->fd, data, dev->envsize);
+
+	return ret;
+}
+
+int libubootenv_mtdgetinfo(int fd, struct uboot_flash_env *dev)
+{
+	return ioctl(fd, MEMGETINFO, &dev->mtdinfo);
+}
+
+int libubootenv_set_obsolete_flag(struct uboot_flash_env *dev)
+{
+	uint8_t offsetflags = offsetof(struct uboot_env_redund, flags);
+	unsigned char flag = 0;
+	struct erase_info_user erase;
+	int ret = 0;
+
+	dev->fd = open(dev->devname, O_RDWR);
+	if (dev->fd < 0)
+		return -EBADF;
+	if (lseek(dev->fd, dev->offset + offsetflags, SEEK_SET) < 0) {
+		close(dev->fd);
+		return -EBADF;
+	}
+	erase.start = dev->offset;
+	erase.length = dev->sectorsize;
+	MTDUNLOCK(dev, &erase);
+	ret = write(dev->fd, &flag, sizeof(flag));
+	if (ret == sizeof(flag))
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+	MTDLOCK(dev, &erase);
+	close(dev->fd);
+
+	return ret;
+}
+#endif
diff --git a/src/uboot_private.h b/src/uboot_private.h
index 40e5446..92772d2 100644
--- a/src/uboot_private.h
+++ b/src/uboot_private.h
@@ -11,9 +11,56 @@ 
 #include <stdint.h>
 #include <sys/queue.h>
 #include <sys/types.h>
-#include <mtd/mtd-user.h>
 #include "libuboot.h"
 
+#define UBI_MAX_VOLUME			128
+
+#define DEVICE_MTD_NAME 		"/dev/mtd"
+#define DEVICE_UBI_NAME 		"/dev/ubi"
+#define DEVICE_UBI_CTRL 		"/dev/ubi_ctrl"
+#define SYS_UBI				"/sys/class/ubi"
+#define SYS_UBI_MTD_NUM			"/sys/class/ubi/ubi%d/mtd_num"
+#define SYS_UBI_VOLUME_COUNT		"/sys/class/ubi/ubi%d/volumes_count"
+#define SYS_UBI_VOLUME_NAME		"/sys/class/ubi/ubi%d/ubi%d_%d/name"
+
+#if !defined(__FreeBSD__)
+#include <mtd/mtd-user.h>
+#include <mtd/ubi-user.h>
+#define MTDLOCK(dev, psector)	\
+	if (!dev->disable_mtd_lock) ioctl (dev->fd, MEMLOCK, psector)
+#define MTDUNLOCK(dev, psector) \
+	if (!dev->disable_mtd_lock) ioctl (dev->fd, MEMUNLOCK, psector)
+
+#define	LIST_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = LIST_FIRST((head));				\
+	    (var) != NULL &&						\
+	    ((tvar) = LIST_NEXT((var), field), 1);			\
+	    (var) = (tvar))
+#else
+#define MTD_ABSENT		0
+#define MTD_RAM			1
+#define MTD_ROM			2
+#define MTD_NORFLASH		3
+#define MTD_NANDFLASH		4	/* SLC NAND */
+#define MTD_DATAFLASH		6
+#define MTD_UBIVOLUME		7
+#define MTD_MLCNANDFLASH	8	/* MLC NAND (including TLC) */
+#define MTDLOCK
+#define MTDUNLOCK
+
+#define ENODATA ENODEV
+
+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 */
+};
+#endif
+
 typedef enum {
 	TYPE_ATTR_STRING,	/* default */
 	TYPE_ATTR_DECIMAL,
@@ -135,3 +182,23 @@  struct uboot_ctx {
 	/** private pointer to list */
 	struct uboot_ctx *ctxlist;
 };
+
+#if defined(__FreeBSD__)
+#define libubootenv_mtdgetinfo(fd,dev) (-1)
+#define libubootenv_mtdread(dev,data) (-1)
+#define libubootenv_mtdwrite(dev,data) (-1)
+#define libubootenv_ubiread(dev,data) (-1)
+#define libubootenv_ubiwrite(dev,data) (-1)
+#define libubootenv_ubi_update_name(dev) (-1)
+#define libubootenv_set_obsolete_flag(dev) (-1)
+#else
+int libubootenv_mtdgetinfo(int fd, struct uboot_flash_env *dev);
+int libubootenv_mtdread(struct uboot_flash_env *dev, void *data);
+int libubootenv_mtdwrite(struct uboot_flash_env *dev, void *data);
+int libubootenv_ubi_update_name(struct uboot_flash_env *dev);
+int libubootenv_ubiread(struct uboot_flash_env *dev, void *data);
+int libubootenv_ubiwrite(struct uboot_flash_env *dev, void *data);
+int libubootenv_set_obsolete_flag(struct uboot_flash_env *dev);
+#endif
+
+