blob: 4219801bdecbf741de291da646665ec87d1e4979 [file] [log] [blame]
/*
* ramfs.c - a malloc based filesystem
*
* 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 <linux/stat.h>
#include <linux/ctype.h>
#include <xfuncs.h>
#include <fcntl.h>
#include "ff.h"
#include "integer.h"
#include "diskio.h"
struct fat_priv {
struct cdev *cdev;
FATFS fat;
};
/* ---------------------------------------------------------------*/
DRESULT disk_read(FATFS *fat, BYTE *buf, DWORD sector, BYTE count)
{
struct fat_priv *priv = fat->userdata;
int ret;
debug("%s: sector: %ld count: %d\n", __func__, sector, count);
ret = cdev_read(priv->cdev, buf, count << 9, sector * 512, 0);
if (ret != count << 9)
return ret;
return 0;
}
DRESULT disk_write(FATFS *fat, const BYTE *buf, DWORD sector, BYTE count)
{
struct fat_priv *priv = fat->userdata;
int ret;
debug("%s: buf: %p sector: %ld count: %d\n",
__func__, buf, sector, count);
ret = cdev_write(priv->cdev, buf, count << 9, sector * 512, 0);
if (ret != count << 9)
return ret;
return 0;
}
DSTATUS disk_status(FATFS *fat)
{
return 0;
}
DWORD get_fattime(void)
{
return 0;
}
DRESULT disk_ioctl (FATFS *fat, BYTE command, void *buf)
{
return 0;
}
WCHAR ff_convert(WCHAR src, UINT dir)
{
if (src <= 0x80)
return src;
else
return '?';
}
WCHAR ff_wtoupper(WCHAR chr)
{
if (chr <= 0x80)
return toupper(chr);
else
return '?';
}
/* ---------------------------------------------------------------*/
#ifdef CONFIG_FS_FAT_WRITE
static int fat_create(struct device_d *dev, const char *pathname, mode_t mode)
{
struct fat_priv *priv = dev->priv;
FIL f_file;
int ret;
ret = f_open(&priv->fat, &f_file, pathname, FA_OPEN_ALWAYS);
if (ret)
return -EINVAL;
f_close(&f_file);
return 0;
}
static int fat_unlink(struct device_d *dev, const char *pathname)
{
struct fat_priv *priv = dev->priv;
int ret;
ret = f_unlink(&priv->fat, pathname);
if (ret)
return ret;
cdev_flush(priv->cdev);
return 0;
}
static int fat_mkdir(struct device_d *dev, const char *pathname)
{
struct fat_priv *priv = dev->priv;
int ret;
ret = f_mkdir(&priv->fat, pathname);
if (ret)
return ret;
cdev_flush(priv->cdev);
return 0;
}
static int fat_rmdir(struct device_d *dev, const char *pathname)
{
struct fat_priv *priv = dev->priv;
int ret;
ret = f_unlink(&priv->fat, pathname);
if (ret)
return ret;
cdev_flush(priv->cdev);
return 0;
}
static int fat_write(struct device_d *_dev, FILE *f, const void *buf, size_t insize)
{
FIL *f_file = f->inode;
int outsize;
int ret;
ret = f_write(f_file, buf, insize, &outsize);
debug("%s: %d %d %d %p\n", __func__, ret, insize, outsize, f_file);
if (ret)
return ret;
if (!outsize)
return -ENOSPC;
return outsize;
}
static int fat_truncate(struct device_d *dev, FILE *f, ulong size)
{
FIL *f_file = f->inode;
unsigned long lastofs;
int ret;
lastofs = f_file->fptr;
ret = f_lseek(f_file, size);
if (ret)
return ret;
ret = f_truncate(f_file);
if (ret)
return ret;
ret = f_lseek(f_file, lastofs);
if (ret)
return ret;
return 0;
}
#endif /* CONFIG_FS_FAT_WRITE */
static int fat_open(struct device_d *dev, FILE *file, const char *filename)
{
struct fat_priv *priv = dev->priv;
FIL *f_file;
int ret;
unsigned long flags = 0;
f_file = xzalloc(sizeof(*f_file));
switch (file->flags & O_ACCMODE) {
case O_RDONLY:
flags = FA_READ;
break;
case O_WRONLY:
flags = FA_WRITE;
break;
case O_RDWR:
flags = FA_READ | FA_WRITE;
break;
}
ret = f_open(&priv->fat, f_file, filename, flags);
if (ret) {
free(f_file);
return -EINVAL;
}
if (file->flags & O_APPEND) {
ret = f_lseek(f_file, f_file->fsize);
}
file->inode = f_file;
file->size = f_file->fsize;
return 0;
}
static int fat_close(struct device_d *dev, FILE *f)
{
struct fat_priv *priv = dev->priv;
FIL *f_file = f->inode;
f_close(f_file);
free(f_file);
cdev_flush(priv->cdev);
return 0;
}
static int fat_read(struct device_d *_dev, FILE *f, void *buf, size_t insize)
{
int ret;
FIL *f_file = f->inode;
int outsize;
ret = f_read(f_file, buf, insize, &outsize);
debug("%s: %d %d %d %p\n", __func__, ret, insize, outsize, f_file);
if (ret)
return ret;
return outsize;
}
static off_t fat_lseek(struct device_d *dev, FILE *f, off_t pos)
{
FIL *f_file = f->inode;
int ret;
ret = f_lseek(f_file, pos);
if (ret)
return ret;
return pos;
}
static DIR* fat_opendir(struct device_d *dev, const char *pathname)
{
struct fat_priv *priv = dev->priv;
DIR *dir;
FF_DIR *ff_dir;
int ret;
debug("%s: %s\n", __func__, pathname);
ff_dir = xzalloc(sizeof(*ff_dir));
if (pathname)
ret = f_opendir(&priv->fat, ff_dir, pathname);
else
ret = f_opendir(&priv->fat, ff_dir, "/");
if (ret)
return NULL;
dir = xzalloc(sizeof(*dir));
dir->priv = ff_dir;
return dir;
}
static struct dirent* fat_readdir(struct device_d *dev, DIR *dir)
{
FF_DIR *ff_dir = dir->priv;
FILINFO finfo;
int ret;
#ifdef CONFIG_FS_FAT_LFN
char name[PATH_MAX];
#endif
memset(&finfo, 0, sizeof(finfo));
#ifdef CONFIG_FS_FAT_LFN
finfo.lfname = name;
finfo.lfsize = PATH_MAX;
#endif
ret = f_readdir(ff_dir, &finfo);
if (ret)
return NULL;
if (finfo.fname[0] == '\0')
return NULL;
#ifdef CONFIG_FS_FAT_LFN
if (*finfo.lfname)
strcpy(dir->d.d_name, finfo.lfname);
else
#endif
strcpy(dir->d.d_name, finfo.fname);
return &dir->d;
}
static int fat_closedir(struct device_d *dev, DIR *dir)
{
FF_DIR *ff_dir = dir->priv;
free(ff_dir);
free(dir);
return 0;
}
static int fat_stat(struct device_d *dev, const char *filename, struct stat *s)
{
struct fat_priv *priv = dev->priv;
FILINFO finfo;
int ret;
ret = f_stat(&priv->fat, filename, &finfo);
if (ret)
return ret;
s->st_size = finfo.fsize;
s->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
if (finfo.fattrib & AM_DIR)
s->st_mode |= S_IFDIR;
else
s->st_mode |= S_IFREG;
return 0;
}
static int fat_probe(struct device_d *dev)
{
struct fs_device_d *fsdev = dev->type_data;
struct fat_priv *priv = xzalloc(sizeof(struct fat_priv));
char *backingstore = fsdev->backingstore;
dev->priv = priv;
if (!strncmp(backingstore , "/dev/", 5))
backingstore += 5;
priv->cdev = cdev_open(backingstore, O_RDWR);
if (!priv->cdev)
return -EINVAL;
priv->fat.userdata = priv;
f_mount(&priv->fat);
return 0;
}
static void fat_remove(struct device_d *dev)
{
struct fat_priv *priv = dev->priv;
cdev_close(priv->cdev);
free(dev->priv);
}
static struct fs_driver_d fat_driver = {
.open = fat_open,
.close = fat_close,
.read = fat_read,
.lseek = fat_lseek,
.opendir = fat_opendir,
.readdir = fat_readdir,
.closedir = fat_closedir,
.stat = fat_stat,
#ifdef CONFIG_FS_FAT_WRITE
.create = fat_create,
.unlink = fat_unlink,
.mkdir = fat_mkdir,
.rmdir = fat_rmdir,
.write = fat_write,
.truncate = fat_truncate,
#endif
.flags = 0,
.drv = {
.probe = fat_probe,
.remove = fat_remove,
.name = "fat",
.type_data = &fat_driver,
}
};
static int fat_init(void)
{
return register_fs_driver(&fat_driver);
}
coredevice_initcall(fat_init);