diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2011-04-04 13:13:30 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2011-04-11 12:36:07 +0200 |
commit | 30be97aff25f618f3925d1733a39996d50fc2213 (patch) | |
tree | 58eae347941d2bc1197a374668c77cd0bf43d0f4 /fs/devfs-core.c | |
parent | f1bb89fd9e5c6f3527ebaafcf57346fec19e4d5f (diff) | |
download | barebox-30be97aff25f618f3925d1733a39996d50fc2213.tar.gz barebox-30be97aff25f618f3925d1733a39996d50fc2213.tar.xz |
devfs: factor out core devfs functionality
This makes it possible to compile without devfs. devfs_create/devfs_remove
is used by drivers and thus must still be present even without devfs support.
Also, this patch adds cdev_open/cdev_close/cdev_flush/cdev_ioctl calls to
work with devices without using the file api.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'fs/devfs-core.c')
-rw-r--r-- | fs/devfs-core.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/fs/devfs-core.c b/fs/devfs-core.c new file mode 100644 index 0000000000..519e18e6b7 --- /dev/null +++ b/fs/devfs-core.c @@ -0,0 +1,236 @@ +/* + * devfs.c - a device file system for barebox + * + * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <common.h> +#include <driver.h> +#include <errno.h> +#include <malloc.h> +#include <ioctl.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> + +LIST_HEAD(cdev_list); + +struct cdev *cdev_by_name(const char *filename) +{ + struct cdev *cdev; + + list_for_each_entry(cdev, &cdev_list, list) { + if (!strcmp(cdev->name, filename)) + return cdev; + } + return NULL; +} + +struct cdev *cdev_open(const char *name, unsigned long flags) +{ + struct cdev *cdev = cdev_by_name(name); + int ret; + + if (!cdev) + return NULL; + + if (cdev->ops->open) { + ret = cdev->ops->open(cdev); + if (ret) + return NULL; + } + + return cdev; +} + +void cdev_close(struct cdev *cdev) +{ + if (cdev->ops->close) + cdev->ops->close(cdev); +} + +ssize_t cdev_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags) +{ + if (!cdev->ops->read) + return -ENOSYS; + + return cdev->ops->read(cdev, buf, count, cdev->offset +offset, flags); +} + +ssize_t cdev_write(struct cdev *cdev, const void *buf, size_t count, ulong offset, ulong flags) +{ + if (!cdev->ops->write) + return -ENOSYS; + + return cdev->ops->write(cdev, buf, count, cdev->offset + offset, flags); +} + +int cdev_flush(struct cdev *cdev) +{ + if (!cdev->ops->flush) + return 0; + + return cdev->ops->flush(cdev); +} + +static int partition_ioctl(struct cdev *cdev, int request, void *buf) +{ + size_t offset; + struct mtd_info_user *user = buf; + + switch (request) { + case MEMSETBADBLOCK: + case MEMGETBADBLOCK: + offset = (off_t)buf; + offset += cdev->offset; + return cdev->ops->ioctl(cdev, request, (void *)offset); + case MEMGETINFO: + if (cdev->mtd) { + user->type = cdev->mtd->type; + user->flags = cdev->mtd->flags; + user->size = cdev->mtd->size; + user->erasesize = cdev->mtd->erasesize; + user->oobsize = cdev->mtd->oobsize; + user->mtd = cdev->mtd; + /* The below fields are obsolete */ + user->ecctype = -1; + user->eccsize = 0; + return 0; + } + if (!cdev->ops->ioctl) + return -EINVAL; + return cdev->ops->ioctl(cdev, request, buf); + default: + return -EINVAL; + } +} + +int cdev_ioctl(struct cdev *cdev, int request, void *buf) +{ + if (cdev->flags & DEVFS_IS_PARTITION) + return partition_ioctl(cdev, request, buf); + + if (!cdev->ops->ioctl) + return -EINVAL; + + return cdev->ops->ioctl(cdev, request, buf); +} + +int cdev_erase(struct cdev *cdev, size_t count, unsigned long offset) +{ + if (!cdev->ops->erase) + return -ENOSYS; + + return cdev->ops->erase(cdev, count, cdev->offset + offset); +} + +int devfs_create(struct cdev *new) +{ + struct cdev *cdev; + + cdev = cdev_by_name(new->name); + if (cdev) + return -EEXIST; + + list_add_tail(&new->list, &cdev_list); + if (new->dev) + list_add_tail(&new->devices_list, &new->dev->cdevs); + + return 0; +} + +int devfs_remove(struct cdev *cdev) +{ + if (cdev->open) + return -EBUSY; + + list_del(&cdev->list); + if (cdev->dev) + list_del(&cdev->devices_list); + + return 0; +} + +int devfs_add_partition(const char *devname, unsigned long offset, size_t size, + int flags, const char *name) +{ + struct cdev *cdev, *new; + + cdev = cdev_by_name(name); + if (cdev) + return -EEXIST; + + cdev = cdev_by_name(devname); + if (!cdev) + return -ENOENT; + + if (offset + size > cdev->size) + return -EINVAL; + + new = xzalloc(sizeof (*new)); + new->name = strdup(name); + new->ops = cdev->ops; + new->priv = cdev->priv; + new->size = size; + new->offset = offset + cdev->offset; + new->dev = cdev->dev; + new->flags = flags | DEVFS_IS_PARTITION; + +#ifdef CONFIG_PARTITION_NEED_MTD + if (cdev->mtd) { + new->mtd = mtd_add_partition(cdev->mtd, offset, size, flags, name); + if (IS_ERR(new->mtd)) { + int ret = PTR_ERR(new->mtd); + free(new); + return ret; + } + } +#endif + + devfs_create(new); + + return 0; +} + +int devfs_del_partition(const char *name) +{ + struct cdev *cdev; + int ret; + + cdev = cdev_by_name(name); + if (!cdev) + return -ENOENT; + + if (!(cdev->flags & DEVFS_IS_PARTITION)) + return -EINVAL; + if (cdev->flags & DEVFS_PARTITION_FIXED) + return -EPERM; + +#ifdef CONFIG_PARTITION_NEED_MTD + if (cdev->mtd) + mtd_del_partition(cdev->mtd); +#endif + + ret = devfs_remove(cdev); + if (ret) + return ret; + + free(cdev->name); + free(cdev); + + return 0; +} |