Skip to content

Commit

Permalink
devfs: Dynamic file system for device nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
Joakim Nohlgård committed Mar 7, 2017
1 parent e61ab32 commit 88cbeab
Show file tree
Hide file tree
Showing 11 changed files with 588 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,10 @@ ifneq (,$(filter constfs,$(USEMODULE)))
USEMODULE += vfs
endif

ifneq (,$(filter devfs,$(USEMODULE)))
USEMODULE += vfs
endif

# include package dependencies
-include $(USEPKG:%=$(RIOTPKG)/%/Makefile.dep)

Expand Down
4 changes: 4 additions & 0 deletions sys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ ifneq (,$(filter constfs,$(USEMODULE)))
DIRS += fs/constfs
endif

ifneq (,$(filter devfs,$(USEMODULE)))
DIRS += fs/devfs
endif

DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))

include $(RIOTBASE)/Makefile.base
5 changes: 5 additions & 0 deletions sys/auto_init/auto_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ void auto_init(void)
DEBUG("Auto init gcoap module.\n");
gcoap_init();
#endif
#ifdef MODULE_DEVFS
DEBUG("Mounting /dev\n");
extern void auto_init_devfs(void);
auto_init_devfs();
#endif

/* initialize network devices */
#ifdef MODULE_AUTO_INIT_GNRC_NETIF
Expand Down
2 changes: 2 additions & 0 deletions sys/fs/devfs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
MODULE=devfs
include $(RIOTBASE)/Makefile.base
37 changes: 37 additions & 0 deletions sys/fs/devfs/auto_init_devfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2016 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*
*/

/**
* @ingroup auto_init_fs
* @{
*
* @file
* @brief Automatic mount of DevFS on /dev
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* @}
*/

#include "vfs.h"
#include "fs/devfs.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

static vfs_mount_t _devfs_auto_init_mount = {
.fs = &devfs_file_system,
.mount_point = "/dev",
};

void auto_init_devfs(void)
{
DEBUG("auto_init_devfs: mounting /dev\n");
vfs_mount(&_devfs_auto_init_mount);
}
242 changes: 242 additions & 0 deletions sys/fs/devfs/devfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*
* Copyright (C) 2016 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup fs_devfs
* @{
*
* @file
* @brief DevFS implementation
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* @}
*/

/* Required for strnlen in string.h, when building with -std=c99 */
#define _DEFAULT_SOURCE 1
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include "fs/devfs.h"
#include "vfs.h"
#include "mutex.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

/**
* @internal
* @brief DevFS list head
*
* DevFS operates as a singleton, the same files show up in all mounted instances.
*/
static clist_node_t _devfs_list;
/**
* @internal
* @brief mutex to protect the DevFS list from corruption
*/
static mutex_t _devfs_mutex = MUTEX_INIT;

/* No need for file system operations, no extra work to be done on
* mount/umount. unlink is not permitted, use devfs_unregister instead */

/* File operations */
/* open is overloaded to allow searching for the correct device */
static int devfs_open(vfs_file_t *filp, const char *name, int flags, mode_t mode, const char *abs_path);
/* A minimal fcntl is also provided to enable SETFL handling */
static int devfs_fcntl(vfs_file_t *filp, int cmd, int arg);

/* Directory operations */
static int devfs_opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path);
static int devfs_readdir(vfs_DIR *dirp, vfs_dirent_t *entry);
static int devfs_closedir(vfs_DIR *dirp);

static const vfs_file_ops_t devfs_file_ops = {
.open = devfs_open,
.fcntl = devfs_fcntl,
};

static const vfs_dir_ops_t devfs_dir_ops = {
.opendir = devfs_opendir,
.readdir = devfs_readdir,
.closedir = devfs_closedir,
};

const vfs_file_system_t devfs_file_system = {
.f_op = &devfs_file_ops,
.d_op = &devfs_dir_ops,
};

static int devfs_open(vfs_file_t *filp, const char *name, int flags, mode_t mode, const char *abs_path)
{
DEBUG("devfs_open: %p, \"%s\", 0x%x, 0%03lo, \"%s\"\n", (void *)filp, name, flags, (unsigned long)mode, abs_path);
/* linear search through the device list */
mutex_lock(&_devfs_mutex);
clist_node_t *it = _devfs_list.next;
if (it == NULL) {
/* list empty */
mutex_unlock(&_devfs_mutex);
return -ENOENT;
}
do {
it = it->next;
devfs_t *devp = container_of(it, devfs_t, list_entry);
if (strcmp(devp->path, name) == 0) {
mutex_unlock(&_devfs_mutex);
DEBUG("devfs_open: Found :)\n");
/* Add private data from DevFS node */
filp->private_data.ptr = devp->private_data;
/* Replace f_op with the operations provided by the device driver */
filp->f_op = devp->f_op;
/* Chain the open() method for the specific device */
if (filp->f_op->open != NULL) {
return filp->f_op->open(filp, name, flags, mode, abs_path);
}
return 0;
}
} while (it != _devfs_list.next);
mutex_unlock(&_devfs_mutex);
DEBUG("devfs_open: Not found :(\n");
return -ENOENT;
}

