/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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, see . */
#include "file_system_struct.h"
#include "ro_fs.h"
#include "pfs.h"
#include "pfs_structs.h"
#include "device_struct.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct devfs_file_struct;
struct devfs_file_operations_struct {
int (*stat)(const struct devfs_file_struct *, struct stat_struct *);
int (*read)(const struct devfs_file_struct *, unsigned long, char *, unsigned int);
int (*write)(const struct devfs_file_struct *, unsigned long, const char *, unsigned int);
int (*ioctl)(const struct devfs_file_struct *, int request, void *fs_arg);
};
struct devfs_file_struct {
struct pfs_dir_entry_struct dir_entry; /* entree de repertoire */
union {
struct pfs_dir_struct dir; /* repertoire */
int device_id; /* numero de peripherique */
};
const struct devfs_file_operations_struct *operations;
};
static int mount_count; /* nombre de montages */
static long mount_time; /* heure du premier montage */
/* Gestion des fichiers */
static int init_device_file(struct devfs_file_struct *file, const struct device_struct *device, const struct devfs_file_operations_struct *operations)
{
int rv;
file->dir_entry.info.id = device->id;
if ((rv = pfs_set_dir_entry_info_name_dup(&file->dir_entry.info, device->name)) < 0)
return rv;
file->dir_entry.info.type = device->type;
file->device_id = device->id;
file->operations = operations;
return 0;
}
static void destroy_device_file(struct devfs_file_struct *file)
{
pfs_free_dir_entry_info_name(&file->dir_entry.info);
}
/* Operations sur les fichiers */
static int find(const struct devfs_file_struct *file, const char *fs_basename, unsigned long *id, int *type)
{
return pfs_find(&file->dir, fs_basename, id, type);
}
static int get_name(const struct devfs_file_struct *file, char name[])
{
strcpy(name, file->dir_entry.info.name);
return 0;
}
static int spec_get_device(const struct devfs_file_struct *file, struct device_struct **spec_device)
{
struct device_struct *device;
if (!(device = get_device(file->device_id)))
return -ENXIO;
*spec_device = device;
return 0;
}
static int stat_common(const struct devfs_file_struct *file, struct stat_struct *fs_buf, int device_id, long mtime)
{
struct stat_struct statbuf;
if (!verify_area(fs_buf, sizeof (struct stat_struct), PF_WRITE))
return -EFAULT;
statbuf.dev = DEV_NONE;
statbuf.id = file->dir_entry.info.id;
statbuf.type = file->dir_entry.info.type;
statbuf.rdev = device_id;
statbuf.size = 0;
statbuf.mtime = mtime;
memcpy_tofs(fs_buf, &statbuf, sizeof (struct stat_struct));
return 0;
}
static int root_stat(const struct devfs_file_struct *file, struct stat_struct *fs_buf)
{
return stat_common(file, fs_buf, DEV_NONE, mount_time);
}
static int spec_stat(const struct devfs_file_struct *file, struct stat_struct *fs_buf)
{
return stat_common(file, fs_buf, file->device_id, 0);
}
static int read_dir(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
return pfs_read_dir(&file->dir, pos, fs_buf, count);
}
static int block_read(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
const struct device_struct *device;
if (!(device = get_device(file->device_id)))
return -ENXIO;
return pfs_read_block(device, pos, fs_buf, count);
}
static int devfs_console_read(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
return console_read(fs_buf, count);
}
static int null_read(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
return 0;
}
static int zero_read(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
return pfs_read_zeros(fs_buf, count);
}
static int block_write(const struct devfs_file_struct *file, unsigned long pos, const char *fs_buf, unsigned int count)
{
const struct device_struct *device;
if (!(device = get_device(file->device_id)))
return -ENXIO;
return pfs_write_block(device, pos, fs_buf, count);
}
static int devfs_console_write(const struct devfs_file_struct *file, unsigned long pos, const char *fs_buf, unsigned int count)
{
return console_write(fs_buf, count);
}
static int null_write(const struct devfs_file_struct *file, unsigned long pos, const char *fs_buf, unsigned int count)
{
return count;
}
static int full_write(const struct devfs_file_struct *file, unsigned long pos, const char *fs_buf, unsigned int count)
{
return -ENOSPC;
}
static int inval_ioctl(const struct devfs_file_struct *file, int request, void *fs_arg)
{
return -EINVAL;
}
static int devfs_console_ioctl(const struct devfs_file_struct *file, int request, void *fs_arg)
{
return console_ioctl(request, fs_arg);
}
static const struct devfs_file_operations_struct root_operations = {
.stat = root_stat
};
static const struct devfs_file_operations_struct fd_operations = {
.stat = spec_stat,
.read = block_read,
.write = block_write,
.ioctl = inval_ioctl
};
static const struct devfs_file_operations_struct console_operations = {
.stat = spec_stat,
.read = devfs_console_read,
.write = devfs_console_write,
.ioctl = devfs_console_ioctl
};
static const struct devfs_file_operations_struct null_operations = {
.stat = spec_stat,
.read = null_read,
.write = null_write,
.ioctl = inval_ioctl
};
static const struct devfs_file_operations_struct zero_operations = {
.stat = spec_stat,
.read = zero_read,
.write = null_write,
.ioctl = inval_ioctl
};
static const struct devfs_file_operations_struct full_operations = {
.stat = spec_stat,
.read = zero_read,
.write = full_write,
.ioctl = inval_ioctl
};
/* Fichiers */
/* racine */
static struct devfs_file_struct root_file = {
.operations = &root_operations,
.dir_entry = {
.info = {
.id = 0,
.type = FT_DIR
}
}
};
/* fichiers peripheriques */
static const struct devfs_file_operations_struct *const device_operations[NR_DEVICES] = {
[DEV_FD] = &fd_operations,
[DEV_CONSOLE] = &console_operations,
[DEV_NULL] = &null_operations,
[DEV_ZERO] = &zero_operations,
[DEV_FULL] = &full_operations
};
/* index des fichiers par identifiant */
static struct devfs_file_struct *file_index[NR_DEVICES];
/* creation des fichiers selon les peripheriques existants */
static int probe_devices()
{
int id;
const struct device_struct *device;
struct devfs_file_struct *file;
int retval;
for (id = 1; id < NR_DEVICES; id++)
if ((device = get_device(id)) && device_operations[id]) {
if (!(file = kmalloc(sizeof (struct devfs_file_struct)))) {
retval = -ENOMEM;
goto error;
}
if ((retval = init_device_file(file, device, device_operations[id])) < 0)
goto error_free;
pfs_insert_dir_entry(&file->dir_entry, &root_file.dir);
file_index[id] = file;
}
else
file_index[id] = NULL;
return 0;
error_free:
kfree(file, sizeof (struct devfs_file_struct));
error:
while (id > 1) {
id--;
if (file_index[id]) {
destroy_device_file(file_index[id]);
kfree(file_index[id], sizeof (struct devfs_file_struct));
}
}
return retval;
}
static int init()
{
int rv;
pfs_init_dir(&root_file.dir);
file_index[0] = &root_file;
if ((rv = probe_devices()) < 0)
return rv;
return 0;
}
static void destroy()
{
int id;
for (id = 1; id < NR_DEVICES; id++)
if (file_index[id]) {
destroy_device_file(file_index[id]);
kfree(file_index[id], sizeof (struct devfs_file_struct));
}
}
/* resolution des fichiers */
static struct devfs_file_struct *get_file(unsigned long id)
{
return file_index[id];
}
/* Methodes du systeme de fichiers */
static int devfs_mount(const struct device_struct *device, void **data)
{
int rv;
if (!mount_count) {
mount_time = get_time();
if ((rv = init()) < 0)
return rv;
}
mount_count++;
*data = NULL;
return 0;
}
static void devfs_umount(void *data)
{
if (!--mount_count)
destroy();
}
static int devfs_find(void *data, unsigned long dir_id, const char *fs_basename, unsigned long *id, int *type)
{
return find(get_file(dir_id), fs_basename, id, type);
}
static int devfs_get_name(void *data, unsigned long id, char name[])
{
return get_name(get_file(id), name);
}
static int devfs_get_device(void *data, unsigned long spec_id, struct device_struct **spec_device)
{
return spec_get_device(get_file(spec_id), spec_device);
}
static int devfs_stat(void *data, unsigned long id, struct stat_struct *fs_buf)
{
const struct devfs_file_struct *file;
file = get_file(id);
return file->operations->stat(file, fs_buf);
}
static int devfs_read_dir(void *data, unsigned long dir_id, unsigned long pos, char *fs_buf, unsigned int count)
{
return read_dir(get_file(dir_id), pos, fs_buf, count);
}
static int devfs_read(void *data, unsigned long leaf_id, unsigned long pos, char *fs_buf, unsigned int count)
{
const struct devfs_file_struct *file;
file = get_file(leaf_id);
return file->operations->read(file, pos, fs_buf, count);
}
static int devfs_write(void *data, unsigned long leaf_id, unsigned long pos, const char *fs_buf, unsigned int count)
{
const struct devfs_file_struct *file;
file = get_file(leaf_id);
return file->operations->write(file, pos, fs_buf, count);
}
static int devfs_ioctl(void *data, unsigned long spec_id, int request, void *fs_arg)
{
const struct devfs_file_struct *file;
file = get_file(spec_id);
return file->operations->ioctl(file, request, fs_arg);
}
const struct file_system_struct devfs = {
.name = "devfs",
.type = FT_NONE,
.mount = devfs_mount,
.umount = devfs_umount,
.find = devfs_find,
.get_name = devfs_get_name,
.get_device = devfs_get_device,
.get_size = NULL,
.stat = devfs_stat,
.read_dir = devfs_read_dir,
.read = devfs_read,
.write = devfs_write,
.ioctl = devfs_ioctl,
.create = no_create,
.mkdir = no_mkdir,
.can_remove = no_can_remove,
.remove = no_remove,
.rmdir = no_rmdir,
.rename = no_rename
};