| /* |
| * devfs.c - a device file system for barebox |
| * |
| * Copyright (c) 2007 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 <init.h> |
| #include <malloc.h> |
| #include <fs.h> |
| #include <command.h> |
| #include <errno.h> |
| #include <xfuncs.h> |
| #include <linux/stat.h> |
| #include <ioctl.h> |
| #include <nand.h> |
| #include <linux/err.h> |
| #include <linux/mtd/mtd.h> |
| #include <linux/mtd/mtd-abi.h> |
| #include <partition.h> |
| |
| extern struct list_head cdev_list; |
| |
| static int devfs_read(struct device_d *_dev, FILE *f, void *buf, size_t size) |
| { |
| struct cdev *cdev = f->inode; |
| |
| return cdev_read(cdev, buf, size, f->pos, f->flags); |
| } |
| |
| static int devfs_write(struct device_d *_dev, FILE *f, const void *buf, size_t size) |
| { |
| struct cdev *cdev = f->inode; |
| |
| return cdev_write(cdev, buf, size, f->pos, f->flags); |
| } |
| |
| static off_t devfs_lseek(struct device_d *_dev, FILE *f, off_t pos) |
| { |
| struct cdev *cdev = f->inode; |
| off_t ret = -1; |
| |
| if (cdev->ops->lseek) |
| ret = cdev->ops->lseek(cdev, pos + cdev->offset); |
| |
| if (ret != -1) |
| f->pos = pos; |
| |
| return ret - cdev->offset; |
| } |
| |
| static int devfs_erase(struct device_d *_dev, FILE *f, size_t count, unsigned long offset) |
| { |
| struct cdev *cdev = f->inode; |
| |
| if (!cdev->ops->erase) |
| return -ENOSYS; |
| |
| return cdev->ops->erase(cdev, count, offset + cdev->offset); |
| } |
| |
| static int devfs_protect(struct device_d *_dev, FILE *f, size_t count, unsigned long offset, int prot) |
| { |
| struct cdev *cdev = f->inode; |
| |
| if (!cdev->ops->protect) |
| return -ENOSYS; |
| |
| return cdev->ops->protect(cdev, count, offset + cdev->offset, prot); |
| } |
| |
| static int devfs_memmap(struct device_d *_dev, FILE *f, void **map, int flags) |
| { |
| struct cdev *cdev = f->inode; |
| int ret = -ENOSYS; |
| |
| if (!cdev->ops->memmap) |
| return -EINVAL; |
| |
| ret = cdev->ops->memmap(cdev, map, flags); |
| |
| if (!ret) |
| *map = (void *)((unsigned long)*map + cdev->offset); |
| |
| return ret; |
| } |
| |
| static int devfs_open(struct device_d *_dev, FILE *f, const char *filename) |
| { |
| struct cdev *cdev; |
| int ret; |
| |
| cdev = cdev_by_name(filename + 1); |
| |
| if (!cdev) |
| return -ENOENT; |
| |
| f->size = cdev->size; |
| f->inode = cdev; |
| |
| if (cdev->ops->open) { |
| ret = cdev->ops->open(cdev, f->flags); |
| if (ret) |
| return ret; |
| } |
| |
| cdev->open++; |
| |
| return 0; |
| } |
| |
| static int devfs_close(struct device_d *_dev, FILE *f) |
| { |
| struct cdev *cdev = f->inode; |
| int ret; |
| |
| if (cdev->ops->close) { |
| ret = cdev->ops->close(cdev); |
| if (ret) |
| return ret; |
| } |
| |
| cdev->open--; |
| |
| return 0; |
| } |
| |
| static int devfs_flush(struct device_d *_dev, FILE *f) |
| { |
| struct cdev *cdev = f->inode; |
| |
| if (cdev->ops->flush) |
| return cdev->ops->flush(cdev); |
| |
| return 0; |
| } |
| |
| static int devfs_ioctl(struct device_d *_dev, FILE *f, int request, void *buf) |
| { |
| struct cdev *cdev = f->inode; |
| |
| return cdev_ioctl(cdev, request, buf); |
| } |
| |
| static int devfs_truncate(struct device_d *dev, FILE *f, ulong size) |
| { |
| if (size > f->dev->size) |
| return -ENOSPC; |
| return 0; |
| } |
| |
| static DIR* devfs_opendir(struct device_d *dev, const char *pathname) |
| { |
| DIR *dir; |
| |
| dir = xzalloc(sizeof(DIR)); |
| |
| if (!list_empty(&cdev_list)) |
| dir->priv = list_first_entry(&cdev_list, struct cdev, list); |
| |
| return dir; |
| } |
| |
| static struct dirent* devfs_readdir(struct device_d *_dev, DIR *dir) |
| { |
| struct cdev *cdev = dir->priv; |
| |
| if (!cdev) |
| return NULL; |
| |
| list_for_each_entry_from(cdev, &cdev_list, list) { |
| strcpy(dir->d.d_name, cdev->name); |
| dir->priv = list_entry(cdev->list.next, struct cdev, list); |
| return &dir->d; |
| } |
| return NULL; |
| } |
| |
| static int devfs_closedir(struct device_d *dev, DIR *dir) |
| { |
| free(dir); |
| return 0; |
| } |
| |
| static int devfs_stat(struct device_d *_dev, const char *filename, struct stat *s) |
| { |
| struct cdev *cdev; |
| |
| cdev = cdev_by_name(filename + 1); |
| if (!cdev) |
| return -ENOENT; |
| |
| s->st_mode = S_IFCHR; |
| s->st_size = cdev->size; |
| if (cdev->ops->write) |
| s->st_mode |= S_IWUSR; |
| if (cdev->ops->read) |
| s->st_mode |= S_IRUSR; |
| |
| return 0; |
| } |
| |
| static int devfs_probe(struct device_d *dev) |
| { |
| return 0; |
| } |
| |
| static void devfs_delete(struct device_d *dev) |
| { |
| } |
| |
| static struct fs_driver_d devfs_driver = { |
| .read = devfs_read, |
| .write = devfs_write, |
| .lseek = devfs_lseek, |
| .open = devfs_open, |
| .close = devfs_close, |
| .flush = devfs_flush, |
| .ioctl = devfs_ioctl, |
| .opendir = devfs_opendir, |
| .readdir = devfs_readdir, |
| .truncate = devfs_truncate, |
| .closedir = devfs_closedir, |
| .stat = devfs_stat, |
| .erase = devfs_erase, |
| .protect = devfs_protect, |
| .memmap = devfs_memmap, |
| .flags = FS_DRIVER_NO_DEV, |
| .drv = { |
| .probe = devfs_probe, |
| .remove = devfs_delete, |
| .name = "devfs", |
| .type_data = &devfs_driver, |
| } |
| }; |
| |
| static int devfs_init(void) |
| { |
| return register_fs_driver(&devfs_driver); |
| } |
| |
| coredevice_initcall(devfs_init); |