static int devfs_fcntl(vfs_file_t *filp, int cmd, int arg)
{
DEBUG("devfs_fcntl: %p, 0x%x, 0x%x\n", (void *)filp, cmd, arg);
switch (cmd) {
/* F_GETFL is handled directly by vfs_fcntl */
case F_SETFL:
DEBUG("devfs_fcntl: SETFL: %d\n", arg);
filp->flags = arg;
return filp->flags;
default:
return -EINVAL;
}
}

static int devfs_opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path)
{
(void) abs_path;
DEBUG("devfs_opendir: %p, \"%s\", \"%s\"\n", (void *)dirp, dirname, abs_path);
if (strncmp(dirname, "/", 2) != 0) {
/* We keep it simple and only support a flat file system, only a root directory */
return -ENOENT;
}
dirp->private_data.ptr = NULL;
return 0;
}

static int devfs_readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
{
DEBUG("devfs_readdir: %p, %p\n", (void *)dirp, (void *)entry);
mutex_lock(&_devfs_mutex);
clist_node_t *it = dirp->private_data.ptr;
if (it == _devfs_list.next) {
/* end of list was reached */
mutex_unlock(&_devfs_mutex);
return 0;
}
if (it == NULL) {
/* first readdir after opendir */
it = _devfs_list.next;
if (it == NULL) {
/* empty list */
mutex_unlock(&_devfs_mutex);
return 0;
}
}
it = it->next;
dirp->private_data.ptr = it;
mutex_unlock(&_devfs_mutex);
devfs_t *devp = container_of(it, devfs_t, list_entry);
if (devp->path == NULL) {
/* skip past the broken entry and try again */
return -EAGAIN;
}
size_t len = strnlen(devp->path, VFS_NAME_MAX + 1);
if (len > VFS_NAME_MAX) {
/* name does not fit in vfs_dirent_t buffer */
/* skip past the broken entry and try again */
return -EAGAIN;
}
/* clear the dirent */
memset(entry, 0, sizeof(*entry));
/* copy the string, including terminating null */
memcpy(&entry->d_name[0], devp->path, len + 1);
return 1;
}

static int devfs_closedir(vfs_DIR *dirp)
{
/* Just an example, it's not necessary to define closedir if there is
* nothing to clean up */
(void) dirp;
DEBUG("devfs_closedir: %p\n", (void *)dirp);
return 0;
}

int devfs_register(devfs_t *devp)
{
DEBUG("devfs_register: %p\n", (void *)devp);
if (devp == NULL) {
return -EINVAL;
}
DEBUG("devfs_register: \"%s\" -> (%p, %p)\n", devp->path, (void *)devp->f_op, devp->private_data);
if (devp->path == NULL) {
return -EINVAL;
}
if (devp->f_op == NULL) {
return -EINVAL;
}
mutex_lock(&_devfs_mutex);
clist_node_t *it = _devfs_list.next;
if (it != NULL) {
/* list not empty */
do {
it = it->next;
if (it == &devp->list_entry) {
/* Already registered */
mutex_unlock(&_devfs_mutex);
DEBUG("devfs_register: %p already registered\n", (void *)devp);
return -EEXIST;
}
devfs_t *devit = container_of(it, devfs_t, list_entry);
if (strcmp(devit->path, devp->path) == 0) {
/* Path already registered */
mutex_unlock(&_devfs_mutex);
DEBUG("devfs_register: \"%s\" occupied\n", devp->path);
return -EEXIST;
}
} while(it != _devfs_list.next);
}
/* insert last in list */
clist_rpush(&_devfs_list, &devp->list_entry);
mutex_unlock(&_devfs_mutex);
return 0;
}

int devfs_unregister(devfs_t *devp)
{
DEBUG("devfs_unregister: %p\n", (void *)devp);
if (devp == NULL) {
return -EINVAL;
}
mutex_lock(&_devfs_mutex);
/* find devp in the list and remove it */
clist_node_t *node = clist_remove(&_devfs_list, &devp->list_entry);
mutex_unlock(&_devfs_mutex);
if (node == NULL) {
/* not found */
DEBUG("devfs_unregister: ERR not registered!\n");
return -ENOENT;
}
return 0;
}
Loading

0 comments on commit 88cbeab

Please sign in to comment.