
From: Simon Glass <sjg@chromium.org> Directories can have a number of files within them. U-Boot only needs to create a file object for those that have been opened for read/write. Add the concept of a file, with its directory as parent. Signed-off-by: Simon Glass <sjg@chromium.org> --- MAINTAINERS | 2 + fs/Kconfig | 9 ++++ fs/Makefile | 1 + fs/dir-uclass.c | 10 ++++ fs/file-uclass.c | 103 +++++++++++++++++++++++++++++++++++++++++ include/dir.h | 12 +++++ include/dm/uclass-id.h | 1 + include/file.h | 86 ++++++++++++++++++++++++++++++++++ 8 files changed, 224 insertions(+) create mode 100644 fs/file-uclass.c create mode 100644 include/file.h diff --git a/MAINTAINERS b/MAINTAINERS index 9fcff19857d..e5389aeae45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1197,7 +1197,9 @@ T: git https://source.denx.de/u-boot/custodians/u-boot-microblaze.git F: drivers/fpga/ F: cmd/fpga.c F: fs/dir-uclass.c +F: fs/file-uclass.c F: include/dir.h +F: include/file.h F: include/fpga.h F: include/fs.h F: include/iovec.h diff --git a/fs/Kconfig b/fs/Kconfig index 6a3b3d8218e..718172e472e 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -20,6 +20,15 @@ config DIR Provides an interface for directories within filesystems, allowing them to be listed. +config FILE + bool "Support for files" + default y if SANDBOX || VENDOR_EMULATION || ARCH_QEMU + help + Provides an interface for files, allowing them to be opened, read, + written and modified. Files are children of a filesystem device + (UCLASS_FS). Note that a UCLASS_FILE device is only created when it + is opened. + config FS_LEGACY def_bool y help diff --git a/fs/Makefile b/fs/Makefile index 4e33f312caf..d69d6986c41 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_$(PHASE_)FS_LEGACY) += fs_legacy.o fs_internal.o obj-$(CONFIG_$(PHASE_)FS) += fs-uclass.o obj-$(CONFIG_$(PHASE_)DIR) += dir-uclass.o +obj-$(CONFIG_$(PHASE_)FILE) += file-uclass.o ifdef CONFIG_XPL_BUILD obj-$(CONFIG_SPL_FS_FAT) += fat/ diff --git a/fs/dir-uclass.c b/fs/dir-uclass.c index dcbef8dd9ff..f2c3a7c3806 100644 --- a/fs/dir-uclass.c +++ b/fs/dir-uclass.c @@ -104,6 +104,16 @@ int dir_close(struct udevice *dev, struct fs_dir_stream *strm) return 0; } +int dir_open_file(struct udevice *dev, const char *leaf, + enum dir_open_flags_t oflags, struct udevice **filp) +{ + struct dir_ops *ops = dir_get_ops(dev); + + log_debug("start %s\n", dev->name); + + return ops->open_file(dev, leaf, oflags, filp); +} + UCLASS_DRIVER(dir) = { .name = "dir", .id = UCLASS_DIR, diff --git a/fs/file-uclass.c b/fs/file-uclass.c new file mode 100644 index 00000000000..91ced31a94e --- /dev/null +++ b/fs/file-uclass.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of files on a filesystem + * + * Copyright 2025 Simon Glass <sjg@chromium.org> + */ + +#include <dir.h> +#include <dm.h> +#include <file.h> +#include <malloc.h> +#include <dm/device-internal.h> + +int file_add_probe(struct udevice *dir, struct driver *drv, const char *leaf, + size_t size, enum dir_open_flags_t flags, + struct udevice **devp) +{ + char dev_name[30], *str, *dup_leaf; + struct file_uc_priv *uc_priv; + struct udevice *dev; + int ret; + + snprintf(dev_name, sizeof(dev_name), "%s.file.%x", dir->name, + device_get_child_count(dir) + 1); + ret = -ENOMEM; + str = strdup(dev_name); + if (!str) + goto no_dev_name; + dup_leaf = strdup(leaf); + if (!str) + goto no_dev_path; + + ret = device_bind_with_driver_data(dir, drv, str, 0 /* data */, + ofnode_null(), &dev); + if (ret) { + log_debug("bind failed %d\n", ret); + goto no_bind; + } + device_set_name_alloced(dev); + + ret = device_probe(dev); + if (ret) + goto no_probe; + uc_priv = dev_get_uclass_priv(dev); + + uc_priv->leaf = dup_leaf; + uc_priv->size = size; + *devp = dev; + + return 0; + +no_probe: + device_unbind(dir); +no_bind: + free(dup_leaf); +no_dev_path: + free(str); +no_dev_name: + + return ret; +} + +long file_read(struct udevice *dev, void *buf, long len) +{ + struct file_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct file_ops *ops = file_get_ops(dev); + struct iov_iter iter; + ssize_t ret; + + iter_ubuf(&iter, true, buf, len); + + ret = ops->read_iter(dev, &iter, uc_priv->pos); + if (ret < 0) + return log_msg_ret("fir", ret); + uc_priv->pos += ret; + + return ret; +} + +long file_read_at(struct udevice *dev, void *buf, loff_t offset, long len) +{ + struct file_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct file_ops *ops = file_get_ops(dev); + struct iov_iter iter; + ssize_t ret; + + if (!len) + len = uc_priv->size - offset; + iter_ubuf(&iter, true, buf, len); + + ret = ops->read_iter(dev, &iter, offset); + if (ret < 0) + return log_msg_ret("fir", ret); + uc_priv->pos = offset + ret; + + return ret; +} + +UCLASS_DRIVER(file) = { + .name = "file", + .id = UCLASS_FILE, + .per_device_auto = sizeof(struct file_uc_priv), +}; diff --git a/include/dir.h b/include/dir.h index 8e883a6c6b2..f948843c6b3 100644 --- a/include/dir.h +++ b/include/dir.h @@ -126,4 +126,16 @@ int dir_close(struct udevice *dev, struct fs_dir_stream *strm); int dir_add_probe(struct udevice *fsdev, struct driver *drv, const char *path, struct udevice **devp); +/** + * dir_open_file() - Create a new file device for a file + * + * @dev: Directory device (UCLASS_DIR) + * @leaf: Filename within the directory + * @flags: Open-mode flags to use + * @filp: Returns the UCLASS_FILE device + * Return: 0 if OK, -ve on error + */ +int dir_open_file(struct udevice *dev, const char *leaf, + enum dir_open_flags_t oflags, struct udevice **filp); + #endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 247c6322c76..c558c95f465 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -65,6 +65,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ UCLASS_EXTCON, /* External Connector Class */ + UCLASS_FILE, /* File on a filesystem */ UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ diff --git a/include/file.h b/include/file.h new file mode 100644 index 00000000000..d1fdf3c13f1 --- /dev/null +++ b/include/file.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Implementation of files on a filesystem + * Copyright 2025 Simon Glass <sjg@chromium.org> + */ + +#ifndef __FILE_H +#define __FILE_H + +#include <dir.h> +#include <iovec.h> + +struct udevice; + +enum { + /* Maximum length of a pathname */ + FILE_MAX_PATH_LEN = 1024, +}; + +/** + * struct file_uc_priv - Uclass information about each file + * + * @leaf: Filename leaf + * @pos: Current file position + * @size: File size + */ +struct file_uc_priv { + const char *leaf; + loff_t pos; + size_t size; +}; + +struct file_ops { + /** + * read_iter() - Read data from a file + * + * Reads from the current file position, which is advanced + * + * @dev: File to read from + * @iter: Interator to receive data + * @pos: File position to read from + * Return: number of bytes read, or -ve error code + */ + ssize_t (*read_iter)(struct udevice *dev, struct iov_iter *iter, + loff_t pos); +}; + +/* Get access to a file's operations */ +#define file_get_ops(dev) ((struct file_ops *)(dev)->driver->ops) + +/** + * file_read() - Read data from a file + * + * Reads from the current file position, which is advanced + * + * @dev: File to read from + * @buf: Buffer to read into + * @len: Number of bytes to read + */ +long file_read(struct udevice *dev, void *buf, long len); + +/** + * read_at() - Read data from a file at a particular position + * + * @dev: File to read from + * @buf: Buffer to read into + * @offset: Offset within the file to start reading + * @len: Number of bytes to read (0 to read as many as possible) + */ +long file_read_at(struct udevice *dev, void *buf, loff_t offset, long len); + +/** + * file_add_probe() - Create a new file device for a file + * + * @dev: Directory device (UCLASS_DIR) + * @leaf: Filename within the directory + * @size: Size of the file in bytes + * @flags: Open-mode flags to use + * @filp: Returns the UCLASS_FILE device + * Return: 0 if OK, -ve on error + */ +int file_add_probe(struct udevice *dir, struct driver *drv, const char *leaf, + size_t size, enum dir_open_flags_t flags, + struct udevice **devp); + +#endif -- 2.43.0