mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
devfs: Dynamic file system for device nodes
This commit is contained in:
parent
fdd129db68
commit
0371769e72
@ -601,6 +601,10 @@ ifneq (,$(filter constfs,$(USEMODULE)))
|
|||||||
USEMODULE += vfs
|
USEMODULE += vfs
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter devfs,$(USEMODULE)))
|
||||||
|
USEMODULE += vfs
|
||||||
|
endif
|
||||||
|
|
||||||
# include package dependencies
|
# include package dependencies
|
||||||
-include $(USEPKG:%=$(RIOTPKG)/%/Makefile.dep)
|
-include $(USEPKG:%=$(RIOTPKG)/%/Makefile.dep)
|
||||||
|
|
||||||
|
@ -116,6 +116,10 @@ ifneq (,$(filter constfs,$(USEMODULE)))
|
|||||||
DIRS += fs/constfs
|
DIRS += fs/constfs
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter devfs,$(USEMODULE)))
|
||||||
|
DIRS += fs/devfs
|
||||||
|
endif
|
||||||
|
|
||||||
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))
|
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))
|
||||||
|
|
||||||
include $(RIOTBASE)/Makefile.base
|
include $(RIOTBASE)/Makefile.base
|
||||||
|
@ -152,6 +152,11 @@ void auto_init(void)
|
|||||||
DEBUG("Auto init gcoap module.\n");
|
DEBUG("Auto init gcoap module.\n");
|
||||||
gcoap_init();
|
gcoap_init();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef MODULE_DEVFS
|
||||||
|
DEBUG("Mounting /dev\n");
|
||||||
|
extern void auto_init_devfs(void);
|
||||||
|
auto_init_devfs();
|
||||||
|
#endif
|
||||||
|
|
||||||
/* initialize network devices */
|
/* initialize network devices */
|
||||||
#ifdef MODULE_AUTO_INIT_GNRC_NETIF
|
#ifdef MODULE_AUTO_INIT_GNRC_NETIF
|
||||||
|
2
sys/fs/devfs/Makefile
Normal file
2
sys/fs/devfs/Makefile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
MODULE=devfs
|
||||||
|
include $(RIOTBASE)/Makefile.base
|
37
sys/fs/devfs/auto_init_devfs.c
Normal file
37
sys/fs/devfs/auto_init_devfs.c
Normal file
@ -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
sys/fs/devfs/devfs.c
Normal file
242
sys/fs/devfs/devfs.c
Normal file
@ -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;
|
||||||
|
}
|
94
sys/include/fs/devfs.h
Normal file
94
sys/include/fs/devfs.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup fs_devfs DevFS device file system
|
||||||
|
* @ingroup fs
|
||||||
|
* @brief Dynamic device file system
|
||||||
|
*
|
||||||
|
* This file system implementation allows devices to register file names for
|
||||||
|
* easier access to device drivers from shell commands etc.
|
||||||
|
*
|
||||||
|
* The idea is similar to the /dev directory on Unix.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
* @file
|
||||||
|
* @brief DevFS public API
|
||||||
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DEVFS_H_
|
||||||
|
#define DEVFS_H_
|
||||||
|
|
||||||
|
#include "clist.h"
|
||||||
|
#include "vfs.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DevFS node typedef
|
||||||
|
*/
|
||||||
|
typedef struct devfs devfs_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A device "file" consists of a file name and an opaque pointer to device driver private data
|
||||||
|
*
|
||||||
|
* The file system is implemented as a linked list.
|
||||||
|
*/
|
||||||
|
struct devfs {
|
||||||
|
clist_node_t list_entry; /**< List item entry */
|
||||||
|
const char *path; /**< File system relative path to this node */
|
||||||
|
const vfs_file_ops_t *f_op; /**< Pointer to file operations table for this device */
|
||||||
|
void *private_data; /**< Pointer to device driver specific data */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DevFS file system driver
|
||||||
|
*
|
||||||
|
* For use with vfs_mount
|
||||||
|
*/
|
||||||
|
extern const vfs_file_system_t devfs_file_system;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a node in DevFS
|
||||||
|
*
|
||||||
|
* The node will immediately become available to @c vfs_open, if DevFS is already
|
||||||
|
* mounted somewhere.
|
||||||
|
*
|
||||||
|
* If DevFS is not mounted, the node will be registered and will become
|
||||||
|
* available to @c vfs_open when DevFS is mounted.
|
||||||
|
*
|
||||||
|
* @param[in] node DevFS node to register
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
* @return <0 on error
|
||||||
|
*/
|
||||||
|
int devfs_register(devfs_t *node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a registration from DevFS
|
||||||
|
*
|
||||||
|
* The node will no longer be available to @c vfs_open, but any already opened FDs
|
||||||
|
* will remain open.
|
||||||
|
*
|
||||||
|
* @param[in] node DevFS node to unregister
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
* @return <0 on error
|
||||||
|
*/
|
||||||
|
int devfs_unregister(devfs_t *node);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
1
tests/unittests/tests-devfs/Makefile
Normal file
1
tests/unittests/tests-devfs/Makefile
Normal file
@ -0,0 +1 @@
|
|||||||
|
include $(RIOTBASE)/Makefile.base
|
2
tests/unittests/tests-devfs/Makefile.include
Normal file
2
tests/unittests/tests-devfs/Makefile.include
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
USEMODULE += vfs
|
||||||
|
USEMODULE += devfs
|
160
tests/unittests/tests-devfs/tests-devfs.c
Normal file
160
tests/unittests/tests-devfs/tests-devfs.c
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Unittests for DevFS
|
||||||
|
*
|
||||||
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||||
|
*/
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "fs/devfs.h"
|
||||||
|
|
||||||
|
#include "embUnit/embUnit.h"
|
||||||
|
|
||||||
|
#include "tests-devfs.h"
|
||||||
|
|
||||||
|
static int _mock_open(vfs_file_t *filp, const char *name, int flags, mode_t mode, const char *abs_path);
|
||||||
|
static ssize_t _mock_read(vfs_file_t *filp, void *dest, size_t nbytes);
|
||||||
|
static ssize_t _mock_write(vfs_file_t *filp, const void *src, size_t nbytes);
|
||||||
|
|
||||||
|
static volatile int _mock_open_calls = 0;
|
||||||
|
static volatile int _mock_read_calls = 0;
|
||||||
|
static volatile int _mock_write_calls = 0;
|
||||||
|
|
||||||
|
static int _mock_private_data;
|
||||||
|
|
||||||
|
static const vfs_file_ops_t _mock_devfs_ops = {
|
||||||
|
.open = _mock_open,
|
||||||
|
.read = _mock_read,
|
||||||
|
.write = _mock_write,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int _mock_private_data_tag = 4321;
|
||||||
|
|
||||||
|
static devfs_t _mock_devfs_node = {
|
||||||
|
.path = "/mock0",
|
||||||
|
.f_op = &_mock_devfs_ops,
|
||||||
|
.private_data = &_mock_private_data_tag,
|
||||||
|
};
|
||||||
|
|
||||||
|
static vfs_mount_t _test_devfs_mount = {
|
||||||
|
.fs = &devfs_file_system,
|
||||||
|
.mount_point = "/test",
|
||||||
|
.private_data = &_mock_private_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int _mock_open(vfs_file_t *filp, const char *name, int flags, mode_t mode, const char *abs_path)
|
||||||
|
{
|
||||||
|
(void) name;
|
||||||
|
(void) flags;
|
||||||
|
(void) mode;
|
||||||
|
(void) abs_path;
|
||||||
|
if (filp->private_data.ptr != &_mock_private_data_tag) {
|
||||||
|
return -4321;
|
||||||
|
}
|
||||||
|
int *np = filp->mp->private_data;
|
||||||
|
++(*np);
|
||||||
|
++_mock_open_calls;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t _mock_read(vfs_file_t *filp, void *dest, size_t nbytes)
|
||||||
|
{
|
||||||
|
(void) dest;
|
||||||
|
(void) nbytes;
|
||||||
|
if (filp->private_data.ptr != &_mock_private_data_tag) {
|
||||||
|
return -4321;
|
||||||
|
}
|
||||||
|
int *np = filp->mp->private_data;
|
||||||
|
++(*np);
|
||||||
|
++_mock_read_calls;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t _mock_write(vfs_file_t *filp, const void *src, size_t nbytes)
|
||||||
|
{
|
||||||
|
(void) src;
|
||||||
|
(void) nbytes;
|
||||||
|
if (filp->private_data.ptr != &_mock_private_data_tag) {
|
||||||
|
return -4321;
|
||||||
|
}
|
||||||
|
int *np = filp->mp->private_data;
|
||||||
|
++(*np);
|
||||||
|
++_mock_write_calls;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_devfs_register(void)
|
||||||
|
{
|
||||||
|
int res = devfs_register(NULL);
|
||||||
|
TEST_ASSERT(res < 0);
|
||||||
|
|
||||||
|
res = devfs_register(&_mock_devfs_node);
|
||||||
|
TEST_ASSERT(res == 0);
|
||||||
|
|
||||||
|
res = devfs_register(&_mock_devfs_node);
|
||||||
|
TEST_ASSERT(res < 0);
|
||||||
|
|
||||||
|
res = devfs_unregister(&_mock_devfs_node);
|
||||||
|
TEST_ASSERT(res == 0);
|
||||||
|
|
||||||
|
res = devfs_unregister(&_mock_devfs_node);
|
||||||
|
TEST_ASSERT(res < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_devfs_mount_open(void)
|
||||||
|
{
|
||||||
|
_mock_private_data = 12345;
|
||||||
|
int res;
|
||||||
|
res = vfs_mount(&_test_devfs_mount);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, res);
|
||||||
|
TEST_ASSERT_EQUAL_INT(_mock_private_data, 12345);
|
||||||
|
|
||||||
|
res = devfs_register(&_mock_devfs_node);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, res);
|
||||||
|
|
||||||
|
int count = _mock_open_calls;
|
||||||
|
int fd = vfs_open("/test/mock0", O_RDWR, 0);
|
||||||
|
TEST_ASSERT(fd >= 0);
|
||||||
|
TEST_ASSERT_EQUAL_INT(count + 1, _mock_open_calls);
|
||||||
|
TEST_ASSERT_EQUAL_INT(_mock_private_data, 12346);
|
||||||
|
|
||||||
|
res = vfs_close(fd);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, res);
|
||||||
|
|
||||||
|
res = devfs_unregister(&_mock_devfs_node);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, res);
|
||||||
|
|
||||||
|
res = vfs_umount(&_test_devfs_mount);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test *tests_devfs_tests(void)
|
||||||
|
{
|
||||||
|
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||||
|
new_TestFixture(test_devfs_register),
|
||||||
|
new_TestFixture(test_devfs_mount_open),
|
||||||
|
};
|
||||||
|
|
||||||
|
EMB_UNIT_TESTCALLER(devfs_tests, NULL, NULL, fixtures);
|
||||||
|
|
||||||
|
return (Test *)&devfs_tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tests_devfs(void)
|
||||||
|
{
|
||||||
|
TESTS_RUN(tests_devfs_tests());
|
||||||
|
}
|
||||||
|
/** @} */
|
37
tests/unittests/tests-devfs/tests-devfs.h
Normal file
37
tests/unittests/tests-devfs/tests-devfs.h
Normal file
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup unittests
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Unittests for DevFS
|
||||||
|
*
|
||||||
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||||
|
*/
|
||||||
|
#ifndef TESTS_DEVFS_H
|
||||||
|
#define TESTS_DEVFS_H
|
||||||
|
|
||||||
|
#include "embUnit.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The entry point of this test suite.
|
||||||
|
*/
|
||||||
|
void tests_devfs(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* TESTS_DEVFS_H */
|
||||||
|
/** @} */
|
Loading…
Reference in New Issue
Block a user